Preliminary gpio module.
The gpio ISR needs to go into the platform layer most likely, but that's for later.
This commit is contained in:
parent
c4911ca391
commit
c505cc0270
|
@ -73,6 +73,12 @@ config LUA_MODULE_FILE
|
|||
help
|
||||
Includes the file module (recommended).
|
||||
|
||||
config LUA_MODULE_GPIO
|
||||
bool "GPIO module"
|
||||
default "y"
|
||||
help
|
||||
Includes the GPIO module (recommended).
|
||||
|
||||
config LUA_MODULE_NET
|
||||
bool "Net module"
|
||||
default "y"
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
// vim: ts=2 sw=2 et ai
|
||||
/*
|
||||
* Copyright (c) 2016 Johny Mattsson
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* - Neither the name of the copyright holders nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "module.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lmem.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "task/task.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define PULL_UP 1
|
||||
#define PULL_DOWN 2
|
||||
|
||||
// TODO: change this to dynamically allocated once that framework is available!
|
||||
#define MAP_GPIO_PRO_INT_NO 12
|
||||
|
||||
static int *gpio_cb_refs = NULL; // Lazy init
|
||||
static task_handle_t cb_task;
|
||||
|
||||
static int check_err (lua_State *L, esp_err_t err)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case ESP_ERR_INVALID_ARG: luaL_error (L, "invalid argument");
|
||||
case ESP_OK: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Lua: gpio.config({
|
||||
* gpio= x || { x, y ... }
|
||||
* dir= IN || OUT || IN_OUT
|
||||
* opendrain= 0 || 1 (output only)
|
||||
* pull= UP || DOWN || UP_DOWN || FLOATING
|
||||
* }, ...
|
||||
*/
|
||||
static int lgpio_config (lua_State *L)
|
||||
{
|
||||
const int n = lua_gettop (L);
|
||||
luaL_checkstack (L, 5, "out of mem");
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
luaL_checkanytable (L, i);
|
||||
gpio_config_t cfg;
|
||||
cfg.intr_type = GPIO_INTR_DISABLE;
|
||||
|
||||
lua_getfield (L, i, "dir");
|
||||
cfg.mode = luaL_checkinteger (L, -1);
|
||||
lua_getfield(L, i, "opendrain");
|
||||
if (luaL_optint (L, -1, 0) && (cfg.mode & GPIO_MODE_DEF_OUTPUT))
|
||||
cfg.mode |= GPIO_MODE_DEF_OD;
|
||||
|
||||
lua_getfield(L, i, "pull");
|
||||
int pulls = luaL_optint (L, -1, 0);
|
||||
cfg.pull_down_en =
|
||||
(pulls & PULL_DOWN) ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE;
|
||||
cfg.pull_up_en =
|
||||
(pulls & PULL_UP) ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE;
|
||||
|
||||
lua_pop (L, 3);
|
||||
|
||||
cfg.pin_bit_mask = 0;
|
||||
lua_getfield(L, i, "gpio");
|
||||
int type = lua_type (L, -1);
|
||||
if (type == LUA_TNUMBER)
|
||||
cfg.pin_bit_mask = 1 << lua_tointeger (L, -1);
|
||||
else if (type == LUA_TTABLE)
|
||||
{
|
||||
lua_pushnil (L);
|
||||
while (lua_next (L, -2) != 0)
|
||||
{
|
||||
lua_pushvalue (L, -1); // copy, so lua_tonumber() doesn't break iter
|
||||
int pin = lua_tointeger (L, -1);
|
||||
lua_pop (L, 2); // leave key
|
||||
cfg.pin_bit_mask |= 1 << pin;
|
||||
}
|
||||
}
|
||||
else
|
||||
return luaL_error (L, "missing/bad 'gpio' field");
|
||||
lua_settop (L, n); // discard remaining temporaries
|
||||
check_err (L, gpio_config (&cfg));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Lua: gpio.read(gpio) => 0 || 1
|
||||
static int lgpio_read (lua_State *L)
|
||||
{
|
||||
int gpio = luaL_checkint (L, 1);
|
||||
if (!GPIO_IS_VALID_GPIO(gpio))
|
||||
return check_err (L, ESP_ERR_INVALID_ARG);
|
||||
lua_pushinteger (L, gpio_get_level (gpio));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Lua: gpio.trig(gpio, UP/DOWN/etc, function(gpio, level) ... end || nil)
|
||||
// up, down, both, low, high
|
||||
static int lgpio_trig (lua_State *L)
|
||||
{
|
||||
int gpio = luaL_checkint (L, 1);
|
||||
int intr_type = luaL_checkint (L, 2);
|
||||
if (!lua_isnoneornil (L, 3))
|
||||
luaL_checkanyfunction (L, 3);
|
||||
|
||||
lua_settop (L, 3);
|
||||
|
||||
if (gpio < 0 || gpio >= GPIO_PIN_COUNT)
|
||||
return luaL_error (L, "invalid gpio");
|
||||
|
||||
if (!gpio_cb_refs)
|
||||
{
|
||||
gpio_cb_refs = luaM_newvector (L, GPIO_PIN_COUNT, int);
|
||||
for (unsigned i = 0; i < GPIO_PIN_COUNT; ++i)
|
||||
gpio_cb_refs[i] = LUA_NOREF;
|
||||
}
|
||||
|
||||
// Set/update interrupt callback
|
||||
if (!lua_isnoneornil (L, 3))
|
||||
{
|
||||
luaL_unref (L, LUA_REGISTRYINDEX, gpio_cb_refs[gpio]);
|
||||
gpio_cb_refs[gpio] = luaL_ref (L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
// Disable interrupt while reconfiguring
|
||||
check_err (L, gpio_intr_disable (gpio));
|
||||
|
||||
if (gpio_cb_refs[gpio] == LUA_NOREF)
|
||||
check_err (L, gpio_set_intr_type (gpio, GPIO_INTR_DISABLE));
|
||||
else
|
||||
{
|
||||
check_err (L, gpio_set_intr_type (gpio, intr_type));
|
||||
check_err (L, gpio_intr_enable (gpio));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Lua: gpio.wakeup(gpio, INTR_NONE | INTR_LOW | INTR_HIGH)
|
||||
static int lgpio_wakeup (lua_State *L)
|
||||
{
|
||||
int gpio = luaL_checkint (L, 1);
|
||||
int intr_type = luaL_optint (L, 2, GPIO_INTR_DISABLE);
|
||||
if (intr_type == GPIO_INTR_DISABLE)
|
||||
check_err (L, gpio_wakeup_disable (gpio));
|
||||
else
|
||||
check_err (L, gpio_wakeup_enable (gpio, intr_type));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Lua: gpio.write(gpio, 0 || 1)
|
||||
static int lgpio_write (lua_State *L)
|
||||
{
|
||||
int gpio = luaL_checkint (L, 1);
|
||||
int level = luaL_checkint (L, 2);
|
||||
check_err (L, gpio_set_level (gpio, level));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// TODO: move this to the platform layer so it can be shared
|
||||
// TODO: can/should we attempt to guard against task q overflow?
|
||||
_Static_assert(GPIO_PIN_COUNT<256, "task post encoding assumes < 256 gpios");
|
||||
static void IRAM_ATTR nodemcu_gpio_isr (void *p)
|
||||
{
|
||||
uint32_t intrs = READ_PERI_REG(GPIO_STATUS_REG);
|
||||
uint32_t intrs_rtc = READ_PERI_REG(GPIO_STATUS1_REG);
|
||||
|
||||
#define handle_gpio_intr(gpio_num) do { \
|
||||
gpio_intr_disable (gpio_num); \
|
||||
task_post_low (cb_task, (gpio_num) | (gpio_get_level (gpio_num) << 8)); \
|
||||
} while (0)
|
||||
|
||||
// Regular gpios
|
||||
for (uint32_t gpio = 0; intrs && gpio < 32; ++gpio)
|
||||
{
|
||||
if (intrs & BIT(gpio))
|
||||
handle_gpio_intr (gpio);
|
||||
}
|
||||
// RTC gpios
|
||||
for (uint32_t gpio = 0; intrs_rtc && gpio < (GPIO_PIN_COUNT - 32); ++gpio)
|
||||
{
|
||||
if (intrs_rtc & BIT(gpio))
|
||||
handle_gpio_intr (gpio + 32);
|
||||
}
|
||||
|
||||
SET_PERI_REG_MASK(GPIO_STATUS_W1TC_REG, intrs);
|
||||
SET_PERI_REG_MASK(GPIO_STATUS1_W1TC_REG, intrs_rtc);
|
||||
}
|
||||
|
||||
|
||||
static void nodemcu_gpio_callback_task (task_param_t param, task_prio_t prio)
|
||||
{
|
||||
(void)prio;
|
||||
uint32_t gpio = (uint32_t)param & 0xffu;
|
||||
int level = ((int)param) & 0x100u;
|
||||
|
||||
lua_State *L = lua_getstate ();
|
||||
if (gpio_cb_refs[gpio] != LUA_NOREF)
|
||||
{
|
||||
lua_rawgeti (L, LUA_REGISTRYINDEX, gpio_cb_refs[gpio]);
|
||||
lua_pushinteger (L, gpio);
|
||||
lua_pushinteger (L, level);
|
||||
lua_call (L, 2, 0);
|
||||
gpio_intr_enable (gpio);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int nodemcu_gpio_init (lua_State *L)
|
||||
{
|
||||
cb_task = task_get_id (nodemcu_gpio_callback_task);
|
||||
check_err (L,
|
||||
gpio_isr_register (MAP_GPIO_PRO_INT_NO, nodemcu_gpio_isr, NULL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const LUA_REG_TYPE lgpio_map[] =
|
||||
{
|
||||
{ LSTRKEY( "config" ), LFUNCVAL( lgpio_config ) },
|
||||
{ LSTRKEY( "read" ), LFUNCVAL( lgpio_read ) },
|
||||
{ LSTRKEY( "trig" ), LFUNCVAL( lgpio_trig ) },
|
||||
{ LSTRKEY( "wakeup" ), LFUNCVAL( lgpio_wakeup ) },
|
||||
{ LSTRKEY( "write" ), LFUNCVAL( lgpio_write ) },
|
||||
|
||||
|
||||
{ LSTRKEY( "OUT" ), LNUMVAL( GPIO_MODE_OUTPUT ) },
|
||||
{ LSTRKEY( "IN" ), LNUMVAL( GPIO_MODE_INPUT ) },
|
||||
{ LSTRKEY( "IN_OUT" ), LNUMVAL( GPIO_MODE_INPUT_OUTPUT ) },
|
||||
|
||||
{ LSTRKEY( "FLOATING"), LNUMVAL( 0 ) },
|
||||
{ LSTRKEY( "PULL_UP" ), LNUMVAL( PULL_UP ) },
|
||||
{ LSTRKEY( "PULL_DOWN" ), LNUMVAL( PULL_DOWN ) },
|
||||
{ LSTRKEY( "PULL_UP_DOWN"), LNUMVAL( PULL_UP | PULL_DOWN ) },
|
||||
|
||||
{ LSTRKEY( "INTR_NONE" ), LNUMVAL( GPIO_INTR_DISABLE ) },
|
||||
{ LSTRKEY( "INTR_UP" ), LNUMVAL( GPIO_INTR_POSEDGE ) },
|
||||
{ LSTRKEY( "INTR_DOWN" ), LNUMVAL( GPIO_INTR_NEGEDGE ) },
|
||||
{ LSTRKEY( "INTR_UP_DOWN" ), LNUMVAL( GPIO_INTR_ANYEDGE ) },
|
||||
{ LSTRKEY( "INTR_LOW" ), LNUMVAL( GPIO_INTR_LOW_LEVEL ) },
|
||||
{ LSTRKEY( "INTR_HIGH" ), LNUMVAL( GPIO_INTR_HIGH_LEVEL ) },
|
||||
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
NODEMCU_MODULE(GPIO, "gpio", lgpio_map, nodemcu_gpio_init);
|
Loading…
Reference in New Issue