Adds C++ std::function to Serial.onReceive() (#6364)

* Adds C++ std::function to Serial.onReceive()

* fixes LOCK macro when disabled
This commit is contained in:
Rodrigo Garcia
2022-03-02 10:20:43 -03:00
committed by GitHub
parent 95b8e7e42b
commit 7d4992a811
4 changed files with 201 additions and 90 deletions

View File

@ -7,6 +7,7 @@
#include "HardwareSerial.h" #include "HardwareSerial.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "driver/uart.h" #include "driver/uart.h"
#include "freertos/queue.h"
#ifndef SOC_RX0 #ifndef SOC_RX0
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
@ -115,7 +116,131 @@ void serialEventRun(void)
} }
#endif #endif
HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL), _rxBufferSize(256) {} #if !CONFIG_DISABLE_HAL_LOCKS
#define HSERIAL_MUTEX_LOCK() do {} while (xSemaphoreTake(_lock, portMAX_DELAY) != pdPASS)
#define HSERIAL_MUTEX_UNLOCK() xSemaphoreGive(_lock)
#else
#define HSERIAL_MUTEX_LOCK()
#define HSERIAL_MUTEX_UNLOCK()
#endif
HardwareSerial::HardwareSerial(int uart_nr) :
_uart_nr(uart_nr),
_uart(NULL),
_rxBufferSize(256),
_onReceiveCB(NULL),
_onReceiveErrorCB(NULL),
_eventTask(NULL)
#if !CONFIG_DISABLE_HAL_LOCKS
,_lock(NULL)
#endif
{
#if !CONFIG_DISABLE_HAL_LOCKS
if(_lock == NULL){
_lock = xSemaphoreCreateMutex();
if(_lock == NULL){
log_e("xSemaphoreCreateMutex failed");
return;
}
}
#endif
}
HardwareSerial::~HardwareSerial()
{
end();
#if !CONFIG_DISABLE_HAL_LOCKS
if(_lock != NULL){
vSemaphoreDelete(_lock);
}
#endif
}
void HardwareSerial::_createEventTask(void *args)
{
// Creating UART event Task
xTaskCreate(_uartEventTask, "uart_event_task", 2048, this, configMAX_PRIORITIES - 1, &_eventTask);
if (_eventTask == NULL) {
log_e(" -- UART%d Event Task not Created!", _uart_nr);
}
}
void HardwareSerial::_destroyEventTask(void)
{
if (_eventTask != NULL) {
vTaskDelete(_eventTask);
_eventTask = NULL;
}
}
void HardwareSerial::onReceiveError(OnReceiveErrorCb function)
{
HSERIAL_MUTEX_LOCK();
// function may be NULL to cancel onReceive() from its respective task
_onReceiveErrorCB = function;
// this can be called after Serial.begin(), therefore it shall create the event task
if (function != NULL && _uart != NULL && _eventTask == NULL) {
_createEventTask(this);
}
HSERIAL_MUTEX_UNLOCK();
}
void HardwareSerial::onReceive(OnReceiveCb function)
{
HSERIAL_MUTEX_LOCK();
// function may be NULL to cancel onReceive() from its respective task
_onReceiveCB = function;
// this can be called after Serial.begin(), therefore it shall create the event task
if (function != NULL && _uart != NULL && _eventTask == NULL) {
_createEventTask(this);
}
HSERIAL_MUTEX_UNLOCK();
}
void HardwareSerial::_uartEventTask(void *args)
{
HardwareSerial *uart = (HardwareSerial *)args;
uart_event_t event;
QueueHandle_t uartEventQueue = NULL;
uartGetEventQueue(uart->_uart, &uartEventQueue);
if (uartEventQueue != NULL) {
for(;;) {
//Waiting for UART event.
if(xQueueReceive(uartEventQueue, (void * )&event, (portTickType)portMAX_DELAY)) {
switch(event.type) {
case UART_DATA:
if(uart->_onReceiveCB && uart->available() > 0) uart->_onReceiveCB();
break;
case UART_FIFO_OVF:
log_w("UART%d FIFO Overflow. Consider adding Hardware Flow Control to your Application.", uart->_uart_nr);
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(UART_FIFO_OVF_ERROR);
break;
case UART_BUFFER_FULL:
log_w("UART%d Buffer Full. Consider encreasing your buffer size of your Application.", uart->_uart_nr);
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(UART_BUFFER_FULL_ERROR);
break;
case UART_BREAK:
log_w("UART%d RX break.", uart->_uart_nr);
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(UART_BREAK_ERROR);
break;
case UART_PARITY_ERR:
log_w("UART%d parity error.", uart->_uart_nr);
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(UART_PARITY_ERROR);
break;
case UART_FRAME_ERR:
log_w("UART%d frame error.", uart->_uart_nr);
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(UART_FRAME_ERROR);
break;
default:
log_w("UART%d unknown event type %d.", uart->_uart_nr, event.type);
break;
}
}
}
}
vTaskDelete(NULL);
}
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms, uint8_t rxfifo_full_thrhd) void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms, uint8_t rxfifo_full_thrhd)
{ {
@ -124,6 +249,14 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
return; return;
} }
#if !CONFIG_DISABLE_HAL_LOCKS
if(_lock == NULL){
log_e("MUTEX Lock failed. Can't begin.");
return;
}
#endif
HSERIAL_MUTEX_LOCK();
// First Time or after end() --> set default Pins // First Time or after end() --> set default Pins
if (!uartIsDriverInstalled(_uart)) { if (!uartIsDriverInstalled(_uart)) {
switch (_uart_nr) { switch (_uart_nr) {
@ -176,11 +309,12 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
_uart = NULL; _uart = NULL;
} }
} }
} // create a task to deal with Serial Events when, for example, calling begin() twice to change the baudrate,
// or when setting the callback before calling begin()
void HardwareSerial::onReceive(void(*function)(void)) if (_uart != NULL && (_onReceiveCB != NULL || _onReceiveErrorCB != NULL) && _eventTask == NULL) {
{ _createEventTask(this);
uartOnReceive(_uart, function); }
HSERIAL_MUTEX_UNLOCK();
} }
void HardwareSerial::updateBaudRate(unsigned long baud) void HardwareSerial::updateBaudRate(unsigned long baud)
@ -188,14 +322,21 @@ void HardwareSerial::updateBaudRate(unsigned long baud)
uartSetBaudRate(_uart, baud); uartSetBaudRate(_uart, baud);
} }
void HardwareSerial::end(bool turnOffDebug) void HardwareSerial::end(bool fullyTerminate)
{ {
if(turnOffDebug && uartGetDebug() == _uart_nr) { // default Serial.end() will completely disable HardwareSerial,
uartSetDebug(0); // including any tasks or debug message channel (log_x()) - but not for IDF log messages!
if(fullyTerminate) {
_onReceiveCB = NULL;
_onReceiveErrorCB = NULL;
if (uartGetDebug() == _uart_nr) {
uartSetDebug(0);
}
} }
delay(10); delay(10);
uartEnd(_uart); uartEnd(_uart);
_uart = 0; _uart = 0;
_destroyEventTask();
} }
void HardwareSerial::setDebugOutput(bool en) void HardwareSerial::setDebugOutput(bool en)

