由 爱笑 贡献,Ai-Thinker 进行整理
【Ai-WB2入门篇】UART数据发送与接收
通用异步收发传输器(Universal Asynchronous Receiver/Transmiter,通常称为UART)是一种异步收发传输器,提供了与外部设备进行全双工数据交换的灵活方式。Ai-WB2共有2组UART口(UARTO和UART1),通过配合DMA使用,可以实现高效的数据通信。
本文将详细介绍如何使用Ai-WB2的UART模块。主要内容包括,轮询模式下的数据接收与发送;中断模式下的数据接收与发送以及DMA方式数据接收与发送。 一:Ai-WB2的UART介绍 Ai-WB2共有2 组UART口(UARTO 和UART1),通过配合DMA使用,可以实现高效的数据通信。 Ai-WB2的UART具有如下特性:
📜 点击展开完整代码
**·** 全双工异步通信
**·** 数据位长度可选择51617/8 比特
**·** 停止位长度可选择0.5/1/1.5/2 比特
**·** 支持奇/偶/无校验比特
**·** 可侦测错误的起始比特
**·** 丰富的中断控制
**·** 支持硬件流控(RTS/CTS)
**·** 便捷的波特率编程
**·** 可配置MSB/LSB 优先传输
**·** 普通/固定字符的自动波特率检测
**·** 32 字节发送/接收FIFO
**·** 支持DMA 传输模式
**·** 最高工作频率为160MHZ
在上述表格中,当选择UART功能时,只是选择了UART的一个信号,并没有指定该引脚的具体功能(比如是UART TX还是UART RX),还需要通过UART_ SIGX_SEL(X=0-7)进一步选择具体的UART信号及对应的功能。每一个UART_SIGX_SEL可以选择的信号包括:
📜 点击展开完整代码
**·** 0 : UARTO RTS
**·** 1 : UARTO CTS
**·** 2 : UARTO TXD
**·** 3 : UARTO RXD
**·** 4 : UART1 RTS
**·** 5 : UART1 CTS
**·** 6 : UART1 TXD
**·** 7 : UART1 RXD以GPIO0为例,当fun_sel选择UART的时候,GPIO0选择的是UART_SIG0,在默认情况下UART_SIGO_SEL的值是0,也就是UART0_RTS,即GPIO是UART0_RTS功能。如果应用程序想把GPIO作为UART1_TXD,那只要把UART_SIGO_SEL设置为6,那么GPIO0的功能就是UART1_TXD。 二:UART相关驱动API介绍 UART的HOSAL层高级驱动API在头文件 components/platform/hosal/include/hosal_uart.h定义并封装BL602的高级UART操作操作。常用的函数如下: · int hosal_uart_init(hosal_uart_dev_t *uart):初始化UART。其参数说明如下: · uart:UART的设备定义。内容如下:
📜 点击展开完整代码
/**
* @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;hosal_uart_dev_t 结构中的 hosal_uart_configt字段用于定义UART的配置,其内容如下
📜 点击展开完整代码
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;📜 点击展开完整代码
**·** 返回值:成功时返回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 :波特率设置,p_arg 为波特率
- HOSAL_UART_BAUD_GET : 波特率获取,p_arg 是波特率的指针
- HOSAL_UART_DATA_WIDTH_SET : 设置数据宽度,p_arg 为 hosal_uart_data_width_t
- HOSAL_UART_DATA_WIDTH_GET : 获取数据宽度,p_arg为hosal_uart_data_width_t的指针
- HOSAL_UART_STOP_BITS_SET :设置停止位,p_arg为hosal_uart_stop_bits_t
- HOSAL_UART_STOP_BITS_GET :获取停止位,p_arg是hosal_uart_stop_bits_t的指针
- HOSAL_UART_PARITY_SET :设置奇偶校验,p_arg为hosal_uart_parity_t
- HOSAL_UART_PARITY_GET :获取奇偶校验,p_arg是hosal_uart_parity_t的指针
- HOSAL_UART_MODE_SET :UART模式设置,p_arg为hosal_uart_mode_t
- HOSAL_UART_MODE_GET :UART模式获取,p_arg是hosal_uart_mode_t的指针
- HOSAL_UART_FLOWMODE_SET :UART流模式设置,p_arg为hosal_uart_flow_control_t
- HOSAL_UART_FLOWSTAT_GET :UART流状态获取,p_arg是hosal_uart_flow_control_t的指针
- HOSAL_UART_FREE_TXFIFO_GET :获取 uart 空闲 tx fifo 大小(字节)
- HOSAL_UART_FREE_RXFIFO_GET :获取 uart 空闲 rx fifo 大小(字节)
- HOSAL_UART_FLUSH :等待发送完成
- HOSAL_UART_TX_TRIGGER_ON :UART TX 触发打开
- HOSAL_UART_TX_TRIGGER_OFF :UART TX 触发关闭
- HOSAL_UART_DMA_CONFIG : p_arg 是 hosal_uart_dma_cfg_t 的指针
- HOSAL_UART_DMA_TX_START : UART DMA TX start trans p_arg 是 hosal_uart_dma_cfg_t 的指针
- HOSAL_UART_DMA_RX_START :UART DMA RX 开始传输 p_arg 是 hosal_uart_dma_cfg_t 的指针
· p_arg:控制命令参数 · 返回值:成功时返回0,否则返回EIO或其他值 · int hosal_uart_callback_set(hosal_uart_dev_t *uart, int callback_type, hosal_uart_callback_t pfn_callback, void *arg):UART回调函数设置。 · uart:UART设备 · callback_type:回调函数类型,其定义如下:
- HOSAL_UART_TX_CALLBACK
- HOSAL_UART_RX_CALLBACK
- HOSAL_UART_TX_DMA_CALLBACK
- HOSAL_UART_RX_DMA_CALLBACK
· opfn_callback:回调函数指针 · arg:回调函数参数 · 返回值:成功时返回0,否则返回EIO或其他值:
📜 点击展开完整代码
**·** int hosal_uart_finalize(hosal_uart_dev_t *uart):释放UART。
**三:UART使用示例**
**1. 轮询方式数据接收与发送**BL602 UART的发送器包含一个32 字节的发送FIFO,用来存放待发送的数据。软件可以通过APB 总线写TXFIFO,也可以通过DMA将数据搬入TXFIFO。当发送使能位被设置时,FIFO 中存放的数据会从TX引脚输出。软件可以选择通过DMA 或APB 总线这两种方式将数据传入TX FIFO。软件可以通过寄存器UART_FIFO_CONFIG_1的位查询TXFIFO 剩余可用空间计数值来检查发送器的状态。发送器的自由运行 (FreeRun)模式如下: · 如果没有开启自由运行(freeRun)模式,则当发送字节达到指定长度时发送行为终止并产生中断,如果要继续发送则需重新关闭再使能发送使能位。 · 如果开启自由运行(FreeRun)模式,则当TXFIFO 里存在数据时,发送器就会进行发送,不会因为发送字节达到指定长度而终止。 BL602 UART的接收器包含一个32 字节的接收FIFO,用来存放接收到的数据。软件可以通过寄存器UART_FIFO_CONEIG_1的位查询RX FIFO 可用数据计数值来检查接收器的状态。寄存器URX_RTO_TIMER 的低8 位用于设定一个接收超时门限,当接收器超过该时间值未收到数据时,会触发中断。寄存器URX_CONFIG 的位和用于使能去毛刺功能和设置门限值,其控制的是UART 采样之前的滤波部分UART 会将波形当中宽度低于门限值的毛刺滤掉,然后在将其送去采样。
📜 点击展开完整代码
#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);
}在示例代码中, 第一步,我们通过 HOSAL_UART_DEV_DECL 宏来创建UART的配置 hosal_uart_config_t 对象. 第二步,在 uart_init 函数中,通过调用 hosal_uart_init 函数和 hosal_uartioct1函数分别初始化UART和设置UART的工作模式为Pol模式。 第三步,创建一个FreeRTOS任务,用于接收和发送UART数据。当hosa1_uart_receive 接收到数据后,再将接收到的数据通过调用hosal_uart_send 发送。 第四步,在main函数中,创建并启动任务。 运行结果如下:

2. 中断方式数据接收与发送 BL602的UART 有着丰富的中断控制,包括以下几种中断模式:
📜 点击展开完整代码
**·** TX 传输结束中断
**·** RX 传输结束中断
**·** TX FIFO 请求中断
**·** RX FIFO 请求中断
**·** RX 超时中断
**·** RX 奇偶校验错误中断
**·** TX FIFO 溢出中断
**·** RX FIFO 溢出中断TX 和RX可以通过寄存器UTX_CONFIG 和URX_CONFIG 的高16 位分别设置一个传输长度值,当传输的字节数达到这个数值时,就会触发对应的TXRX 传输结束中断。TX/RXFIFO 请求中断会在其FIFO 可用计数值大于寄存器UART_FIFO_CONFIG_1中所设定的阈值时触发,当条件不满足时该中断标志会自动清除。RX超时中断会在接收器超过超时门限值未收到数据时触发,而RX 奇偶校验错误中断会发生在奇偶校验出错时。如果TXIRXFIFO 发生了上溢或者下溢,会触发对应的溢出中断,当FIFO 清除位TFICLRIRFICLR 被置1时,对立的FIFO 会被清空,同时溢出中断标志会自动清除。可以通过寄存器UART_INT_STS 查询各中断状态,通过向寄存器UART_INT_CLEAR 相应的位写1清除中断。 在前面的基础上,接下来,我们将实现中断方式接收和发送数据。 第一步,定义发送中断回调函数
📜 点击展开完整代码
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;
}第二步,定义接收中断回调函数。
📜 点击展开完整代码
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;
}第三步,初始化UART工作模式为中断模式
📜 点击展开完整代码
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);
}第四步,定义任务
📜 点击展开完整代码
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);
}
}第五步,启动任务
📜 点击展开完整代码
void main(void) {
printf("start to init uart...\r\n");
xTaskCreate(uart_task, "uart_task", 1024, NULL, 15, NULL);
}三:DMA方式接收与发送数据 BL602的UART 支持DMA传输模式。使用该模式需要通过寄存器UART_FIFO_CONFIG_1的位和分别设置TX和RXFIFO的阈值,当该模式启用后,UART会对TX/RXFIFO 进行检查,一日TXRX的FIFO 可用计数值大千基设定的闹值,将会发起DMA 请求,DMA 会按照设定将数据搬移至TX FIFO 中或从RX FIFO 中移出。
📜 点击展开完整代码
#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);
}注意:需要调用 hosa1_dma_init()函数初始化DMA模块后,UART才能正常使用DMA发送和接收数据。

