Contributed by 爱笑, organized by Ai-Thinker
[Ai-WB2 Getting Started] UART Data Transmission and Reception
The Universal Asynchronous Receiver/Transmitter (usually referred to as UART) is an asynchronous serial communication device that provides a flexible way to exchange full-duplex data with external devices. The Ai-WB2 has two UART ports (UART0 and UART1). When used together with DMA, efficient data communication can be achieved.
This article describes in detail how to use the UART module of the Ai-WB2. The main contents include data reception and transmission in polling mode, data reception and transmission in interrupt mode, and data reception and transmission via DMA. 1. Ai-WB2 UART Introduction The Ai-WB2 has two UART ports (UART0 and UART1). When used together with DMA, efficient data communication can be achieved. The UART of the Ai-WB2 has the following features:
Click to expand full code
**·** 全双工异步通信
**·** 数据位长度可选择51617/8 比特
**·** 停止位长度可选择0.5/1/1.5/2 比特
**·** 支持奇/偶/无校验比特
**·** 可侦测错误的起始比特
**·** 丰富的中断控制
**·** 支持硬件流控(RTS/CTS)
**·** 便捷的波特率编程
**·** 可配置MSB/LSB 优先传输
**·** 普通/固定字符的自动波特率检测
**·** 32 字节发送/接收FIFO
**·** 支持DMA 传输模式
**·** 最高工作频率为160MHZ
In the table above, when the UART function is selected, only one UART signal is chosen for the pin; the specific function of the pin (e.g., UART TX or UART RX) is not yet specified. The specific UART signal and its corresponding function need to be further selected via UART_SIGx_SEL (x=0-7). The signals that can be selected for each UART_SIGx_SEL include:
Click to expand full code
**·** 0 : UARTO RTS
**·** 1 : UARTO CTS
**·** 2 : UARTO TXD
**·** 3 : UARTO RXD
**·** 4 : UART1 RTS
**·** 5 : UART1 CTS
**·** 6 : UART1 TXD
**·** 7 : UART1 RXDTaking GPIO0 as an example: when UART is selected for fun_sel, GPIO0 is mapped to UART_SIG0. By default, the value of UART_SIG0_SEL is 0, which is UART0_RTS, meaning the GPIO functions as UART0_RTS. If the application wants to use the GPIO as UART1_TXD, simply set UART_SIG0_SEL to 6, and GPIO0 will function as UART1_TXD. 2. UART Driver API Introduction The HOSAL layer high-level driver API for UART is defined in the header file components/platform/hosal/include/hosal_uart.h, which defines and encapsulates the high-level UART operations of the BL602. Commonly used functions are as follows: · int hosal_uart_init(hosal_uart_dev_t *uart): initializes the UART. Its parameters are described as follows: · uart: the UART device definition, as follows:
Click to expand full code
/**
* @brief UART device type
*/
typedef struct {
uint8_t port; /**< @brief UART 端口(BL602中分别为UART0和UART1) */
hosal_uart_config_t config; /**< @brief UART 配置 */
hosal_uart_callback_t tx_cb; /**< @brief UART 发送完成中断回调函数 */
void *p_txarg; /**< @brief UART 发送完成回调函数参数 */
hosal_uart_callback_t rx_cb; /**< @brief UART 接收完成中断回调函数 */
void *p_rxarg; /**< @brief UART rx 接收完成中断回调函数函数 */
hosal_uart_callback_t txdma_cb; /**< @brief UART DMA方式发送完成中断回调函数 */
void *p_txdma_arg; /**< @brief UART DMA方式发送完成中断回调函数参数 */
hosal_uart_callback_t rxdma_cb; /**< @brief UART DMA方式接收完成中断回调函数 */
void *p_rxdma_arg; /**< @brief UART DMA方式接收完成中断回调函数 */
hosal_dma_chan_t dma_tx_chan; /**< @brief UART DMA发送通道 */
hosal_dma_chan_t dma_rx_chan; /**< @brief UART DMA接收通道 */
void *priv; /**< @brief UART 用户自定义数据 */
} hosal_uart_dev_t;The hosal_uart_config_t field in the hosal_uart_dev_t structure is used to define the UART configuration, as follows:
Click to expand full code
typedef struct {
uint8_t uart_id; /**< @brief UART id */
uint8_t tx_pin; /**< @brief UART tx pin */
uint8_t rx_pin; /**< @brief UART rx pin */
uint8_t cts_pin; /**< @brief UART cts pin */
uint8_t rts_pin; /**< @brief UART rts pin */
uint32_t baud_rate; /**< @brief UART baud rate */
hosal_uart_data_width_t data_width; /**< @brief UART data width */
hosal_uart_parity_t parity; /**< @brief UART parity bit */
hosal_uart_stop_bits_t stop_bits; /**< @brief UART stop btis */
hosal_uart_flow_control_t flow_control; /**< @brief UART flow control */
hosal_uart_mode_t mode; /**< @brief UART int or pull mode */
} hosal_uart_config_t;Click to expand full code
**·** 返回值:成功时返回0,否则返回E10或其他值,
**·** int hosal_uart_send(hosal_uart_dev_t *uart, const void *txbuf, uint32_t size):轮询方式UART发送数据,其参数说明如下:
**·** uart:UART设备
**·** txbuf:发送的数据
**·** size:发送的数据长度
**·** 返回值:当返回值大于0时,表示数据发送成功。否则,表示失败
**·** int hosal_uart_receive(hosal uart_dev_t *uart, void *data, uint32_t expect_size):轮询方式UART接收数据,其参数说明如下:
**·** uart:UART设备
**·** data:接收的数据
**·** expect_size:期望接收的数据长度
**·** 返回值:当返回值大于0时,表示数据接收成功。否则,表示失败
**·** int hosal_uart_abr_get(hosal_uart_dev_t*uart,uint8_t mode):自动获取UART接囗的波特率
**·** int hosal_uart_ioctl(hosal_uart_dev_t *uart, intctl,void *p_arg):UART控制函数
**·** uart:UART设备
**·** ctl:控制命令,其定义如下:- HOSAL_UART_BAUD_SET: sets the baud rate, with p_arg being the baud rate
- HOSAL_UART_BAUD_GET: gets the baud rate, with p_arg being a pointer to the baud rate
- HOSAL_UART_DATA_WIDTH_SET: sets the data width, with p_arg being hosal_uart_data_width_t
- HOSAL_UART_DATA_WIDTH_GET: gets the data width, with p_arg being a pointer to hosal_uart_data_width_t
- HOSAL_UART_STOP_BITS_SET: sets the stop bits, with p_arg being hosal_uart_stop_bits_t
- HOSAL_UART_STOP_BITS_GET: gets the stop bits, with p_arg being a pointer to hosal_uart_stop_bits_t
- HOSAL_UART_PARITY_SET: sets the parity, with p_arg being hosal_uart_parity_t
- HOSAL_UART_PARITY_GET: gets the parity, with p_arg being a pointer to hosal_uart_parity_t
- HOSAL_UART_MODE_SET: sets the UART mode, with p_arg being hosal_uart_mode_t
- HOSAL_UART_MODE_GET: gets the UART mode, with p_arg being a pointer to hosal_uart_mode_t
- HOSAL_UART_FLOWMODE_SET: sets the UART flow mode, with p_arg being hosal_uart_flow_control_t
- HOSAL_UART_FLOWSTAT_GET: gets the UART flow status, with p_arg being a pointer to hosal_uart_flow_control_t
- HOSAL_UART_FREE_TXFIFO_GET: gets the free TX FIFO size of the UART (in bytes)
- HOSAL_UART_FREE_RXFIFO_GET: gets the free RX FIFO size of the UART (in bytes)
- HOSAL_UART_FLUSH: waits for transmission to complete
- HOSAL_UART_TX_TRIGGER_ON: turns on the UART TX trigger
- HOSAL_UART_TX_TRIGGER_OFF: turns off the UART TX trigger
- HOSAL_UART_DMA_CONFIG: p_arg is a pointer to hosal_uart_dma_cfg_t
- HOSAL_UART_DMA_TX_START: starts the UART DMA TX transfer, with p_arg being a pointer to hosal_uart_dma_cfg_t
- HOSAL_UART_DMA_RX_START: starts the UART DMA RX transfer, with p_arg being a pointer to hosal_uart_dma_cfg_t
· p_arg: the control command parameter · Return value: 0 on success, otherwise EIO or another value · int hosal_uart_callback_set(hosal_uart_dev_t *uart, int callback_type, hosal_uart_callback_t pfn_callback, void *arg): sets the UART callback function. · uart: the UART device · callback_type: the callback function type, defined as follows:
- HOSAL_UART_TX_CALLBACK
- HOSAL_UART_RX_CALLBACK
- HOSAL_UART_TX_DMA_CALLBACK
- HOSAL_UART_RX_DMA_CALLBACK
· pfn_callback: the callback function pointer · arg: the callback function parameter · Return value: 0 on success, otherwise EIO or another value
Click to expand full code
**·** int hosal_uart_finalize(hosal_uart_dev_t *uart):释放UART。
**三:UART使用示例**
**1. 轮询方式数据接收与发送**The transmitter of the BL602 UART contains a 32-byte transmit FIFO for storing data to be sent. Software can write to the TX FIFO through the APB bus, or move data into it via DMA. When the transmit enable bit is set, the data stored in the FIFO is output from the TX pin. Software can feed data into the TX FIFO through either DMA or the APB bus. The transmitter status can be checked by querying the remaining free space count of the TX FIFO via the bits of the UART_FIFO_CONFIG_1 register. The free-run mode of the transmitter is as follows: · If free-run mode is not enabled, the transmission terminates and generates an interrupt when the number of bytes sent reaches the specified length. To continue sending, the transmit enable bit must be turned off and then re-enabled. · If free-run mode is enabled, the transmitter sends as long as there is data in the TX FIFO, and does not stop when the number of bytes sent reaches the specified length. The receiver of the BL602 UART contains a 32-byte receive FIFO for storing received data. Software can check the receiver status by querying the available data count of the RX FIFO via the bits of the UART_FIFO_CONFIG_1 register. The low 8 bits of the URX_RTO_TIMER register are used to set a receive timeout threshold; an interrupt is triggered when the receiver receives no data beyond this time. The bits of the URX_CONFIG register enable the de-glitch function and set the threshold value. They control the filtering part before UART sampling: the UART filters out glitches in the waveform whose width is below the threshold before sending the signal for sampling.
Click to expand full code
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <bl_gpio.h>
#include <stdio.h>
#include <hosal_gpio.h>
#include <hosal_dma.h>
#include <blog.h>
#include <stdbool.h>
#include <hosal_uart.h>
#define TAG "uart_demo"
// UART引脚
#define RX_PIN 4
#define TX_PIN 3
// 创建UART配置
HOSAL_UART_DEV_DECL(uart_dev_int, 1, TX_PIN, RX_PIN, 115200);
static void uart_init(void){
uart_dev_int.config.uart_id = 1;
/* 初始化UART */
hosal_uart_init(&uart_dev_int);
/* UART配置为轮询模式 */
hosal_uart_ioctl(&uart_dev_int, HOSAL_UART_MODE_SET, (void *)HOSAL_UART_MODE_POLL);
}
static void uart_task(void* params){
uart_init();
uint8_t data[32];
int ret;
printf("uart task inited\r\n");
hosal_uart_send(&uart_dev_int, "hello,uart1 demo\r\n",18);
while(true){
memset(data,0,32);
ret = hosal_uart_receive(&uart_dev_int, data, sizeof(data));
if (ret > 0) {
/* Uart send poll */
hosal_uart_send(&uart_dev_int, data, ret);
data[ret - 1] = '\0';
printf("recv:%s\r\n",data);
}
vTaskDelay(1);
}
}
void main(void) {
printf("uart demo inited\r\n");
xTaskCreate(uart_task, "uart_task", 1024, NULL, 15, NULL);
}In the example code: In the first step, we use the HOSAL_UART_DEV_DECL macro to create the UART configuration object hosal_uart_config_t. In the second step, in the uart_init function, the hosal_uart_init function and the hosal_uart_ioctl function are called to initialize the UART and set its working mode to poll mode respectively. In the third step, a FreeRTOS task is created to receive and send UART data. After data is received by hosal_uart_receive, the received data is sent by calling hosal_uart_send. In the fourth step, the task is created and started in the main function. The running result is as follows:

