Просмотр исходного кода

SoftUART module fixes and code simplification (#3104)

* SoftUART fixes:

- Simplify code by using lua_L* functions and using userdata properly
- Fix some edge-cases
- Add more examples to documentation

* Don't de-register interrupt hook if there is more RX instances

* More bug fixes and registering simplification with luaL_reref and unref2

* Correct documentation of SoftUART module
galjonsfigur 3 лет назад
Родитель
Сommit
e7620b0647
2 измененных файлов с 116 добавлено и 171 удалено
  1. 115 170
      app/modules/softuart.c
  2. 1 1
      docs/modules/softuart.md

+ 115 - 170
app/modules/softuart.c

@@ -14,6 +14,7 @@
 #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;
@@ -23,19 +24,15 @@ typedef struct {
 } 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;
+	uint8_t pin_rx;
+	uint8_t pin_tx;
 } 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
@@ -60,7 +57,7 @@ static inline uint8_t checkbit(uint8_t data, uint8_t bit)
 	}
 }
 
-uint32_t ICACHE_RAM_ATTR softuart_intr_handler(uint32_t ret_gpio_status)
+static uint32_t ICACHE_RAM_ATTR softuart_intr_handler(uint32_t ret_gpio_status)
 {
 	// Disable all interrupts
 	ets_intr_lock();
@@ -74,16 +71,16 @@ uint32_t ICACHE_RAM_ATTR softuart_intr_handler(uint32_t ret_gpio_status)
 		// 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]);
+		if (softuart_rx_cb_ref[pin_num_inv[gpio_bit]] == LUA_NOREF) continue;
+		if (!s->armed) continue;
+		// There is an armed SoftUART rx instance on that pin
 		// 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
+		//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);
@@ -104,7 +101,7 @@ uint32_t ICACHE_RAM_ATTR softuart_intr_handler(uint32_t ret_gpio_status)
 
 				// Read bit
 				if(GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[s->pin_rx]))) {
-					// If high, set msb of 8bit to 1
+					// If high, set MSB of byte to 1
 					byte |= 0x80;
 				}
 				// Recalculate start time for next bit
@@ -125,7 +122,7 @@ uint32_t ICACHE_RAM_ATTR softuart_intr_handler(uint32_t ret_gpio_status)
 						((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);
+						task_post_medium(uart_recieve_task, (task_param_t)s);
 				}
 			}
 			// Check for overflow after appending new byte
@@ -136,11 +133,11 @@ uint32_t ICACHE_RAM_ATTR softuart_intr_handler(uint32_t ret_gpio_status)
 			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);
-
+			// Break the loop after reading of the frame
+			break;
 		}
 	}
 	// re-enable all interrupts
@@ -156,6 +153,7 @@ static void softuart_putchar(softuart_t *s, char data)
 	// Set start bit
 	GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[s->pin_tx]), 0);
 	for (uint32_t i = 0; i < 8; i++) {
+		// Wait to transmit another bit
 		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));
@@ -172,25 +170,29 @@ static void softuart_putchar(softuart_t *s, char data)
 	ets_intr_unlock();
 }
 
-static void softuart_init(softuart_t *s)
+static int softuart_init(softuart_t *s)
 {
-    NODE_DBG("SoftUART initialize gpio\n");
 
 	// Init tx pin
-    if (s->pin_tx != 0xFF){
+    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){
+    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);
+		softuart_gpio_instances[s->pin_rx] = s;
+		// Preserve other rx gpio pins
+		uint32_t mask = 0;
+		for (uint8_t i = 0; i < SOFTUART_GPIO_COUNT; i++) {
+			if (softuart_gpio_instances[i] != NULL) {
+				mask = mask | (1 << pin_num[softuart_gpio_instances[i]->pin_rx]);
+			}
+		}
+		return platform_gpio_register_intr_hook(mask, softuart_intr_handler);
     }
 }
 
@@ -199,70 +201,59 @@ 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");
-	}
+	softuart_t *softuart = NULL;
+
+	NODE_DBG("[SoftUART]: setup called\n");
+	baudrate = (uint32_t)luaL_checkinteger(L, 1); // Get Baudrate from
+	luaL_argcheck(L, (baudrate > 0 && baudrate < 230400), 1, "Invalid baud rate");
+	lua_remove(L, 1); // Remove baudrate argument from stack
+	if (lua_gettop(L) == 2) { // 2 arguments: 1st can be nil
+		if (lua_isnil(L, 1)) {
+			tx_gpio_id = 0xFF;
+		} else {
+			tx_gpio_id = (uint8_t)luaL_checkinteger(L, 1);
+			luaL_argcheck(L, (platform_gpio_exists(tx_gpio_id) && tx_gpio_id != 0)
+				, 2, "Invalid SoftUART tx GPIO");
+		}
+		rx_gpio_id = (uint8_t)luaL_checkinteger(L, 2);
+		luaL_argcheck(L, (platform_gpio_exists(rx_gpio_id) && rx_gpio_id != 0)
+			, 3, "Invalid SoftUART rx GPIO");
+		luaL_argcheck(L, softuart_gpio_instances[rx_gpio_id] == NULL
+			, 3, "SoftUART rx already configured on the pin");
 
-	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 {
+	} else if (lua_gettop(L) == 1) { // 1 argument: transmit part only
 		rx_gpio_id = 0xFF;
+		tx_gpio_id = (uint8_t)luaL_checkinteger(L, 1);
+		luaL_argcheck(L, (platform_gpio_exists(tx_gpio_id) && tx_gpio_id != 0)
+			, 2, "Invalid SoftUART tx GPIO");
+	} else {
+		// SoftUART object without receive and transmit part would be useless
+		return luaL_error(L, "Not enough arguments");
 	}
 
-	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;
+	softuart = (softuart_t*)lua_newuserdata(L, sizeof(softuart_t));
+	softuart->pin_rx = rx_gpio_id;
+	softuart->pin_tx = tx_gpio_id;
+	softuart->need_len = SOFTUART_MAX_RX_BUFF;
+	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;
+	softuart->buffer.buffer_first = 0;
+	softuart->buffer.buffer_last = 0;
+	softuart->buffer.bytes_count = 0;
+	softuart->buffer.buffer_overflow = 0;
 
 	// Set bit time
-    suart->softuart->bit_time = system_get_cpu_freq() * 1000000 / baudrate;
+    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);
+	int result = softuart_init(softuart);
+	if (result == 0) {
+		luaL_error(L, "Couldn't register interrupt");
+	}
 	return 1;
 }
 
