介绍
黄山派的每个 GPIO 都支持串口功能,只需要设置pinmux
之后即可初始化IO,后续操作外设即可。我们此例程中用到了 uart2,在采用 RT-Thread 操作系统时,uart2 外设会虚拟成了一个 rt_device 来进行读写操作,此时需要确认工程编译所在路径下 rtconfig.h
文件中是否包含了下面 3 个宏:
#define BSP_USING_UART 1
#define BSP_USING_UART2 1
#define BSP_UART2_RX_USING_DMA 1
2
3
只有包含了上面三个宏,在rt_hw_usart_init函数中才会通过rt_hw_serial_register函数注册"uart2这个rt_device,后面该设备才能rt_device_find和rt_device_open成功。
如果缺失上面三个宏,就需要通过menuconfig如下命令进行打开(注意:缺失可能并不会报出错误,若配置串口没有信息打印请及时查看是否打开)
终端执行 scons --board=sf32lb52-lchspi-ulp --menuconfig 后,使用⇧和⇩方向键在不同菜单项间移动,按Enter键进入子菜单,进On-chip Peripheral RTOS Drivers->Enable UART ,选择uart2和rx dma,保存并退出menuconfig。如下下图所示:
函数定义
读取设备
该函数将从设备读取数据。
rt_size_t rt_device_read ( rt_device_t dev,
rt_off_t pos,
void * buffer,
rt_size_t size
)
2
3
4
5
函数参数和返回值含义如下:
参数:
- dev 设备句柄
- pos 读取的偏移量
- buffer 用于保存读取数据的数据缓冲区
- size 缓冲区的大小
- 返回 成功返回实际读取的大小,如果是字符设备,返回大小以字节为单位,如果是块设备,返回的大小以块为单位;失败则返回0。
无等待获取信号量
当用户不想在申请的信号量上挂起线程进行等待时,可以调用该函数以无等待方式获取信号量
rt_err_t rt_sem_trytake ( rt_sem_t sem )
函数参数和返回值含义如下:
参数:
- sem 信号量对象的句柄
- 返回 RT_EOK 成功获得信号量;RT_ETIMEOUT 获取失败
写设备
该函数将向设备写入数据。
rt_size_t rt_device_write ( rt_device_t dev,
rt_off_t pos,
const void * buffer,
rt_size_t size
)
2
3
4
5
函数参数和返回值含义如下:
参数:
- dev 设备句柄
- pos 写入的偏移量
- buffer 要写入设备的数据缓冲区
- size 写入数据的大小
- 返回 成功返回实际写入数据的大小,如果是字符设备,返回大小以字节为单位;如果是块设备,返回的大小以块为单位;失败则返回0。
设置设备接收回调函数
此函数将设置设备接收回调函数。该函数的回调函数由调用者提供。 当硬件设备接收到数据时,会回调这个函数并把收到的数据长度放在size参数中传递给上层应用。 上层应用线程应在收到指示后,立刻从设备中读取数据。
rt_err_t rt_device_set_rx_indicate ( rt_device_t dev,
rt_err_t(*)(rt_device_t dev, rt_size_t size) rx_ind
)
2
3
函数参数和返回值含义如下:
参数:
- dev 设备句柄
- rx_ind 回调函数
- 返回 RT_EOK
初始化信号量
该函数将初始化信号量并将其置于内核管理器的控制之下。
rt_err_t rt_sem_init ( rt_sem_t sem,
const char * name,
rt_uint32_t value,
rt_uint8_t flag
)
2
3
4
5
函数参数和返回值含义如下:
参数:
- sem 指定的信号量对象句柄
- name 信号量的名称
- value 信号量初始化时的值
- flag 信号量的标志位,它可以取值:RT_IPC_FLAG_FIFO或RT_IPC_FLAG_PRIO
- 返回 RT_EOK 成功
启动线程
此函数将启动一个线程并将其放入系统就绪队列
rt_err_t rt_thread_startup ( rt_thread_t thread )
函数参数和返回值含义如下:
参数:
- thread 被要被启动的线程句柄
- 返回 成功 RT_EOK, 失败 RT_ERROR
程序讲解
串口初始化函数
int uart2_init(void)部分内容讲解:
先后rt_device_find,rt_device_control,rt_device_open分别查找、配置和打开uart2设备
//前置准备
#define DBG_TAG "uart2" // 当前模块的日志标签为 "uart2"
#define DBG_LVL DBG_LOG //日志输出级别为 LOG 级别
#include <rtdbg.h> // 包含 RT-Thread 的日志系统支持
#define UART_DEMO_NAME "uart2" //uart设备名称
#define ONE_DATA_MAXLEN 256 //单次最大接收数据长度为 256 字节
static rt_device_t g_uart_device = RT_NULL;//用于存储找到的设备
static struct rt_semaphore rx_sem;//一个信号量,用于通知主线程接收到数据
static uint8_t data[ONE_DATA_MAXLEN] = { 0 };//一个静态缓冲区,用于存储从 UART 接收的数据
2
3
4
5
6
7
8
9
10
11
查找设备
以一定的模式(如:以DMA模式)打开设备,先注册接收回调函数并创建接收线程,将线程打开
//串口初始化函数
void uart2_init(void)
{
g_uart_device = rt_device_find(UART_DEMO_NAME);//查找uart2设备
if (!g_uart_device)
{
LOG_E("find %s failed!\n", UART_DEMO_NAME);
return RT_ERROR;
}
{ //单独括起来限制err的作用域,防止后续代码同名变量影响
rt_err_t err;
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; // 使用默认uart配置
config.baud_rate = 1000000; // 修改波特率
err = rt_device_control(g_uart_device, RT_DEVICE_CTRL_CONFIG, &config);//应用上述的配置
LOG_D("uart device config %d", err);
}
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);//初始化信号量为0
// 尝试以 DMA 模式打开设备
rt_err_t open_result = rt_device_open(g_uart_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX);
if (open_result == -RT_EIO)// 如果不支持 DMA,则使用中断模式
{
rt_device_open(g_uart_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
}
/* 设置接收回调函数 */
rt_device_set_rx_indicate(g_uart_device, uart_rx_ind);
/* 创建接收线程 */
rt_thread_t thread = rt_thread_create("g_uart_device",
serial_rx_thread_entry,
RT_NULL,
3 * 1024, // 线程栈大小
12, // 优先级
10); // 时间片
if (thread != RT_NULL)
{
rt_thread_startup(thread); // 启动线程
}
else
{
ret = RT_ERROR; // 线程创建失败
}
return ret;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
接收回调函数
回调函数我们做释放信号量的处理,具体处理接收的数据我们用线程去处理
static rt_err_t uart_rx_ind(rt_device_t dev, rt_size_t size)//接收回调函数,当接收到数据时被调用
{
if (size > 0)
{
rt_sem_release(&rx_sem); // 释放信号量,唤醒接收线程
}
return RT_EOK;
}
2
3
4
5
6
7
8
线程入口函数
线程内等待接收回调函数释放的信号量
static void serial_rx_thread_entry(void *parameter)//UART 接收线程入口函数
{
uint16_t count = 0; // 已接收字节数
uint8_t cnt = 0; // 每次读取的字节数
while (1)
{
// 等待接收信号量(阻塞直到有数据)
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
while (1)
{
// 从 UART 设备读取数据
cnt = rt_device_read(g_uart_device, -1, &data[count], ONE_DATA_MAXLEN);
count += cnt;
// 打印调试信息
rt_kprintf("uart_rec: cnt = %d,count = %d\n", cnt, count);
if (0 == cnt) break; // 如果没有更多数据可读,退出循环
}
rt_kprintf("rev:"); // 打印接收提示
for (uint16_t i = 0; i < count; i++)
{
rt_kprintf("%c", data[i]); // 打印接收到的字符
}
count = 0; // 清空计数器
rt_kprintf("\n"); // 换行
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
程序编写
#include "rtthread.h"
#include "bf0_hal.h"
#include "drv_io.h"
#include "stdio.h"
#include "string.h"
#include "board.h"
#define DBG_TAG "uart2"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
/* uart example for RT-Thread based platform -----------------------------------------------*/
#define UART_DEMO_NAME "uart2"
#define ONE_DATA_MAXLEN 256
static rt_device_t g_uart_device = RT_NULL;
static struct rt_semaphore rx_sem;
static uint8_t data[ONE_DATA_MAXLEN] = { 0 };
/* receiving thread */
static void serial_rx_thread_entry(void *parameter)
{
uint16_t count = 0;
uint8_t cnt = 0;
while (1)
{
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
while (1)
{
cnt = rt_device_read(g_uart_device, -1, &data[count], ONE_DATA_MAXLEN);
count += cnt;
rt_kprintf("uart_rec: cnt = %d,count = %d\n", cnt, count);
if (0 == cnt) break;
}
rt_kprintf("rev:");
for (uint16_t i = 0; i < count; i++)
{
rt_kprintf("%c", data[i]);
}
count = 0;
rt_kprintf("\n");
}
}
/* uart callback function */
static rt_err_t uart_rx_ind(rt_device_t dev, rt_size_t size)
{
/* release the semaphore when recieve the data */
if (size > 0)
{
rt_sem_release(&rx_sem);
}
return RT_EOK;
}
static void uart_send_data(uint8_t *p_data, uint16_t length)
{
uint16_t write_len = 0;
if (g_uart_device == RT_NULL)
{
return;
}
write_len = rt_device_write(g_uart_device, 0, p_data, length);
rt_kprintf("send:");
for (uint16_t i = 0; i <= write_len; i++)
{
rt_kprintf("%c", *(p_data + i));
}
}
int uart2_init(void)
{
rt_err_t ret = RT_EOK;
/* 1, pinmux set to uart mode */
HAL_PIN_Set(PAD_PA18, USART2_RXD, PIN_PULLUP, 1);
HAL_PIN_Set(PAD_PA19, USART2_TXD, PIN_PULLUP, 1);
/* 2, find and config uart2 device */
g_uart_device = rt_device_find(UART_DEMO_NAME);
if (!g_uart_device)
{
LOG_E("find %s failed!\n", UART_DEMO_NAME);
return RT_ERROR;
}
/* config uart2 baud_rate */
{
rt_err_t err;
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
config.baud_rate = 1000000;
err = rt_device_control(g_uart_device, RT_DEVICE_CTRL_CONFIG, &config);
LOG_D("uart device config %d", err);
}
/* Initialize the semaphore */
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
//using DMA mode first
rt_err_t open_result = rt_device_open(g_uart_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX);
//using interring mode when DMA mode not supported
if (open_result == -RT_EIO)
{
rt_device_open(g_uart_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
}
/* set the callback function of recieving */
rt_device_set_rx_indicate(g_uart_device, uart_rx_ind);
/* creat the thread of g_uart_device */
rt_thread_t thread = rt_thread_create("g_uart_device", serial_rx_thread_entry, RT_NULL, 3 * 1024, 12, 10);
/* start the thread of g_uart_device */
if (thread != RT_NULL)
{
rt_thread_startup(thread);
}
else
{
ret = RT_ERROR;
}
return ret;
}
/**
* @brief Main program
* @param None
* @retval 0 if success, otherwise failure number
*/
int main(void)
{
uint8_t tx_data[ONE_DATA_MAXLEN] = {'u', 'a', 'r', 't', '2', ' ', 'd', 'e', 'm', 'o', '\n'};
rt_kprintf("Start uart demo!\n");
uart2_init();
uart_send_data(tx_data, 12);
rt_kprintf("uart demo end!\n");
while (1)
{
rt_thread_mdelay(5000);
//rt_kprintf("__main loop__\r\n");
}
return RT_EOK;
}
/************************ (C) COPYRIGHT Sifli Technology *******END OF FILE****/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
硬件连接
- PA19软件配置为UART2的TX,连接到电脑usb转串口的RX
- PA18软件配置为UART2的RX,连接到电脑usb转串口的TX
- GND连接到usb转串口的GND,如下图所示:
例程输出结果展示:
log输出:左边为机器log,右边为电脑usb转串口端发送数据和接收数据:
注意:串口开启有两个串口,一个是代码配置电脑usb转串口的uart2,一个是自带的调试口uart1(用于验证uart2的接收能力)
log结尾收到的rev: 为接收到的电脑USB转串口TX发来的字符
注意:这里打印接收数据是用默认串口打印而不是uart2,因为uart2的发送数据功能也是例程中的一项
SFBL
Serial:c2,Chip:4,Package:3,Rev:3 Reason:00000080
\ | /
- SiFli Corporation
/ | \ build on Oct 23 2024, 2.2.0 build 00000000
2020 - 2022 Copyright by SiFli team
mount /dev sucess
[32m][490] I/drv.rtc: PSCLR=0x80000100 DivAI=128 DivAF=0 B=256
[0m][32m][517] I/drv.rtc: RTC use LXT RTC_CR=00000001
[0m][32m][538] I/drv.rtc: Init RTC, wake = 1
[0m][32m][565] I/drv.audprc: init 00 ADC_PATH_CFG0 0x606
[0m][32m][587] I/drv.audprc: HAL_AUDPRC_Init res 0
[0m][32m][609] I/drv.audcodec: HAL_AUDCODEC_Init res 0
[0m][32m][630] I/TOUCH: Regist touch screen driver, probe=1203a299
[0mcall par CFG1](35bb)
fc 9, xtal 2000, pll 2050
call par CFG1(35bb)
fc 9, xtal 2000, pll 2051
Start uart demo!
[796] D/uart2: uart device config 0
send:uart2 demo
uart demo end!
msh />
uart_rec: cnt = 5,count = 5
uart_rec: cnt = 0,count = 5
rev:abc
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
下面为开发板复位后电脑USB转串口发送的数据uart2 demo字符以及检验uart2是否能接收串口数据而人为发送的数据
uart2 demo
TX:abc
2
注意:
1、UART引脚怎么选:可以配置到任意带有PA_I2C_UART功能的IO输出UART2波形(想查询引脚复用表可在项目路径下文件中查找如:bf0_pin_const.c)
本例程演示的是外接USB转串口模块,因此引脚选择有排针引出的引脚
2、HAL_PIN_Set 最后一个参数为HCPU/LCPU选择, 1:选择HCPU,0:选择LCPU
3、HCPU的PA口不能配置为LCPU的uart外设,比如uart5,uart6输出
异常诊断
1、uart2无波形输出
- pin status 18/19命令查看对应PA18,PA19的IO状态FUNC对不对,VAL电平应该是1
msh />
TX:pin status 18
pin status 18
[I/TEST.GPIO] PIN 18, FUNC=4, VAL=1, DIG_IO_PU, GPIO_MODE_INPUT, irqhdr=/, arg=/
msh />
msh />
TX:pin status 19
pin status 19
[I/TEST.GPIO] PIN 19, FUNC=4, VAL=1, DIG_IO_PU, GPIO_MODE_INPUT, irqhdr=/, arg=/
msh />
msh />
2
3
4
5
6
7
8
9
10
11
- list_device命令查看uart2设备是不是存在并且打开了
- 检查uart2配置流程是否都已生效
2、uart2波形正常,电脑串口接收不到数据
- 检测uart2输出和电脑串口连接是否正常,需要TX接RX,RX连接TX,波特率是否一致,以及引脚对应的TX,RX是否正确
- 检测硬件连接,包括uart2输出电平跟电脑uart电平是否一致
3、电脑串口发出数据,uart2出现数据接收丢失现象
- 确认串口DMA的buffer长度是否足够 #define RT_SERIAL_RB_BUFSZ 256