3.3.7
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
This Sketch demonstrates how to detect and set the baud rate when the UART0 is connected to
|
||||
some port that is sending data. It can be used with the Arduino IDE Serial Monitor to send the data.
|
||||
|
||||
Serial.begin(0) will start the baud rate detection. Valid range is 300 to 230400 baud.
|
||||
It will try to detect for 20 seconds, by default, while reading RX.
|
||||
This timeout of 20 seconds can be changed in the begin() function through <<timeout_ms>> parameter:
|
||||
|
||||
void HardwareSerial::begin(baud, config, rxPin, txPin, invert, <<timeout_ms>>, rxfifo_full_thrhd)
|
||||
|
||||
It is necessary that the other end sends some data within <<timeout_ms>>, otherwise the detection won't work.
|
||||
|
||||
IMPORTANT NOTE: baud rate detection seem to only work with ESP32 and ESP32-S2.
|
||||
In other other SoCs, it doesn't work.
|
||||
|
||||
*/
|
||||
|
||||
// Open the Serial Monitor with testing baud start typing and sending characters
|
||||
void setup() {
|
||||
Serial.begin(0); // it will try to detect the baud rate for 20 seconds
|
||||
|
||||
Serial.print("\n==>The baud rate is ");
|
||||
Serial.println(Serial.baudRate());
|
||||
|
||||
//after 20 seconds timeout, when not detected, it will return zero - in this case, we set it back to 115200.
|
||||
if (Serial.baudRate() == 0) {
|
||||
// Trying to set Serial to a safe state at 115200
|
||||
Serial.end();
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
delay(1000);
|
||||
log_e("Baud rate detection failed.");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
Hardware Flow Control Demo for ESP32
|
||||
|
||||
This sketch demonstrates UART hardware flow control using RTS (Request To Send)
|
||||
and CTS (Clear To Send) signals with UART1 (HardwareSerial Serial1).
|
||||
|
||||
CONFIGURATION:
|
||||
==============
|
||||
|
||||
Set USE_INTERNAL_MATRIX_PIN_LOOPBACK to 1 for internal GPIO matrix connections
|
||||
(no external wires needed). Set to 0 to use external wire connections.
|
||||
|
||||
PIN CONNECTIONS (when USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0):
|
||||
============================================================
|
||||
|
||||
For basic loopback with hardware flow control:
|
||||
- Connect GPIO2 (RTS1) to GPIO4 (CTS1) - Flow control loopback
|
||||
- Connect TX1 pin to RX1 pin - Data loopback
|
||||
|
||||
For GPIO-controlled flow control demonstration:
|
||||
- Connect TX1 pin to RX1 pin - Data loopback
|
||||
- Connect GPIO2 (RTS1) to GPIO5 (GPIO_RTS_MONITOR) - Monitor RTS state
|
||||
- Connect GPIO4 (CTS1) to GPIO13 (GPIO_CTS_CTRL) - Control CTS signal
|
||||
- Use GPIO13 to manually control CTS signal (LOW = allow, HIGH = block)
|
||||
|
||||
HARDWARE FLOW CONTROL EXPLANATION:
|
||||
===================================
|
||||
|
||||
RTS (Request To Send):
|
||||
- Output signal from UART (GPIO2 in this example)
|
||||
- Asserted LOW when UART is ready to receive data (RX buffer has space)
|
||||
- De-asserted HIGH when RX buffer is getting full (threshold reached)
|
||||
|
||||
CTS (Clear To Send):
|
||||
- Input signal to UART (GPIO4 in this example)
|
||||
- UART will only transmit when CTS is LOW (asserted)
|
||||
- UART will pause transmission when CTS is HIGH (de-asserted)
|
||||
|
||||
OPERATION:
|
||||
==========
|
||||
|
||||
The sketch demonstrates:
|
||||
- Periodic transmission of messages every second
|
||||
- Automatic flow control when USE_GPIO_CONTROL = false
|
||||
- Manual CTS control when USE_GPIO_CONTROL = true
|
||||
- Loopback reception of transmitted data
|
||||
- Status monitoring of RTS/CTS pin states
|
||||
|
||||
NOTE: When USE_INTERNAL_MATRIX_PIN_LOOPBACK = 1, no external connections
|
||||
are needed as the ESP32 GPIO matrix handles the loopback internally.
|
||||
*/
|
||||
|
||||
// setting it to 1 will allow internal matrix pin connection for RX1<->TX1 and RTS1<->CTS1
|
||||
// otherwise it needs a wire for cross connecting the pins
|
||||
#define USE_INTERNAL_MATRIX_PIN_LOOPBACK 1
|
||||
|
||||
// Pin definitions for UART1
|
||||
#define UART1_RX_PIN RX1 // Default GPIO - UART1 RX pin
|
||||
#define UART1_TX_PIN TX1 // Default GPIO - UART1 TX pin
|
||||
#define UART1_RTS_PIN 2 // GPIO2 - UART1 RTS pin (output from UART)
|
||||
#define UART1_CTS_PIN 4 // GPIO4 - UART1 CTS pin (input to UART)
|
||||
|
||||
// Optional: GPIO pins for manual flow control demonstration
|
||||
// If using GPIO-controlled flow control, connect:
|
||||
// - RTS1 to GPIO_RTS_MONITOR (to monitor RTS state)
|
||||
// - CTS1 to GPIO_CTS_CTRL (to control CTS signal)
|
||||
#define GPIO_RTS_MONITOR 5 // GPIO5 - Monitor RTS signal (connect RTS1 to this)
|
||||
#define GPIO_CTS_CTRL 13 // GPIO13 - Control CTS signal (connect CTS1 to this)
|
||||
|
||||
// Set to true to use GPIO-controlled flow control, false for simple loopback
|
||||
// Note: this control will be overridden by USE_INTERNAL_MATRIX_PIN_LOOPBACK when it is 1
|
||||
#define USE_GPIO_CONTROL false
|
||||
|
||||
// Variables for demonstration
|
||||
unsigned long lastSendTime = 0;
|
||||
unsigned long lastStatusTime = 0;
|
||||
const unsigned long sendInterval = 1000; // Send data every 1 second
|
||||
const unsigned long statusInterval = 2000; // Print status every 2 seconds
|
||||
int sendCounter = 0;
|
||||
|
||||
void printPinStatus() {
|
||||
Serial.println("\n=== UART1 Pin Status ===");
|
||||
Serial.printf("RX Pin (GPIO%d): Receiving data\n", UART1_RX_PIN);
|
||||
Serial.printf("TX Pin (GPIO%d): Transmitting data\n", UART1_TX_PIN);
|
||||
|
||||
if (USE_GPIO_CONTROL) {
|
||||
// Read RTS state from monitor GPIO (connected to RTS1)
|
||||
bool rtsState = digitalRead(GPIO_RTS_MONITOR);
|
||||
Serial.printf("RTS Pin (GPIO%d): %s (LOW = ready to receive)\n", UART1_RTS_PIN, rtsState == LOW ? "LOW (Ready)" : "HIGH (Busy)");
|
||||
|
||||
// Read CTS state from control GPIO (connected to CTS1)
|
||||
bool ctsState = digitalRead(GPIO_CTS_CTRL);
|
||||
Serial.printf("CTS Pin (GPIO%d): %s (LOW = can transmit)\n", UART1_CTS_PIN, ctsState == LOW ? "LOW (Clear)" : "HIGH (Blocked)");
|
||||
} else {
|
||||
Serial.printf("RTS Pin (GPIO%d): Hardware controlled (LOW = ready to receive)\n", UART1_RTS_PIN);
|
||||
Serial.printf("CTS Pin (GPIO%d): Hardware controlled (LOW = can transmit)\n", UART1_CTS_PIN);
|
||||
Serial.println("Note: RTS/CTS pins are hardware-controlled. Connect RTS1 to CTS1 for loopback.");
|
||||
}
|
||||
|
||||
Serial.printf("Available for write: %d bytes\n", Serial1.availableForWrite());
|
||||
Serial.printf("Available to read: %d bytes\n", Serial1.available());
|
||||
Serial.println("========================\n");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// Initialize Serial (USB) for debugging
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
|
||||
Serial.println("\n\n========================================");
|
||||
Serial.println("ESP32 Hardware Flow Control Demo");
|
||||
Serial.println("========================================\n");
|
||||
|
||||
// Configure GPIOs for flow control (only if using GPIO-controlled mode)
|
||||
if (USE_GPIO_CONTROL) {
|
||||
// Configure CTS control GPIO - this will control the CTS signal
|
||||
pinMode(GPIO_CTS_CTRL, OUTPUT);
|
||||
digitalWrite(GPIO_CTS_CTRL, LOW); // Start with CTS LOW (clear to send)
|
||||
|
||||
// Configure RTS monitor GPIO - this will monitor the RTS signal
|
||||
pinMode(GPIO_RTS_MONITOR, INPUT);
|
||||
Serial.println("Using GPIO-controlled flow control mode");
|
||||
} else {
|
||||
Serial.println("Using hardware-controlled flow control (simple loopback)");
|
||||
}
|
||||
|
||||
// Initialize UART1 with hardware flow control
|
||||
Serial.println("Initializing UART1...");
|
||||
|
||||
// Begin UART1 with 115200 baud, 8N1 configuration
|
||||
Serial1.begin(115200);
|
||||
|
||||
// Set all pins for UART1
|
||||
if (!Serial1.setPins(UART1_RX_PIN, UART1_TX_PIN, UART1_CTS_PIN, UART1_RTS_PIN)) {
|
||||
Serial.println("ERROR: Failed to set CTS and RTS UART1 pins!");
|
||||
while (1) {
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("Enabling hardware flow control...");
|
||||
if (!Serial1.setHwFlowCtrlMode(UART_HW_FLOWCTRL_CTS_RTS, 64)) {
|
||||
Serial.println("ERROR: Failed to enable hardware flow control!");
|
||||
while (1) {
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_INTERNAL_MATRIX_PIN_LOOPBACK
|
||||
uart_internal_loopback(1, UART1_RX_PIN);
|
||||
uart_internal_hw_flow_ctrl_loopback(1, UART1_CTS_PIN);
|
||||
#endif
|
||||
|
||||
// Diagnostic: Check initial state after enabling flow control
|
||||
Serial.println("\nPost-initialization diagnostics:");
|
||||
Serial.printf(" Serial1.available(): %d bytes\n", Serial1.available());
|
||||
Serial.printf(" Serial1.availableForWrite(): %d bytes\n", Serial1.availableForWrite());
|
||||
if (USE_GPIO_CONTROL) {
|
||||
Serial.printf(" GPIO%d (CTS control): %s\n", GPIO_CTS_CTRL, digitalRead(GPIO_CTS_CTRL) == LOW ? "LOW" : "HIGH");
|
||||
Serial.printf(" GPIO%d (RTS monitor): %s\n", GPIO_RTS_MONITOR, digitalRead(GPIO_RTS_MONITOR) == LOW ? "LOW" : "HIGH");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
Serial.println("UART1 initialized successfully!");
|
||||
Serial.println("Hardware flow control: ENABLED (RTS + CTS)");
|
||||
Serial.printf("RX Pin: GPIO%d\n", UART1_RX_PIN);
|
||||
Serial.printf("TX Pin: GPIO%d\n", UART1_TX_PIN);
|
||||
Serial.printf("RTS Pin: GPIO%d (output from UART)\n", UART1_RTS_PIN);
|
||||
Serial.printf("CTS Pin: GPIO%d (input to UART)\n", UART1_CTS_PIN);
|
||||
|
||||
#if USE_INTERNAL_MATRIX_PIN_LOOPBACK
|
||||
Serial.println("\nNO EXTERNAL PIN CONNECTIONS ARE REQUIRED:");
|
||||
Serial.println("-------------------------");
|
||||
Serial.println("Internal GPIO Matrix connection with flow control mode:");
|
||||
Serial.printf(" 1. Automatic Internal Connection of GPIO%d (TX1) to GPIO%d (RX1) - Loopback\n", UART1_TX_PIN, UART1_RX_PIN);
|
||||
Serial.printf(" 2. Automatic Internal Connection of GPIO%d (RTS1) to GPIO%d (CTS1) - Flow control loopback\n", UART1_RTS_PIN, UART1_CTS_PIN);
|
||||
Serial.println("\n Note: In this mode, RTS/CTS are automatically controlled by hardware.");
|
||||
Serial.println(" RTS goes LOW when ready to receive, HIGH when buffer is full.");
|
||||
Serial.println(" CTS must be LOW for transmission to proceed.");
|
||||
#else
|
||||
Serial.println("\nPIN CONNECTIONS REQUIRED:");
|
||||
Serial.println("-------------------------");
|
||||
if (USE_GPIO_CONTROL) {
|
||||
Serial.println("GPIO-controlled flow control mode:");
|
||||
Serial.printf(" 1. Connect GPIO%d (TX1) to GPIO%d (RX1) - Loopback\n", UART1_TX_PIN, UART1_RX_PIN);
|
||||
Serial.printf(" 2. Connect GPIO%d (RTS1) to GPIO%d - Monitor RTS state\n", UART1_RTS_PIN, GPIO_RTS_MONITOR);
|
||||
Serial.printf(" 3. Connect GPIO%d (CTS1) to GPIO%d - Control CTS signal\n", UART1_CTS_PIN, GPIO_CTS_CTRL);
|
||||
} else {
|
||||
Serial.println("Hardware-controlled flow control (simple loopback):");
|
||||
Serial.printf(" 1. Connect GPIO%d (TX1) to GPIO%d (RX1) - Loopback\n", UART1_TX_PIN, UART1_RX_PIN);
|
||||
Serial.printf(" 2. Connect GPIO%d (RTS1) to GPIO%d (CTS1) - Flow control loopback\n", UART1_RTS_PIN, UART1_CTS_PIN);
|
||||
Serial.println("\n Note: In this mode, RTS/CTS are automatically controlled by hardware.");
|
||||
Serial.println(" RTS goes LOW when ready to receive, HIGH when buffer is full.");
|
||||
Serial.println(" CTS must be LOW for transmission to proceed.");
|
||||
}
|
||||
#endif
|
||||
Serial.println("\nStarting demonstration in 2 seconds...\n");
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
// Print status periodically
|
||||
if (currentTime - lastStatusTime >= statusInterval) {
|
||||
lastStatusTime = currentTime;
|
||||
printPinStatus();
|
||||
|
||||
// Demonstrate flow control by toggling CTS (only in GPIO-controlled mode)
|
||||
if (USE_GPIO_CONTROL) {
|
||||
static bool ctsState = false;
|
||||
ctsState = !ctsState;
|
||||
|
||||
if (ctsState) {
|
||||
Serial.println(">>> Blocking transmission (CTS HIGH)...");
|
||||
digitalWrite(GPIO_CTS_CTRL, HIGH); // Block transmission
|
||||
} else {
|
||||
Serial.println(">>> Allowing transmission (CTS LOW)...");
|
||||
digitalWrite(GPIO_CTS_CTRL, LOW); // Allow transmission
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send data periodically
|
||||
if (currentTime - lastSendTime >= sendInterval) {
|
||||
lastSendTime = currentTime;
|
||||
sendCounter++;
|
||||
|
||||
// Check if we can transmit (CTS must be LOW)
|
||||
// In GPIO-controlled mode, check the control GPIO; otherwise hardware handles it
|
||||
bool canTransmit = true;
|
||||
if (USE_GPIO_CONTROL) {
|
||||
canTransmit = (digitalRead(GPIO_CTS_CTRL) == LOW);
|
||||
}
|
||||
|
||||
if (canTransmit) {
|
||||
char message[64];
|
||||
snprintf(message, sizeof(message), "Message #%d: Hello from UART1! Time: %lu ms\r\n", sendCounter, currentTime);
|
||||
|
||||
Serial.print("Sending: ");
|
||||
Serial.print(message);
|
||||
|
||||
size_t bytesWritten = Serial1.write((const uint8_t *)message, strlen(message));
|
||||
Serial.printf(" -> Written: %d bytes\n", bytesWritten);
|
||||
|
||||
// Flush to ensure data is sent
|
||||
Serial1.flush();
|
||||
} else {
|
||||
Serial.println("!!! Transmission blocked - CTS is HIGH !!!");
|
||||
}
|
||||
}
|
||||
|
||||
// Read and echo received data
|
||||
if (Serial1.available()) {
|
||||
Serial.print("Received: ");
|
||||
while (Serial1.available()) {
|
||||
char c = Serial1.read();
|
||||
Serial.write(c);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
// Small delay to prevent tight loop
|
||||
delay(10);
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
# Hardware Flow Control Demo
|
||||
|
||||
This example demonstrates UART hardware flow control using RTS (Request To Send) and CTS (Clear To Send) signals with ESP32's HardwareSerial (UART1).
|
||||
|
||||
## Overview
|
||||
|
||||
Hardware flow control is a mechanism that prevents data loss by controlling when data can be transmitted and received. It uses two additional signals:
|
||||
|
||||
- **RTS (Request To Send)**: Output signal from the UART indicating it's ready to receive data
|
||||
- **CTS (Clear To Send)**: Input signal to the UART that controls when transmission is allowed
|
||||
|
||||
## Configuration Options
|
||||
|
||||
The sketch supports two configuration options:
|
||||
|
||||
### USE_INTERNAL_MATRIX_PIN_LOOPBACK
|
||||
|
||||
**Location in code:** Line 55 in `HardwareFlowControl_Demo.ino`
|
||||
```cpp
|
||||
#define USE_INTERNAL_MATRIX_PIN_LOOPBACK 1 // Set to 1 for internal loopback, 0 for external wires
|
||||
```
|
||||
|
||||
- **`USE_INTERNAL_MATRIX_PIN_LOOPBACK = 1`** (Default): Uses ESP32's internal GPIO matrix to create loopback connections. **No external wires needed!**
|
||||
- **`USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0`**: Requires external wire connections (see Pin Connections section below)
|
||||
|
||||
### USE_GPIO_CONTROL
|
||||
|
||||
**Location in code:** Line 72 in `HardwareFlowControl_Demo.ino`
|
||||
```cpp
|
||||
#define USE_GPIO_CONTROL false // Set to true or false
|
||||
```
|
||||
|
||||
**Note:** When `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 1`, the `USE_GPIO_CONTROL` setting is overridden and hardware-controlled mode is used.
|
||||
|
||||
## Pin Connections
|
||||
|
||||
**Important:** Pin connections are only needed when `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0`.
|
||||
|
||||
### Default Pin Assignments
|
||||
|
||||
- **RX1**: Uses board default (`RX1` constant - typically GPIO26 for ESP32, varies by board)
|
||||
- **TX1**: Uses board default (`TX1` constant - typically GPIO27 for ESP32, varies by board)
|
||||
- **RTS1**: GPIO2 (configurable via `UART1_RTS_PIN`)
|
||||
- **CTS1**: GPIO4 (configurable via `UART1_CTS_PIN`)
|
||||
- **GPIO_RTS_MONITOR**: GPIO5 (for GPIO-controlled mode, configurable via `GPIO_RTS_MONITOR`)
|
||||
- **GPIO_CTS_CTRL**: GPIO13 (for GPIO-controlled mode, configurable via `GPIO_CTS_CTRL`)
|
||||
|
||||
**Note:** RX1 and TX1 pin numbers are board-specific. Check your board's pin definitions or use the serial output to see the actual GPIO numbers being used.
|
||||
|
||||
### Option 1: Simple Loopback (`USE_GPIO_CONTROL = false`)
|
||||
|
||||
For a basic loopback test with automatic flow control:
|
||||
|
||||
```
|
||||
ESP32 Pin Connections:
|
||||
- TX1 ────┐
|
||||
├──> RX1 (Data loopback)
|
||||
|
||||
- GPIO2 (RTS1) ────┐
|
||||
├──> GPIO4 (CTS1) (Flow control loopback)
|
||||
```
|
||||
|
||||
**Physical Connections (when USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0):**
|
||||
1. Connect TX1 pin to RX1 pin with a jumper wire - read the console serial output to know which pins are the default ones
|
||||
2. Connect GPIO2 (RTS1) to GPIO4 (CTS1) with a jumper wire
|
||||
|
||||
### Option 2: GPIO-Controlled Flow Control (`USE_GPIO_CONTROL = true`)
|
||||
|
||||
For manual control of flow control signals:
|
||||
|
||||
```
|
||||
ESP32 Pin Connections:
|
||||
- TX1 ────> RX1 (Data loopback)
|
||||
- GPIO2 (RTS1) ────> GPIO5 (RTS Monitor) (Monitor RTS state)
|
||||
- GPIO4 (CTS1) <─── GPIO13 (CTS Control) (Control CTS signal)
|
||||
```
|
||||
|
||||
**Physical Connections (when USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0):**
|
||||
1. Connect TX1 pin to RX1 pin with a jumper wire
|
||||
2. Connect GPIO2 (RTS1) to GPIO5 with a jumper wire
|
||||
3. Connect GPIO4 (CTS1) to GPIO13 with a jumper wire
|
||||
|
||||
## How Hardware Flow Control Works
|
||||
|
||||
### RTS (Request To Send)
|
||||
- **Output signal** from the UART
|
||||
- **LOW (0 V)**: UART is ready to receive data (RX buffer has space)
|
||||
- **HIGH (3.3 V)**: UART RX buffer is getting full, cannot receive more data
|
||||
|
||||
### CTS (Clear To Send)
|
||||
- **Input signal** to the UART
|
||||
- **LOW (0 V)**: UART is allowed to transmit data
|
||||
- **HIGH (3.3 V)**: UART must pause transmission (transmission is blocked)
|
||||
|
||||
### Flow Control Behavior
|
||||
|
||||
1. **Receiving Data (RTS)**:
|
||||
- When UART1's RX buffer has space, RTS1 is driven LOW
|
||||
- When RX buffer fills up (threshold reached), RTS1 is driven HIGH
|
||||
- This signals the sender to stop transmitting
|
||||
|
||||
2. **Transmitting Data (CTS)**:
|
||||
- UART1 checks CTS1 before transmitting
|
||||
- If CTS1 is LOW, transmission proceeds normally
|
||||
- If CTS1 is HIGH, UART1 pauses transmission until CTS1 goes LOW again
|
||||
|
||||
### Understanding the Two Modes
|
||||
|
||||
**`USE_GPIO_CONTROL = false` (Default - Simple Loopback)**
|
||||
- Use this mode when you connect RTS1 directly to CTS1 (hardware loopback)
|
||||
- Flow control operates automatically - no software intervention needed
|
||||
- RTS/CTS signals are controlled entirely by the UART hardware
|
||||
- Best for: Basic testing and understanding automatic flow control behavior
|
||||
- **Wiring (when USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0):** Connect GPIO2 (RTS1) → GPIO4 (CTS1)
|
||||
|
||||
**`USE_GPIO_CONTROL = true` (GPIO-Controlled Mode)**
|
||||
- Use this mode when you want to manually control or monitor flow control signals
|
||||
- Software can read RTS state and control CTS signal via GPIO pins
|
||||
- Demonstrates explicit flow control blocking behavior
|
||||
- Best for: Testing flow control behavior, interfacing with external logic, or learning how external devices control UART transmission
|
||||
- **Wiring (when USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0):**
|
||||
- Connect GPIO2 (RTS1) → GPIO5 (to monitor RTS)
|
||||
- Connect GPIO4 (CTS1) → GPIO13 (to control CTS)
|
||||
- **Note:** This mode is overridden when `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 1`
|
||||
|
||||
### How to Configure
|
||||
|
||||
1. Open `HardwareFlowControl_Demo.ino` in Arduino IDE
|
||||
2. **For internal loopback (no wires):** Set `USE_INTERNAL_MATRIX_PIN_LOOPBACK` to `1` (default)
|
||||
3. **For external wires:** Set `USE_INTERNAL_MATRIX_PIN_LOOPBACK` to `0` and configure `USE_GPIO_CONTROL`:
|
||||
- `false` for hardware-controlled mode
|
||||
- `true` for GPIO-controlled mode
|
||||
4. Make sure your physical wiring matches the selected mode (only if `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0`)
|
||||
5. Upload the sketch
|
||||
|
||||
**Important:**
|
||||
- When `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 1`, no external connections are needed
|
||||
- When `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0`, the wiring configuration must match the `USE_GPIO_CONTROL` setting
|
||||
|
||||
## Code Explanation
|
||||
|
||||
### Key Functions Used
|
||||
|
||||
1. **`begin(baudrate)`**
|
||||
- Initializes the UART with the specified baud rate
|
||||
- Must be called before setting pins and enabling hardware flow control
|
||||
|
||||
2. **`setPins(rxPin, txPin, ctsPin, rtsPin)`**
|
||||
- Configures the UART pins
|
||||
- Note: The order `begin()` then `setPins()` is important for proper initialization
|
||||
|
||||
3. **`uart_internal_loopback(uartNum, rxPin)`** (when `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 1`)
|
||||
- Creates internal GPIO matrix connection for TX→RX loopback
|
||||
- No external wires needed
|
||||
|
||||
4. **`uart_internal_hw_flow_ctrl_loopback(uartNum, ctsPin)`** (when `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 1`)
|
||||
- Creates internal GPIO matrix connection for RTS→CTS flow control loopback
|
||||
- No external wires needed
|
||||
|
||||
5. **`setHwFlowCtrlMode(mode, threshold)`**
|
||||
- Enables hardware flow control
|
||||
- Modes:
|
||||
- `UART_HW_FLOWCTRL_DISABLE`: Disable flow control
|
||||
- `UART_HW_FLOWCTRL_RTS`: Enable RX flow control only
|
||||
- `UART_HW_FLOWCTRL_CTS`: Enable TX flow control only
|
||||
- `UART_HW_FLOWCTRL_CTS_RTS`: Enable full flow control (default)
|
||||
- Threshold: Number of bytes in RX FIFO before RTS is asserted (default: 64)
|
||||
|
||||
### Example Behavior
|
||||
|
||||
The sketch demonstrates:
|
||||
- Periodic transmission of messages
|
||||
- Flow control blocking when CTS is HIGH
|
||||
- Monitoring of RTS/CTS pin states
|
||||
- Loopback reception of transmitted data
|
||||
|
||||
## Expected Output and Behavior
|
||||
|
||||
The sketch behavior differs depending on the `USE_GPIO_CONTROL` setting (see Configuration section above for details).
|
||||
|
||||
### Mode 1: Hardware-Controlled Flow Control (`USE_GPIO_CONTROL = false`)
|
||||
|
||||
**Behavior:**
|
||||
- RTS and CTS signals are automatically controlled by the UART hardware
|
||||
- RTS1 is directly connected to CTS1 (hardware loopback)
|
||||
- Flow control operates automatically without software intervention
|
||||
- RTS goes LOW when ready to receive, HIGH when buffer is full
|
||||
- CTS must be LOW for transmission to proceed (automatically controlled by RTS)
|
||||
|
||||
**Expected Output:**
|
||||
|
||||
```
|
||||
========================================
|
||||
ESP32 Hardware Flow Control Demo
|
||||
========================================
|
||||
|
||||
Initializing UART1...
|
||||
Using hardware-controlled flow control (simple loopback)
|
||||
UART1 initialized successfully!
|
||||
Hardware flow control: ENABLED (RTS + CTS)
|
||||
RX Pin: GPIO26 (for the ESP32 RX1 or board-specific RX1 default)
|
||||
TX Pin: GPIO27 (for the ESP32 TX1 or board-specific TX1 default)
|
||||
RTS Pin: GPIO2 (output from UART)
|
||||
CTS Pin: GPIO4 (input to UART)
|
||||
|
||||
NO EXTERNAL PIN CONNECTIONS ARE REQUIRED:
|
||||
-------------------------
|
||||
Internal GPIO Matrix connection with flow control mode:
|
||||
1. Automatic Internal Connection of TX1 to RX1 - Loopback (via GPIO matrix)
|
||||
2. Automatic Internal Connection of GPIO2 (RTS1) to GPIO4 (CTS1) - Flow control loopback
|
||||
|
||||
Note: In this mode, RTS/CTS are automatically controlled by hardware.
|
||||
RTS goes LOW when ready to receive, HIGH when buffer is full.
|
||||
CTS must be LOW for transmission to proceed.
|
||||
|
||||
Starting demonstration in 2 seconds...
|
||||
|
||||
=== UART1 Pin Status ===
|
||||
RX Pin (GPIO26): ESP32 Receiving data (or board-specific RX1)
|
||||
TX Pin (GPIO27): ESP32 Transmitting data (or board-specific TX1)
|
||||
RTS Pin (GPIO2): Hardware controlled (LOW = ready to receive)
|
||||
CTS Pin (GPIO4): Hardware controlled (LOW = can transmit)
|
||||
Note: RTS/CTS pins are hardware-controlled. Connect RTS1 to CTS1 for loopback.
|
||||
Available for write: 128 bytes
|
||||
Available to read: 0 bytes
|
||||
========================
|
||||
|
||||
Sending: Message #1: Hello from UART1! Time: 1000 ms
|
||||
-> Written: 45 bytes
|
||||
Received: Message #1: Hello from UART1! Time: 1000 ms
|
||||
|
||||
=== UART1 Pin Status ===
|
||||
RX Pin (GPIO26): ESP32 Receiving data (or board-specific RX1)
|
||||
TX Pin (GPIO27): ESP32 Transmitting data (or board-specific TX1)
|
||||
RTS Pin (GPIO2): Hardware controlled (LOW = ready to receive)
|
||||
CTS Pin (GPIO4): Hardware controlled (LOW = can transmit)
|
||||
Note: RTS/CTS pins are hardware-controlled. Connect RTS1 to CTS1 for loopback.
|
||||
Available for write: 128 bytes
|
||||
Available to read: 45 bytes
|
||||
========================
|
||||
|
||||
Sending: Message #2: Hello from UART1! Time: 2000 ms
|
||||
-> Written: 45 bytes
|
||||
Received: Message #2: Hello from UART1! Time: 2000 ms
|
||||
```
|
||||
|
||||
**Key Characteristics:**
|
||||
- No manual CTS toggling messages
|
||||
- Flow control happens automatically based on RX buffer state
|
||||
- RTS/CTS states are not directly readable (hardware-controlled)
|
||||
- Transmission is always allowed (CTS follows RTS automatically)
|
||||
|
||||
### Mode 2: GPIO-Controlled Flow Control (`USE_GPIO_CONTROL = true`)
|
||||
|
||||
**Behavior:**
|
||||
- RTS signal is monitored via GPIO5 (connected to RTS1)
|
||||
- CTS signal is controlled via GPIO13 (connected to CTS1)
|
||||
- Software can manually block/allow transmission by controlling GPIO13
|
||||
- Demonstrates explicit flow control blocking behavior
|
||||
- Shows how external devices can control UART transmission
|
||||
- **Note:** This mode only works when `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0`
|
||||
|
||||
**Expected Output:**
|
||||
|
||||
```
|
||||
========================================
|
||||
ESP32 Hardware Flow Control Demo
|
||||
========================================
|
||||
|
||||
Initializing UART1...
|
||||
Using GPIO-controlled flow control mode
|
||||
UART1 initialized successfully!
|
||||
Hardware flow control: ENABLED (RTS + CTS)
|
||||
RX Pin: GPIO26 (or board-specific RX1 default)
|
||||
TX Pin: GPIO27 (or board-specific TX1 default)
|
||||
RTS Pin: GPIO2 (output from UART)
|
||||
CTS Pin: GPIO4 (input to UART)
|
||||
|
||||
PIN CONNECTIONS REQUIRED:
|
||||
-------------------------
|
||||
GPIO-controlled flow control mode:
|
||||
1. Connect TX1 to RX1 - Loopback (board-specific pins)
|
||||
2. Connect GPIO2 (RTS1) to GPIO5 - Monitor RTS state
|
||||
3. Connect GPIO4 (CTS1) to GPIO13 - Control CTS signal
|
||||
|
||||
Starting demonstration in 2 seconds...
|
||||
|
||||
=== UART1 Pin Status ===
|
||||
RX Pin (GPIO26): Receiving data (or board-specific RX1)
|
||||
TX Pin (GPIO27): Transmitting data (or board-specific TX1)
|
||||
RTS Pin (GPIO2): LOW (Ready) (LOW = ready to receive)
|
||||
CTS Pin (GPIO4): LOW (Clear) (LOW = can transmit)
|
||||
Available for write: 128 bytes
|
||||
Available to read: 0 bytes
|
||||
========================
|
||||
|
||||
Sending: Message #1: Hello from UART1! Time: 1000 ms
|
||||
-> Written: 45 bytes
|
||||
Received: Message #1: Hello from UART1! Time: 1000 ms
|
||||
|
||||
=== UART1 Pin Status ===
|
||||
RX Pin (GPIO26): Receiving data (or board-specific RX1)
|
||||
TX Pin (GPIO27): Transmitting data (or board-specific TX1)
|
||||
RTS Pin (GPIO2): LOW (Ready) (LOW = ready to receive)
|
||||
CTS Pin (GPIO4): LOW (Clear) (LOW = can transmit)
|
||||
Available for write: 128 bytes
|
||||
Available to read: 45 bytes
|
||||
========================
|
||||
|
||||
>>> Allowing transmission (CTS LOW)...
|
||||
Sending: Message #2: Hello from UART1! Time: 2000 ms
|
||||
-> Written: 45 bytes
|
||||
Received: Message #2: Hello from UART1! Time: 2000 ms
|
||||
|
||||
=== UART1 Pin Status ===
|
||||
RX Pin (GPIO26): Receiving data (or board-specific RX1)
|
||||
TX Pin (GPIO27): Transmitting data (or board-specific TX1)
|
||||
RTS Pin (GPIO2): LOW (Ready) (LOW = ready to receive)
|
||||
CTS Pin (GPIO4): HIGH (Blocked) (LOW = can transmit)
|
||||
Available for write: 128 bytes
|
||||
Available to read: 45 bytes
|
||||
========================
|
||||
|
||||
>>> Blocking transmission (CTS HIGH)...
|
||||
!!! Transmission blocked - CTS is HIGH !!!
|
||||
|
||||
=== UART1 Pin Status ===
|
||||
RX Pin (GPIO26): Receiving data (or board-specific RX1)
|
||||
TX Pin (GPIO27): Transmitting data (or board-specific TX1)
|
||||
RTS Pin (GPIO2): LOW (Ready) (LOW = ready to receive)
|
||||
CTS Pin (GPIO4): HIGH (Blocked) (LOW = can transmit)
|
||||
Available for write: 128 bytes
|
||||
Available to read: 45 bytes
|
||||
========================
|
||||
|
||||
>>> Allowing transmission (CTS LOW)...
|
||||
Sending: Message #3: Hello from UART1! Time: 3000 ms
|
||||
-> Written: 45 bytes
|
||||
Received: Message #3: Hello from UART1! Time: 3000 ms
|
||||
```
|
||||
|
||||
**Key Characteristics:**
|
||||
- Explicit messages showing CTS state changes ("Allowing transmission" / "Blocking transmission")
|
||||
- Transmission blocking messages when CTS is HIGH
|
||||
- RTS/CTS pin states are readable via GPIO5 and GPIO13
|
||||
- Demonstrates manual flow control
|
||||
- Useful for testing flow control behavior or interfacing with external flow control logic
|
||||
- Only works when `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
1. **No data received**:
|
||||
- If `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 1`: Check that the internal loopback functions are being called
|
||||
- If `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0`: Check that TX1 is connected to RX1
|
||||
2. **Transmission always blocked**: Verify CTS pin connection and state (only applies when `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0`)
|
||||
3. **RTS always HIGH**: RX buffer may be full, try reading data
|
||||
4. **Compilation errors**: Ensure you're using ESP32 Arduino Core 2.0.0 or later
|
||||
5. **GPIO-controlled mode not working**: Make sure `USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0` (internal loopback overrides GPIO control)
|
||||
|
||||
## Notes
|
||||
|
||||
- **Internal Loopback Mode** (`USE_INTERNAL_MATRIX_PIN_LOOPBACK = 1`): No external connections needed! The ESP32 GPIO matrix handles all connections internally. This is the easiest way to test hardware flow control.
|
||||
- **External Wire Mode** (`USE_INTERNAL_MATRIX_PIN_LOOPBACK = 0`): Requires physical connections between RTS and CTS pins (and TX/RX for data loopback)
|
||||
- The threshold parameter controls when RTS is asserted (default: 64 bytes = half of 128-byte FIFO)
|
||||
- Flow control is most useful when communicating with devices that support it (modems, some sensors, etc.)
|
||||
- For simple point-to-point communication without flow control support, you can disable it
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
|
||||
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
|
||||
every time 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 first, 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 occurs, it will raise the UART error interrupt.
|
||||
|
||||
This sketch produces BREAK UART error in the beginning 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 = received_bytes + available;
|
||||
Serial.printf("onReceive Callback:: There are %zu bytes available: {", available);
|
||||
while (available--) {
|
||||
char c = Serial1.read();
|
||||
Serial.printf("0x%x='%c'", c, c);
|
||||
if (available) {
|
||||
Serial.print(" ");
|
||||
}
|
||||
}
|
||||
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 BEGINNING\n================================\n", i + 1);
|
||||
// Now sending BREAK at the beginning 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 different 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 %zu 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 %zu bytes\n", sent_bytes);
|
||||
} else {
|
||||
Serial.printf("BREAK event will be sent at the BEGINNING of the %zu bytes\n", sent_bytes);
|
||||
}
|
||||
Serial.flush(); // wait Serial FIFO to be empty and then spend almost no time processing it
|
||||
Serial1.setRxFIFOFull(fifoFull); // testing different 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 %zu bytes from Serial1 TX to Serial1 RX\n", sent_bytes);
|
||||
Serial.printf("onReceive() has read a total of %zu 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
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
|
||||
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
|
||||
every time 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 initialization 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 = received_bytes + available;
|
||||
Serial.printf("onReceive Callback:: There are %zu 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 behavior
|
||||
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 different 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 %zu 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 different 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 %zu bytes from Serial1 TX to Serial1 RX\n", sent_bytes);
|
||||
Serial.printf("onReceive() has read a total of %zu bytes\n", received_bytes);
|
||||
Serial.println("========================\nFinished!");
|
||||
|
||||
Serial1.onReceive(NULL); // resets/disables the RX callback function for Serial 1
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
This Sketch demonstrates how to use the Hardware Serial peripheral to communicate over an RS485 bus.
|
||||
|
||||
Data received on the primary serial port is relayed to the bus acting as an RS485 interface and vice versa.
|
||||
|
||||
UART to RS485 translation hardware (e.g., MAX485, MAX33046E, ADM483) is assumed to be configured in half-duplex
|
||||
mode with collision detection as described in
|
||||
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/uart.html#circuit-a-collision-detection-circuit
|
||||
|
||||
To use the script open the Arduino serial monitor (or alternative serial monitor on the Arduino port). Then,
|
||||
using an RS485 tranciver, connect another serial monitor to the RS485 port. Entering data on one terminal
|
||||
should be displayed on the other terminal.
|
||||
*/
|
||||
#include "hal/uart_types.h"
|
||||
|
||||
#define RS485_RX_PIN 16
|
||||
#define RS485_TX_PIN 5
|
||||
#define RS485_RTS_PIN 4
|
||||
|
||||
#define RS485 Serial1
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
RS485.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
while (!RS485) {
|
||||
delay(10);
|
||||
}
|
||||
if (!RS485.setPins(-1, -1, -1, RS485_RTS_PIN)) {
|
||||
Serial.print("Failed to set RS485 pins");
|
||||
}
|
||||
|
||||
// Certain versions of Arduino core don't define MODE_RS485_HALF_DUPLEX and so fail to compile.
|
||||
// By using UART_MODE_RS485_HALF_DUPLEX defined in hal/uart_types.h we work around this problem.
|
||||
// If using a newer IDF and Arduino core you can omit including hal/uart_types.h and use MODE_RS485_HALF_DUPLEX
|
||||
// defined in esp32-hal-uart.h (included during other build steps) instead.
|
||||
if (!RS485.setMode(UART_MODE_RS485_HALF_DUPLEX)) {
|
||||
Serial.print("Failed to set RS485 mode");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (RS485.available()) {
|
||||
Serial.write(RS485.read());
|
||||
}
|
||||
if (Serial.available()) {
|
||||
RS485.write(Serial.read());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
*
|
||||
* 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 different 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 different 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; // codespell:ignore pasttime
|
||||
Serial.printf("\nIt has sent %zu bytes from Serial1 TX to Serial1 RX\n", sentBytes);
|
||||
Serial.printf("It took %lu milliseconds to read %d bytes\n", pastTime, bytesReceived); // codespell:ignore pasttime
|
||||
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 %03lu bytes after %lu 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!");
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
|
||||
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 available to the Arduino API.
|
||||
|
||||
There is an important detail about how HardwareSerial works using ESP32 and ESP32-S2:
|
||||
If the baud rate is lower than 250,000, it will select REF_TICK as clock source in order to avoid that
|
||||
the baud rate may change when the CPU Frequency is changed. Default UART clock source is APB, which changes
|
||||
when CPU clock source is also changed. But when it selects REF_TICK as UART clock source, RX Timeout is limited to 1.
|
||||
Therefore, in order to change the ESP32/ESP32-S2 RX Timeout it is necessary to fix the UART Clock Source to APB.
|
||||
|
||||
In the case of the other SoC, such as ESP32-S3, C3, C6, H2 and P4, there is no such RX Timeout limitation.
|
||||
Those will set the UART Source Clock as XTAL, which allows the baud rate to be high and it is steady, not
|
||||
changing with the CPU Frequency.
|
||||
*/
|
||||
|
||||
#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
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
// UART_CLK_SRC_APB will allow higher values of RX Timeout
|
||||
// default for ESP32 and ESP32-S2 is REF_TICK which limits the RX Timeout to 1
|
||||
// setClockSource() must be called before begin()
|
||||
Serial1.setClockSource(UART_CLK_SRC_APB);
|
||||
#endif
|
||||
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 different 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 different 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 available 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; // codespell:ignore pasttime
|
||||
Serial.printf("\nIt has sent %zu bytes from Serial1 TX to Serial1 RX\n", sentBytes);
|
||||
Serial.printf("It took %lu milliseconds to read %d bytes\n", pastTime, bytesReceived); // codespell:ignore pasttime
|
||||
Serial.print("Received data: [");
|
||||
Serial.write(dataReceived, DATA_SIZE);
|
||||
Serial.println("]");
|
||||
Serial.println("========================\nFinished!");
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Simple Sketch for testing HardwareSerial with different CPU Frequencies
|
||||
Changing the CPU Frequency may affect peripherals and Wireless functionality
|
||||
In ESP32 Arduino, UART shall work correctly in order to let the user see DGB info
|
||||
and other application messages.
|
||||
|
||||
CPU Frequency is usually lowered in sleep modes
|
||||
and some other Low Power configurations
|
||||
|
||||
*/
|
||||
|
||||
int cpufreqs[6] = {240, 160, 80, 40, 20, 10};
|
||||
#define NUM_CPU_FREQS (sizeof(cpufreqs) / sizeof(int))
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
Serial.println("\n Starting...\n");
|
||||
Serial.flush();
|
||||
|
||||
// initial information
|
||||
uint32_t Freq = getCpuFrequencyMhz();
|
||||
Serial.print("CPU Freq = ");
|
||||
Serial.print(Freq);
|
||||
Serial.println(" MHz");
|
||||
Freq = getXtalFrequencyMhz();
|
||||
Serial.print("XTAL Freq = ");
|
||||
Serial.print(Freq);
|
||||
Serial.println(" MHz");
|
||||
Freq = getApbFrequency();
|
||||
Serial.print("APB Freq = ");
|
||||
Serial.print(Freq);
|
||||
Serial.println(" Hz");
|
||||
delay(500);
|
||||
|
||||
// ESP32-C3 and other RISC-V target may not support 240MHz
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C3
|
||||
uint8_t firstFreq = 1;
|
||||
#else
|
||||
uint8_t firstFreq = 0;
|
||||
#endif
|
||||
|
||||
// testing HardwareSerial for all possible CPU/APB Frequencies
|
||||
for (uint8_t i = firstFreq; i < NUM_CPU_FREQS; i++) {
|
||||
Serial.printf("\n------- Trying CPU Freq = %d ---------\n", cpufreqs[i]);
|
||||
Serial.flush(); // wait to empty the UART FIFO before changing the CPU Freq.
|
||||
setCpuFrequencyMhz(cpufreqs[i]);
|
||||
Serial.updateBaudRate(115200);
|
||||
|
||||
Freq = getCpuFrequencyMhz();
|
||||
Serial.print("CPU Freq = ");
|
||||
Serial.print(Freq);
|
||||
Serial.println(" MHz");
|
||||
Freq = getXtalFrequencyMhz();
|
||||
Serial.print("XTAL Freq = ");
|
||||
Serial.print(Freq);
|
||||
Serial.println(" MHz");
|
||||
Freq = getApbFrequency();
|
||||
Serial.print("APB Freq = ");
|
||||
Serial.print(Freq);
|
||||
Serial.println(" Hz");
|
||||
if (i < NUM_CPU_FREQS - 1) {
|
||||
Serial.println("Moving to the next frequency after a pause of 2 seconds.");
|
||||
delay(2000);
|
||||
}
|
||||
}
|
||||
Serial.println("\n-------------------\n");
|
||||
Serial.println("End of testing...");
|
||||
Serial.println("\n-------------------\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Nothing here so far
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* This is C++ example that demonstrates the usage of a std::function as OnReceive Callback function to all the UARTs
|
||||
* It basically defines a general onReceive function that receives an extra parameter, the Serial pointer that is
|
||||
* executing the callback.
|
||||
*
|
||||
* For each HardwareSerial object (Serial, Serial1, Serial2), it is necessary to set the callback with
|
||||
* the respective Serial pointer. It is done using lambda expression as a std::function.
|
||||
* Example:
|
||||
* Serial1.onReceive([]() { processOnReceiving(&Serial1); });
|
||||
*
|
||||
*/
|
||||
|
||||
// soc/soc_caps.h has information about each SoC target
|
||||
// in this example, we use SOC_UART_HP_NUM that goes from 1 to 3,
|
||||
// depending on the number of available UARTs in the ESP32xx
|
||||
// This makes the code transparent to what SoC is used.
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
// This example shall use UART1 or UART2 for testing and UART0 for console messages
|
||||
// If UART0 is used for testing, it is necessary to manually send data to it, using the Serial Monitor/Terminal
|
||||
// In case that USB CDC is available, it may be used as console for messages.
|
||||
#define TEST_UART 1 // Serial# (0, 1 or 2) will be used for the loopback
|
||||
#define RXPIN 4 // GPIO 4 => RX for Serial1 or Serial2
|
||||
#define TXPIN 5 // GPIO 5 => TX for Serial1 or Serial2
|
||||
|
||||
// declare testingSerial (as reference) related to TEST_UART number defined above (only for Serial1 and Serial2)
|
||||
#if SOC_UART_HP_NUM > 1 && TEST_UART == 1
|
||||
HardwareSerial &testingSerial = Serial1;
|
||||
#elif SOC_UART_HP_NUM > 2 && TEST_UART == 2
|
||||
HardwareSerial &testingSerial = Serial2;
|
||||
#endif
|
||||
|
||||
// General callback function for any UART -- used with a lambda std::function within HardwareSerial::onReceive()
|
||||
void processOnReceiving(HardwareSerial &mySerial) {
|
||||
// detects which Serial# is being used here
|
||||
int8_t uart_num = -1;
|
||||
if (&mySerial == &Serial0) {
|
||||
uart_num = 0;
|
||||
#if SOC_UART_HP_NUM > 1
|
||||
} else if (&mySerial == &Serial1) {
|
||||
uart_num = 1;
|
||||
#endif
|
||||
#if SOC_UART_HP_NUM > 2
|
||||
} else if (&mySerial == &Serial2) {
|
||||
uart_num = 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
//Prints some information on the current Serial (UART0 or USB CDC)
|
||||
if (uart_num == -1) {
|
||||
Serial.println("This is not a know Arduino Serial# object...");
|
||||
return;
|
||||
}
|
||||
Serial.printf("\nOnReceive Callback --> Received Data from UART%d\n", uart_num);
|
||||
Serial.printf("Received %d bytes\n", mySerial.available());
|
||||
Serial.printf("First byte is '%c' [0x%02x]\n", mySerial.peek(), mySerial.peek());
|
||||
uint8_t charPerLine = 0;
|
||||
while (mySerial.available()) {
|
||||
char c = mySerial.read();
|
||||
Serial.printf("'%c' [0x%02x] ", c, c);
|
||||
if (++charPerLine == 10) {
|
||||
charPerLine = 0;
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// Serial can be the USB or UART0, depending on the settings and which SoC is used
|
||||
Serial.begin(115200);
|
||||
|
||||
// when data is received from UART0, it will call the general function
|
||||
// passing Serial0 as parameter for processing
|
||||
#if TEST_UART == 0
|
||||
Serial0.begin(115200); // keeps default GPIOs
|
||||
Serial0.onReceive([]() {
|
||||
processOnReceiving(Serial0);
|
||||
});
|
||||
#else
|
||||
// and so on for the other UARTs (Serial1 and Serial2)
|
||||
// Rx = 4, Tx = 5 will work for ESP32, S2, S3, C3, C6 and H2
|
||||
testingSerial.begin(115200, SERIAL_8N1, RXPIN, TXPIN);
|
||||
testingSerial.onReceive([]() {
|
||||
processOnReceiving(testingSerial);
|
||||
});
|
||||
#endif
|
||||
|
||||
// this helper function will connect TX pin (from TEST_UART number) to its RX pin
|
||||
// creating a loopback that will allow to write to TEST_UART number
|
||||
// and send it to RX with no need to physically connect both pins
|
||||
#if TEST_UART > 0
|
||||
uart_internal_loopback(TEST_UART, RXPIN);
|
||||
#else
|
||||
// when UART0 is used for testing, it is necessary to send data using the Serial Monitor/Terminal
|
||||
// Data must be sent by the CP2102, manually using the Serial Monitor/Terminal
|
||||
#endif
|
||||
|
||||
delay(500);
|
||||
Serial.printf("\nSend bytes to UART%d in order to\n", TEST_UART);
|
||||
Serial.println("see a single processing function display information about");
|
||||
Serial.println("the received data.\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// All done by the UART callback functions
|
||||
// just write a random number of bytes into the testing UART
|
||||
char serial_data[24];
|
||||
size_t len = random(sizeof(serial_data) - 1) + 1; // at least 1 byte will be sent
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
serial_data[i] = 'A' + i;
|
||||
}
|
||||
|
||||
#if TEST_UART > 0
|
||||
Serial.println("\n\n==================================");
|
||||
Serial.printf("Sending %zu bytes to UART%d...\n", len, TEST_UART);
|
||||
testingSerial.write(serial_data, len);
|
||||
#else
|
||||
// when UART0 is used for testing, it is necessary to send data using the Serial Monitor/Terminal
|
||||
Serial.println("Use the Serial Monitor/Terminal to send data to UART0");
|
||||
#endif
|
||||
Serial.println("pausing for 15 seconds.");
|
||||
delay(15000);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
|
||||
This Sketch demonstrates how to use onReceive(callbackFunc) with HardwareSerial
|
||||
|
||||
void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout = false)
|
||||
|
||||
It is possible to register an UART callback function that will be called
|
||||
every time that UART receives data and an associated UART interrupt is generated.
|
||||
|
||||
In summary, HardwareSerial::onReceive() works like an RX Interrupt callback, that
|
||||
can be adjusted using HardwareSerial::setRxFIFOFull() and HardwareSerial::setRxTimeout().
|
||||
|
||||
In case that <onlyOnTimeout> is not changed or it is set to <false>, the callback function is
|
||||
executed whenever any event happens first (FIFO Full or RX Timeout).
|
||||
OnReceive will be called when every 120 bytes are received(default FIFO Full),
|
||||
or when RX Timeout occurs after 1 UART symbol by default.
|
||||
|
||||
This example demonstrates a way to create a String with all data received from UART0 only
|
||||
after RX Timeout. This example uses an RX timeout of about 3.5 Symbols as a way to know
|
||||
when the reception of data has finished.
|
||||
In order to achieve it, the sketch sets <onlyOnTimeout> to <true>.
|
||||
|
||||
The onReceive() callback is called whenever the RX ISR is triggered.
|
||||
It 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 in the UART initialization using HardwareSerial::begin()
|
||||
This will depend on the baud rate used when begin() is executed.
|
||||
For a baud rate of 115200 or lower, it it just 1 byte, mimicking original Arduino UART driver.
|
||||
For a baud rate over 115200 it will be 120 bytes for higher performance.
|
||||
Anyway, it can be changed by the application at any time.
|
||||
|
||||
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).
|
||||
<rxTimeout> is bound to the clock source.
|
||||
In order to use it properly, ESP32 and ESP32-S2 shall set the UART Clock Source to APB.
|
||||
|
||||
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 can be used by the application to tell Arduino to only execute
|
||||
the callback when Rx Timeout happens, by setting it to <true>.
|
||||
At this time all received data will be available to be read by the Arduino application.
|
||||
The application shall set an appropriate RX buffer size using
|
||||
HardwareSerial::setRxBufferSize(size_t new_size) before executing begin() for the Serial port.
|
||||
|
||||
MODBUS timeout of 3.5 symbol is based on these documents:
|
||||
https://www.automation.com/en-us/articles/2012-1/introduction-to-modbus
|
||||
https://minimalmodbus.readthedocs.io/en/stable/serialcommunication.html
|
||||
*/
|
||||
|
||||
// global variable to keep the results from onReceive()
|
||||
String uart_buffer = "";
|
||||
// The Modbus RTU standard prescribes a silent period corresponding to 3.5 characters between each
|
||||
// message, to be able to figure out where one message ends and the next one starts.
|
||||
const uint32_t modbusRxTimeoutLimit = 4;
|
||||
const uint32_t baudrate = 19200;
|
||||
|
||||
// UART_RX_IRQ will be executed as soon as data is received by the UART and an RX Timeout occurs
|
||||
// This is a callback function executed from a high priority monitor task
|
||||
// All data will be buffered into RX Buffer, which may have its size set to whatever necessary
|
||||
void UART0_RX_CB() {
|
||||
while (Serial0.available()) {
|
||||
uart_buffer += (char)Serial0.read();
|
||||
}
|
||||
}
|
||||
|
||||
// setup() and loop() are functions executed by a low priority task
|
||||
// Therefore, there are 2 tasks running when using onReceive()
|
||||
void setup() {
|
||||
// Using Serial0 will work in any case (using or not USB CDC on Boot)
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
// UART_CLK_SRC_APB will allow higher values of RX Timeout
|
||||
// default for ESP32 and ESP32-S2 is REF_TICK which limits the RX Timeout to 1
|
||||
// setClockSource() must be called before begin()
|
||||
Serial0.setClockSource(UART_CLK_SRC_APB);
|
||||
#endif
|
||||
// the amount of data received or waiting to be proessed shall not exceed this limit of 1024 bytes
|
||||
Serial0.setRxBufferSize(1024); // default is 256 bytes
|
||||
Serial0.begin(baudrate); // default pins and default mode 8N1 (8 bits data, no parity bit, 1 stopbit)
|
||||
// set RX Timeout based on UART symbols ~ 3.5 symbols of 11 bits (MODBUS standard) ~= 2 ms at 19200
|
||||
Serial0.setRxTimeout(modbusRxTimeoutLimit); // 4 symbols at 19200 8N1 is about 2.08 ms (40 bits)
|
||||
// sets the callback function that will be executed only after RX Timeout
|
||||
Serial0.onReceive(UART0_RX_CB, true);
|
||||
Serial0.println("Send data using Serial Monitor in order to activate the RX callback");
|
||||
}
|
||||
|
||||
uint32_t counter = 0;
|
||||
void loop() {
|
||||
// String <uart_buffer> is filled by the UART Callback whenever data is received and RX Timeout occurs
|
||||
if (uart_buffer.length() > 0) {
|
||||
// process the received data from Serial - example, just print it beside a counter
|
||||
Serial0.print("[");
|
||||
Serial0.print(counter++);
|
||||
Serial0.print("] [");
|
||||
Serial0.print(uart_buffer.length());
|
||||
Serial0.print(" bytes] ");
|
||||
Serial0.println(uart_buffer);
|
||||
uart_buffer = ""; // reset uart_buffer for the next UART reading
|
||||
}
|
||||
delay(1);
|
||||
}
|
||||
Reference in New Issue
Block a user