@@ -271,13 +262,14 @@ 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++) {
+	uint8_t buffer_length = softuart->buffer.bytes_count;
+	for (int i = 0; i < buffer_length; i++) {
 		softuart_rx_buffer[i] = softuart->buffer.receive_buffer[softuart->buffer.buffer_first];
 		softuart->buffer.buffer_first++;
 		softuart->buffer.bytes_count--;
@@ -285,111 +277,63 @@ static void softuart_rx_callback(task_param_t arg)
 			softuart->buffer.buffer_first = 0;
 		}
 	}
-	lua_pushlstring(L, softuart_rx_buffer, buffer_lenght);
+	lua_pushlstring(L, softuart_rx_buffer, buffer_length);
 	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;
+	NODE_DBG("[SoftUART] on: called\n");
 	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;
+	softuart_t *softuart = (softuart_t*)luaL_checkudata(L, 1, "softuart.port");
+	const char *method = luaL_checklstring(L, 2, &name_len);
+	luaL_argcheck(L, lua_isfunction(L, 4), -1, "No callback function specified");
+	luaL_argcheck(L, (name_len == 4 && strcmp(method, "data") == 0), 2, "Method not supported");
+    luaL_argcheck(L, softuart->pin_rx != 0xFF, 1, "Rx pin was not declared");
+
+	if (lua_isnumber(L, 3)) {
+		luaL_argcheck(L, luaL_checkinteger(L, 3) < SOFTUART_MAX_RX_BUFF,
+				2, "Argument bigger than SoftUART buffer");
+		softuart->end_char = 0;
+		softuart->need_len = (uint16_t) luaL_checkinteger(L, 3);
+	} else if (lua_isstring(L, 3)) {
+		const char *end = luaL_checklstring(L , 3,  &arg_len);
+		luaL_argcheck(L, arg_len == 1, 3, "Wrong end char length");
+		softuart->end_char = end[0];
+		softuart->need_len = 0;
 	} 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");
-    }
+    lua_settop(L, 4); // Move to the top of the stack
+    // Register callback or reregister new one
+    luaL_reref(L, LUA_REGISTRYINDEX, &softuart_rx_cb_ref[softuart->pin_rx]);
+    // Arm the instance
+	softuart->armed = 1;
     return 0;
 }
 
 static int softuart_write(lua_State *L)
 {
-	NODE_DBG("SoftUART write called\n");
-	softuart_userdata *suart = NULL;
-	uint8_t stack = 1;
+	softuart_t *softuart = NULL;
 	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) {
+	softuart = (softuart_t*) luaL_checkudata(L, 1, "softuart.port");
+	luaL_argcheck(L, softuart->pin_tx != 0xFF, 1, "Tx pin was not declared");
+
+	if (lua_isnumber(L, 2)) {
 		// 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)) {
+		uint32_t byte = (uint32_t)luaL_checkinteger(L, 2);
+		luaL_argcheck(L, byte < 256, 2, "Integer too large for a byte");
+		softuart_putchar(softuart, (char)byte);
+	} else if (lua_isstring(L, 2)) {
 		// Send string
-		const char *string = luaL_checklstring(L , stack,  &str_len);
+		const char *string = luaL_checklstring(L, 2, &str_len);
 		for (size_t i = 0; i < str_len; i++) {
-			softuart_putchar(suart->softuart, string[i]);
+			softuart_putchar(softuart, string[i]);
 		}
 	} else {
 		return luaL_error(L, "Wrong argument type");
@@ -400,17 +344,18 @@ static int softuart_write(lua_State *L)
 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);
+	softuart_t *softuart = NULL;
+	softuart = (softuart_t*) luaL_checkudata(L, 1, "softuart.port");
+	uint8_t last_instance = 1;
+	for(uint8_t instance = 0; instance < SOFTUART_GPIO_COUNT; instance++)
+		if (softuart_gpio_instances[instance] != NULL && instance != softuart->pin_rx)
+			last_instance = 0;
+
+	softuart_gpio_instances[softuart->pin_rx] = NULL;
+	luaL_unref2(L, LUA_REGISTRYINDEX, softuart_rx_cb_ref[softuart->pin_rx]);
+	// Try to unregister the interrupt hook if this was last or the only instance
+	if (last_instance)
+		platform_gpio_register_intr_hook(0, softuart_intr_handler);
 	return 0;
 }
 

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
docs/modules/softuart.md


Некоторые файлы не были показаны из-за большого количества измененных файлов