UART (Universal Asynchronous Receiver/Transmitter), as a kind of asynchronous serial communication protocol, the working principle is to transmit each character of the transmitted data one by one. It is the most frequently used data bus during application development.
The UART serial port is characterized by sequentially transmitting data one bit at a time. As long as two transmission lines can realize two-way communication, one line transmits data while the other receives data . There are several important functions for UART serial communication, namely baud rate, start bit, data bit, stop bit and parity bit. For two ports that use UART serial port communication, these functions must be matched, otherwise the communication can't be carried out normally. The data format of the UART serial port transmission is as shown below:
![Serial Transmission Data Format](figures/uart1.png)
* Start bit: Indicates the start of data transfer and the level logic is "0".
- Data bits: Possible values are 5, 6, 7, 8, and 9, indicating that these bits are transmitted. The value is generally 8, because an ASCII character value is 8 bits.
- Parity check bit: It it used by the receiver to verify the received data. The number of bits is used in the check of "1" is even (even parity) or odd (odd parity) ,in order to verify the data transmission. It is also fine by not using this bit .
- Stop Bit: Indicates the end of one frame of data. The level logic is "1".
- Baudrate: It is the rate at which a serial port communicates, which expressed in bits per second (bps) of the binary code transmitted in unit time. The common baud rate values are 4800, 9600, 14400, 38400, 115200, etc. The higher the value is, the faster the data transmission will be.
## Access UART Device
The application accesses the serial port hardware through the I/O device management interface provided by RT-Thread. The related interfaces are as follows:
| rt_device_set_rx_indicate() | set receive callback function |
| rt_device_set_tx_complete() | set send complete callback function |
| rt_device_close() | close device |
### Find UART Device
The application obtains the device handle according to the uart device name, and then can operate the uart device.The device find function is shown below
Through the device handle, the application can open and close the device. When the device is opened, it will detect whether the device has been initialized. If it is not initialized, it will call the initialization interface to initialize the device by default. Open the device through the following functions:
| -RT_EBUSY | If the standalone parameter RT_DEVICE_FLAG_STANDALONE is included in the functions specified when the device is registered, the device will not be allowed to be opened repeatedly |
| Other error codes | device failed to open |
oflags parameters support the following values (Use OR logic to support multiple values):
There are three modes of uart data receiving and sending: interrupt mode, polling mode and DMA mode. When used, only one of the three modes can be selected. If the open parameter oflag of the serial port does not specify the use of interrupt mode or DMA mode, the polling mode is used by default.
The DMA (Direct Memory Access) transfer mode does not require the CPU to directly control the transfer, and does not have the process of reserving the scene and restoring the scene as they have in the interrupt processing mode. The DMA controller opens a path for directly transferring data to the RAM and the I/O device, which saves CPU resources to do other things. Using DMA transfer can continuously acquire or send a piece of information without taking up interrupts or delays, which is useful when communication is frequent or when large pieces of information are to be transmitted.
>RT_DEVICE_FLAG_STREAM: Stream mode is used to output a string to the serial terminal: when the output character is `"\n"` (corresponding to the hexadecimal value 0x0A), a ``\r"` is automatically output in front (corresponding to hexadecimal value is 0x0D).
The stream mode `RT_DEVICE_FLAG_STREAM` can be used with the receive and send mode parameter with the "|" logic.
An example of using a uart device in **interrupt receive mode and polling mode** as follows:
```c
#define SAMPLE_UART_NAME "uart2" /* uart device name */
/* Open the uart device in interrupt receive mode and polling mode*/
rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
```
If the uart is to use the DMA receive mode, the oflags takes the value RT_DEVICE_FLAG_DMA_RX. An example of using a uart device in the **DMA receive and polling send mode** is as follows:
```c
#define SAMPLE_UART_NAME "uart2" /* uart device's name */
* The prototype of control parameter structure: struct serial_configure is as follows:
```c
struct serial_configure
{
rt_uint32_t baud_rate; /* Baudrate */
rt_uint32_t data_bits :4; /* Data bit */
rt_uint32_t stop_bits :2; /* Stop bit */
rt_uint32_t parity :2; /* Parity bit */
rt_uint32_t bit_order :1; /* Prioritized by order */
rt_uint32_t invert :1; /* Mode */
rt_uint32_t bufsz :16; /* Receive data buffer size */
rt_uint32_t reserved :4; /* Reserved bit */
};
```
* The default macro configuration provided by RT-Thread is as follows:
```c
#define RT_SERIAL_CONFIG_DEFAULT \
{ \
BAUD_RATE_115200, /* 115200 bps */ \
DATA_BITS_8, /* 8 databits */ \
STOP_BITS_1, /* 1 stopbit */ \
PARITY_NONE, /* No parity */ \
BIT_ORDER_LSB, /* LSB first sent */ \
NRZ_NORMAL, /* Normal mode */ \
RT_SERIAL_RB_BUFSZ, /* Buffer size */ \
0 \
}
```
The configuration parameters provided by RT-Thread can be defined as the following macro definitions::
```c
/* The baudrate can be defined as*/
#define BAUD_RATE_2400 2400
#define BAUD_RATE_4800 4800
#define BAUD_RATE_9600 9600
#define BAUD_RATE_19200 19200
#define BAUD_RATE_38400 38400
#define BAUD_RATE_57600 57600
#define BAUD_RATE_115200 115200
#define BAUD_RATE_230400 230400
#define BAUD_RATE_460800 460800
#define BAUD_RATE_921600 921600
#define BAUD_RATE_2000000 2000000
#define BAUD_RATE_3000000 3000000
/* Data bits can be defined as*/
#define DATA_BITS_5 5
#define DATA_BITS_6 6
#define DATA_BITS_7 7
#define DATA_BITS_8 8
#define DATA_BITS_9 9
/* Stop bits can be defined as */
#define STOP_BITS_1 0
#define STOP_BITS_2 1
#define STOP_BITS_3 2
#define STOP_BITS_4 3
/* Parity bits can be defined as */
#define PARITY_NONE 0
#define PARITY_ODD 1
#define PARITY_EVEN 2
/* Bit order can be defined as */
#define BIT_ORDER_LSB 0
#define BIT_ORDER_MSB 1
/* Mode canbe defined as */
#define NRZ_NORMAL 0 /* normal mode */
#define NRZ_INVERTED 1 /* inverted mode */
/* Default size of the receive data buffer */
#define RT_SERIAL_RB_BUFSZ 64
```
**Receive Buffer**
When the uart device is opened using interrupt receive mode, the uart driver framework will open a buffer according to the size of RT_SERIAL_RB_BUFSZ to save the received data. When the underlying driver receives a data, it will put the data into the buffer in the interrupt service program.
>The default size of the receive data buffer is 64 bytes. If the number of received data in one-time is too large and the data is not read in time, the data of the buffer will be overwritten by the newly received data, resulting in data loss. It is recommended to increase the buffer.
A sample for configuring uart hardware parameters such as data bits, check bits, stop bits, and so on are shown below:
```c
#define SAMPLE_UART_NAME "uart2" /* uart device's name */
When the application calls `rt_device_write()` to write data, if the underlying hardware can support automatic transmission, the upper application can set a callback function. This callback function is called after the underlying hardware data has been sent (for example, when the DMA transfer is complete or the FIFO has been written to complete the completion interrupt). You can set the device to send completion instructions by the following function:
When this function is called, the callback function is provided by the user. When the hardware device sends the data, the device driver calls back this function and passes the sent data block address buffer as a parameter to the upper application. When the application (thread) receives the indication, it will release the buffer memory block or use it as the buffer for the next write data according to the condition of sending the buffer.
### Set The Receive Callback Function
The data receiving instruction can be set by the following function. When the serial port receives the data, it will inform the upper application thread that the data has arrived:
| dev | device handle (callback function parameter) |
| size | buffer data size (callback function parameter) |
| **back** | —— |
| RT_EOK | set up successfully |
The callback function for this function is provided by the user. If the uart device is opened in interrupt receive mode, the callback function will be called when the serial port receives a data, and the data size of the buffer will be placed in the `size` parameter, and the uart device handle will be placed in the `dev` parameter.
If the uart is opened in DMA receive mode, the callback function is called when the DMA completes receiving a batch of data.
Normally the receiving callback function can send a semaphore or event to notify the serial port data processing thread that data has arrived. The example is as follows:
```c
#define SAMPLE_UART_NAME "uart2" /* uart device name */
| -RT_ERROR | The device has been completely shut down and cannot be shut down repeatedly |
| other error codes | fail to close the device |
Use the `rt_device_close()` interface and `rt_device_open()` interface in pair. When you open the device, you need to close the device once, so that the device will be completely shut down, otherwise the device will remain open.
## Examples Of Using UART Device
### Interrupt Receiving And Polling Send
The main steps of the sample code are as follows:
1. First find the uart device to get the device handle.
2. Initialize the semaphore that the callback function sends, and then open the uart device in read/write and interrupt receive mode.
3. Set the receive callback function of the uart device, then send the string and create a read data thread.
4. The read data thread will try to read a character data. If there is no data, it will hang and wait for the semaphore. When the uart device receives a data, it will trigger an interrupt and call the receive callback function. This function will send a semaphore to wake up the thread. At this point, the thread will immediately read the received data.
5. This sample code is not limited to a specific BSP. According to the uart device registered by BSP, modify the uart device's name corresponding to the sample code's macro definition SAMPLE_UART_NAME to run.
The running sequence diagram is shown as follows:
![Serial Port Interrupt Reception and Polling Transmission Sequence Diagram](figures/uart-int.png)
```c
/*
* Program list: This is a uart device usage routine
* The routine exports the uart_sample command to the control terminal
* Format of command: uart_sample uart2
* Command explanation: the second parameter of the command is the name of the uart device. If it is null, the default uart device wil be used
* Program function: output the string "hello RT-Thread!" through the serial port, and then malposition the input character
When the serial port receives a batch of data, it will call the receive callback function. The receive callback function will send the data size of the buffer at this time to the waiting data processing thread through the message queue. After the thread gets the message, it is activated and reads the data. In general, the DMA receive mode completes data reception in conjunction with the DMA receive completion interrupt and the serial port idle interrupt.
* This sample code is not limited to a specific BSP. According to the uart device registered by BSP, modify the sample code macro to define the uart device name corresponding to SAMPLE_UART_NAME to run.
The running sequence diagram is shown below:
![Serial DMA Receiving and Polling Transmission Sequence Diagram](figures/uart-dma.png)
```c
/*
* Program list: This is a uart device DMA receive usage routine
* The routine exports the uart_dma_sample command to the control terminal
* Command format: uart_dma_sample uart3
* Command explanation: The second parameter of the command is the name of the uart device to be used. If it is empty, the default uart device will be used.
* Program function: output the string "hello RT-Thread!" through the serial port, and output the received data through the serial port, and then print the received data.
*/
#include <rtthread.h>
#define SAMPLE_UART_NAME "uart3" /* uart device name */