2. Data Reception and Transmission in Interrupt Mode The UART of the BL602 has rich interrupt control, including the following interrupt modes:
Click to expand full code
**·** TX 传输结束中断
**·** RX 传输结束中断
**·** TX FIFO 请求中断
**·** RX FIFO 请求中断
**·** RX 超时中断
**·** RX 奇偶校验错误中断
**·** TX FIFO 溢出中断
**·** RX FIFO 溢出中断A transmission length value can be set for TX and RX via the high 16 bits of the UTX_CONFIG and URX_CONFIG registers respectively. When the number of transmitted bytes reaches this value, the corresponding TX/RX transmission-complete interrupt is triggered. The TX/RX FIFO request interrupt is triggered when the available count of the FIFO is greater than the threshold set in the UART_FIFO_CONFIG_1 register; the interrupt flag is cleared automatically when the condition is no longer met. The RX timeout interrupt is triggered when the receiver receives no data beyond the timeout threshold, and the RX parity error interrupt occurs when a parity error is detected. If the TX/RX FIFO overflows or underflows, the corresponding overflow interrupt is triggered. When the FIFO clear bits TFICLR/RFICLR are set to 1, the corresponding FIFO is cleared and the overflow interrupt flag is cleared automatically. Interrupt status can be queried through the UART_INT_STS register, and interrupts can be cleared by writing 1 to the corresponding bits of the UART_INT_CLEAR register. Based on the above, next we will implement data reception and transmission in interrupt mode. Step 1: Define the transmit interrupt callback function
Click to expand full code
static int __uart_tx_callback(void *p_arg)
{
static uint8_t tx_counts = 0;
char buf[] = "TX interrupt TEST\r\n";
hosal_uart_dev_t *p_dev = (hosal_uart_dev_t *)p_arg;
if (tx_counts < sizeof(buf)) {
hosal_uart_send(p_dev, &buf[tx_counts++], 1);
} else {
/*如果数据传输完成,关闭TX触发模式 */
hosal_uart_ioctl(p_dev, HOSAL_UART_TX_TRIGGER_OFF, NULL);
}
return 0;
}Step 2: Define the receive interrupt callback function.
Click to expand full code
static int __uart_rx_callback(void *p_arg)
{
int ret;
hosal_uart_dev_t *p_dev = (hosal_uart_dev_t *)p_arg;
ret = hosal_uart_receive(p_dev, data_buf, RX_DATA_SIZE);
hosal_uart_send(p_dev, data_buf, ret);
return 0;
}Step 3: Initialize the UART working mode to interrupt mode
Click to expand full code
static void uart_init(void){
uart_dev_int.config.uart_id = 1;
/* 初始化UART */
hosal_uart_init(&uart_dev_int);
/* UART配置为中断模式 */
hosal_uart_ioctl(&uart_dev_int, HOSAL_UART_MODE_SET, (void *)HOSAL_UART_MODE_INT);
/* 设置接收和发送中断回调函数 */
hosal_uart_callback_set(&uart_dev_int, HOSAL_UART_RX_CALLBACK,
__uart_rx_callback, &uart_dev_int);
hosal_uart_callback_set(&uart_dev_int, HOSAL_UART_TX_CALLBACK,
__uart_tx_callback, &uart_dev_int);
/*启用发送中断 */
hosal_uart_ioctl(&uart_dev_int, HOSAL_UART_TX_TRIGGER_ON, NULL);
/*启用接收中断 */
hosal_uart_ioctl(&uart_dev_int, HOSAL_UART_RX_TRIGGER_ON, NULL);
}Step 4: Define the task
Click to expand full code
static void uart_task(void* params) {
uart_init();
printf("uart task start\r\n");
hosal_uart_send(&uart_dev_int, "hello,uart1 demo\r\n", 18);
while (true) {
vTaskDelay(1);
}
}Step 5: Start the task
Click to expand full code
void main(void) {
printf("start to init uart...\r\n");
xTaskCreate(uart_task, "uart_task", 1024, NULL, 15, NULL);
}3. Data Reception and Transmission via DMA The UART of the BL602 supports the DMA transfer mode. To use this mode, the thresholds of the TX and RX FIFOs need to be set via the bits of the UART_FIFO_CONFIG_1 register. When this mode is enabled, the UART checks the TX/RX FIFOs. Once the available count of the TX/RX FIFO is greater than the set threshold, a DMA request is initiated, and the DMA moves data into the TX FIFO or out of the RX FIFO according to the configuration.
Click to expand full code
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <bl_gpio.h>
#include <stdio.h>
#include <hosal_gpio.h>
#include <hosal_dma.h>
#include <blog.h>
#include <stdbool.h>
#include <hosal_uart.h>
#define RX_PIN 4
#define TX_PIN 3
#define TAG "uart_dma_demo"
#define RX_DATA_SIZE 16
HOSAL_UART_DEV_DECL(uart_dev, 1, TX_PIN, RX_PIN, 115200);
static uint8_t rx_data_buffer[RX_DATA_SIZE + 1];
static uint8_t tx_data_buffer[RX_DATA_SIZE + 1];
static bool is_rx_done = false;
static hosal_uart_dma_cfg_t txdam_cfg = {
.dma_buf = tx_data_buffer,
.dma_buf_size = RX_DATA_SIZE,
};
static hosal_uart_dma_cfg_t rxdam_cfg = {
.dma_buf = rx_data_buffer,
.dma_buf_size = RX_DATA_SIZE,
};
/**
* hal uart DMA RX interrupt callback
*/
static int __uart_rx_dma_callback(void* p_arg) {
/**
* If RX transmission is completed
* g_rx_buf is received data
*/
printf("dma recv:%s\r\n", rx_data_buffer);
memcpy(tx_data_buffer, rx_data_buffer, RX_DATA_SIZE);
hosal_uart_ioctl(&uart_dev, HOSAL_UART_DMA_TX_START, &txdam_cfg);
is_rx_done = true;
return 0;
}
/**
* hal uart DMA TX interrupt callback
*/
static int __uart_tx_dma_callback(void* p_arg) {
/* If TX transmission is completed */
is_rx_done = false;
memset(rx_data_buffer, 0, RX_DATA_SIZE);
memset(tx_data_buffer, 0, RX_DATA_SIZE);
// 重新启动TX DMA接收
hosal_uart_ioctl(&uart_dev, HOSAL_UART_DMA_RX_START, &rxdam_cfg);
return 0;
}
void uart_init(void) {
uart_dev.config.uart_id = 1;
/* 初始化UART设备 */
hosal_uart_init(&uart_dev);
/* 配置UART为中断模式 */
hosal_uart_ioctl(&uart_dev, HOSAL_UART_MODE_SET, (void*)HOSAL_UART_MODE_INT);
/* 设置 DMA RX TX中断回调函数 */
hosal_uart_callback_set(&uart_dev, HOSAL_UART_TX_DMA_CALLBACK,
__uart_tx_dma_callback, &uart_dev);
hosal_uart_callback_set(&uart_dev, HOSAL_UART_RX_DMA_CALLBACK,
__uart_rx_dma_callback, &uart_dev);
/* 启动UART TX DMA 传输 */
//hosal_uart_ioctl(&uart_dev, HOSAL_UART_DMA_TX_START, &txdam_cfg);
/* 启动 UART RX DMA 传输 */
hosal_uart_ioctl(&uart_dev, HOSAL_UART_DMA_RX_START, &rxdam_cfg);
}
static void uart_task(void* params) {
uart_init();
printf("uart task start\r\n");
hosal_uart_send(&uart_dev, "hello,uart1 dma demo\r\n", 22);
while (true) {
vTaskDelay(1);
}
}
void main(void) {
hosal_dma_init();
printf("start to init uart...\r\n");
xTaskCreate(uart_task, "uart_task", 1024, NULL, 15, NULL);
}Note: the DMA module must be initialized by calling the hosal_dma_init() function before the UART can use DMA to send and receive data normally.

