通用异步收发传输器(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
在前面的【Ai-WB2入门篇】GPIO使用的文章中,我们知道,每个GPIO基本上都可以选择上述可选功能,当选择某个可选功能时,GP1O与对应的功能信号如下表所示:
在上述表格中,当选择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发送和接收数据。
|
|