RTT(RT-Thread)串口设备(RTT保姆级教程)_rt-thread 串口

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

#define STOP_BITS_4 3
/极性位可取值/
#define PARITY_NONE 0
#define PARITY_ODD 1
#define PARITY_EVEN 2
/* 高低位顺序可取值*/
#define BIT_ORDER_LSB 0
#define BIT_ORDER_MSB 1
/模式可取值/
#define NRZ_NORMAL 0 /* normal mode*/
#define NRZ_INVERTED 1 /* invertedmode /
/
接收数据缓冲区默认大小*/
#define RT_SERIAL_RB_BUFSZ 64

RT-Thread 提供的默认串口配置
#define RT_SERIAL_CONFIG_DEFAULT
{
BAUD_RATE_115200, /* 115200 bits/s /
DATA_BITS_8, /
8 databits /
STOP_BITS_1, /
1 stopbit /
PARITY_NONE, /
No parity /
BIT_ORDER_LSB, /
LSB first sent /
NRZ_NORMAL, /
Normal mode /
RT_SERIAL_RB_BUFSZ, /
Buffer size */
0
}


注:缓冲区通过 control 接口修改,缓冲区大小无法动态改变,只有在 open 设备之前可以配置。open 设备之后,缓冲区大小不可再进行更改。但除过缓冲区之外的其他参数,在 open 设备前 / 后,均可进行更改。


* 发送数据


