Fix uart regressions & bugs (#2817)

* Fix uart regressions & bugs.

Using `uart.on()` with a search character was broken in that it did
not invoke the callback on a full UART buffer as documented. Logic reworked
to match docs again.

Fixed memory leak on `task_post()` failure (eep!).

Improved logic to attempt to coalesce input bytes to reduce the number of
`task_post()` slots used up by the platform uart.

Finally, added a semaphore to prevent the platform uart from overrunning
the `task_post()` slots all the time on high baud rates (e.g. 1mbit).
With the semaphore in there, the LVM RTOS task gets a chance to actually
process the received data and free up a `task_post()` slot or two.
The above mentioned read coalescing then allows the platform uart to
immediately catch up.

Also added an error log message if the `task_post()` actually does fail.

* Don't cache the uart delims.

Doing so makes reconfiguring those settings from within the callback not
take effect until the currently buffered bytes have been processed.
This commit is contained in:
Johny Mattsson 2019-10-13 09:49:44 +11:00 committed by GitHub
parent a4fa6c5cd3
commit b0558d5bbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 43 additions and 26 deletions

View File

@ -5,7 +5,7 @@
#include "driver/uart.h"
#include <stdio.h>
#include <string.h>
#include <freertos/semphr.h>
#include "lua.h"
#include "esp_log.h"
@ -36,12 +36,13 @@ static const char *UART_TAG = "uart";
uart_status_t uart_status[NUM_UART];
SemaphoreHandle_t sem = NULL;
extern bool uart_on_data_cb(unsigned id, const char *buf, size_t len);
extern bool uart_on_error_cb(unsigned id, const char *buf, size_t len);
task_handle_t uart_event_task_id = 0;
void uart_event_task( task_param_t param, task_prio_t prio ) {
size_t p;
uint16_t need_len;
int16_t end_char;
char ch;
@ -50,31 +51,25 @@ void uart_event_task( task_param_t param, task_prio_t prio ) {
uart_event_post_t *post = (uart_event_post_t *)param;
id = post->id;
us = & uart_status[id];
xSemaphoreGive(sem);
if(post->type == PLATFORM_UART_EVENT_DATA) {
need_len = us->need_len;
end_char = us->end_char;
for(p = 0; p < post->size; p++) {
ch = *(post->data + p);
for(size_t p = 0; p < post->size; p++) {
ch = post->data[p];
us->line_buffer[us->line_position] = ch;
us->line_position++;
if((end_char == -1 && us->line_position == (need_len == 0? LUA_MAXINPUT : need_len))
|| (end_char >= 0 && (unsigned char)ch == (unsigned char)end_char)) {
// us->line_buffer[us->line_position] = 0;
need_len = us->need_len;
end_char = us->end_char;
size_t max_wanted =
(end_char >= 0 && need_len == 0) ? LUA_MAXINPUT : need_len;
bool at_end = (us->line_position >= max_wanted);
bool end_char_found =
(end_char >= 0 && (uint8_t)ch == (uint8_t)end_char);
if (at_end || end_char_found) {
uart_on_data_cb(id, us->line_buffer, us->line_position);
us->line_position = 0;
}
if(us->line_position + 1 >= LUA_MAXINPUT) {
us->line_position = 0;
}
}
if(end_char == -1 && need_len == 0 && us->line_position > 0) {
// us->line_buffer[us->line_position] = 0;
uart_on_data_cb(id, us->line_buffer, us->line_position);
us->line_position = 0;
}
free(post->data);
} else {
char *err;
@ -97,20 +92,32 @@ void uart_event_task( task_param_t param, task_prio_t prio ) {
static void task_uart( void *pvParameters ){
unsigned id = (unsigned)pvParameters;
// 4 chosen as a number smaller than the number of nodemcu task slots
// available, to make it unlikely we encounter a task_post failing
if (sem == NULL)
sem = xSemaphoreCreateCounting(4, 4);
uart_event_post_t* post = NULL;
uart_event_t event;
for(;;) {
if(xQueueReceive(uart_status[id].queue, (void * )&event, (portTickType)portMAX_DELAY)) {
switch(event.type) {
case UART_DATA:
case UART_DATA: {
post = (uart_event_post_t*)malloc(sizeof(uart_event_post_t));
if(post == NULL) {
ESP_LOGE(UART_TAG, "Can not alloc memory in task_uart()");
// reboot here?
continue;
}
post->data = malloc(event.size);
// Attempt to coalesce received bytes to reduce risk of overrunning
// the task event queue.
size_t len;
if (uart_get_buffered_data_len(id, &len) != ESP_OK)
len = event.size;
if (len == 0)
continue; // we already gobbled all the bytes
post->data = malloc(len);
if(post->data == NULL) {
ESP_LOGE(UART_TAG, "Can not alloc memory in task_uart()");
post->id = id;
@ -118,9 +125,10 @@ static void task_uart( void *pvParameters ){
} else {
post->id = id;
post->type = PLATFORM_UART_EVENT_DATA;
post->size = uart_read_bytes(id, (uint8_t *)post->data, event.size, 0);
post->size = uart_read_bytes(id, (uint8_t *)post->data, len, 0);
}
break;
}
case UART_BREAK:
post = (uart_event_post_t*)malloc(sizeof(uart_event_post_t));
if(post == NULL) {
@ -130,6 +138,7 @@ static void task_uart( void *pvParameters ){
}
post->id = id;
post->type = PLATFORM_UART_EVENT_BREAK;
post->data = NULL;
break;
case UART_FIFO_OVF:
case UART_BUFFER_FULL:
@ -143,13 +152,21 @@ static void task_uart( void *pvParameters ){
}
post->id = id;
post->type = PLATFORM_UART_EVENT_RX;
post->data = NULL;
break;
case UART_PATTERN_DET:
default:
;
}
if(post != NULL) {
task_post_medium( uart_event_task_id, (task_param_t)post );
if (post != NULL) {
xSemaphoreTake(sem, portMAX_DELAY);
if (!task_post_medium(uart_event_task_id, (task_param_t)post))
{
ESP_LOGE(UART_TAG, "Task event overrun in task_uart()");
xSemaphoreGive(sem);
free(post->data);
free(post);
}
post = NULL;
}
}