RT-Thread | UART设备驱动框架解析

1024G 嵌入式资源大放送!包括但不限于C/C++、单片机、Linux等。关注微信公众号【嵌入式大杂烩】,回复1024,即可免费获取!

UART简介

STM32 芯片具有多个 USART 外设用于串口通讯,它是 Universal Synchronous Asynchronous Receiver and Transmitter 的缩写,即通用同步异步收发器可以灵活地与外部设备进行全双工数据交换。

有别于 USART,它还有具有 UART 外设(Universal Asynchronous Receiver and Transmitter),它是在 USART 基础上裁剪掉了同步通信功能,只有异步通信。

简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。

UART 作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。

其工作原理是将传输数据的每个字符一位接一位地传输。其传输数据格式如下:

UART设备框架学习笔记

RT-Thread 提供了一套简单的 I/O 设备模型框架,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层 :

应用程序访问串口设备的接口:

下面我们直接来看个实例:

同时使用两个串口(uart1uart3),uart1作为系统打印调试串口,用来打印一些日志信息,uart3作为我们本次实验的测试串口,实现与串口调试助手的收发测试。

uart3设备启动后,往串口调试助手发送字符串I am uart3。同时,uart3设备使用中断的方式接收数据,然后再错位输出数据,比如收到ASCII码字符A,则会回复B