![](https://img-blog.csdnimg.cn/e4536fbfebef43c2b0455c2e904922fc.png)


* 设置发送完成回调函数


  在应用程序调用rt\_device\_write()写入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。这个回调函数会在底层硬件数据发送完成后 (例如 DMA 传送完成或 FIFO 已经写入完毕产生完成中断时) 调用


![](https://img-blog.csdnimg.cn/fd733029bf7540f6ae44f74ce5ff8351.png)


* 设置接收回调函数


若串口以中断接收模式打开,当串口接收到一个数据产生中断时,就会调用回调函数,并且会把此时缓冲区的数据大小放在size参数里,把串口设备句柄放在dev参数里供调用者获取。


若串口以 DMA 接收模式打开,当 DMA 完成一批数据的接收后会调用此回调函数。


![](https://img-blog.csdnimg.cn/ceea38912bbd4f76a8cfcfcd6a625de9.png)


 使用:一般情况下接收回调函数可以发送一个信号量或者事件通知串口数据处理线程有数据到达。(也就是说接收数据需要我们另外开启一个线程来接收,通过回调函数来通知线程是否有数据到达。当没有数据的时候,接收线程应该处于挂起状态,可以采用信号量或事件通知的方式)


比如在线程接收数据的时候,先获取一下信号量,如果有资源,就执行,如果没有就让接收线程挂起。我们需要在设置接收回调函数里面发送信号量。当接收回调函数被调用的时候,说明接收到了数据,一旦接收到了数据就设置信号量来唤醒挂起状态中的接收线程。


* 接收数据


![](https://img-blog.csdnimg.cn/c2fb30967bb149cab86d6b33e1ef19b3.png)


* 关闭串口设备



rt_err_t rt_device_close(rt_device_t dev);


### 数据发送方法


一般来说,对于串口发送没有太多的要求,我们在想要发送的时候只需要调用write函数写入数据即可,只要发送的频率不是那么快(如在while循环里一直发送,可能会导致发送的数据出问题),一般都会间隔一段时间再发,如用延时函数或者定时器一定时间后发送采集到的传感器数据。


但如果发送的频率过快,有可能硬件的上一个数据还没发送出去,下一条发送指令就已经执行到了。这种情况就需要用到发送中断或者DMA的发送方式。


### 数据接收方法


对于接收来讲,有一个问题。接收并不是由主控设备自己来决定的,因为接收数据的前提条件是对方由数据发过来之后,接收方才能接收。因此接收这块我们必须想方法,看采用什么样的方式才能够更好地接收到数据。


一种方式就是写一个while死循环,然后在循环里面一直读,什么时候对方发送数据,什么时候就接收到数据。但这样在RTT中需要我们单独开启一个线程一直去读,线程一直在while循环里面去读,而且不加延时,这样就与RTT线程的创建和线程的要求相矛盾。所以我们在RT-Thread不采用这种方式。


因此我们可以采用中断或者DMA的方式来接收数据。


中断读取:当硬件设备接收到数据以后,触发接收中断,在中断处理函数里面我们将数据接收到。


DMA读取:开启DMA以后,通过DMA的方式直接从串口数据寄存器中接收数据,接收到数据以后先放到缓冲区。接收一段时间以后,通过触发一个中断,然后我们将数据从缓冲区读走。


### 串口设备使用流程


其中我们只需要编写应用程序代码,ISR和串口外设相关的硬件驱动代码在操作系统中是有驱动支持的,不需要人为关心。


![](https://img-blog.csdnimg.cn/f172755997564089a9a4e086a8fa89c2.png)


在硬件产生一个中断以后是如何通知应用层的呢?我们是通过设置回调函数的方式来接收中断信息的。


先来看我们的应用程序端:


应用程序端,我们在编写的时候,首先要初始化信号量,要通过信号量来进行数据的接收;然后设置接收回调函数;接着创建数据处理线程,用来专门处理接收到的数据;最后我们在创建好线程以后,先开启线程,然后让线程阻塞等待信号量,信号量的作用就是实现数据接收的同步。


![](https://img-blog.csdnimg.cn/47cfd964a8a740cb84d1a99b229a5f40.png)


再解释一下,我们在线程处理函数中写一个while循环,一直去接收数据。但在接收数据之前要先获取信号量,如果信号量能够获取成功,就往下读取数据。如果信号量获取失败,则线程进入休眠态。


接收线程的接收数据还是休眠(挂起),由信号量来决定。也就是说信号量标志着是否有数据发送过来了,而数据是否发过来是由接受回调函数决定的,一旦回调函数被执行。我们就在回调函数中发送信号量,从而唤醒数据处理线程去接收数据。


再来看一下硬件驱动:


当用户从电脑通过串口输入一个字符发送给STM32时,单片机接收到数据之后就会触发一个接收中断,从而调用我们再应用层设置的接收回调函数。在中断服务程序(ISR)中,会将接收的数据放入缓冲区,并在接收回调函数中发送信号量激活线程,也就是说我们设置的回调函数中一定要记得发送信号量。


![](https://img-blog.csdnimg.cn/b6a3d3992bed486e829bfa1739b4332c.png)


### 串口中断接受实例


#### 串口配置及串口发送



1.首先我们先将串口配置好,比如我们在创建工程的时候,选择了串口2。那么创建好工程后,系统会自动配置串口2,并在board.h中可以查看


![](https://img-blog.csdnimg.cn/85b78266521842829f43f64711d0ba4e.png)


2.如果我们想再添加一个UART1,我们可以参考自带的步骤说明


![](https://img-blog.csdnimg.cn/2e7ab4093fd24990a2cc6b230620a251.png)


(1)首先我们要定义要使用串口的宏


(2)说明所要使用串口的发送和接收引脚


(3)和(4)是使用DMA来配置的,在后面介绍



/** After configuring corresponding UART or UART DMA, you can use it.
*

  • STEP 1, define macro define related to the serial port opening based on the serial port number
  •             such as     #define BSP_USING_UART1
    
  • STEP 2, according to the corresponding pin of serial port, define the related serial port information macro
  •             such as     #define BSP_UART1_TX_PIN       "PA9"
    
  •                         #define BSP_UART1_RX_PIN       "PA10"
    
  • STEP 3, if you want using SERIAL DMA, you must open it in the RT-Thread Settings.
  •             RT-Thread Setting -> Components -> Device Drivers -> Serial Device Drivers -> Enable Serial DMA Mode
    
  • STEP 4, according to serial port number to define serial port tx/rx DMA function in the board.h file
  •             such as     #define BSP_UART1_RX_USING_DMA
    

*/


3.我们将UART1添加进去


![](https://img-blog.csdnimg.cn/5507db6d0a7f43a79720f04ba4b8d039.png)


4.添加完之后,我们在main.c中完成对UART1的应用层配置


(1)首先查找串口1设备句柄


![](https://img-blog.csdnimg.cn/45fa99df816f412cb41cd1461f9258e5.png)


(2)成功找到之后,打开串口设备(注意要以读写和接收中断的方式打开)


![](https://img-blog.csdnimg.cn/e96bc2919aab48c8a519039dcb16ab7b.png)


(3)打开设备之后,我们还需要对串口设备进行协议相关的配置,我们使用rt\_device\_control函数


首先我们要创建一个串口信息结构体,这里我们使用默认的串口配置


![](https://img-blog.csdnimg.cn/e862733aa1524d5bacc8a0edcf798af3.png)


默认配置具体内容如下:


![](https://img-blog.csdnimg.cn/f1fe797e360a448bbfed5ad6c3ad19e6.png)


然后我们将结构体取地址传入设备控制函数中,并将cmd设置为设备配置模式


![](https://img-blog.csdnimg.cn/9c3739176a534383910d076ab02f5e54.png)


5.配置完成以后,如果我们想要发送数据,需要调用rt\_device\_write函数,其中第一个参数传入发送的设备,第二个参数为相对于buffer起始地址的偏移量(起始位置),第三个参数为buff(要发送的内容),第四个参数为buff的大小


![](https://img-blog.csdnimg.cn/676f7b58ab8940edadf0b3d031fab588.png)


编译一下,发现有错误


![](https://img-blog.csdnimg.cn/768f1669c74e4e54a73112f51db41a30.png)


这是因为没有包含串口相关的头文件,我们将serial.h包含进来


![](https://img-blog.csdnimg.cn/15969bd61df648eba45d9115f55d9b85.png)


编译发送,仍有错:找不到头文件


![](https://img-blog.csdnimg.cn/50d74f01d34e440d80397e002a02ee29.png)


我们通过光标定位找到头文件位置


![](https://img-blog.csdnimg.cn/eca807cb38fe4d598ead571f67bb347a.png)


![](https://img-blog.csdnimg.cn/d28772449a3c4b74ac2ca8fa2fe72935.png)


我们将头文件路径手动添加到工程中:


(1)打开构建配置


![](https://img-blog.csdnimg.cn/effc780d1be44c49bf659d3bcc747473.png)


(2)打开C/C++构建中的设置


![](https://img-blog.csdnimg.cn/f3c0ea4a9133435c84d1f75bb57280b6.png)


(3)右边出现汇编工具和C编译工具,我们选择C Compiler的Includes


![](https://img-blog.csdnimg.cn/ea04ddfeaed54ad0bdb32da20492c2b3.png)


(4)我们添加目录路径,通过文件系统的方式来添加


![](https://img-blog.csdnimg.cn/36e15ae984964a018bbe8c0125264f37.png)


![](https://img-blog.csdnimg.cn/f7939adefd9c465da790502123dde461.png)


![](https://img-blog.csdnimg.cn/56bb0d2e755d4b2586a4d4630ecf5040.png)


![](https://img-blog.csdnimg.cn/5b2bd6baa95b438f98a513ca5a6c916b.png)


(5)重新编译,发现新的错误,serial.h中有些类型无法找到


![](https://img-blog.csdnimg.cn/052012c41fca417cb96e449ebd5c270d.png)


我们通过定位将相关头文件包含进serial.h中


![](https://img-blog.csdnimg.cn/4b86dc034ed044229581402ec79f0e36.png)


![](https://img-blog.csdnimg.cn/6f9494d67f5e4e379955abd694e4b096.png)


(6)再次编译发现还是有错误,我们需要将上一级ipc的路径也手动添加到工程中


![](https://img-blog.csdnimg.cn/788e0efd7ed549f1938c6f202782e326.png)


![](https://img-blog.csdnimg.cn/7b77a58a105245bfa6c4a487d644a984.png)


最终编译发现没有任何错误


![](https://img-blog.csdnimg.cn/394655aca2a4448bb3b288d5fe009257.png)


6.我们验证下程序是否正确


首先下载程序,然后打开串口


![](https://img-blog.csdnimg.cn/7a960cc0a2ec4447ae2b17781655d3eb.png)


然后选择相应串口,并点击确定


![](https://img-blog.csdnimg.cn/dc8b4c39e7a144519be793b11ffaaf71.png)


复位STM32开发板,成功发送信息


![](https://img-blog.csdnimg.cn/2802cc78bb414437a1dad7d9cb85104e.png)


#### 串口中断接收


在之前已经成功验证了串口1可以正常使用,下面就来写一下中断接收的方式


1.首先设置接收回调函数


![](https://img-blog.csdnimg.cn/5c738aaebaf342ab9f204a417f442f16.png)


第二个参数为我们需要调用的回调函数的函数指针


(1)我们拿到函数原型


![](https://img-blog.csdnimg.cn/ea1c0112362844c9bac86e1d78bd45ea.png)


(2)然后到main.c中定义


我们起名叫做rx\_callback,并返回值设置为RT\_EOK,即返回0


![](https://img-blog.csdnimg.cn/257d3808f2bd48109de6c20a5201e928.png)


(3)最后将设置回调函数相关参数填入


![](https://img-blog.csdnimg.cn/1220c5b0980e4a11951ac7f587e0016e.png)


2.回调函数设置好以后,也就是说当硬件接收到数据以后,触发中断。触发中断就会回调我们设置的rx\_callback函数。下面我们还需要加上信号量和线程。


3.创建信号量


我们之前使用的是动态创建信号量,所以这里再以静态创建信号量为例


(1)我们首先要定义一个信号量结构体变量,待会儿取地址传入参数


![](https://img-blog.csdnimg.cn/5ef809a7318245b49ce509675272e723.png)


(2)初始化信号量


第一个参数传入刚刚创建的结构体地址


第二个参数为信号量名字,我们表示为接收信号量


第三个参数:信号量的资源值设置为0,因为我们一开始是没有数据的。只有当对方发送数据之后,调用接收回调函数,在回调函数中发送信号量,这样信号量的value值就会加1,从而唤醒接收线程。


第四个参数模式设置为先进先出


这里返回值就不做判断了。


![](https://img-blog.csdnimg.cn/694f04d57325412baf755e81bf09a92a.png)


4.创建线程


我们使用动态创建的方式


(1)首先创建线程结构体指针和线程处理函数


![](https://img-blog.csdnimg.cn/1a542a9b93f745d9ac31abcc051b2f41.png)


(2)然后动态创建线程(由于时间问题,返回值就不做判断了)


![](https://img-blog.csdnimg.cn/25cd4633a16446d4a42a8e4319c8efc8.png)


5.启动线程


![](https://img-blog.csdnimg.cn/cb7fdf8f2fee463fae6b8426e7abc5e7.png)


6.完善线程处理函数


(1)我们使用while循环在线程处理函数中一直读取,这里单次读取一个字符,如果读取成功就进入一次中断


![](https://img-blog.csdnimg.cn/716d003153a343e98846d6ee28e6f1f6.png)


(2)如果成功接收到数据,就将数据通过调试串口打印出去(调试串口为创建工程时选择的串口)。


![](https://img-blog.csdnimg.cn/9740a810830c4b0ca25701d19683d2fe.png)


7.完善接收回调函数,一旦回调函数被执行就释放信号量


![](https://img-blog.csdnimg.cn/bf0be5a771d24e6ebf9e98b0df3619d6.png)


8.实验现象:


我们将程序下载到开发板,打开串口2的终端,可以发现暂时没有任何数据输出


![](https://img-blog.csdnimg.cn/77eb7dc5463b4a1085d76af242a7d257.png)


由于我们的接收数据是通过串口1接收的,因此我们需要打开一个串口调试助手,通过上位机向串口1发送数据,然后串口2将串口1接收到的数据打印出来。


![](https://img-blog.csdnimg.cn/45a98f19f50243eaab241360d6010cd8.png)


在本例中我们是一个字符一个字符来接收的,每一次接收新的字符都会将buffer中的数据给覆盖掉。在实际的应用开发中,我们应该定义某些数据类型将每次接收的数据保存起来,如可以定义数组或者使用复杂点的数据结构(如队列),将数据循环的保存到一块缓存区。需要处理的时候,再将数据从数组或队列中读走再处理。


#### DMA接收



**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
![img](https://img-blog.csdnimg.cn/img_convert/5f1e3f39a8e46ea460094b02eb66a32b.png)
![img](https://img-blog.csdnimg.cn/img_convert/6952796c67a5500eea7487bf005f1ce7.png)

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

收新的字符都会将buffer中的数据给覆盖掉。在实际的应用开发中,我们应该定义某些数据类型将每次接收的数据保存起来,如可以定义数组或者使用复杂点的数据结构(如队列),将数据循环的保存到一块缓存区。需要处理的时候,再将数据从数组或队列中读走再处理。


#### DMA接收



**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
[外链图片转存中...(img-vjHj9Rhs-1715791915129)]
[外链图片转存中...(img-Jx7BQj5O-1715791915129)]

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值