123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- #include "ets_sys.h"
- #include "osapi.h"
- #include "gpio.h"
- #include "os_type.h"
- #include "user_interface.h"
- #include "module.h"
- #include "lauxlib.h"
- #include "task/task.h"
- #include "platform.h"
- #include <stdlib.h>
- #include <string.h>
- #define SOFTUART_MAX_RX_BUFF 128
- #define SOFTUART_GPIO_COUNT 13
- //TODO: Overflow flag as callback function + docs
- typedef struct {
- char receive_buffer[SOFTUART_MAX_RX_BUFF];
- uint8_t buffer_first;
- uint8_t buffer_last;
- uint8_t bytes_count;
- uint8_t buffer_overflow;
- } softuart_buffer_t;
- typedef struct {
- uint8_t pin_rx;
- uint8_t pin_tx;
- volatile softuart_buffer_t buffer;
- uint16_t bit_time;
- uint16_t need_len; // Buffer length needed to run callback function
- char end_char; // Used to run callback if last char in buffer will be the same
- uint8_t armed;
- } softuart_t;
- typedef struct {
- softuart_t *softuart;
- } softuart_userdata;
- // Array of pointers to SoftUART instances
- softuart_t * softuart_gpio_instances[SOFTUART_GPIO_COUNT] = {NULL};
- // Array of callback reference to be able to find which callback is used to which rx pin
- static int softuart_rx_cb_ref[SOFTUART_GPIO_COUNT];
- // Task for receiving data
- static task_handle_t uart_recieve_task = 0;
- // Receiving buffer for callback usage
- static char softuart_rx_buffer[SOFTUART_MAX_RX_BUFF];
- static inline int32_t asm_ccount(void) {
- int32_t r;
- asm volatile ("rsr %0, ccount" : "=r"(r));
- return r;
- }
- static inline uint8_t checkbit(uint8_t data, uint8_t bit)
- {
- if ((data & bit) != 0) {
- return 1;
- } else {
- return 0;
- }
- }
- uint32_t ICACHE_RAM_ATTR softuart_intr_handler(uint32_t ret_gpio_status)
- {
- // Disable all interrupts
- ets_intr_lock();
- int32_t start_time = asm_ccount();
- uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
- uint32_t gpio_bits = gpio_status;
- for (uint8_t gpio_bit = 0; gpio_bits != 0; gpio_bit++, gpio_bits >>= 1) {
- // Check all pins for interrupts
- if (! (gpio_bits & 0x01)) continue;
- // We got pin that was interrupted
- // Load instance which has rx pin on interrupt pin attached
- softuart_t *s = softuart_gpio_instances[pin_num_inv[gpio_bit]];
- if (s == NULL) continue;
- if (softuart_rx_cb_ref[pin_num_inv[gpio_bit]] == LUA_NOREF) continue;
- if (!s->armed) continue;
- // There is SoftUART rx instance on that pin
- // Clear interrupt status on that pin
- GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & (1 << pin_num[s->pin_rx]));
- ret_gpio_status &= ~(1 << pin_num[s->pin_rx]);
- // Start listening to transmission
- // TODO: inverted
- if (! (GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[s->pin_rx])))) {
- //pin is low - therefore we have a start bit
- unsigned byte = 0;
- // Casting and using signed types to always be able to compute elapsed time even if there is a overflow
- uint32_t elapsed_time = (uint32_t)(asm_ccount() - start_time);
- // Wait till start bit is half over so we can sample the next one in the center
- if (elapsed_time < s->bit_time / 2) {
- uint16_t wait_time = s->bit_time / 2 - elapsed_time;
- while ((uint32_t)(asm_ccount() - start_time) < wait_time);
- start_time += wait_time;
- }
- // Sample bits
- // TODO: How many bits? Add other configs to softuart
- for (uint8_t i = 0; i < 8; i ++ ) {
- while ((uint32_t)(asm_ccount() - start_time) < s->bit_time);
- //shift d to the right
- byte >>= 1;
- // Read bit
- if(GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[s->pin_rx]))) {
- // If high, set msb of 8bit to 1
- byte |= 0x80;
- }
- // Recalculate start time for next bit
- start_time += s->bit_time;
- }
- // Store byte in buffer
- // If buffer full, set the overflow flag and return
- if (s->buffer.bytes_count == SOFTUART_MAX_RX_BUFF) {
- s->buffer.buffer_overflow = 1;
- } else if (s->buffer.bytes_count < SOFTUART_MAX_RX_BUFF) {
- s->buffer.receive_buffer[s->buffer.buffer_last] = byte;
- s->buffer.buffer_last++;
- s->buffer.bytes_count++;
- // Check for callback conditions
- if (((s->need_len != 0) && (s->buffer.bytes_count >= s->need_len)) || \
- ((s->need_len == 0) && ((char)byte == s->end_char))) {
- // Send the pointer to task handler
- s->armed = 0;
- task_post_low(uart_recieve_task, (task_param_t)s);
- }
- }
- // Check for overflow after appending new byte
- if (s->buffer.bytes_count == SOFTUART_MAX_RX_BUFF) {
- s->buffer.buffer_overflow = 1;
- }
- // Roll over buffer index if necessary
- if (s->buffer.buffer_last == SOFTUART_MAX_RX_BUFF) {
- s->buffer.buffer_last = 0;
- }
- // Wait for stop bit
- // TODO: Add config for stop bits and parity bits
- while ((uint32_t)(asm_ccount() - start_time) < s->bit_time);
- }
- }
- // re-enable all interrupts
- ets_intr_unlock();
- return ret_gpio_status;
- }
- static void softuart_putchar(softuart_t *s, char data)
- {
- // Disable all interrupts
- ets_intr_lock();
- int32_t start_time = asm_ccount();
- // Set start bit
- GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[s->pin_tx]), 0);
- for (uint32_t i = 0; i < 8; i++) {
- while ((uint32_t)(asm_ccount() - start_time) < s->bit_time);
- GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[s->pin_tx]), checkbit(data, 1 << i));
- // Recalculate start time for next bit
- start_time += s->bit_time;
- }
- // Stop bit
- while ((uint32_t)(asm_ccount() - start_time) < s->bit_time);
- GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[s->pin_tx]), 1);
- // Delay after byte, for new sync
- os_delay_us(s->bit_time*6 / system_get_cpu_freq());
- // Re-enable all interrupts
- ets_intr_unlock();
- }
- static void softuart_init(softuart_t *s)
- {
- NODE_DBG("SoftUART initialize gpio\n");
- // Init tx pin
- if (s->pin_tx != 0xFF){
- platform_gpio_mode(s->pin_tx, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP);
- platform_gpio_write(s->pin_tx, PLATFORM_GPIO_HIGH);
- }
- // Init rx pin
- if (s->pin_rx != 0xFF){
- platform_gpio_mode(s->pin_rx, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
- uint32_t mask = 1 << pin_num[s->pin_rx];
- platform_gpio_register_intr_hook(mask, softuart_intr_handler);
- softuart_gpio_instances[s->pin_rx] = s;
- // Enable interrupt for pin on falling edge
- platform_gpio_intr_init(s->pin_rx, GPIO_PIN_INTR_NEGEDGE);
- }
- }
- static int softuart_setup(lua_State *L)
- {
- uint32_t baudrate;
- uint8_t tx_gpio_id, rx_gpio_id;
- uint8_t stack = 1;
- softuart_userdata *suart = NULL;
- NODE_DBG("SoftUART setup called\n");
- if(lua_isnumber(L, stack)) {
- baudrate = (uint32_t)luaL_checkinteger( L, stack);
- //230400 Is the max baudrate the author of Arduino-Esp8266-Software-UART tested
- if (baudrate <= 0 || baudrate > 230400) {
- return luaL_error(L, "Invalid baud rate" );
- }
- stack++;
- } else {
- return luaL_error(L, "Invalid argument type");
- }
- if(lua_isnumber(L, stack)) {
- tx_gpio_id = (uint8_t)luaL_checkinteger( L, stack);
- if (!platform_gpio_exists(tx_gpio_id) || tx_gpio_id == 0) {
- return luaL_error(L, "SoftUART tx GPIO not valid");
- }
- stack++;
- } else {
- tx_gpio_id = 0xFF;
- stack++;
- }
- if (lua_isnumber(L, stack)) {
- rx_gpio_id = (uint8_t)luaL_checkinteger( L, stack);
- if (!platform_gpio_exists(rx_gpio_id) || rx_gpio_id == 0) {
- return luaL_error(L, "SoftUART rx GPIO not valid");
- }
- if (softuart_gpio_instances[rx_gpio_id] != NULL) {
- return luaL_error( L, "SoftUART rx already configured on the pin.");
- }
- } else {
- rx_gpio_id = 0xFF;
- }
- suart = (softuart_userdata*)lua_newuserdata(L, sizeof(softuart_userdata));
- suart->softuart = malloc(sizeof(softuart_t));
- if (!suart->softuart) {
- free(suart->softuart);
- suart->softuart = NULL;
- return luaL_error(L, "Not enough memory");
- }
- suart->softuart->pin_rx = rx_gpio_id;
- suart->softuart->pin_tx = tx_gpio_id;
- suart->softuart->need_len = RX_BUFF_SIZE;
- suart->softuart->armed = 0;
- // Set buffer
- suart->softuart->buffer.buffer_first = 0;
- suart->softuart->buffer.buffer_last = 0;
- suart->softuart->buffer.bytes_count = 0;
- suart->softuart->buffer.buffer_overflow = 0;
- // Set bit time
- suart->softuart->bit_time = system_get_cpu_freq() * 1000000 / baudrate;
- // Set metatable
- luaL_getmetatable(L, "softuart.port");
- lua_setmetatable(L, -2);
- // Init SoftUART
- softuart_init(suart->softuart);
- return 1;
- }
- static void softuart_rx_callback(task_param_t arg)
- {
- softuart_t *softuart = (softuart_t*)arg; //Receive pointer from ISR
- lua_State *L = lua_getstate();
- lua_rawgeti(L, LUA_REGISTRYINDEX, softuart_rx_cb_ref[softuart->pin_rx]);
- // Clear overflow flag if needed
- if(softuart->buffer.bytes_count == SOFTUART_MAX_RX_BUFF) {
- softuart->buffer.buffer_overflow = 0;
- }
- // Copy volatile data to static buffer
- uint8_t buffer_lenght = softuart->buffer.bytes_count;
- for (int i = 0; i < buffer_lenght; i++) {
- softuart_rx_buffer[i] = softuart->buffer.receive_buffer[softuart->buffer.buffer_first];
- softuart->buffer.buffer_first++;
- softuart->buffer.bytes_count--;
- if (softuart->buffer.buffer_first == SOFTUART_MAX_RX_BUFF) {
- softuart->buffer.buffer_first = 0;
- }
- }
- lua_pushlstring(L, softuart_rx_buffer, buffer_lenght);
- softuart->armed = 1;
- luaL_pcallx(L, 1, 0);
- }
- // Arguments: event name, minimum buffer filled to run callback, callback function
- static int softuart_on(lua_State *L)
- {
- NODE_DBG("SoftUART on called\n");
- softuart_userdata *suart = NULL;
- size_t name_len, arg_len;
- uint8_t stack = 1;
- suart = (softuart_userdata *)luaL_checkudata(L, 1, "softuart.port");
- luaL_argcheck(L, suart, stack, "softuart.port expected");
- if (suart == NULL) {
- NODE_DBG("Userdata is nil\n");
- return 0;
- }
- stack++;
- const char *method = luaL_checklstring(L, stack, &name_len);
- if (method == NULL)
- return luaL_error(L, "Wrong argument type");
- stack++;
- if (lua_type(L, stack) == LUA_TNUMBER) {
- suart->softuart->need_len = (uint16_t)luaL_checkinteger( L, stack );
- stack++;
- suart->softuart->end_char = 0;
- if (suart->softuart->need_len > SOFTUART_MAX_RX_BUFF) {
- suart->softuart->need_len = 0;
- return luaL_error(L, "Argument bigger than SoftUART buffer");
- }
- suart->softuart->armed = 1;
- } else if (lua_isstring(L, stack)) {
- const char *end = luaL_checklstring(L , stack, &arg_len);
- stack++;
- if ( arg_len != 1) {
- return luaL_error(L, "Wrong end char length");
- }
- suart->softuart->end_char = end[0];
- suart->softuart->need_len = 0;
- suart->softuart->armed = 1;
- } else {
- return luaL_error(L, "Wrong argument type");
- }
- if (lua_type(L, stack) == LUA_TFUNCTION) {
- lua_pushvalue(L, stack); // Copy to top of the stack
- } else {
- lua_pushnil(L);
- }
- if (name_len == 4 && strcmp(method, "data") == 0) {
- if(suart->softuart->pin_rx == 0xFF) {
- return luaL_error(L, "Rx pin was not declared");
- }
- if (softuart_rx_cb_ref[suart->softuart->pin_rx] != LUA_NOREF) {
- luaL_unref(L, LUA_REGISTRYINDEX, softuart_rx_cb_ref[suart->softuart->pin_rx]);
- softuart_rx_cb_ref[suart->softuart->pin_rx] = LUA_NOREF;
- }
- if (! lua_isnil(L, -1)) {
- softuart_rx_cb_ref[suart->softuart->pin_rx] = luaL_ref(L, LUA_REGISTRYINDEX);
- } else {
- lua_pop(L, 1);
- }
- } else {
- lua_pop(L, 1);
- return luaL_error(L, "Method not supported");
- }
- return 0;
- }
- static int softuart_write(lua_State *L)
- {
- NODE_DBG("SoftUART write called\n");
- softuart_userdata *suart = NULL;
- uint8_t stack = 1;
- size_t str_len;
- suart = (softuart_userdata *)luaL_checkudata(L, 1, "softuart.port");
- luaL_argcheck(L, suart, stack, "softuart.port expected");
- if (suart == NULL) {
- NODE_DBG("Userdata is nil\n");
- return 0;
- }
- stack++;
- if(suart->softuart->pin_tx == 0xFF) {
- return luaL_error(L, "Tx pin was not declared");
- }
- if (lua_type(L, stack) == LUA_TNUMBER) {
- // Send byte
- uint32_t byte = (uint32_t)luaL_checkinteger( L, stack );
- if (byte > 255) {
- return luaL_error(L, "Integer too large for a byte");
- }
- softuart_putchar(suart->softuart, (char)byte);
- } else if (lua_isstring(L, stack)) {
- // Send string
- const char *string = luaL_checklstring(L , stack, &str_len);
- for (size_t i = 0; i < str_len; i++) {
- softuart_putchar(suart->softuart, string[i]);
- }
- } else {
- return luaL_error(L, "Wrong argument type");
- }
- return 0;
- }
- static int softuart_gcdelete(lua_State *L)
- {
- NODE_DBG("SoftUART GC called\n");
- softuart_userdata *suart = NULL;
- suart = (softuart_userdata *)luaL_checkudata(L, 1, "softuart.port");
- luaL_argcheck(L, suart, 1, "softuart.port expected");
- if (suart == NULL) {
- NODE_DBG("Userdata is nil\n");
- return 0;
- }
- softuart_gpio_instances[suart->softuart->pin_rx] = NULL;
- luaL_unref(L, LUA_REGISTRYINDEX, softuart_rx_cb_ref[suart->softuart->pin_rx]);
- softuart_rx_cb_ref[suart->softuart->pin_rx] = LUA_NOREF;
- free(suart->softuart);
- return 0;
- }
- // Port function map
- LROT_BEGIN(softuart_port, NULL, LROT_MASK_GC_INDEX)
- LROT_FUNCENTRY( __gc, softuart_gcdelete)
- LROT_TABENTRY( __index, softuart_port)
- LROT_FUNCENTRY( on, softuart_on)
- LROT_FUNCENTRY( write, softuart_write)
- LROT_END(softuart_port, NULL, LROT_MASK_GC_INDEX)
- // Module function map
- LROT_BEGIN(softuart, LROT_TABLEREF(softuart_port), 0)
- LROT_FUNCENTRY( setup, softuart_setup)
- LROT_END(softuart, LROT_TABLEREF(softuart_port), 0)
- static int luaopen_softuart(lua_State *L)
- {
- for(int i = 0; i < SOFTUART_GPIO_COUNT; i++) {
- softuart_rx_cb_ref[i] = LUA_NOREF;
- }
- uart_recieve_task = task_get_id((task_callback_t) softuart_rx_callback);
- luaL_rometatable(L, "softuart.port", LROT_TABLEREF(softuart_port));
- return 0;
- }
- NODEMCU_MODULE(SOFTUART, "softuart", softuart, luaopen_softuart);
|