2017-04-28 22:27:57 +02:00
|
|
|
// Module for interfacing with i2s hardware
|
|
|
|
|
|
|
|
#include "module.h"
|
|
|
|
#include "lauxlib.h"
|
|
|
|
#include "lmem.h"
|
|
|
|
#include "platform.h"
|
|
|
|
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
|
|
#include "freertos/task.h"
|
|
|
|
#include "freertos/queue.h"
|
|
|
|
|
|
|
|
#include "esp_task.h"
|
|
|
|
|
|
|
|
#include "task/task.h"
|
|
|
|
|
|
|
|
#include "driver/i2s.h"
|
|
|
|
|
|
|
|
#define MAX_I2C_NUM 2
|
|
|
|
#define I2S_CHECK_ID(id) if(id >= MAX_I2C_NUM) luaL_error( L, "i2s not exists" )
|
|
|
|
|
|
|
|
typedef struct {
|
2017-04-29 17:06:58 +02:00
|
|
|
xTaskHandle taskHandle;
|
|
|
|
QueueHandle_t *event_queue;
|
|
|
|
int event_cb;
|
2017-04-28 22:27:57 +02:00
|
|
|
} i2s_status_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
i2s_event_t event;
|
|
|
|
i2s_status_t* status;
|
|
|
|
} i2s_event_post_type;
|
|
|
|
|
|
|
|
static task_handle_t i2s_event_task_id;
|
|
|
|
|
|
|
|
static i2s_status_t i2s_status[MAX_I2C_NUM];
|
|
|
|
|
|
|
|
// LUA
|
|
|
|
static void i2s_event_task( task_param_t param, task_prio_t prio ) {
|
|
|
|
i2s_event_post_type *post = (i2s_event_post_type *)param;
|
|
|
|
|
|
|
|
lua_State *L = lua_getstate();
|
|
|
|
int event_cb = post->status->event_cb;
|
|
|
|
if(event_cb == LUA_NOREF) {
|
2017-04-29 17:06:58 +02:00
|
|
|
free( post );
|
|
|
|
return;
|
2017-04-28 22:27:57 +02:00
|
|
|
}
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, event_cb);
|
|
|
|
if(post->event.type == I2S_EVENT_TX_DONE)
|
2017-04-29 17:06:58 +02:00
|
|
|
lua_pushstring(L, "sent");
|
2017-04-28 22:27:57 +02:00
|
|
|
else if(post->event.type == I2S_EVENT_RX_DONE)
|
2017-04-29 17:06:58 +02:00
|
|
|
lua_pushstring(L, "data");
|
2017-04-28 22:27:57 +02:00
|
|
|
else
|
2017-04-29 17:06:58 +02:00
|
|
|
lua_pushstring(L, "error");
|
2017-04-28 22:27:57 +02:00
|
|
|
lua_pushinteger(L, post->event.size);
|
|
|
|
free( post );
|
|
|
|
lua_call(L, 2, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// RTOS
|
|
|
|
static void task_I2S( void *pvParameters ){
|
|
|
|
i2s_status_t* is = (i2s_status_t*)pvParameters;
|
|
|
|
|
2017-05-06 22:10:17 +02:00
|
|
|
i2s_event_t event;
|
2017-04-28 22:27:57 +02:00
|
|
|
|
|
|
|
for (;;){
|
2017-05-06 22:10:17 +02:00
|
|
|
if( xQueueReceive( is->event_queue, &event, 3 * portTICK_PERIOD_MS ) == pdTRUE ){
|
|
|
|
i2s_event_post_type *post = (i2s_event_post_type *)malloc( sizeof( i2s_event_post_type ) );
|
2017-04-29 17:06:58 +02:00
|
|
|
post->status = is;
|
2017-05-06 22:10:17 +02:00
|
|
|
memcpy(&(post->event), &event, sizeof(i2s_event_t));
|
|
|
|
task_post_high( i2s_event_task_id, (task_param_t)post );
|
2017-04-28 22:27:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lua: start( i2s_id, {}, callback )
|
|
|
|
static int node_i2s_start( lua_State *L )
|
|
|
|
{
|
|
|
|
int i2s_id = luaL_checkinteger( L, 1 );
|
|
|
|
I2S_CHECK_ID( i2s_id );
|
|
|
|
|
|
|
|
luaL_checkanytable (L, 2);
|
|
|
|
|
|
|
|
i2s_config_t i2s_config;
|
|
|
|
i2s_pin_config_t pin_config;
|
|
|
|
|
|
|
|
lua_getfield (L, 2, "mode");
|
|
|
|
i2s_config.mode = luaL_optint(L, -1, I2S_MODE_MASTER | I2S_MODE_TX);
|
|
|
|
lua_getfield (L, 2, "rate");
|
|
|
|
i2s_config.sample_rate = luaL_optint(L, -1, 44100);
|
|
|
|
lua_getfield (L, 2, "bits");
|
|
|
|
i2s_config.bits_per_sample = 16;
|
|
|
|
lua_getfield (L, 2, "channel");
|
|
|
|
i2s_config.channel_format = luaL_optint(L, -1, I2S_CHANNEL_FMT_RIGHT_LEFT);
|
|
|
|
lua_getfield (L, 2, "format");
|
|
|
|
i2s_config.communication_format = luaL_optint(L, -1, I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB);
|
|
|
|
lua_getfield (L, 2, "buffer_count");
|
|
|
|
i2s_config.dma_buf_count = luaL_optint(L, -1, 2);
|
|
|
|
lua_getfield (L, 2, "buffer_len");
|
|
|
|
i2s_config.dma_buf_len = luaL_optint(L, -1, i2s_config.sample_rate / 100);
|
|
|
|
i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2;
|
|
|
|
|
|
|
|
lua_getfield (L, 2, "bck_pin");
|
|
|
|
pin_config.bck_io_num = luaL_optint(L, -1, I2S_PIN_NO_CHANGE);
|
|
|
|
lua_getfield (L, 2, "ws_pin");
|
|
|
|
pin_config.ws_io_num = luaL_optint(L, -1, I2S_PIN_NO_CHANGE);
|
|
|
|
lua_getfield (L, 2, "data_out_pin");
|
|
|
|
pin_config.data_out_num = luaL_optint(L, -1, I2S_PIN_NO_CHANGE);
|
|
|
|
lua_getfield (L, 2, "data_in_pin");
|
|
|
|
pin_config.data_in_num = luaL_optint(L, -1, I2S_PIN_NO_CHANGE);
|
|
|
|
|
|
|
|
lua_settop(L, 3);
|
|
|
|
i2s_status[i2s_id].event_cb = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
|
2017-04-29 17:06:58 +02:00
|
|
|
esp_err_t err = i2s_driver_install(i2s_id, &i2s_config, i2s_config.dma_buf_count, &i2s_status[i2s_id].event_queue);
|
|
|
|
if(err != ESP_OK)
|
|
|
|
luaL_error( L, "i2s can not start" );
|
2017-04-28 22:27:57 +02:00
|
|
|
i2s_set_pin(i2s_id, &pin_config);
|
|
|
|
|
|
|
|
char pcName[5];
|
|
|
|
snprintf( pcName, 5, "I2S%d", i2s_id );
|
|
|
|
pcName[4] = '\0';
|
2017-05-06 22:10:17 +02:00
|
|
|
xTaskCreate(task_I2S, pcName, 2048, &i2s_status[i2s_id], ESP_TASK_MAIN_PRIO + 1, &i2s_status[i2s_id].taskHandle);
|
2017-04-28 22:27:57 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lua: stop( i2s_id )
|
|
|
|
static int node_i2s_stop( lua_State *L )
|
|
|
|
{
|
|
|
|
int i2s_id = luaL_checkinteger( L, 1 );
|
|
|
|
I2S_CHECK_ID( i2s_id );
|
|
|
|
|
|
|
|
i2s_driver_uninstall(i2s_id);
|
|
|
|
i2s_status[i2s_id].event_queue = NULL;
|
|
|
|
|
|
|
|
if(i2s_status[i2s_id].taskHandle != NULL) {
|
|
|
|
vTaskDelete(i2s_status[i2s_id].taskHandle);
|
|
|
|
i2s_status[i2s_id].taskHandle = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(i2s_status[i2s_id].event_cb != LUA_NOREF) {
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, i2s_status[i2s_id].event_cb);
|
|
|
|
i2s_status[i2s_id].event_cb = LUA_NOREF;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lua: read( i2s_id, bytes[, wait_ms] )
|
|
|
|
static int node_i2s_read( lua_State *L )
|
|
|
|
{
|
|
|
|
int i2s_id = luaL_checkinteger( L, 1 );
|
|
|
|
I2S_CHECK_ID( i2s_id );
|
|
|
|
|
|
|
|
size_t bytes = luaL_checkinteger( L, 2 );
|
|
|
|
int wait_ms = luaL_optint(L, 3, 0);
|
|
|
|
char * data = luaM_malloc( L, bytes );
|
|
|
|
size_t read = i2s_read_bytes(i2s_id, data, bytes, wait_ms / portTICK_RATE_MS);
|
|
|
|
lua_pushlstring(L, data, read);
|
|
|
|
luaM_free(L, data);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lua: write( i2s_id, data[, wait_ms] )
|
|
|
|
static int node_i2s_write( lua_State *L )
|
|
|
|
{
|
|
|
|
int i2s_id = luaL_checkinteger( L, 1 );
|
|
|
|
I2S_CHECK_ID( i2s_id );
|
|
|
|
|
|
|
|
size_t bytes;
|
|
|
|
char *data;
|
|
|
|
if( lua_istable( L, 2 ) ) {
|
2017-04-29 17:06:58 +02:00
|
|
|
bytes = lua_objlen( L, 2 );
|
|
|
|
data = (char *)luaM_malloc( L, bytes );
|
|
|
|
for( int i = 0; i < bytes; i ++ ) {
|
|
|
|
lua_rawgeti( L, 2, i + 1 );
|
|
|
|
data[i] = (uint8_t)luaL_checkinteger( L, -1 );
|
|
|
|
}
|
2017-04-28 22:27:57 +02:00
|
|
|
} else {
|
2017-05-06 22:10:17 +02:00
|
|
|
data = (char *) luaL_checklstring(L, 2, &bytes);
|
2017-04-28 22:27:57 +02:00
|
|
|
}
|
|
|
|
int wait_ms = luaL_optint(L, 3, 0);
|
|
|
|
size_t wrote = i2s_write_bytes(i2s_id, data, bytes, wait_ms / portTICK_RATE_MS);
|
|
|
|
if( lua_istable( L, 2 ) )
|
|
|
|
luaM_free(L, data);
|
|
|
|
lua_pushinteger( L, ( lua_Integer ) wrote );
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Module function map
|
|
|
|
static const LUA_REG_TYPE i2s_map[] =
|
|
|
|
{
|
|
|
|
{ LSTRKEY( "start" ), LFUNCVAL( node_i2s_start ) },
|
|
|
|
{ LSTRKEY( "stop" ), LFUNCVAL( node_i2s_stop ) },
|
|
|
|
{ LSTRKEY( "read" ), LFUNCVAL( node_i2s_read ) },
|
|
|
|
{ LSTRKEY( "write" ), LFUNCVAL( node_i2s_write ) },
|
|
|
|
{ LSTRKEY( "FORMAT_I2S" ), LNUMVAL( I2S_COMM_FORMAT_I2S ) },
|
|
|
|
{ LSTRKEY( "FORMAT_I2S_MSB" ), LNUMVAL( I2S_COMM_FORMAT_I2S_MSB ) },
|
|
|
|
{ LSTRKEY( "FORMAT_I2S_LSB" ), LNUMVAL( I2S_COMM_FORMAT_I2S_LSB ) },
|
|
|
|
{ LSTRKEY( "FORMAT_PCM" ), LNUMVAL( I2S_COMM_FORMAT_PCM ) },
|
|
|
|
{ LSTRKEY( "FORMAT_PCM_SHORT" ), LNUMVAL( I2S_COMM_FORMAT_PCM_SHORT ) },
|
|
|
|
{ LSTRKEY( "FORMAT_PCM_LONG" ), LNUMVAL( I2S_COMM_FORMAT_PCM_LONG ) },
|
|
|
|
|
|
|
|
{ LSTRKEY( "CHANNEL_RIGHT_LEFT" ), LNUMVAL( I2S_CHANNEL_FMT_RIGHT_LEFT ) },
|
|
|
|
{ LSTRKEY( "CHANNEL_ALL_LEFT" ), LNUMVAL( I2S_CHANNEL_FMT_ALL_LEFT ) },
|
|
|
|
{ LSTRKEY( "CHANNEL_ONLY_LEFT" ), LNUMVAL( I2S_CHANNEL_FMT_ONLY_LEFT ) },
|
|
|
|
{ LSTRKEY( "CHANNEL_ALL_RIGHT" ), LNUMVAL( I2S_CHANNEL_FMT_ALL_RIGHT ) },
|
|
|
|
{ LSTRKEY( "CHANNEL_ONLY_RIGHT" ), LNUMVAL( I2S_CHANNEL_FMT_ONLY_RIGHT ) },
|
|
|
|
|
|
|
|
{ LSTRKEY( "MODE_MASTER" ), LNUMVAL( I2S_MODE_MASTER ) },
|
|
|
|
{ LSTRKEY( "MODE_SLAVE" ), LNUMVAL( I2S_MODE_SLAVE ) },
|
|
|
|
{ LSTRKEY( "MODE_TX" ), LNUMVAL( I2S_MODE_TX ) },
|
|
|
|
{ LSTRKEY( "MODE_RX" ), LNUMVAL( I2S_MODE_RX ) },
|
|
|
|
{ LSTRKEY( "MODE_DAC_BUILT_IN" ), LNUMVAL( I2S_MODE_DAC_BUILT_IN ) },
|
|
|
|
|
|
|
|
{ LNILKEY, LNILVAL }
|
|
|
|
};
|
|
|
|
|
|
|
|
int luaopen_i2s( lua_State *L ) {
|
|
|
|
for(int i2s_id = 0; i2s_id < MAX_I2C_NUM; i2s_id++) {
|
2017-04-29 17:06:58 +02:00
|
|
|
i2s_status[i2s_id].event_queue = NULL;
|
|
|
|
i2s_status[i2s_id].taskHandle = NULL;
|
|
|
|
i2s_status[i2s_id].event_cb = LUA_NOREF;
|
2017-04-28 22:27:57 +02:00
|
|
|
}
|
|
|
|
i2s_event_task_id = task_get_id( i2s_event_task );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
NODEMCU_MODULE(I2S, "i2s", i2s_map, luaopen_i2s);
|