View File

@ -46,23 +46,40 @@
#define HardwareSerial_h #define HardwareSerial_h
#include <inttypes.h> #include <inttypes.h>
#include <functional>
#include "Stream.h" #include "Stream.h"
#include "esp32-hal.h" #include "esp32-hal.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "HWCDC.h" #include "HWCDC.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
typedef enum {
UART_BREAK_ERROR,
UART_BUFFER_FULL_ERROR,
UART_FIFO_OVF_ERROR,
UART_FRAME_ERROR,
UART_PARITY_ERROR
} hardwareSerial_error_t;
typedef std::function<void(void)> OnReceiveCb;
typedef std::function<void(hardwareSerial_error_t)> OnReceiveErrorCb;
class HardwareSerial: public Stream class HardwareSerial: public Stream
{ {
public: public:
HardwareSerial(int uart_nr); HardwareSerial(int uart_nr);
~HardwareSerial();
// onReceive will setup a callback for whenever UART data is received // onReceive will setup a callback for whenever UART data is received
// it will work as UART Rx interrupt // it will work as UART Rx interrupt -- Using C++ 11 std::fuction
void onReceive(void(*function)(void)); void onReceive(OnReceiveCb function);
void onReceiveError(OnReceiveErrorCb function);
void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 112); void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 112);
void end(bool turnOffDebug = true); void end(bool fullyTerminate = true);
void updateBaudRate(unsigned long baud); void updateBaudRate(unsigned long baud);
int available(void); int available(void);
int availableForWrite(void); int availableForWrite(void);
@ -120,6 +137,16 @@ protected:
int _uart_nr; int _uart_nr;
uart_t* _uart; uart_t* _uart;
size_t _rxBufferSize; size_t _rxBufferSize;
OnReceiveCb _onReceiveCB;
OnReceiveErrorCb _onReceiveErrorCB;
TaskHandle_t _eventTask;
void _createEventTask(void *args);
void _destroyEventTask(void);
static void _uartEventTask(void *args);
#if !CONFIG_DISABLE_HAL_LOCKS
SemaphoreHandle_t _lock;
#endif
}; };
extern void serialEventRun(void) __attribute__((weak)); extern void serialEventRun(void) __attribute__((weak));

