【Ai-WB2入门篇】UART数据发送与接收

[复制链接]
查看2204 | 回复16 | 2024-8-28 09:29:05 | 显示全部楼层 |阅读模式
通用异步收发传输器(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与对应的功能信号如下表所示:
1.png
在上述表格中,当选择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的设备定义。内容如下:
  1.   /**
  2.    * @brief UART device type
  3.    */
  4.   typedef struct {
  5.       uint8_t       port;                        /**< @brief UART 端口(BL602中分别为UART0和UART1) */
  6.       hosal_uart_config_t config;                /**< @brief UART 配置 */
  7.       hosal_uart_callback_t tx_cb;                /**< @brief UART 发送完成中断回调函数 */
  8.       void *p_txarg;                                /**< @brief UART 发送完成回调函数参数 */
  9.       hosal_uart_callback_t rx_cb;                /**< @brief UART 接收完成中断回调函数 */
  10.       void *p_rxarg;                                /**< @brief UART rx 接收完成中断回调函数函数 */
  11.       hosal_uart_callback_t txdma_cb;                /**< @brief UART DMA方式发送完成中断回调函数 */
  12.       void *p_txdma_arg;                                /**< @brief UART DMA方式发送完成中断回调函数参数 */
  13.       hosal_uart_callback_t rxdma_cb;                /**< @brief UART DMA方式接收完成中断回调函数 */
  14.       void *p_rxdma_arg;                                /**< @brief UART DMA方式接收完成中断回调函数 */
  15.       hosal_dma_chan_t dma_tx_chan;                /**< @brief UART DMA发送通道 */
  16.       hosal_dma_chan_t dma_rx_chan;                /**< @brief UART DMA接收通道 */
  17.       void         *priv;                        /**< @brief UART 用户自定义数据 */
  18.   } hosal_uart_dev_t;
复制代码
hosal_uart_dev_t 结构中的 hosal_uart_configt字段用于定义UART的配置,其内容如下
  1. typedef struct {
  2.     uint8_t                   uart_id;        /**< @brief UART id */
  3.     uint8_t                   tx_pin;                /**< @brief UART tx pin */
  4.     uint8_t                   rx_pin;                /**< @brief UART rx pin */
  5.     uint8_t                   cts_pin;        /**< @brief UART cts pin */
  6.     uint8_t                   rts_pin;        /**< @brief UART rts pin */
  7.     uint32_t                  baud_rate;        /**< @brief UART baud rate */
  8.     hosal_uart_data_width_t   data_width;        /**< @brief UART data width */
  9.     hosal_uart_parity_t       parity;                /**< @brief UART parity bit */
  10.     hosal_uart_stop_bits_t    stop_bits;        /**< @brief UART stop btis */
  11.     hosal_uart_flow_control_t flow_control;        /**< @brief UART flow control */
  12.     hosal_uart_mode_t         mode;                /**< @brief UART int or pull mode */
  13. } 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:控制命令,其定义如下:

  1.   HOSAL_UART_BAUD_SET :波特率设置,p_arg 为波特率
  2.   HOSAL_UART_BAUD_GET : 波特率获取,p_arg 是波特率的指针
  3.   HOSAL_UART_DATA_WIDTH_SET : 设置数据宽度,p_arg 为 hosal_uart_data_width_t
  4.   HOSAL_UART_DATA_WIDTH_GET : 获取数据宽度,p_arg为hosal_uart_data_width_t的指针
  5.   HOSAL_UART_STOP_BITS_SET :设置停止位,p_arg为hosal_uart_stop_bits_t
  6.   HOSAL_UART_STOP_BITS_GET :获取停止位,p_arg是hosal_uart_stop_bits_t的指针
  7.   HOSAL_UART_PARITY_SET :设置奇偶校验,p_arg为hosal_uart_parity_t
  8.   HOSAL_UART_PARITY_GET :获取奇偶校验,p_arg是hosal_uart_parity_t的指针
  9.   HOSAL_UART_MODE_SET :UART模式设置,p_arg为hosal_uart_mode_t
  10.   HOSAL_UART_MODE_GET :UART模式获取,p_arg是hosal_uart_mode_t的指针
  11.   HOSAL_UART_FLOWMODE_SET :UART流模式设置,p_arg为hosal_uart_flow_control_t
  12.   HOSAL_UART_FLOWSTAT_GET :UART流状态获取,p_arg是hosal_uart_flow_control_t的指针
  13.   HOSAL_UART_FREE_TXFIFO_GET :获取 uart 空闲 tx fifo 大小(字节)
  14.   HOSAL_UART_FREE_RXFIFO_GET :获取 uart 空闲 rx fifo 大小(字节)
  15.   HOSAL_UART_FLUSH :等待发送完成
  16.   HOSAL_UART_TX_TRIGGER_ON :UART TX 触发打开
  17.   HOSAL_UART_TX_TRIGGER_OFF :UART TX 触发关闭
  18.   HOSAL_UART_DMA_CONFIG : p_arg 是 hosal_uart_dma_cfg_t 的指针
  19.   HOSAL_UART_DMA_TX_START : UART DMA TX start trans p_arg 是 hosal_uart_dma_cfg_t 的指针
  20.   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:回调函数类型,其定义如下:
  1.   HOSAL_UART_TX_CALLBACK
  2.   HOSAL_UART_RX_CALLBACK
  3.   HOSAL_UART_TX_DMA_CALLBACK
  4.   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 会将波形当中宽度低于门限值的毛刺滤掉,然后在将其送去采样。
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <FreeRTOS.h>
  4. #include <task.h>
  5. #include <bl_gpio.h>
  6. #include <stdio.h>
  7. #include <hosal_gpio.h>
  8. #include <hosal_dma.h>
  9. #include <blog.h>
  10. #include <stdbool.h>
  11. #include <hosal_uart.h>

  12. #define TAG "uart_demo"

  13. // UART引脚
  14. #define RX_PIN 4
  15. #define TX_PIN 3

  16. // 创建UART配置
  17. HOSAL_UART_DEV_DECL(uart_dev_int, 1, TX_PIN, RX_PIN, 115200);

  18. static void uart_init(void){
  19.     uart_dev_int.config.uart_id = 1;
  20.     /* 初始化UART */
  21.     hosal_uart_init(&uart_dev_int);

  22.     /* UART配置为轮询模式 */
  23.     hosal_uart_ioctl(&uart_dev_int, HOSAL_UART_MODE_SET, (void *)HOSAL_UART_MODE_POLL);
  24.    
  25. }

  26. static void uart_task(void* params){
  27.     uart_init();
  28.     uint8_t data[32];
  29.     int ret;
  30.     printf("uart task inited\r\n");
  31.     hosal_uart_send(&uart_dev_int, "hello,uart1 demo\r\n",18);
  32.     while(true){
  33.         memset(data,0,32);
  34.         ret = hosal_uart_receive(&uart_dev_int, data, sizeof(data));
  35.         if (ret > 0) {
  36.             /* Uart send poll */
  37.             hosal_uart_send(&uart_dev_int, data, ret);
  38.             data[ret - 1] = '\0';
  39.             printf("recv:%s\r\n",data);
  40.         }
  41.         vTaskDelay(1);
  42.     }
  43.    
  44. }

  45. void main(void) {
  46.     printf("uart demo inited\r\n");
  47.     xTaskCreate(uart_task, "uart_task", 1024, NULL, 15, NULL);
  48. }
复制代码
在示例代码中,
第一步,我们通过 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函数中,创建并启动任务。
运行结果如下:
12.gif
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清除中断。
在前面的基础上,接下来,我们将实现中断方式接收和发送数据。
第一步,定义发送中断回调函数
  1. static int __uart_tx_callback(void *p_arg)
  2. {
  3.     static uint8_t tx_counts = 0;
  4.     char buf[] = "TX interrupt TEST\r\n";
  5.     hosal_uart_dev_t *p_dev = (hosal_uart_dev_t *)p_arg;

  6.     if (tx_counts < sizeof(buf)) {
  7.         hosal_uart_send(p_dev, &buf[tx_counts++], 1);
  8.     } else {
  9.         /*如果数据传输完成,关闭TX触发模式 */
  10.         hosal_uart_ioctl(p_dev, HOSAL_UART_TX_TRIGGER_OFF, NULL);
  11.     }

  12.     return 0;
  13. }
复制代码
第二步,定义接收中断回调函数。
  1. static int __uart_rx_callback(void *p_arg)
  2. {
  3.     int ret;
  4.    
  5.     hosal_uart_dev_t *p_dev = (hosal_uart_dev_t *)p_arg;

  6.     ret = hosal_uart_receive(p_dev, data_buf, RX_DATA_SIZE);
  7.     hosal_uart_send(p_dev, data_buf, ret);
  8.    
  9.     return 0;
  10. }
复制代码
第三步,初始化UART工作模式为中断模式
  1. static void uart_init(void){
  2.     uart_dev_int.config.uart_id = 1;

  3.     /* 初始化UART */
  4.     hosal_uart_init(&uart_dev_int);

  5.     /* UART配置为中断模式 */
  6.      hosal_uart_ioctl(&uart_dev_int, HOSAL_UART_MODE_SET, (void *)HOSAL_UART_MODE_INT);
  7.    

  8.     /* 设置接收和发送中断回调函数 */
  9.      hosal_uart_callback_set(&uart_dev_int, HOSAL_UART_RX_CALLBACK,
  10.                            __uart_rx_callback, &uart_dev_int);
  11.      hosal_uart_callback_set(&uart_dev_int, HOSAL_UART_TX_CALLBACK,
  12.                            __uart_tx_callback, &uart_dev_int);

  13.     /*启用发送中断 */
  14.      hosal_uart_ioctl(&uart_dev_int, HOSAL_UART_TX_TRIGGER_ON, NULL);
  15.     /*启用接收中断 */
  16.      hosal_uart_ioctl(&uart_dev_int, HOSAL_UART_RX_TRIGGER_ON, NULL);
  17. }
复制代码
第四步,定义任务
  1. static void uart_task(void* params) {
  2.     uart_init();
  3.     printf("uart task start\r\n");
  4.     hosal_uart_send(&uart_dev_int, "hello,uart1 demo\r\n", 18);
  5.     while (true) {
  6.       
  7.         vTaskDelay(1);
  8.     }

  9. }
复制代码
第五步,启动任务
  1. void main(void) {
  2.     printf("start to init uart...\r\n");
  3.     xTaskCreate(uart_task, "uart_task", 1024, NULL, 15, NULL);
  4. }
复制代码
三:DMA方式接收与发送数据
BL602的UART 支持DMA传输模式。使用该模式需要通过寄存器UART_FIFO_CONFIG_1的位和分别设置TX和RXFIFO的阈值,当该模式启用后,UART会对TX/RXFIFO 进行检查,一日TXRX的FIFO 可用计数值大千基设定的闹值,将会发起DMA 请求,DMA 会按照设定将数据搬移至TX FIFO 中或从RX FIFO 中移出。
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <FreeRTOS.h>
  4. #include <task.h>
  5. #include <bl_gpio.h>
  6. #include <stdio.h>
  7. #include <hosal_gpio.h>
  8. #include <hosal_dma.h>
  9. #include <blog.h>
  10. #include <stdbool.h>
  11. #include <hosal_uart.h>

  12. #define RX_PIN 4
  13. #define TX_PIN 3

  14. #define TAG "uart_dma_demo"

  15. #define RX_DATA_SIZE 16

  16. HOSAL_UART_DEV_DECL(uart_dev, 1, TX_PIN, RX_PIN, 115200);

  17. static uint8_t rx_data_buffer[RX_DATA_SIZE + 1];
  18. static uint8_t tx_data_buffer[RX_DATA_SIZE + 1];

  19. static bool is_rx_done = false;

  20. static hosal_uart_dma_cfg_t txdam_cfg = {
  21.        .dma_buf = tx_data_buffer,
  22.        .dma_buf_size = RX_DATA_SIZE,
  23. };
  24. static hosal_uart_dma_cfg_t rxdam_cfg = {
  25.     .dma_buf = rx_data_buffer,
  26.     .dma_buf_size = RX_DATA_SIZE,
  27. };

  28. /**
  29. * hal uart DMA RX interrupt callback
  30. */
  31. static int __uart_rx_dma_callback(void* p_arg) {
  32.     /**
  33.      * If RX transmission is completed
  34.      * g_rx_buf is received data
  35.      */
  36.     printf("dma recv:%s\r\n", rx_data_buffer);
  37.     memcpy(tx_data_buffer, rx_data_buffer, RX_DATA_SIZE);
  38.     hosal_uart_ioctl(&uart_dev, HOSAL_UART_DMA_TX_START, &txdam_cfg);
  39.     is_rx_done = true;
  40.     return 0;
  41. }

  42. /**
  43. * hal uart DMA TX interrupt callback
  44. */
  45. static int __uart_tx_dma_callback(void* p_arg) {
  46.     /* If TX transmission is completed */
  47.     is_rx_done = false;
  48.     memset(rx_data_buffer, 0, RX_DATA_SIZE);
  49.     memset(tx_data_buffer, 0, RX_DATA_SIZE);
  50.     // 重新启动TX DMA接收
  51.     hosal_uart_ioctl(&uart_dev, HOSAL_UART_DMA_RX_START, &rxdam_cfg);
  52.     return 0;
  53. }

  54. void uart_init(void) {

  55.     uart_dev.config.uart_id = 1;

  56.     /* 初始化UART设备 */
  57.     hosal_uart_init(&uart_dev);

  58.     /* 配置UART为中断模式 */
  59.     hosal_uart_ioctl(&uart_dev, HOSAL_UART_MODE_SET, (void*)HOSAL_UART_MODE_INT);

  60.     /* 设置 DMA RX TX中断回调函数 */
  61.     hosal_uart_callback_set(&uart_dev, HOSAL_UART_TX_DMA_CALLBACK,
  62.         __uart_tx_dma_callback, &uart_dev);
  63.     hosal_uart_callback_set(&uart_dev, HOSAL_UART_RX_DMA_CALLBACK,
  64.         __uart_rx_dma_callback, &uart_dev);

  65.     /* 启动UART TX DMA 传输 */
  66.     //hosal_uart_ioctl(&uart_dev, HOSAL_UART_DMA_TX_START, &txdam_cfg);

  67.     /* 启动 UART RX DMA 传输 */
  68.     hosal_uart_ioctl(&uart_dev, HOSAL_UART_DMA_RX_START, &rxdam_cfg);

  69. }

  70. static void uart_task(void* params) {
  71.     uart_init();
  72.     printf("uart task start\r\n");
  73.     hosal_uart_send(&uart_dev, "hello,uart1 dma demo\r\n", 22);
  74.     while (true) {
  75.         
  76.         vTaskDelay(1);
  77.     }

  78. }

  79. void main(void) {
  80.     hosal_dma_init();
  81.     printf("start to init uart...\r\n");
  82.     xTaskCreate(uart_task, "uart_task", 1024, NULL, 15, NULL);
  83. }
复制代码
注意:需要调用 hosa1_dma_init()函数初始化DMA模块后,UART才能正常使用DMA发送和接收数据。

本帖被以下淘专辑推荐:

用心做好保姆工作
回复

使用道具 举报

WT_0213 | 2024-8-28 09:31:08 | 显示全部楼层
园长这是准备发全套教程了吗
回复 支持 反对

使用道具 举报

爱笑 | 2024-8-28 09:33:54 | 显示全部楼层
WT_0213 发表于 2024-8-28 09:31
园长这是准备发全套教程了吗

对的,打算把wb2的教程完善了。
用心做好保姆工作
回复 支持 反对

使用道具 举报

bzhou830 | 2024-8-28 09:40:15 | 显示全部楼层
速度太快了,下一步把wb2的arduino也支持了吗?
选择去发光,而不是被照亮
回复 支持 反对

使用道具 举报

爱笑 | 2024-8-28 09:44:22 | 显示全部楼层
bzhou830 发表于 2024-8-28 09:40
速度太快了,下一步把wb2的arduino也支持了吗?

哈哈我不太清楚
用心做好保姆工作
回复 支持 反对

使用道具 举报

妖猊 | 2024-8-28 09:59:10 | 显示全部楼层
我打卡学习了,期待下一篇,坐等
回复 支持 反对

使用道具 举报

WangChong | 2024-8-28 10:07:43 | 显示全部楼层
这是园长写的吗
回复 支持 反对

使用道具 举报

bzhou830 | 2024-8-28 10:15:43 | 显示全部楼层
妖猊 发表于 2024-8-28 09:59
我打卡学习了,期待下一篇,坐等

学慢点,等等我
选择去发光,而不是被照亮
回复 支持 反对

使用道具 举报

iiv | 2024-8-28 10:24:10 | 显示全部楼层
我打卡学习了,期待下一篇,坐等
回复 支持 反对

使用道具 举报

爱笑 | 2024-8-28 10:24:52 | 显示全部楼层

这是技术写的!
用心做好保姆工作
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则