未验证 提交 5dff15ce 编写于 作者: R Rodrigo Garcia 提交者: GitHub

Fixes inconsistencies and adds extended HardwareSerial examples (#7412)

* adds extended HardwareSerial examples

* Adds new example with Serial RxTimeout

* adds and improves Serial onReceive expamples

* adjust includes CMake - UART example

* adjust includes CMake - UART example

* fixes CMake and CI

* adds ESP/Serial to CMakeList

* adds ESP/Serial to CMakeList

* fixes demo include

* fixes BREAK demo

* fixes onReceive demo

* Changes FIFO Full criteria

Changed the "1-by-1" Serial only when baud rate is 57600 or lower.

* example code replacement

* replaces functions in hal
上级 a95ce27a
......@@ -139,8 +139,9 @@ _rxBufferSize(256),
_txBufferSize(0),
_onReceiveCB(NULL),
_onReceiveErrorCB(NULL),
_onReceiveTimeout(true),
_onReceiveTimeout(false),
_rxTimeout(2),
_rxFIFOFull(0),
_eventTask(NULL)
#if !CONFIG_DISABLE_HAL_LOCKS
,_lock(NULL)
......@@ -206,12 +207,23 @@ void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout)
HSERIAL_MUTEX_LOCK();
// function may be NULL to cancel onReceive() from its respective task
_onReceiveCB = function;
// When Rx timeout is Zero (disabled), there is only one possible option that is callback when FIFO reaches 120 bytes
_onReceiveTimeout = _rxTimeout > 0 ? onlyOnTimeout : false;
// this can be called after Serial.begin(), therefore it shall create the event task
if (function != NULL && _uart != NULL && _eventTask == NULL) {
_createEventTask(this); // Create event task
// setting the callback to NULL will just disable it
if (_onReceiveCB != NULL) {
// When Rx timeout is Zero (disabled), there is only one possible option that is callback when FIFO reaches 120 bytes
_onReceiveTimeout = _rxTimeout > 0 ? onlyOnTimeout : false;
// in case that onReceive() shall work only with RX Timeout, FIFO shall be high
// this is a work around for an IDF issue with events and low FIFO Full value (< 3)
if (_onReceiveTimeout) {
uartSetRxFIFOFull(_uart, 120);
log_w("OnReceive is set to Timeout only, thus FIFO Full is now 120 bytes.");
}
// this method can be called after Serial.begin(), therefore it shall create the event task
if (_uart != NULL && _eventTask == NULL) {
_createEventTask(this); // Create event task
}
}
HSERIAL_MUTEX_UNLOCK();
}
......@@ -224,7 +236,14 @@ void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout)
void HardwareSerial::setRxFIFOFull(uint8_t fifoBytes)
{
HSERIAL_MUTEX_LOCK();
// in case that onReceive() shall work only with RX Timeout, FIFO shall be high
// this is a work around for an IDF issue with events and low FIFO Full value (< 3)
if (_onReceiveCB != NULL && _onReceiveTimeout) {
fifoBytes = 120;
log_w("OnReceive is set to Timeout only, thus FIFO Full is now 120 bytes.");
}
uartSetRxFIFOFull(_uart, fifoBytes); // Set new timeout
if (fifoBytes > 0 && fifoBytes < SOC_UART_FIFO_LEN - 1) _rxFIFOFull = fifoBytes;
HSERIAL_MUTEX_UNLOCK();
}
......@@ -299,7 +318,6 @@ void HardwareSerial::_uartEventTask(void *args)
}
if (currentErr != UART_NO_ERROR) {
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(currentErr);
if(uart->_onReceiveCB && uart->available() > 0) uart->_onReceiveCB(); // forces User Callback too
}
}
}
......@@ -388,8 +406,24 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
// Set UART RX timeout
uartSetRxTimeout(_uart, _rxTimeout);
// Set UART FIFO Full depending on the baud rate.
// Lower baud rates will force to emulate byte-by-byte reading
// Higher baud rates will keep IDF default of 120 bytes for FIFO FULL Interrupt
// It can also be changed by the application at any time
if (!_rxFIFOFull) { // it has not being changed before calling begin()
// set a default FIFO Full value for the IDF driver
uint8_t fifoFull = 1;
if (baud > 57600 || (_onReceiveCB != NULL && _onReceiveTimeout)) {
fifoFull = 120;
}
uartSetRxFIFOFull(_uart, fifoFull);
_rxFIFOFull = fifoFull;
}
_rxPin = rxPin;
_txPin = txPin;
HSERIAL_MUTEX_UNLOCK();
}
......@@ -408,8 +442,12 @@ void HardwareSerial::end(bool fullyTerminate)
if (uartGetDebug() == _uart_nr) {
uartSetDebug(0);
}
_rxFIFOFull = 0;
uartDetachPins(_uart, _rxPin, _txPin, _ctsPin, _rtsPin);
_rxPin = _txPin = _ctsPin = _rtsPin = -1;
}
delay(10);
uartEnd(_uart);
......
......@@ -177,7 +177,7 @@ protected:
OnReceiveErrorCb _onReceiveErrorCB;
// _onReceive and _rxTimeout have be consistent when timeout is disabled
bool _onReceiveTimeout;
uint8_t _rxTimeout;
uint8_t _rxTimeout, _rxFIFOFull;
TaskHandle_t _eventTask;
#if !CONFIG_DISABLE_HAL_LOCKS
SemaphoreHandle_t _lock;
......
......@@ -22,7 +22,9 @@
#include "hal/uart_ll.h"
#include "soc/soc_caps.h"
#include "soc/uart_struct.h"
#include "soc/uart_periph.h"
#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "esp_rom_gpio.h"
......@@ -743,3 +745,54 @@ uartDetectBaudrate(uart_t *uart)
return 0;
#endif
}
/*
These functions are for testing puspose only and can be used in Arduino Sketches
Those are used in the UART examples
*/
/*
This is intended to make an internal loopback connection using IOMUX
The function uart_internal_loopback() shall be used right after Arduino Serial.begin(...)
This code "replaces" the physical wiring for connecting TX <--> RX in a loopback
*/
// gets the right TX SIGNAL, based on the UART number
#if SOC_UART_NUM > 2
#define UART_TX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : (uartNumber == UART_NUM_1 ? U1TXD_OUT_IDX : U2TXD_OUT_IDX))
#else
#define UART_TX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : U1TXD_OUT_IDX)
#endif
/*
Make sure UART's RX signal is connected to TX pin
This creates a loop that lets us receive anything we send on the UART
*/
void uart_internal_loopback(uint8_t uartNum, int8_t rxPin)
{
if (uartNum > SOC_UART_NUM - 1 || !GPIO_IS_VALID_GPIO(rxPin)) return;
esp_rom_gpio_connect_out_signal(rxPin, UART_TX_SIGNAL(uartNum), false, false);
}
/*
This is intended to generate BREAK in an UART line
*/
// Forces a BREAK in the line based on SERIAL_8N1 configuration at any baud rate
void uart_send_break(uint8_t uartNum)
{
uint32_t currentBaudrate = 0;
uart_get_baudrate(uartNum, &currentBaudrate);
// calculates 10 bits of breaks in microseconds for baudrates up to 500mbps
// This is very sensetive timing... it works fine for SERIAL_8N1
uint32_t breakTime = (uint32_t) (10.0 * (1000000.0 / currentBaudrate));
uart_set_line_inverse(uartNum, UART_SIGNAL_TXD_INV);
ets_delay_us(breakTime);
uart_set_line_inverse(uartNum, UART_SIGNAL_INV_DISABLE);
}
// Sends a buffer and at the end of the stream, it generates BREAK in the line
int uart_send_msg_with_break(uint8_t uartNum, uint8_t *msg, size_t msgSize)
{
// 12 bits long BREAK for 8N1
return uart_write_bytes_with_break(uartNum, (const void *)msg, msgSize, 12);
}
\ No newline at end of file
......@@ -102,6 +102,22 @@ void uartSetHwFlowCtrlMode(uart_t *uart, uint8_t mode, uint8_t threshold);
void uartStartDetectBaudrate(uart_t *uart);
unsigned long uartDetectBaudrate(uart_t *uart);
/*
These functions are for testing puspose only and can be used in Arduino Sketches
Those are used in the UART examples
*/
// Make sure UART's RX signal is connected to TX pin
// This creates a loop that lets us receive anything we send on the UART
void uart_internal_loopback(uint8_t uartNum, int8_t rxPin);
// Routines that generate BREAK in the UART for testing purpose
// Forces a BREAK in the line based on SERIAL_8N1 configuration at any baud rate
void uart_send_break(uint8_t uartNum);
// Sends a buffer and at the end of the stream, it generates BREAK in the line
int uart_send_msg_with_break(uint8_t uartNum, uint8_t *msg, size_t msgSize);
#ifdef __cplusplus
}
......
/*
This Sketch demonstrates how to use onReceiveError(callbackFunc) with HardwareSerial
void HardwareSerial::onReceiveError(OnReceiveErrorCb function)
It is possible to register a UART callback function that will be called
everytime that UART detects an error which is also associated to an interrupt.
There are some possible UART errors:
UART_BREAK_ERROR - when a BREAK event is detected in the UART line. In that case, a BREAK may
be read as one or more bytes ZERO as part of the data received by the UART peripheral.
UART_BUFFER_FULL_ERROR - When the RX UART buffer is full. By default, Arduino will allocate a 256 bytes
RX buffer. As data is received, it is copied to the UART driver buffer, but when it is full and data can't
be copied anymore, this Error is issued. To prevent it the application can use
HardwareSerial::setRxBufferSize(size_t new_size), before using HardwareSerial::begin()
UART_FIFO_OVF_ERROR - When the UART peripheral RX FIFO is full and data is still arriving, this error is issued.
The UART driver will stash RX FIFO and the data will be lost. In order to prevent, the application shall set a
good buffer size using HardwareSerial::setRxBufferSize(size_t new_size), before using HardwareSerial::begin()
UART_FRAME_ERROR - When the UART peripheral detects a UART frame error, this error is issued. It may happen because
of line noise or bad impiedance.
UART_PARITY_ERROR - When the UART peripheral detects a parity bit error, this error will be issued.
In summary, HardwareSerial::onReceiveError() works like an UART Error Notification callback.
Errors have priority in the order of the callbacks, therefore, as soon as an error is detected,
the registered callback is executed firt, and only after that, the OnReceive() registered
callback function will be executed. This will give opportunity for the Application to take action
before reading data, if necessary.
In long UART transmissions, some data will be received based on FIFO Full parameter, and whenever
an error ocurs, it will raise the UART error interrupt.
This sketch produces BREAK UART error in the begining of a transmission and also at the end of a
transmission. It will be possible to understand the order of the events in the logs.
*/
#include <Arduino.h>
// There are two ways to make this sketch work:
// By physically connecting the pins 4 and 5 and then create a physical UART loopback,
// Or by using the internal IO_MUX to connect the TX signal to the RX pin, creating the
// same loopback internally.
#define USE_INTERNAL_PIN_LOOPBACK 1 // 1 uses the internal loopback, 0 for wiring pins 4 and 5 externally
#define DATA_SIZE 26 // 26 bytes is a lower than RX FIFO size (127 bytes)
#define BAUD 9600 // Any baudrate from 300 to 115200
#define TEST_UART 1 // Serial1 will be used for the loopback testing with different RX FIFO FULL values
#define RXPIN 4 // GPIO 4 => RX for Serial1
#define TXPIN 5 // GPIO 5 => TX for Serial1
#define BREAK_BEFORE_MSG 0
#define BREAK_AT_END_MSG 1
uint8_t fifoFullTestCases[] = {120, 20, 5, 1};
// volatile declaration will avoid any compiler optimization when reading variable values
volatile size_t sent_bytes = 0, received_bytes = 0;
const char *uartErrorStrings[] = {
"UART_NO_ERROR",
"UART_BREAK_ERROR",
"UART_BUFFER_FULL_ERROR",
"UART_FIFO_OVF_ERROR",
"UART_FRAME_ERROR",
"UART_PARITY_ERROR"
};
// Callback function that will treat the UART errors
void onReceiveErrorFunction(hardwareSerial_error_t err) {
// This is a callback function that will be activated on UART RX Error Events
Serial.printf("\n-- onReceiveError [ERR#%d:%s] \n", err, uartErrorStrings[err]);
Serial.printf("-- onReceiveError:: There are %d bytes available.\n", Serial1.available());
}
// Callback function that will deal with arriving UART data
void onReceiveFunction() {
// This is a callback function that will be activated on UART RX events
size_t available = Serial1.available();
received_bytes += available;
Serial.printf("onReceive Callback:: There are %d bytes available: {", available);
while (available --) {
Serial.print((char)Serial1.read());
}
Serial.println("}");
}
void setup() {
// UART0 will be used to log information into Serial Monitor
Serial.begin(115200);
// UART1 will have its RX<->TX cross connected
// GPIO4 <--> GPIO5 using external wire
Serial1.begin(BAUD, SERIAL_8N1, RXPIN, TXPIN); // Rx = 4, Tx = 5 will work for ESP32, S2, S3 and C3
#if USE_INTERNAL_PIN_LOOPBACK
uart_internal_loopback(TEST_UART, RXPIN);
#endif
for (uint8_t i = 0; i < sizeof(fifoFullTestCases); i++) {
Serial.printf("\n\n================================\nTest Case #%d BREAK at END\n================================\n", i + 1);
// First sending BREAK at the end of the UART data transmission
testAndReport(fifoFullTestCases[i], BREAK_AT_END_MSG);
Serial.printf("\n\n================================\nTest Case #%d BREAK at BEGINING\n================================\n", i + 1);
// Now sending BREAK at the begining of the UART data transmission
testAndReport(fifoFullTestCases[i], BREAK_BEFORE_MSG);
Serial.println("========================\nFinished!");
}
}
void loop() {
}
void testAndReport(uint8_t fifoFull, bool break_at_the_end) {
// Let's send 125 bytes from Serial1 rx<->tx and mesaure time using diferent FIFO Full configurations
received_bytes = 0;
sent_bytes = DATA_SIZE; // 26 characters
uint8_t dataSent[DATA_SIZE + 1];
dataSent[DATA_SIZE] = '\0'; // string null terminator, for easy printing.
// initialize all data
for (uint8_t i = 0; i < DATA_SIZE; i++) {
dataSent[i] = 'A' + i; // fill it with characters A..Z
}
Serial.printf("\nTesting onReceive for receiving %d bytes at %d baud, using RX FIFO Full = %d.\n", sent_bytes, BAUD, fifoFull);
Serial.println("onReceive is called on both FIFO Full and RX Timeout events.");
if (break_at_the_end) {
Serial.printf("BREAK event will be sent at the END of the %d bytes\n", sent_bytes);
} else {
Serial.printf("BREAK event will be sent at the BEGINING of the %d bytes\n", sent_bytes);
}
Serial.flush(); // wait Serial FIFO to be empty and then spend almost no time processing it
Serial1.setRxFIFOFull(fifoFull); // testing diferent result based on FIFO Full setup
Serial1.onReceive(onReceiveFunction); // sets a RX callback function for Serial 1
Serial1.onReceiveError(onReceiveErrorFunction); // sets a RX callback function for Serial 1
if (break_at_the_end) {
sent_bytes = uart_send_msg_with_break(TEST_UART, dataSent, DATA_SIZE);
} else {
uart_send_break(TEST_UART);
sent_bytes = Serial1.write(dataSent, DATA_SIZE);
}
Serial.printf("\nSent String: %s\n", dataSent);
while (received_bytes < sent_bytes) {
// just wait for receiving all byte in the callback...
}
Serial.printf("\nIt has sent %d bytes from Serial1 TX to Serial1 RX\n", sent_bytes);
Serial.printf("onReceive() has read a total of %d bytes\n", received_bytes);
Serial1.onReceiveError(NULL); // resets/disables the RX Error callback function for Serial 1
Serial1.onReceive(NULL); // resets/disables the RX callback function for Serial 1
}
/*
This Sketch demonstrates how to use onReceive(callbackFunc) with HardwareSerial
void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout = false)
It is possible to register a UART callback function that will be called
everytime that UART receives data and an associated interrupt is generated.
The receiving data interrupt can occur because of two possible events:
1- UART FIFO FULL: it happens when internal UART FIFO reaches a certain number of bytes.
Its full capacity is 127 bytes. The FIFO Full threshold for the interrupt can be changed
using HardwareSerial::setRxFIFOFull(uint8_t fifoFull).
Default FIFO Full Threshold is set at the UART initialzation using HardwareSerial::begin()
This will depend on the baud rate set with when begin() is executed.
For a baudrate of 115200 or lower, it it just 1 byte, mimicking original Arduino UART driver.
For a baudrate over 115200 it will be 120 bytes for higher performance.
Anyway it can be changed by the application at anytime.
2- UART RX Timeout: it happens, based on a timeout equivalent to a number of symbols at
the current baud rate. If the UART line is idle for this timeout, it will raise an interrupt.
This time can be changed by HardwareSerial::setRxTimeout(uint8_t rxTimeout)
When any of those two interrupts occur, IDF UART driver will copy FIFO data to its internal
RingBuffer and then Arduino can read such data. At the same time, Arduino Layer will execute
the callback function defined with HardwareSerial::onReceive().
<bool onlyOnTimeout> parameter (default false) can be used by the application to tell Arduino to
only execute the callback when the second event above happens (Rx Timeout). At this time all
received data will be available to be read by the Arduino application. But if the number of
received bytes is higher than the FIFO space, it will generate an error of FIFO overflow.
In order to avoid such problem, the application shall set an appropriate RX buffer size using
HardwareSerial::setRxBufferSize(size_t new_size) before executing begin() for the Serial port.
In summary, HardwareSerial::onReceive() works like an RX Interrupt callback, that can be adjusted
using HardwareSerial::setRxFIFOFull() and HardwareSerial::setRxTimeout().
*/
#include <Arduino.h>
// There are two ways to make this sketch work:
// By physically connecting the pins 4 and 5 and then create a physical UART loopback,
// Or by using the internal IO_MUX to connect the TX signal to the RX pin, creating the
// same loopback internally.
#define USE_INTERNAL_PIN_LOOPBACK 1 // 1 uses the internal loopback, 0 for wiring pins 4 and 5 externally
#define DATA_SIZE 26 // 26 bytes is a lower than RX FIFO size (127 bytes)
#define BAUD 9600 // Any baudrate from 300 to 115200
#define TEST_UART 1 // Serial1 will be used for the loopback testing with different RX FIFO FULL values
#define RXPIN 4 // GPIO 4 => RX for Serial1
#define TXPIN 5 // GPIO 5 => TX for Serial1
uint8_t fifoFullTestCases[] = {120, 20, 5, 1};
// volatile declaration will avoid any compiler optimization when reading variable values
volatile size_t sent_bytes = 0, received_bytes = 0;
void onReceiveFunction(void) {
// This is a callback function that will be activated on UART RX events
size_t available = Serial1.available();
received_bytes += available;
Serial.printf("onReceive Callback:: There are %d bytes available: ", available);
while (available --) {
Serial.print((char)Serial1.read());
}
Serial.println();
}
void setup() {
// UART0 will be used to log information into Serial Monitor
Serial.begin(115200);
// UART1 will have its RX<->TX cross connected
// GPIO4 <--> GPIO5 using external wire
Serial1.begin(BAUD, SERIAL_8N1, RXPIN, TXPIN); // Rx = 4, Tx = 5 will work for ESP32, S2, S3 and C3
#if USE_INTERNAL_PIN_LOOPBACK
uart_internal_loopback(TEST_UART, RXPIN);
#endif
for (uint8_t i = 0; i < sizeof(fifoFullTestCases); i++) {
Serial.printf("\n\n================================\nTest Case #%d\n================================\n", i + 1);
// onReceive callback will be called on FIFO Full and RX timeout - default behaviour
testAndReport(fifoFullTestCases[i], false);
}
Serial.printf("\n\n================================\nTest Case #6\n================================\n");
// onReceive callback will be called just on RX timeout - using onlyOnTimeout = true
// FIFO Full parameter (5 bytes) won't matter for the execution of this test case
// because onReceive() uses only RX Timeout to be activated
testAndReport(5, true);
}
void loop() {
}
void testAndReport(uint8_t fifoFull, bool onlyOnTimeOut) {
// Let's send 125 bytes from Serial1 rx<->tx and mesaure time using diferent FIFO Full configurations
received_bytes = 0;
sent_bytes = DATA_SIZE; // 26 characters
uint8_t dataSent[DATA_SIZE + 1];
dataSent[DATA_SIZE] = '\0'; // string null terminator, for easy printing.
// initialize all data
for (uint8_t i = 0; i < DATA_SIZE; i++) {
dataSent[i] = 'A' + i; // fill it with characters A..Z
}
Serial.printf("\nTesting onReceive for receiving %d bytes at %d baud, using RX FIFO Full = %d.\n", sent_bytes, BAUD, fifoFull);
if (onlyOnTimeOut) {
Serial.println("onReceive is called just on RX Timeout!");
} else {
Serial.println("onReceive is called on both FIFO Full and RX Timeout events.");
}
Serial.flush(); // wait Serial FIFO to be empty and then spend almost no time processing it
Serial1.setRxFIFOFull(fifoFull); // testing diferent result based on FIFO Full setup
Serial1.onReceive(onReceiveFunction, onlyOnTimeOut); // sets a RX callback function for Serial 1
sent_bytes = Serial1.write(dataSent, DATA_SIZE); // ESP32 TX FIFO is about 128 bytes, 125 bytes will fit fine
Serial.printf("\nSent String: %s\n", dataSent);
while (received_bytes < sent_bytes) {
// just wait for receiving all byte in the callback...
}
Serial.printf("\nIt has sent %d bytes from Serial1 TX to Serial1 RX\n", sent_bytes);
Serial.printf("onReceive() has read a total of %d bytes\n", received_bytes);
Serial.println("========================\nFinished!");
Serial1.onReceive(NULL); // resets/disables the RX callback function for Serial 1
}
/*
*
* This Sketch demonstrates the effect of changing RX FIFO Full parameter into HardwareSerial Class
* Serial.setRxFIFOFull(byte) is used to change it.
* By default, UART ISR will wait for 120 bytes to arrive into UART before making the data available
* to be read by an Arduino Sketch. It may also release fewer bytes after an RX Timeout equivalent by
* default to 2 UART symbols.
*
* The way we demonstrate the effect of this parameter is by measuring the time the Sketch takes
* to read data using Arduino HardwareSerial API.
*
* The higher RX FIFO Full is, the lower consumption of the core to process and make the data available.
* At the same time, it may take longer for the Sketch to be able to read it, because the data must first
* populate RX UART FIFO.
*
* The lower RX FIFO Full is, the higher consumption of the core to process and make the data available.
* This is because the core will be interrupted often and it will copy data from the RX FIFO to the Arduino
* internal buffer to be read by the sketch. By other hand, the data will be made available to the sketch
* faster, in a close to byte by byte communication.
*
* Therefore, it allows the decision of the architecture to be designed by the developer.
* Some application based on certain protocols may need the sketch to read the Serial Port byte by byte, for example.
*
*/
#include <Arduino.h>
// There are two ways to make this sketch work:
// By physically connecting the pins 4 and 5 and then create a physical UART loopback,
// Or by using the internal IO_MUX to connect the TX signal to the RX pin, creating the
// same loopback internally.
#define USE_INTERNAL_PIN_LOOPBACK 1 // 1 uses the internal loopback, 0 for wiring pins 4 and 5 externally
#define DATA_SIZE 125 // 125 bytes is a bit higher than the default 120 bytes of RX FIFO FULL
#define BAUD 9600 // Any baudrate from 300 to 115200
#define TEST_UART 1 // Serial1 will be used for the loopback testing with different RX FIFO FULL values
#define RXPIN 4 // GPIO 4 => RX for Serial1
#define TXPIN 5 // GPIO 5 => TX for Serial1
uint8_t fifoFullTestCases[] = {120, 20, 5, 1};
void setup() {
// UART0 will be used to log information into Serial Monitor
Serial.begin(115200);
// UART1 will have its RX<->TX cross connected
// GPIO4 <--> GPIO5 using external wire
Serial1.begin(BAUD, SERIAL_8N1, RXPIN, TXPIN); // Rx = 4, Tx = 5 will work for ESP32, S2, S3 and C3
#if USE_INTERNAL_PIN_LOOPBACK
uart_internal_loopback(TEST_UART, RXPIN);
#endif
for (uint8_t i = 0; i < sizeof(fifoFullTestCases); i++) {
Serial.printf("\n\n================================\nTest Case #%d\n================================\n", i + 1);
testAndReport(fifoFullTestCases[i]);
}
}
void loop() {
}
void testAndReport(uint8_t fifoFull) {
// Let's send 125 bytes from Serial1 rx<->tx and mesaure time using diferent FIFO Full configurations
uint8_t bytesReceived = 0;
uint8_t dataSent[DATA_SIZE], dataReceived[DATA_SIZE];
uint32_t timeStamp[DATA_SIZE], bytesJustReceived[DATA_SIZE];
uint8_t i;
// initialize all data
for (i = 0; i < DATA_SIZE; i++) {
dataSent[i] = '0' + (i % 10); // fill it with a repeated sequence of 0..9 characters
dataReceived[i] = 0;
timeStamp[i] = 0;
bytesJustReceived[i] = 0;
}
Serial.printf("Testing the time for receiving %d bytes at %d baud, using RX FIFO Full = %d:", DATA_SIZE, BAUD, fifoFull);
Serial.flush(); // wait Serial FIFO to be empty and then spend almost no time processing it
Serial1.setRxFIFOFull(fifoFull); // testing diferent result based on FIFO Full setup
size_t sentBytes = Serial1.write(dataSent, sizeof(dataSent)); // ESP32 TX FIFO is about 128 bytes, 125 bytes will fit fine
uint32_t now = millis();
i = 0;
while (bytesReceived < DATA_SIZE) {
bytesReceived += (bytesJustReceived[i] = Serial1.read(dataReceived + bytesReceived, DATA_SIZE));
timeStamp[i] = millis();
if (bytesJustReceived[i] > 0) i++; // next data only when we read something from Serial1
// safety for array limit && timeout... in 5 seconds...
if (i == DATA_SIZE || millis() - now > 5000) break;
}
uint32_t pastTime = millis() - now;
Serial.printf("\nIt has sent %d bytes from Serial1 TX to Serial1 RX\n", sentBytes);
Serial.printf("It took %d milliseconds to read %d bytes\n", pastTime, bytesReceived);
Serial.printf("Per execution Serial.read() number of bytes data and time information:\n");
for (i = 0; i < DATA_SIZE; i++) {
Serial.printf("#%03d - Received %03d bytes after %d ms.\n", i, bytesJustReceived[i], i > 0 ? timeStamp[i] - timeStamp[i - 1] : timeStamp[i] - now);
if (i != DATA_SIZE - 1 && bytesJustReceived[i + 1] == 0) break;
}
Serial.println("========================\nFinished!");
}
/*
This Sketch demonstrates the effect of changing RX Timeout parameter into HardwareSerial Class
Serial.setRxTimeout(byte) is used to change it.
By default, UART ISR will wait for an RX Timeout equivalent to 2 UART symbols to understand that a flow.
of UART data has ended. For example, if just one byte is received, UART will send about 10 to
11 bits depending of the configuration (parity, number of stopbits). The timeout is measured in
number of UART symbols, with 10 or 11 bits, in the current baudrate.
For 9600 baud, 1 bit takes 1/9600 of a second, equivalent to 104 microseconds, therefore, for 10 bits,
it takes about 1ms. A timeout of 2 UART symbols, with about 20 bits, would take about 2.1 milliseconds
for the ESP32 UART to trigger an IRQ telling the UART driver that the transmission has ended.
Just at this point, the data will be made available to Arduino HardwareSerial API (read(), available(), etc).
The way we demonstrate the effect of this parameter is by measuring the time the Sketch takes
to read data using Arduino HardwareSerial API.
The higher RX Timeout is, the longer it will take to make the data available, when a flow of data ends.
UART driver works copying data from UART FIFO to Arduino internal buffer.
The driver will copy data from FIFO when RX Timeout is detected or when FIFO is full.
ESP32 FIFO has 128 bytes and by default, the driver will copy the data when FIFO reaches 120 bytes.
If UART receives less than 120 bytes, it will wait RX Timeout to understand that the bus is IDLE and
then copy the data from the FIFO to the Arduino internal buffer, making it availble to the Arduino API.
*/
#include <Arduino.h>
// There are two ways to make this sketch work:
// By physically connecting the pins 4 and 5 and then create a physical UART loopback,
// Or by using the internal IO_MUX to connect the TX signal to the RX pin, creating the
// same loopback internally.
#define USE_INTERNAL_PIN_LOOPBACK 1 // 1 uses the internal loopback, 0 for wiring pins 4 and 5 externally
#define DATA_SIZE 10 // 10 bytes is lower than the default 120 bytes of RX FIFO FULL
#define BAUD 9600 // Any baudrate from 300 to 115200
#define TEST_UART 1 // Serial1 will be used for the loopback testing with different RX FIFO FULL values
#define RXPIN 4 // GPIO 4 => RX for Serial1
#define TXPIN 5 // GPIO 5 => TX for Serial1
uint8_t rxTimeoutTestCases[] = {50, 20, 10, 5, 1};
void setup() {
// UART0 will be used to log information into Serial Monitor
Serial.begin(115200);
// UART1 will have its RX<->TX cross connected
// GPIO4 <--> GPIO5 using external wire
Serial1.begin(BAUD, SERIAL_8N1, RXPIN, TXPIN); // Rx = 4, Tx = 5 will work for ESP32, S2, S3 and C3
#if USE_INTERNAL_PIN_LOOPBACK
uart_internal_loopback(TEST_UART, RXPIN);
#endif
for (uint8_t i = 0; i < sizeof(rxTimeoutTestCases); i++) {
Serial.printf("\n\n================================\nTest Case #%d\n================================\n", i + 1);
testAndReport(rxTimeoutTestCases[i]);
}
}
void loop() {
}
void testAndReport(uint8_t rxTimeout) {
// Let's send 10 bytes from Serial1 rx<->tx and mesaure time using diferent Rx Timeout configurations
uint8_t bytesReceived = 0;
uint8_t dataSent[DATA_SIZE], dataReceived[DATA_SIZE];
uint8_t i;
// initialize all data
for (i = 0; i < DATA_SIZE; i++) {
dataSent[i] = '0' + (i % 10); // fill it with a repeated sequence of 0..9 characters
dataReceived[i] = 0;
}
Serial.printf("Testing the time for receiving %d bytes at %d baud, using RX Timeout = %d:", DATA_SIZE, BAUD, rxTimeout);
Serial.flush(); // wait Serial FIFO to be empty and then spend almost no time processing it
Serial1.setRxTimeout(rxTimeout); // testing diferent results based on Rx Timeout setup
// For baud rates lower or equal to 57600, ESP32 Arduino makes it get byte-by-byte from FIFO, thus we will change it here:
Serial1.setRxFIFOFull(120); // forces it to wait receiving 120 bytes in FIFO before making it availble to Arduino
size_t sentBytes = Serial1.write(dataSent, sizeof(dataSent)); // ESP32 TX FIFO is about 128 bytes, 10 bytes will fit fine
uint32_t now = millis();
while (bytesReceived < DATA_SIZE) {
bytesReceived += Serial1.read(dataReceived, DATA_SIZE);
// safety for array limit && timeout... in 5 seconds...
if (millis() - now > 5000) break;
}
uint32_t pastTime = millis() - now;
Serial.printf("\nIt has sent %d bytes from Serial1 TX to Serial1 RX\n", sentBytes);
Serial.printf("It took %d milliseconds to read %d bytes\n", pastTime, bytesReceived);
Serial.print("Received data: [");
Serial.write(dataReceived, DATA_SIZE);
Serial.println("]");
Serial.println("========================\nFinished!");
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册