[国产MCU]-BL602开发实例-UART数据发送与接收
UART数据发送与接收
文章目录
- UART数据发送接收功能概述
-
- 1.1 BL602 UART功能概述
- 2.1 UART驱动功能模块
- 3.1 UART功能模块实现示例
-
- 3.2 UART轮询方式数据接收与发送
- 3.3 UART中断方式数据接收与发送
- 3.4 UART DMA方式数据接收与发送
-
通用型全双工数据收发接口(Universal Asynchronous Receiver/Transmitter, 简称UART)是一种支持外部设备灵活数据交换的接口电路。BL602/BL604系列配备有两组 UART接口(分别标记为UART0和UART1),借助DMA技术辅助处理功能的应用,在完成各自的 UART口配置后能够实现高效的双向数据通信。
本文旨在深入阐述BL602 UART模块的应用方法。其核心内容主要涉及三种不同的工作模式:轮询机制的数据收发过程;中断机制的数据收发过程;以及基于DMA的数据传输机制。
1、BL602的UART介绍
该设备包含两组UART端口( UART0和UART1 ),通过配合DMA机制的配合使用,能够实现高效的通信。该设备的UART端口具备以下特点:
全双工异步通信方案采用灵活完善的中断控制功能
在之前的文章中讨论了GPIO与控制的相关知识后,在我们了解了上述可选功能的基础上,在选择某个可选功能时,请查看下文中的表格。
| GPIO | SDIO | FLASH | SPI | I2C | UART | PWM | Analog | SWGPIO | JTAG |
|---|---|---|---|---|---|---|---|---|---|
| GPIO0 | CLK | D1 | MISO | SCL | SIG0 | CH0 | SWGPIO0 | TMS | |
| GPIO1 | CMD | D2 | MOSI | SDA | SIG1 | CH1 | SWGPIO1 | TDI | |
| GPIO2 | DAT0 | D2 | SS | SCL | SIG2 | CH2 | SWGPIO2 | TCK | |
| GPIO3 | DAT1 | D3 | SCLK | SDA | SIG3 | CH3 | SWGPIO3 | TDO | |
| GPIO4 | DAT2 | MISO | SCL | SIG4 | CH4 | CH1 | SWGPIO4 | TMS | |
| GPIO5 | DAT3 | MOSI | SDA | SIG5 | CH0 | CH4 | SWGPIO5 | TDI | |
| GPIO6 | SS | SCL | SIG6 | CH1 | CH5 | SWGPIO6 | TCK | ||
| GPIO7 | SCLK | SDA | SIG7 | CH2 | SWGPIO7 | TDO | |||
| GPIO8 | MISO | SCL | SIG0 | CH3 | SWGPIO8 | TMS | |||
| GPIO9 | MOSI | SDA | SIG1 | CH4 | CH6/7 | SWGPIO9 | TDI | ||
| GPIO10 | SS | SCL | SIG2 | CH0 | MICBIAS/CH8/9 | SWGPIO10 | TCK | ||
| GPIO11 | SCLK | SDA | SIG3 | CH1 | IROUT/CH10 | SWGPIO11 | TDO | ||
| GPIO12 | MISO | SCL | SIG4 | CH2 | ADC_VREF/CH0 | SWGPIO12 | TMS | ||
| GPIO13 | MOSI | SDA | SIG5 | CH3 | CH3 | SWGPIO13 | TDI | ||
| GPIO14 | SS | SCL | SIG6 | CH4 | CH2 | SWGPIO14 | TCK | ||
| GPIO15 | SCLK | SDA | SIG7 | CH0 | PSWIROUT/CH11 | SWGPIO15 | TDO | ||
| GPIO16 | MISO | SCL | SIG0 | CH1 | SWGPIO16 | TMS | |||
| GPIO17 | D3 | MOSI | SDA | SIG1 | CH2 | DC_TP_OUT | SWGPIO17 | TDI | |
| GPIO18 | D2 | SS | SCL | SIG2 | CH3 | SWGPIO18 | TCK | ||
| GPIO19 | D1 | SCLK | SDA | SIG3 | CH4 | SWGPIO19 | TDO | ||
| GPIO20 | D0 | MISO | SCL | SIG4 | CH0 | SWGPIO20 | TMS | ||
| GPIO21 | CS | MOSI | SDA | SIG5 | CH1 | SWGPIO21 | TDI | ||
| GPIO22 | CLK_OUT | SS | SCL | SIG6 | CH2 | SWGPIO22 | TCK |
改写说明
- 0 : UART0_RTS
- 1 : UART0_CTS
- 2 : UART0_TXD
- 3 : UART0_RXD
- 4 : UART1_RTS
- 5 : UART1_CTS
- 6 : UART1_TXD
- 7 : UART1_RXD
以 GPIO 为例,在本设计中我们主要关注其与 UART 接口的交互机制。当选设参数 fun_sel 被设置为选择 UART 时,则会触发相应的 UART 接口中断服务(I/O)功能。其中,在默认状态下 UART SIG_O 的中断服务控制位(BERTSIG_OSED)被设置为 低电平("L"),即等效于 UART 端口 UART-RTS 功能("RS-TR")。因此,在此配置下 GPIO 将具备 UART-RTS 功能输出能力。若要将 GPIO 重新配置成 UART-BTXD1 输出端口,则需将 BERTSIG_OSED 控制位设置至高电平("H"),此时在新的配置下 GPIO 将具备 UART-BTXD1 输出端口的功能
2、UART相关驱动API介绍
该 UART 系列接口的 HOSAL 层高级驱动 API 由包含文件 components/platform/hosl/include/hosal_uart.h 定义,并将 BL602 的高级 UART 操作封装至此。其中常用的函数包括 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;
在 hosl_UART_dev_t 结构中使用 hosl_UART_config_t 字段来指定 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,否则返回EIO或其他值。
int hosal_uart_send(hosal_uart_dev_t *uart, const void *txbuf, uint32_t size) :轮询方式UART发送数据。其参数说明如下:
- UART:UART装置。
- txbuf是用于传输的信息。
- size是数据传输规模。
- 返回值方面:
- 若返回值为正数,则表明数据传输成功;
- 若为零或负数,则表示传输失败。
该函数采用轮询方式实现UART数据接收操作。其参数包括用于接收UART信号的设备类型指针、用于存储接收到的数据缓冲区指针以及预期的数据长度字段。
uart:UART设备;data:接收的数据;expect_size:期望接收的数据长度;返回结果:当返回结果非零时,表示数据接收成功;若为零,则表示接收失败。
int hosal_uart_abr_get(hosal_uart_dev_t *uart, uint8_t mode):自动生成UART接口的波特率设置。
int hosal_uart_ioctl(hosal_uart_dev_t *uart, int ctl, 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或其他值。
set_UART_callback_function(hosu_UART_device* uart, int type, const auto (*callback)(uint)= nullptr, 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
pfn_callback:回调函数指针
arg:回调函数参数
返回值:成功时返回0,否则返回EIO或其他值。
int hosal_uart_finalize(hosal_uart_dev_t *uart) :释放UART。
3、UART使用示例
3.1 轮询方式数据接收与发送
BL602 UART的发送器由一个32 位的发送FIFO构成,用于暂存待传输的数据.该软件系统可采用APB总线直接操作TX FIFO端口,也可借助DMA机制完成数据传输至该端口.一旦设置发送使能位时,接收端引脚将依次输出FIFO中的数据.软件可通过读取寄存器UART_FIFO_CONFIG_1中的相关位信息来获取TX FIFO剩余空间的数据.该设备在未启用自由运行模式时,仍可手动配置其工作状态参数.
- 当未启用自由运行(FreeRun)模式时,在数据量达到预设上限后将执行终止操作并触发中断响应;若欲继续操作,则需重新关闭相关功能后再重新启用使能位。
- 一旦启用自由运行(FreeRun)模式,在TX FIFO中有数据存入的情况下发送器会立即开始传输过程;即使在数据量达到设定上限的情况下也不会停止传输。
BL602 UART的接收模块具备一个32 位的接收队列。软件可通过寄存器UART_FIFO_CONFIG_1 的位字段读取RX FIFO 的可用数据计数值以评估接收模块的状态。寄存器URX_RTO_TIMER 的低8 位配置了一个接收超时阈值,在接收到的数据量未达到该阈值时(即低于该时间值),系统将触发中断。寄存器URX_CONFIG 包含控制去毛刺功能的位字段以及设定门限值的功能项。这些设置主要用于调节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的参数设置.
在 uart_init 函数过程中,我们利用 hosal_uart_init 和 hsal_uart_ioctl 两个自定义函数依次完成 UART 的初始化,并将其实现工作模式配置为 Poll 模式。
第3步, 建立一个基于FreeRTOS的任务, 负责接收与发送UART数据. 当程序通过 hosal_UART_receive 函数检测到接收到 UART 数据时, 立即调用 hosl_UART_send 函数将捕获的数据进行传输.
第四步,在main函数中,创建并启动任务。
运行结果如下:

3.2 中断方式数据接收与发送
BL602的UART 有着丰富的中断控制,包括以下几种中断模式:
- TX 传输完成中断
- RX 传输完成中断
- TX 队列请求满溢出检测中断
- RX 队列请求满溢出检测中断
- RX 超时检测中断
- RX 数据完整性异常检测中断
- TX 队列满溢出检测 interrupted
- RX 队列满溢出检测 interrupted
TX 和 RX 的数据传输可以通过配置寄存器UTX_CONFIG 和URX_CONFIG 的高位字段来实现。具体来说,在数据传输总量达到该值时就会触发相应的TX 或 RX 传输完成中断。在接收端设计了一个FIFO请求中断,在其可用计数值超过预设阈值时会触发该中断事件。接收端超时中断会在其累计收数据量未达到设定上限的情况下触发相应的错误处理逻辑。若TX/RX FIFO发生上溢或下溢事件,则会分别触发对应的上溢或下溢中断事件。当FIFO清除位TFICLR/RFICLR被置位后(即清空操作完成),系统会自动清除所有相关异常状态标志位并释放资源空间。通过访问寄存器UART_INTSTS可以直接读取各异常标志位的状态信息,并根据需要对相应的异常处理流程进行干预控制。
在前面的基础上,接下来,我们将实现中断方式接收和发送数据。
第一步,定义发送中断回调函数
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);
}
3.3 DMA方式接收与发送数据
BL602的UART 采用了DMA传输模式。在启用该模式时,系统将配置寄存器UART_FIFO_CONFIG_1 中的 高位和低位来设置TX和RX FIFO的门限值。一旦TX或RX FIFO的有效计数值超过预设阈值时(即当TX或RX FIFO的有效计数值超过预设阈值时),系统将执行DMA请求以完成数据传输操作。在这种机制下,控制器将自动向dma控制器发送命令,并根据预先设定的数据格式执行相应的数据转移操作:如果目标端口为Tx,则将数据从Rx FIFO中读取并存储至Tx FIFO中;反之,则从Rx FIFO中读取并存储至Tx 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);
}
在启动UART进行DMA操作之前,请确保已经调用 hosal_dma_init()函数来完成DMA模块的初始化工作。这一步骤对于实现DMA的数据传输功能以及支持DMA模式下的数据交换都是必不可少的。