#define SAMPLE_UART_NAME    "uart3"          /* 串口设备名称           */
/* uart3应用函数 */
static int uart3_app(void)
{
    rt_err_t ret = RT_EOK;                      /* 函数返回值        */
    rt_thread_t tid;                            /* 动态线程句柄      */
    char uart3_name[RT_NAME_MAX];               /* 保存查找的设备名  */
    char usart3_tx_str[] = "I am uart3.\r\n";   /* uart3发送的字符串 */

    rt_strncpy(uart3_name, SAMPLE_UART_NAME, RT_NAME_MAX);
    
    /* 查找串口设备 */
    uart3_dev = rt_device_find(uart3_name);
    if (!uart3_dev)
    {
        rt_kprintf("find %s failed!\n", uart3_name);
        return RT_ERROR;
    }
    /* 初始化信号量 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
    /* 以读写及中断接收方式打开串口设备 */
    rt_device_open(uart3_dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(uart3_dev, uart3_rx_callback);
    /* 发送字符串 */
    rt_device_write(uart3_dev, 0, usart3_tx_str, (sizeof(usart3_tx_str) - 1));
    
    /* 创建动态线程 :优先级 25 ,时间片 5个系统滴答,线程栈512字节 */
    tid = rt_thread_create("uart3_rx_thread",
			static_uart3_rx_entry,
			RT_NULL,
			STACK_SIZE,
			THREAD_PRIORITY,
			TIMESLICE);
    /* 创建成功则启动动态线程 */
    if (tid != RT_NULL)
    {
	rt_thread_startup(tid);
    }
    return ret;
}

我们的应用程序首先根据串口设备名字uart3来查找设备,查找到设备之后则返回串口设备句柄uart3_dev

为什么应用程序可以查找得到名字为uart3的串口设备呢?那是因为我们的硬件驱动层已经把名字为uart3的串口设备注册到系统中:

串口设备注册函数里会调用通用的设备注册函数

在这个流程中涉及到了如下函数:

  • uart3_app函数:在main.c文件中定义。

  • rt_hw_usart_init函数:在drv_usart.c文件中定义。

  • rt_hw_serial_register函数:在serial.c文件中定义。

  • rt_device_register函数:在device.c文件中定义。

  • rt_device_find函数:在device.c文件中定义。

在上面的RT-Thread驱动框架框图中,分为好几层,在这里的对应关系如下:

此处,main.c文件属于应用层,我们的应用程序为:

drv_usart.c文件属于硬件设备驱动层,是RT-Thread为我们提供的,其属于板级支持包中的一部分:

这一层与硬件相关,其调用底层芯片固件库,如:

serial.c文件属于驱动框架(驱动抽象层),是RT-Thread系统的组件:

其在RT-Thread源码中的位置如下:

device.c文件给应用程序提供操作设备的接口,这个文件属于RT-Thread内核文件。
RT-Thread 内核采用面向对象的设计思想进行设计,其中设备属于它的一类对象。其继承关系如下:

在这个应用程序中,我们用到了信号量(用其它同步机制也可以,比如事件),信号量属于IPC机制中的一种:

信号量用于线程与线程中断与线程间的同步中,在我们这个实验中是中断与线程间的同步。

在裸机开发中,有这样一种场景(中断接收数据,主函数中处理数据):

在串口的接收中断函数中接收数据,然后使用一个全局变量作为中断接收的标志,有触发中断,则这个标志变量被置位;另一方面,在我们的主函数的while循环中,判断这个标志位是否被置位,若置位则进行相应的操作,并把该标志变量清零。

在RT-Thread系统中,其IPC机制做的事情与上面这个裸机开发中的标志变量做的事情类似(中断与线程同步)。比如在我们这个实验中,若uart3的接收中断被触发,则会触发回调函数,如:

回调函数中进行释放信号量操作,在线程中阻塞等待接收信号量:

接收到数据之后,再错位发送数据。最终,我们的实验结果如下:

以上就是本次的笔记分享,如有错误,欢迎指出,谢谢!

最后

如果觉得文章不错,转发、在看,也是我们继续更新得动力。

猜你喜欢:

ARM Cortex-M 系列 MCU错误代码自动追踪库的使用

RT-Thread Studio的使用体验:真香!

【RT-Thread笔记】对FLASH进行分区管理

【RT-Thread笔记】临界区问题及IPC机制

串口通讯你真的会了吗?不妨看看这些经验

1024G 嵌入式资源大放送!包括但不限于C/C++、单片机、Linux等。关注微信公众号【嵌入式大杂烩】,回复1024,即可免费获取!

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
RT-Thread Studio 中使用串口设备,可以按照以下步骤进行: 1. 在设备管理器中找到串口设备,并记录下其名称和端口号。 2. 在 RT-Thread Studio 中创建一个名为 uart设备对象,并设置好其配置参数,如波特率、数据位、停止位和校验位等。 3. 在应用程序中调用相应的 API 函数来进行串口通信,如发送数据、接收数据等。 以下是一个使用串口设备的示例代码: ```c #include <rtthread.h> #include <rthw.h> #define UART_NAME "uart1" // 串口设备名称 #define UART_BUFSZ 128 // 缓冲区大小 static rt_device_t uart_dev; // 串口设备对象 static char uart_buffer[UART_BUFSZ];// 串口数据缓冲区 static void uart_thread_entry(void *parameter) { rt_size_t rx_len; while (1) { rt_memset(uart_buffer, 0, UART_BUFSZ); // 从串口设备中读取数据 rx_len = rt_device_read(uart_dev, 0, uart_buffer, UART_BUFSZ); if (rx_len > 0) { // 处理接收到的数据 rt_kprintf("Received: %s\n", uart_buffer); } // 延时一段时间 rt_thread_mdelay(10); } } int main(void) { // 初始化串口设备 uart_dev = rt_device_find(UART_NAME); rt_device_open(uart_dev, RT_DEVICE_FLAG_RDWR); // 创建串口线程 rt_thread_t tid = rt_thread_create("uart", uart_thread_entry, RT_NULL, 1024, 25, 5); rt_thread_startup(tid); return 0; } ``` 在上述代码中,首先通过 rt_device_find() 函数找到名为 uart1 的串口设备,并通过 rt_device_open() 函数打开该设备。然后创建一个名为 uart 的线程,该线程不断从串口设备中读取数据,并进行处理。可以根据实际需要修改线程的优先级、堆栈大小等参数。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式大杂烩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值