View File

@ -34,9 +34,7 @@ struct uart_struct_t {
uint8_t num; uint8_t num;
bool has_peek; bool has_peek;
uint8_t peek_byte; uint8_t peek_byte;
QueueHandle_t uart_event_queue; QueueHandle_t uart_event_queue; // export it by some uartGetEventQueue() function
void (*onReceive)(void);
TaskHandle_t envent_task;
}; };
#if CONFIG_DISABLE_HAL_LOCKS #if CONFIG_DISABLE_HAL_LOCKS
@ -45,12 +43,12 @@ struct uart_struct_t {
#define UART_MUTEX_UNLOCK() #define UART_MUTEX_UNLOCK()
static uart_t _uart_bus_array[] = { static uart_t _uart_bus_array[] = {
{0, false, 0, NULL, NULL, NULL}, {0, false, 0, NULL},
#if SOC_UART_NUM > 1 #if SOC_UART_NUM > 1
{1, false, 0, NULL, NULL, NULL}, {1, false, 0, NULL},
#endif #endif
#if SOC_UART_NUM > 2 #if SOC_UART_NUM > 2
{2, false, 0, NULL, NULL, NULL}, {2, false, 0, NULL},
#endif #endif
}; };
@ -60,12 +58,12 @@ static uart_t _uart_bus_array[] = {
#define UART_MUTEX_UNLOCK() xSemaphoreGive(uart->lock) #define UART_MUTEX_UNLOCK() xSemaphoreGive(uart->lock)
static uart_t _uart_bus_array[] = { static uart_t _uart_bus_array[] = {
{NULL, 0, false, 0, NULL, NULL, NULL}, {NULL, 0, false, 0, NULL},
#if SOC_UART_NUM > 1 #if SOC_UART_NUM > 1
{NULL, 1, false, 0, NULL, NULL, NULL}, {NULL, 1, false, 0, NULL},
#endif #endif
#if SOC_UART_NUM > 2 #if SOC_UART_NUM > 2
{NULL, 2, false, 0, NULL, NULL, NULL}, {NULL, 2, false, 0, NULL},
#endif #endif
}; };
@ -84,69 +82,22 @@ uint32_t _get_effective_baudrate(uint32_t baudrate)
} }
} }
// Routines that take care of UART events will be in the HardwareSerial Class code
void uartOnReceive(uart_t* uart, void(*function)(void)) void uartGetEventQueue(uart_t* uart, QueueHandle_t *q)
{ {
if(uart == NULL || function == NULL) { // passing back NULL for the Queue pointer when UART is not initialized yet
*q = NULL;
if(uart == NULL) {
return; return;
} }
UART_MUTEX_LOCK(); *q = uart->uart_event_queue;
uart->onReceive = function; return;
UART_MUTEX_UNLOCK();
} }
static void uart_event_task(void *args)
{
uart_t* uart = (uart_t *)args;
uart_event_t event;
for(;;) {
//Waiting for UART event.
if(xQueueReceive(uart->uart_event_queue, (void * )&event, (portTickType)portMAX_DELAY)) {
switch(event.type) {
//Event of UART receving data
case UART_DATA:
if(uart->onReceive) uart->onReceive();
break;
//Event of HW FIFO overflow detected
case UART_FIFO_OVF:
log_w("UART%d FIFO Overflow. Flushing data. Consider adding Flow Control to your Application.", uart->num);
uart_flush_input(uart->num);
xQueueReset(uart->uart_event_queue);
break;
//Event of UART ring buffer full
case UART_BUFFER_FULL:
log_w("UART%d Buffer Full. Flushing data. Consider encreasing your buffer size of your Application.", uart->num);
uart_flush_input(uart->num);
xQueueReset(uart->uart_event_queue);
break;
//Event of UART RX break detected
case UART_BREAK:
log_w("UART%d RX break.", uart->num);
break;
//Event of UART parity check error
case UART_PARITY_ERR:
log_w("UART%d parity error.", uart->num);
break;
//Event of UART frame error
case UART_FRAME_ERR:
log_w("UART%d frame error.", uart->num);
break;
//Others
default:
log_w("UART%d unknown event type %d.", uart->num, event.type);
break;
}
}
}
vTaskDelete(NULL);
}
bool uartIsDriverInstalled(uart_t* uart) bool uartIsDriverInstalled(uart_t* uart)
{ {
if(uart == NULL) { if(uart == NULL) {
return 0; return false;
} }
if (uart_is_driver_installed(uart->num)) { if (uart_is_driver_installed(uart->num)) {
@ -222,12 +173,6 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx
ESP_ERROR_CHECK(uart_set_line_inverse(uart_nr, UART_SIGNAL_TXD_INV | UART_SIGNAL_RXD_INV)); ESP_ERROR_CHECK(uart_set_line_inverse(uart_nr, UART_SIGNAL_TXD_INV | UART_SIGNAL_RXD_INV));
} }
// Creating UART event Task
xTaskCreate(uart_event_task, "uart_event_task", 2048, uart, configMAX_PRIORITIES - 1, &(uart->envent_task));
if (!uart->envent_task) {
log_e(" -- UART%d Event Task not Created!", uart_nr);
}
UART_MUTEX_UNLOCK(); UART_MUTEX_UNLOCK();
uartFlush(uart); uartFlush(uart);
@ -242,11 +187,6 @@ void uartEnd(uart_t* uart)
UART_MUTEX_LOCK(); UART_MUTEX_LOCK();
uart_driver_delete(uart->num); uart_driver_delete(uart->num);
if (uart->envent_task) {
vTaskDelete(uart->envent_task);
uart->envent_task = NULL;
uart->onReceive = NULL;
}
UART_MUTEX_UNLOCK(); UART_MUTEX_UNLOCK();
} }

View File

@ -22,6 +22,8 @@ extern "C" {
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#define SERIAL_5N1 0x8000010 #define SERIAL_5N1 0x8000010
#define SERIAL_6N1 0x8000014 #define SERIAL_6N1 0x8000014
@ -62,7 +64,8 @@ typedef struct uart_struct_t uart_t;
uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t queueLen, bool inverted, uint8_t rxfifo_full_thrhd); uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t queueLen, bool inverted, uint8_t rxfifo_full_thrhd);
void uartEnd(uart_t* uart); void uartEnd(uart_t* uart);
void uartOnReceive(uart_t* uart, void(*function)(void)); // This is used to retrieve the Event Queue pointer from a UART IDF Driver in order to allow user to deal with its events
void uartGetEventQueue(uart_t* uart, QueueHandle_t *q);
uint32_t uartAvailable(uart_t* uart); uint32_t uartAvailable(uart_t* uart);
uint32_t uartAvailableForWrite(uart_t* uart); uint32_t uartAvailableForWrite(uart_t* uart);