6. Serial Communication
6.1 Introduction to Serial Communication
A serial port is a communication method that transmits data bit by bit between peripherals and the processor through data signal lines, ground lines, and control lines. Although the transmission speed is lower than parallel transmission, a serial port can send data on one line while receiving data on another line. This communication method uses fewer data lines and can save communication costs in long-distance communication. The most important parameters of serial communication are the baud rate, data bits, stop bits, and parity bit. These parameters must be consistent between the two communication ports.
6.2 Introduction to Serial Communication Parameters
Serial communication parameters include baud rate, data bits, parity bits, stop bits, etc. These parameters describe the basic specifications of the transmitted data. For example, the baud rate defines the rate of data transmission, the data bits determine the number of bits contained in each data byte, the parity bit is used for error detection of the data, and the stop bit indicates the end of the data transmission.
- Baud rate: A parameter that measures the communication speed. It represents the number of bits transmitted per second.
- Data bits: A parameter that measures the actual data bits in communication. It represents the number of data bits contained in one information packet.
- Stop bits: Used to indicate the last bit of a single information packet. Typical values are 1, 1.5, and 2 bits. Since data is transmitted on the transmission line, and each device has its own clock, it is very likely that the communication will be out of sync. The stop bit not only indicates the end of the transmission, but also provides an opportunity to correct clock synchronization. The more stop bits, the greater the tolerance for different clock synchronization, but the slower the data transmission rate.
- Parity bit: Represents a simple way to check for errors. For a more detailed introduction, please search Baidu.
6.3 Serial Port Working Modes
Serial port working modes are divided into three types: simplex, full-duplex, and half-duplex.
- Simplex: At any moment during communication, information can only be transmitted from A to B, or from B to A;
- Half-duplex: At any moment during communication, information can be transmitted from A to B and from B to A, but only one direction of transmission can exist at a time;
- Full-duplex: At any moment during communication, there are bidirectional signal transmissions from A to B and from B to A on the communication line;
6.4 Serial Communication Protocol
The serial communication protocol defines the rules and format for data exchange on the serial port. Common serial communication protocols include the ASCII protocol, Modbus protocol, RS-232 protocol, etc. The protocol specifies the frame structure, data format, and verification method of the data, ensuring that the sending and receiving parties exchange data according to the same rules, thereby achieving correct data transmission and parsing. Serial communication transmits one bit at a time. The transmission of each character always starts with a start bit and ends with a stop bit, and there is no fixed time interval requirement between characters. Each character is preceded by a start bit (low level), followed by 7 data bits, then a parity bit, and finally a stop bit. After the stop bit are variable-length idle bits. Both the stop bit and the idle bits are specified as high level.
6.5 Purpose and Advantages of Serial Communication
The serial port is one of the most common communication interfaces between computers and external devices, and it plays an important role and has wide applications. In the field of computers, the importance of the serial port is reflected in the following aspects:
- Data transmission: The serial port is a commonly used data transmission interface. Through the serial port, the computer can exchange data and communicate with various external devices. Whether it is sensors, actuators, displays, printers, or other external devices, serial communication can achieve data transmission and control.
- Remote control and monitoring: Serial communication is widely used in the field of remote control and monitoring. Through the serial port, computers can remotely control the actions of devices and monitor the status and data information of the devices in real time. This plays an important role in industrial control, automation systems, remote monitoring, and other scenarios.
- Debugging and troubleshooting: Serial communication is an important tool for debugging and troubleshooting. Through the serial port, computers can communicate with embedded systems, microcontrollers, etc., monitor and debug programs in real time, output debugging information, perform error localization and troubleshooting, and perform status monitoring and fault diagnosis of the system.
- Hardware connection: The serial port can serve as a bridge between the computer and various external devices. Through the serial port, you can connect and control various external devices, such as sensors, actuators, and peripherals. The serial port can provide stable data transmission and bidirectional communication functions.
- Communication protocol: The serial communication protocol is the specification and agreement for data transmission between the computer and external devices. By defining different protocols, data interaction and communication between different devices can be achieved. Common serial communication protocols include UART, RS-232, RS-485, etc. In summary, the serial port plays an important role in communication between the computer and external devices. It is a key tool for data transmission, remote control and monitoring, debugging and troubleshooting, and serves as a bridge for connecting and communicating between the computer and external devices. Through serial communication, data interaction with various external devices can be achieved, improving the functionality and performance of the system. The specific purposes and advantages of serial communication can be summarized as follows:
- Data transmission: Serial communication can achieve reliable bidirectional data transmission, including sending and receiving various types of data.
- Remote control and monitoring: Through serial communication, you can remotely control the actions of devices and monitor the status and data information of the devices in real time.
- Debugging and troubleshooting: Serial communication is an important tool for debugging and troubleshooting. It can monitor and debug programs in real time, output debugging information, and perform error localization and troubleshooting.
- Flexibility and real-time performance: Serial communication has high flexibility and real-time performance. You can adjust the baud rate and parameters according to your needs, and process data and respond to external events in a timely manner.
- Cost-effectiveness: Serial communication uses simple, low-cost hardware and is widely used in various fields. It is an economical and practical communication method. In summary, serial communication plays an important role and has many advantages in data transmission, remote control and monitoring, debugging and troubleshooting, etc. It is an important means of achieving data interaction between devices and system functionality.
6.6 Serial Communication Schematic
The ESP32-S3 has three serial ports, namely UART0, UART1, and UART2. Among them, the serial port 0 of the development board has been used for automatic downloading and debugging, so it is not recommended to use serial port 0 to communicate with other devices in actual applications.
We can use UART1 (serial port 1) and UART2 (serial port 2) to communicate with external serial devices. Except for serial port 0, the serial port pins, like other peripherals, can use any GPIO as the communication pin.
6.7 Serial Communication Driver Flow
6.7.1 Import the Header File
include "driver/uart.h"Add the above header file in the source file to access the serial-port-related functions provided by ESP-IDF. This header file provides the corresponding functions and data types for configuring and controlling serial communication.
6.7.2 Initialize the Serial Port
Use the uart_config_t structure to configure the UART. You can set parameters such as baud rate, data bits, stop bits, and parity. Note that you should select the correct UART number (such as UART_NUM_0, UART_NUM_1, etc.) and GPIO pins.
uart_config_t uart_config = {
.baud_rate = 115200, // Set the baud rate
.data_bits = UART_DATA_8_BITS, // Set the data bits
.parity = UART_PARITY_DISABLE, // Set the parity
.stop_bits = UART_STOP_BITS_1, // Set the stop bits
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE // Hardware flow control
};
uart_param_config(UART_NUM_1, &uart_config); // Apply the above parameters to the UART2
3
4
5
6
7
8
9
10
Description of each parameter in the uart_config_t structure:
.baud_rate: The baud rate (data transmission rate) of the serial port, indicating the number of bits transmitted per second. Common baud rates are 9600, 115200, etc. Note that when using a high baud rate, you need to ensure hardware support..data_bits: The number of data bits, that is, the number of bits in each byte. Available values are:UART_DATA_5_BITS,UART_DATA_6_BITS,UART_DATA_7_BITS,UART_DATA_8_BITS..parity: Parity bit settings. Available values are:UART_PARITY_DISABLE(disable parity bit),UART_PARITY_EVEN(even parity),UART_PARITY_ODD(odd parity)..stop_bits: The number of stop bits. Available values are:UART_STOP_BITS_1(1 stop bit),UART_STOP_BITS_1_5(1.5 stop bits, only applicable to 5 data bits), andUART_STOP_BITS_2(2 stop bits)..flow_ctrl: Hardware flow control settings. Available values are:UART_HW_FLOWCTRL_DISABLE(disable flow control),UART_HW_FLOWCTRL_RTS(only enable RTS signal flow control), andUART_HW_FLOWCTRL_CTS(only enable CTS signal flow control). After the parameters are set, use theuart_param_config()function to apply the parameters to the corresponding serial port. Example:
uart_param_config(UART_NUM_1, &uart_config); // Apply the above parameters to the UARTuart_param_config() is a function in the ESP32 for UART parameter configuration, used to set the communication parameters of the specified UART port. The function prototype is as follows:
esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config)Parameter description:
uart_num: UART port number, for exampleUART_NUM_1orUART_NUM_2.uart_config: A pointer to theuart_config_tstructure, containing the UART parameter configuration information.
6.7.3 Bind the Serial Port Pins
We have set the parameters of the serial port, but the communication pins of the serial port still need to be set. Here is an example of binding the serial port pins:
// Pins used
int tx_pin = 9;
int rx_pin = 10;
// Bind pins TX=tx_pin RX=rx_pin RTS=not used CTS=not used
uart_set_pin(UART_NUM_1, tx_pin, rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);2
3
4
5
The uart_set_pin() function is used to set the UART pin connection. It is used to connect the specified UART port to the specified pins. The specific usage and parameter explanation are as follows:
void uart_set_pin(uart_port_t uart_num, int8_t tx_io_num, int8_t rx_io_num, int8_t rts_io_num, int8_t cts_io_numuart_num: UART port number. Available values areUART_NUM_0,UART_NUM_1,UART_NUM_2.tx_io_num: The GPIO number of the UART transmit pin. If the transmit function is not needed, this parameter can be set to-1.rx_io_num: The GPIO number of the UART receive pin. If the receive function is not needed, this parameter can be set to-1.rts_io_num: The GPIO number of the USART Request To Send (RTS) pin. If the RTS function is not needed, this parameter can be set to-1.UART_PIN_NO_CHANGEin the example =-1.cts_io_num: The GPIO number of the USART Clear To Send (CTS) pin. If the CTS function is not needed, this parameter can be set to-1.UART_PIN_NO_CHANGE = -1in the example. Using this function, you can connect the UART transmit, receive, RTS, and CTS pins to the specified GPIO pins.
6.7.4 Set the Working Mode
Select the working mode of the UART according to your needs. You can use the uart_set_mode() function to set it to UART mode or RS485 mode:
uart_set_mode(UART_NUM_1, UART_MODE_RS485_HALF_DUPLEX);uart_set_mode() is a function in ESP-IDF used to set the working mode of the UART (serial port). It can set the serial port to transmission mode, baud rate generator mode, or some special modes. The function prototype is as follows:
esp_err_t uart_set_mode(uart_port_t uart_num, uart_mode_t mode)Parameter description:
uart_num: UART port number. Available values areUART_NUM_0,UART_NUM_1,UART_NUM_2.mode: The working mode of the UART. Available values areUART_MODE_UART,UART_MODE_RS485_HALF_DUPLEX,UART_MODE_RS485_FULL_DUPLEX,UART_MODE_IRDA. Different working modes affect the data transmission method of the UART and the use of hardware control signals. The following is a description of each working mode:UART_MODE_UART: Transmission mode, used for basic serial communication, supports full-duplex transmission.UART_MODE_RS485_HALF_DUPLEX: RS485 half-duplex mode, used for RS485 half-duplex communication in serial communication protocols. In this mode, you need to separately configure the related RS485 parameters.UART_MODE_RS485_FULL_DUPLEX: RS485 full-duplex mode, used for RS485 full-duplex communication in serial communication protocols. In this mode, you need to separately configure the related RS485 parameters.UART_MODE_IRDA: Infrared data transmission mode, used for infrared data communication. Note that when using RS485-related modes, you need to configure the RS485 pins and parameters, and enable the control of the RS485 driver.
6.7.5 Install the Driver
Use the uart_driver_install() function to install the UART driver and specify the size of the receive and transmit buffers:
uart_driver_install(UART_NUM_1, BUF_SIZE * 2, 0, 0, NULL, 0);uart_driver_install is a function in ESP-IDF used to install the UART (serial port) driver and initialize the serial port hardware. This function allocates memory resources and configures the working parameters of the serial port. The function prototype is as follows:
esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t *uart_queue, int intr_alloc_flags)Parameter description:
uart_num: UART port number. Available values areUART_NUM_0,UART_NUM_1,UART_NUM_2.rx_buffer_size: Receive buffer size, used to store received data.tx_buffer_size: Transmit buffer size, used to store data to be sent.queue_size: The size of the internal buffer queue of the UART driver, used to store pending receive and transmit data.uart_queue: Pointer to a user-defined queue handle used to receive data. When receiving data, the received data will be stored in this queue.intr_alloc_flags: UART interrupt allocation flags, used to configure the interrupt allocation strategy. Using theuart_driver_installfunction, you can easily initialize the UART, and specify the corresponding buffer and queue sizes as well as other parameters.
6.7.6 Send and Receive Data
Use the uart_write_bytes() function to send data, and use the uart_read_bytes() function to receive data. uart_write_bytes is a function in ESP-IDF used to send data to the UART (serial port). This function writes the specified data to the UART transmit buffer and triggers data transmission. The function prototype is as follows:
esp_err_t uart_write_bytes(uart_port_t uart_num, const char *src, size_t size)Parameter description:
uart_num: UART port number. Available values areUART_NUM_0,UART_NUM_1,UART_NUM_2.src: Pointer to the source data buffer, containing the data to be sent.size: The number of bytes of data to be sent. The following is an example code snippet:
// Send data to the UART
uart_write_bytes(UART_NUM_1, "WWW.LCKFB.COM", 14);2
Note that when using the uart_write_bytes function to send data, the data will be copied to the UART transmit buffer, and the function will return immediately without waiting for the data to be fully sent. Therefore, if you need to ensure that the data is fully sent, you can use the uart_wait_tx_done function to wait. After ensuring that the UART is initialized successfully and the correct baud rate and other parameters are configured, you can use the uart_write_bytes function to send data to the UART device. Serial port receiving method Create a UART read task: Create a task that continuously reads serial port data and processes it.
xTaskCreate(uart_task, "uart_task", 4096, NULL, configMAX_PRIORITIES-1, NULL);Implement the UART read task function: In the task function, use the uart_read_bytes() function to read data from the serial port receive buffer.
// Serial port read task
void uart_task(void *arg)
{
uint8_t rx_data[200]={0};
while (1)
{
// The length of data received from the serial port
int rx_bytes = uart_read_bytes(UART_NUM_2, rx_data, 200, 10 / portTICK_PERIOD_MS);
if( rx_bytes > 0 ) // If data length is greater than 0, it means data was received
{
rx_data[rx_bytes] = 0; // Set the last byte of the serial port data to 0 to form a string
// Output the received data through serial port 2
uart_write_bytes(UART_NUM_2, (const char*)"uart2 received : ", strlen("uart2 received : "));
uart_write_bytes(UART_NUM_2, (const char*)rx_data, strlen((const char*)rx_data));
// UART ring buffer flush. Discards all data in the UART RX buffer, preparing for the next reception
uart_flush(UART_NUM_2);
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
The above code is an example code for a serial port read task. Through the task, it detects whether there is data in the serial port receive buffer. When there is data, the code reads data from the UART port (UART_NUM_2) through the uart_read_bytes() function and stores the read data in the rx_data array. The task uses an infinite loop while (1) to continuously receive and process UART data. The portMAX_DELAY parameter is used to specify that if the queue has no available data, the task will wait until data is received. uart_read_bytes() is a function used to read data from a UART port. It is one of the UART library functions provided in the ESP-IDF SDK. This function has the following form:
esp_err_t uart_read_bytes(uart_port_t uart_num, uint8_t *data, size_t length, TickType_t ticks_to_wait)The parameter description is as follows:
uart_num: The UART port number to read data from, such asUART_NUM_0,UART_NUM_1,UART_NUM_2, etc.data: A pointer to the buffer for receiving data, used to store the data received from the UART.length: The number of data bytes to read, that is, the length of data to be received.ticks_to_wait: The timeout for reading data, expressed in FreeRTOS ticks. You can useportMAX_DELAYto indicate infinite waiting, or use a specific number of ticks. This function has a return parameter. When it returns -1, it indicates a reception error; when the returned value is greater than or equal to 0, the returned value is the number of bytes read from the UART buffer. Note that this function will block the current task until one of the following conditions is met:
- The specified number of data bytes is received.
- A UART error occurs.
- The specified timeout is exceeded. Note also that before calling the
uart_read_bytesfunction, you must first correctly initialize the UART port and set the relevant UART parameters so that the data can be read normally.
6.8 Hardware Connection and Preparation
This example uses the common CH340 module to connect the development board to the computer to test the serial communication function. The CH340 is a USB-to-serial chip, commonly used for USB-to-serial functions in microcontrollers, microcontrollers, and Arduinos, and can also be used for USB data transmission. When using the CH340 module, you need to install the corresponding driver so that the computer can recognize the serial output of the CH340 chip. For Windows systems, the driver is usually installed automatically; for Mac and Linux systems, you need to download and install the corresponding driver support before you can use it.
File Download
Download the CH340 Driver from the Development Tools section in the Baidu Netdisk link of the Download Center.
File - CH340-Windows Driver The CH340 module usually comes in two models: CH340G and CH340E. The CH340G works in the operating temperature range of 0°C to 70°C, while the CH340E works in the temperature range of -40°C to 85°C. Both models support a variety of baud rate, data bit, and stop bit options to adapt to different serial communication needs.
The CH340 module is quite convenient to use. After connecting the computer and the development board, you only need to send commands through the serial port to communicate with the microcontroller. Its advantages are simple interface, low price, and easy-to-use driver, so it is widely used in development and various projects that need to communicate with a computer. The physical connection is shown in the figure below:
6.9 Serial Communication Verification
Create two new files, bsp_uart.c and bsp_uart.h. Save them in the main/hardware/uart directory. If this directory does not exist, please create it.
Add the header file and path.
Write the following code in the bsp_uart.c file.
#include "bsp_uart.h"
/**
* @brief uart_init_config
* @param uart_port = serial port number
* Available parameters: UART_NUM_0, UART_NUM_1, UART_NUM_2
* baud_rate = serial port baud rate
* tx_pin = transmit pin
* rx_pin = receive pin
* @retval None
*/
void uart_init_config(uart_port_t uart_port, uint32_t baud_rate, int tx_pin, int rx_pin)
{
// Define the serial port configuration structure; it must be initialized, otherwise it will not work
uart_config_t uart_config={0};
uart_config.baud_rate = baud_rate; // Configure the baud rate
uart_config.data_bits = UART_DATA_8_BITS; // Configure the data bits to 8 bits
uart_config.parity = UART_PARITY_DISABLE; // Configure the parity to no parity
uart_config.stop_bits = UART_STOP_BITS_1; // Configure the stop bits to one bit
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; // Disable hardware flow control
// Load the above parameters into the serial port registers
uart_param_config(uart_port, &uart_config);
// Bind pins TX=tx_pin RX=rx_pin RTS=not used CTS=not used
uart_set_pin(uart_port, tx_pin, rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
// Install the serial port driver
uart_driver_install(uart_port, 200, 200, 0, NULL, 0);
}
/**
* @brief Serial port 2 receive task
* @param None
* @retval None
* @note Please create this task via xTaskCreate
*/
void uart2_rx_task(void)
{
uint8_t rx_data[200]={0};
uint8_t temp[50]={0};
while(1)
{
// The length of data received from the serial port
int rx_bytes = uart_read_bytes(UART_NUM_2, rx_data, 200, 10 / portTICK_PERIOD_MS);
if( rx_bytes > 0 ) // If data length is greater than 0, it means data was received
{
rx_data[rx_bytes] = 0; // Set the last byte of the serial port data to 0 to form a string
// Output the length of received data
sprintf((const char*)temp,"uart2 string length : %d\r\n", rx_bytes);
uart_write_bytes(UART_NUM_2, (const char*)temp, strlen((const char*)temp));
// Output the received data through serial port 2
uart_write_bytes(UART_NUM_2, (const char*)"uart2 received : ", strlen("uart2 received : "));
uart_write_bytes(UART_NUM_2, (const char*)rx_data, strlen((const char*)rx_data));
// UART ring buffer flush. Discards all data in the UART RX buffer, preparing for the next reception
uart_flush(UART_NUM_2);
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
Write the following code in the bsp_uart.h file.
#ifndef _BSP_UART_H_
#define _BSP_UART_H_
// Include the serial port device library
#include "driver/uart.h"
// Include the GPIO device library
#include "driver/gpio.h"
// Include strlen function support
#include <string.h>
/**
* @brief uart_init_config
* @param uart_port = serial port number
* Available parameters: UART_NUM_0, UART_NUM_1, UART_NUM_2
* baud_rate = serial port baud rate
* tx_pin = transmit pin
* rx_pin = receive pin
* @retval None
*/
void uart_init_config(uart_port_t uart_port, uint32_t baud_rate, int tx_pin, int rx_pin);
/**
* @brief Serial port 2 receive task
* @param None
* @retval None
* @note Please create this task via xTaskCreate
*/
void uart2_rx_task(void);
#endif2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Write the following code in the main.c file.
#include <stdio.h>
// Include ESP32 log output functionality
#include "esp_log.h"
// Include FreeRTOS task scheduling
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Include the serial port device library
#include "driver/uart.h"
// Include the GPIO device library
#include "driver/gpio.h"
#include "bsp_uart.h"
void app_main(void)
{
// Initialize serial port 2 TX=GPIO10 RX=GPIO9
uart_init_config(UART_NUM_2, 115200, 10, 9);
// Create the serial port 2 receive task
xTaskCreate(uart2_rx_task, "uart2_rx_task", 1024*2, NULL, configMAX_PRIORITIES-1, NULL);
// Send the string "start uart demo" through serial port 2
uart_write_bytes(UART_NUM_2, (const char*)"start uart demo", strlen("start uart demo"));
while(1)
{
// Serial port 2 sends data
uart_write_bytes(UART_NUM_2, (const char*)"Task running : main", strlen("Task running : main"));
// ESP32-S3 log output data
ESP_LOGI("main", "Task running : main\r\n");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
6.10 Serial Communication Effect
Burn the code to the development board, and connect the hardware as described in the Hardware Connection and Preparation section to achieve the following effect.
The development board continuously sends the string "Task running : main" through serial port 2. When serial port 2 of the development board receives data, it echoes the data back through serial port 2 and displays the data length: uart2 string length : 7uart2 received : afddbfg