123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- #include "module.h"
- #include "lauxlib.h"
- #include "lextra.h"
- #include "lmem.h"
- #include "driver/i2c.h"
- #include "i2c_common.h"
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "freertos/queue.h"
- #include "esp_task.h"
- #include "task/task.h"
- #include <string.h>
- #define DEFAULT_BUF_LEN 128
- #define I2C_SLAVE_DEFAULT_RXBUF_LEN DEFAULT_BUF_LEN
- #define I2C_SLAVE_DEFAULT_TXBUF_LEN DEFAULT_BUF_LEN
- // user data descriptor for a port
- typedef struct {
- unsigned port;
- size_t rxbuf_len;
- TaskHandle_t xReceiveTaskHandle;
- int receivedcb_ref;
- } i2c_hw_slave_ud_type;
- // read data descriptor
- // it's used as variable length data block, filled by the i2c driver during read
- typedef struct {
- size_t len;
- uint8_t data[1];
- } i2c_read_type;
- // job descriptor
- // contains all information for the transfer task and subsequent result task
- // to process the transfer
- typedef struct {
- unsigned port;
- esp_err_t err;
- int receivedcb_ref;
- i2c_read_type *read_data;
- } i2c_job_desc_type;
- // the global variables for storing userdata for each I2C port
- static i2c_hw_slave_ud_type i2c_hw_slave_ud[I2C_NUM_MAX];
- // Receive task handle and job queue
- static task_handle_t i2c_receive_task_id;
- // Receive Task, FreeRTOS layer
- // This is a fully-fledged FreeRTOS task which runs concurrently and waits
- // for data from the I2C master using i2c_slave_read_buffer which blocks this task.
- // Received data is posted as a job to the NodeMCU layer task.
- //
- static void vReceiveTask( void *pvParameters )
- {
- i2c_hw_slave_ud_type *ud = (i2c_hw_slave_ud_type *)pvParameters;
- i2c_job_desc_type *job;
- i2c_read_type *read_data;
- for (;;) {
- job = (i2c_job_desc_type *)malloc( sizeof( i2c_job_desc_type ) );
- read_data = (i2c_read_type *)malloc( sizeof( i2c_read_type ) + ud->rxbuf_len-1 );
-
- if (!job || !read_data) {
- // shut down this task in case of memory shortage
- vTaskSuspend( NULL );
- }
- job->read_data = read_data;
- int size = i2c_slave_read_buffer( ud->port, job->read_data->data, ud->rxbuf_len, portMAX_DELAY );
- if (size >= 0) {
- job->read_data->len = size;
- job->err = 0;
- } else {
- job->read_data->len = 0;
- job->err = size;
- }
- job->port = ud->port;
- job->receivedcb_ref = ud->receivedcb_ref;
- task_post_medium( i2c_receive_task_id, (task_param_t)job );
- }
- }
- // Receive Task, NodeMCU layer
- // Is posted by the FreeRTOS layer and triggers the Lua callback with
- // read result data.
- //
- static void i2c_receive_task( task_param_t param, task_prio_t prio )
- {
- i2c_job_desc_type *job = (i2c_job_desc_type *)param;
- lua_State *L = lua_getstate();
- if (job->receivedcb_ref != LUA_NOREF) {
- lua_rawgeti( L, LUA_REGISTRYINDEX, job->receivedcb_ref );
- lua_pushinteger( L, job->err );
- if (job->read_data->len) {
- // all fine, deliver read data
- lua_pushlstring( L, (char *)job->read_data->data, job->read_data->len );
- } else {
- lua_pushnil( L );
- }
- lua_call(L, 2, 0);
- }
- // free all memory
- free( job->read_data );
- free( job );
- }
- static int i2c_lua_checkerr( lua_State *L, esp_err_t err ) {
- const char *msg;
- switch (err) {
- case ESP_OK: return 0;
- case ESP_FAIL: msg = "command failed"; break;
- case ESP_ERR_INVALID_ARG: msg = "parameter error"; break;
- case ESP_ERR_INVALID_STATE: msg = "driver state error"; break;
- case ESP_ERR_TIMEOUT: msg = "timeout"; break;
- default: msg = "unknown error"; break;
- }
- return luaL_error( L, msg );
- }
- #define get_udata(L) \
- unsigned port = luaL_checkint( L, 1 ) - I2C_ID_HW0; \
- luaL_argcheck( L, port < I2C_NUM_MAX, 1, "invalid hardware id" ); \
- i2c_hw_slave_ud_type *ud = &(i2c_hw_slave_ud[port]);
- // Set up FreeRTOS task and queue
- // Prepares the gory tasking stuff.
- //
- static int setup_rtos_task( i2c_hw_slave_ud_type *ud, unsigned port )
- {
- char pcName[configMAX_TASK_NAME_LEN+1];
- snprintf( pcName, configMAX_TASK_NAME_LEN+1, "I2C_Slave_%d", port );
- pcName[configMAX_TASK_NAME_LEN] = '\0';
- // create task with higher priority
- BaseType_t xReturned = xTaskCreate( vReceiveTask,
- pcName,
- 1024,
- (void *)ud,
- ESP_TASK_MAIN_PRIO + 1,
- &(ud->xReceiveTaskHandle) );
- if (xReturned != pdPASS) {
- return 0;
- }
- return 1;
- }
- // Set up the HW as master interface
- // Cares for I2C driver creation and initialization.
- //
- static int li2c_slave_setup( lua_State *L )
- {
- get_udata(L);
- ud->port = port;
- int stack = 1;
- luaL_checkanytable( L, ++stack );
- lua_settop( L, stack );
- i2c_config_t cfg;
- memset( &cfg, 0, sizeof( cfg ) );
- cfg.mode = I2C_MODE_SLAVE;
- lua_getfield( L, stack, "sda" );
- int sda = luaL_optint( L , -1, -1 );
- luaL_argcheck( L, GPIO_IS_VALID_OUTPUT_GPIO(sda), stack, "invalid sda pin" );
- cfg.sda_io_num = (gpio_num_t)sda;
- cfg.sda_pullup_en = GPIO_PULLUP_ENABLE;
- lua_getfield( L, stack, "scl" );
- int scl = luaL_optint( L , -1, -1 );
- luaL_argcheck( L, GPIO_IS_VALID_OUTPUT_GPIO(scl), stack, "invalid scl pin" );
- cfg.scl_io_num = (gpio_num_t)scl;
- cfg.scl_pullup_en = GPIO_PULLUP_ENABLE;
- lua_getfield( L, stack, "10bit" );
- bool en_10bit = luaL_optbool( L , -1, false );
- cfg.slave.addr_10bit_en = en_10bit ? 1 : 0;
- lua_getfield( L, stack, "addr" );
- int slave_addr = luaL_optint( L , -1, -1 );
- if (en_10bit)
- luaL_argcheck( L, slave_addr >= 0 && slave_addr < 1<<10, stack, "invalid slave address" );
- else
- luaL_argcheck( L, slave_addr >= 0 && slave_addr < 1<<7, stack, "invalid slave address" );
- cfg.slave.slave_addr = slave_addr;
- lua_getfield( L, stack, "rxbuf_len" );
- int rxbuf_len = luaL_optint( L , -1, I2C_SLAVE_DEFAULT_RXBUF_LEN );
- luaL_argcheck( L, rxbuf_len >= 0, stack, "invalid rxbuf_len" );
- ud->rxbuf_len = rxbuf_len;
- lua_getfield( L, stack, "txbuf_len" );
- int txbuf_len = luaL_optint( L , -1, I2C_SLAVE_DEFAULT_TXBUF_LEN );
- luaL_argcheck( L, rxbuf_len >= 0, stack, "invalid rxbuf_len" );
- i2c_lua_checkerr( L, i2c_param_config( port, &cfg ) );
- i2c_lua_checkerr( L, i2c_driver_install( port, cfg.mode, rxbuf_len, txbuf_len, 0 ));
- ud->receivedcb_ref = LUA_NOREF;
- if (!setup_rtos_task( ud, port )) {
- i2c_driver_delete( port );
- luaL_error( L, "rtos task creation failed" );
- }
- return 0;
- }
- // Write to slave send buffer
- //
- static int li2c_slave_send( lua_State *L )
- {
- get_udata(L);
- ud = ud;
- const char *pdata;
- size_t datalen, i;
- int numdata;
- uint8_t byte;
- uint32_t wrote = 0;
- unsigned argn;
- if( lua_gettop( L ) < 2 )
- return luaL_error( L, "wrong arg type" );
- for( argn = 2; argn <= lua_gettop( L ); argn ++ )
- {
- // lua_isnumber() would silently convert a string of digits to an integer
- // whereas here strings are handled separately.
- if( lua_type( L, argn ) == LUA_TNUMBER )
- {
- numdata = ( int )luaL_checkinteger( L, argn );
- if( numdata < 0 || numdata > 255 )
- return luaL_error( L, "wrong arg range" );
- byte = (uint8_t)numdata;
- if( i2c_slave_write_buffer( port, &byte, 1, portMAX_DELAY ) < 0 )
- break;
- wrote ++;
- }
- else if( lua_istable( L, argn ) )
- {
- datalen = lua_objlen( L, argn );
- for( i = 0; i < datalen; i ++ )
- {
- lua_rawgeti( L, argn, i + 1 );
- numdata = ( int )luaL_checkinteger( L, -1 );
- lua_pop( L, 1 );
- if( numdata < 0 || numdata > 255 )
- return luaL_error( L, "wrong arg range" );
- byte = (uint8_t)numdata;
- if( i2c_slave_write_buffer( port, &byte, 1, portMAX_DELAY ) < 0 )
- break;
- }
- wrote += i;
- if( i < datalen )
- break;
- }
- else
- {
- pdata = luaL_checklstring( L, argn, &datalen );
- if( i2c_slave_write_buffer( port, (uint8_t *)pdata, datalen, portMAX_DELAY ) < 0 )
- break;
- wrote += datalen;
- }
- }
- lua_pushinteger( L, wrote );
- return 1;
- }
- // Register or unregister an event callback handler
- //
- static int li2c_slave_on( lua_State *L )
- {
- enum events{
- ON_RECEIVE = 0
- };
- const char *const eventnames[] = {"receive", NULL};
- get_udata(L);
- int stack = 1;
- int event = luaL_checkoption(L, ++stack, NULL, eventnames);
- switch (event) {
- case ON_RECEIVE:
- luaL_unref( L, LUA_REGISTRYINDEX, ud->receivedcb_ref );
- ++stack;
- if (lua_isfunction( L, stack ) || lua_islightfunction( L, stack )) {
- lua_pushvalue( L, stack ); // copy argument (func) to the top of stack
- ud->receivedcb_ref = luaL_ref( L, LUA_REGISTRYINDEX );
- }
- break;
- default:
- break;
- }
- return 0;
- }
- const LUA_REG_TYPE li2c_slave_map[] = {
- { LSTRKEY( "on" ), LFUNCVAL( li2c_slave_on ) },
- { LSTRKEY( "setup" ), LFUNCVAL( li2c_slave_setup ) },
- { LSTRKEY( "send" ), LFUNCVAL( li2c_slave_send ) },
- { LNILKEY, LNILVAL }
- };
- void li2c_hw_slave_init( lua_State *L )
- {
- // prepare task id
- i2c_receive_task_id = task_get_id( i2c_receive_task );
- }
|