STM32外设学习-串口发送数据-接收数据(笔记)

上次我们完成了STM32串口外设学习,然后我们进行代码的编写。

一.硬件接线图

1.接线图

这里有个跳线帽,要接在VCC和3.3V的引脚上选择通信的TTL电平为3.3V。然后通信引脚TXD和RXD要接在STM32的PA9和PA10。

这里我们可以看到USART1的TX是PA9口,RX是PA10口。我们计划用USART1进行通信,所以就选这两个引脚,TX和RX要交叉连接,这里的PA9是STM32的TX发送引脚,接的就是串口模块的RXD,接收。两个设备的负极接在一起进行共地,一般多个系统之间都要进行共地。

2.串口驱动

打开设备管理器,确保我们驱动没有问题,有一个CH340的驱动。

二.程序编写

1.函数了解

复制一下OLED显示的程序然后进行改名字。

建立好串口模块的名字。

快速编写.h文件。

主函数架子写上。

之后就是按照流程进行初始化。

老三样,恢复缺省状态,初始化串口,给串口结构体赋默认值。

配置同步时间输出的函数。

串口使能,和配置中断。

开启USART到DMA的通道。

设置地址,唤醒,LIN函数我们都用不到,

第一个是发送数据,第二个是接收数据。发送就是写DR寄存器,接收就是读取DR寄存器

第一个获取标志位,第二个清除标志位,第三个获取中断标志位,第四个清除中断标志位。

2.发送函数编写。

开启USART1的时钟,只有USART1在APB2上,剩下的在APB1上。

开启GPIOA的时钟因为我们要用到端口进行数据发送

然后我们复制一下GPIO初始化函数,因为TX是USART外设控制的的输出引脚,所以要选择复用推挽输出,因为RX是USART外设控制的的输入引脚,所以要选择输入模式,一般配置为浮空输入或者上拉输入。

引脚模式和PIN口。

这里就是把PA9设置为复用推挽输出供给USART的TX使用

3.初始化USART

(1)程序了解

定义结构体,起个新名字。

引出结构体变量我们再修改参数。

波特率,我们可以直接写一个数值,这样子,再init函数内部就会自动帮我们计算好他的分频系数。然后写道BRR寄存器。

硬件流控制,我们可以复制这个代码,然后按ctrl+alt+空格,联想一下代码。

就可以看到他的参数了。使用CTS,使用RTS和CTS,RTS都使用和不使用,我们选择不使用

串口模式,TX发送模式和RX接收模式,如果既需要发送和接受,那么就用或符号把这两个或起来。

我们选择发送模式。

校验位,no是无校验,odd是奇校验,even是偶校验。

停止位0.5,1,1.5,2,这四个停止位。

字长可以选择8位或者9位,我们不需要校验,所以选择8位。

启动串口。

写一个发送数据的函数,调用一次这个函数就可以从TX引脚发送一个字节的数据

写入一个发送数据的函数调用一次这个就会发送一个数据。

来到函数内部,这里Byte传送给Data这个变量之后Data与上01FF就是把无关的高位清零,然后直接赋值给DR寄存器。因为这是写入DR所以最终数据通向TDR也就是发送数据寄存器,TDR再传给发送移位寄存器,最后一位一位的把数据移出到TX引脚,完成数据的发送。

在这里可以看出,单片机是一种软件和硬件高度配合的设备,要想学好单片机程序思维和硬件分析都不可缺少

调用我们上面的函数,变量Byte就写入到TDR了。

之后我们要进行等待,等待TDR数据转移到寄存器才可以。如果不等待的话,我们再写入数据就会产生覆盖,所以再发送之后我们还需要等待一下标志位。

标志位判断

我们需要使用这个TXE发送数据寄存器空标志位

如果数据转移完成哪个TXE会置一个1标志位。如果等于0就会一直循环,直到标志位变为1,跳出循环。

是否要手动清除标志位我们可以看一下手册。

这里说明了,当TDR数据转移完成后,置为1,然后对DR寄存器进行写入时候,就会自动清除标志位。

现在我们可以进行发送部分测试。

声明一下。

添加头文件。

初始化串口。

先随便给个参数。

流程就为上电后初始话串口,在用串口发送一个0X41,调用这个函数后TX引脚就会产生一个0x41对应的波形。这个波形可以发送给其他支持串口的模块。

我们按下复位键,发现串口的指示灯闪烁了一下,就说明有波形出来了。

打开串口助手,串口号是我们这个设备管理器的COM号

下面的参数需要和STM32初始化的保持一致

这样电脑的串口就准备完成了。

我们按一次复位就会接收到一个数据。

这里的hex模式就是原始数据的模式,发送41就是41本身,如果要显示字符串怎么办呢,可以选择文本模式这样子就是以字符的形式显示数据了。

然后41就解析成为了1。

这里我们可以了解。

然后我们对串口函数进行封装,比如说要发送数组。

先写一个数组的封装。由于数组无法判断结束,需要加一个Length进来。

依次取出数组的每一项通过字节进行发送。

声明函数。

定义一个数组。

然后我们就收到了四个数据,就是我们的数组。

由于字符串自带一个结束标志位,所以就不需要进行长度判断了

将字符一个个取出来通过sendbyte进行发送。

声明函数。

封装数字的函数

10的0次方是个位,所以要反过来。假如是2位数,length为2,第一次循环length-i-1=2-0-1=1,就是发送十位,10的1次方。 第二次循环就是2-1-1=0,也就是10的0次方,个位。最终要以字符的形式显示,所以我们要加一个偏移。

声明一下。

(2)移植printf

点击魔术棒

勾选这个。

MicroLIB是keill为嵌入式平台优化的一个精简库。我们要用的printf函数就要用到MicroLIB。

我们还需要对printf进行重定向,因为printf是把数据打印到屏幕,但是我们单片机是没有屏幕的,我们需要将打印的东西输出到串口。

再串口初始化函数中加入stdio.h头文件。

重写fputc函数

这是我们fputc函数的原型,我们直接照着超参数就行。

这样子就可以了。

那么fputc和printf有什么区别呢,因为fputc是printf函数的底层printf再打印的时候就是不断调用fputc进行一个个打印的,我们把fputc函数重定向到了串口,那printf自然就输入到了串口。这样printf就移植好了。

主函数中写入一下测试。

可以看到我们输出了。

(3)第二种办法sprintf

这种printf有个弊端如果重定向到串口1的话,那么串口2就没有用了,所以我们介绍两种不同的printf重定向方法。

就可以用到sprintf,sprintf可以把格式化字符输出到一个字符串里面

主函数先定义一个数组。长度给够,

第一个参数是指定打印的位置。

目前这个格式化的字符串在string里面。

把字符串string通过串口发送出去。这样就完成了,因为sprintf可以指定打印位置,不涉及重定向的东西,所以每隔串口都可以用sprintf进行格式化打印。

编译下载,发现现象是一样的。

因为并不只重定向某一个串口,串口1,2,3都可以使用,串口发送函数可以自己设置使用哪个串口,只需要发送写入的那个数组,相当于间接实现了printf的功能,

(4)第三种办法封装sprintf

可以看到

sprintf每次都得先定义字符串,再打印字符串,再发送字符串,太麻烦了我们需要封装一下这个函数。

由于printf这个函数比较特殊它支持可变的参数,想我们之前写的函数参数都是固定的。可变参数比较复杂,如果需要的话,可以喂给ai。

在串口初始化里面引用stdarg.h文件。

这里就直接写了,这样就完成了对sprintf的封装。

声明一下。

然后把其他的注释掉。

下载可以看到,功能是一样的。

(5)显示汉字的方法

我们需要汉字显示格式选择UTF-8。

打开工程选项,选择,C/C++

然后这里的杂项控制栏写入--no-multibyte-chars。

编译之后0错误。

编码模式选择URF-8这样子就可以显示汉字了,但是有的软件UTF-8兼容不是很好。

打开控制,切换为

GB2312编码。这是汉字的编码方式

然后选择GBK编码发现也是没有问题的。

最终把所有程序都放出来,供大家参考一下。

4.串口接收数据

(1)程序了解

首先我们复制一下,串口接收的数据的代码

我们知道RX引脚接在了PA10引脚,所以需要初始化一下PA10。

不要复制结构体命名,然后复制一下剩下的然后输入改为上拉输入,端口选择PA10。

然后串口我们选择接收和发送同时工作。

对于串口接收来说,分为查询和中断两种方法,如果使用查询,那么串口初始化到这里就结束了

如果要使用中断,就还需要在这里使用中断开启NVIC。

(2)查询模式

查询的模式就是在主函数里不断地判断RXNE的标志位,如果置1了就说明收到数据了,那么再调用receivedata_DR就可以读取DR寄存器了,这样就行了。

先定义一个变量。

然后判断标志位,如果等与1说明数据来了就可以把数据存起来。

还有一个清理标志位的问题,我们可以看一下手册,当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位置被硬件置位,如果RXNEIE为1就可以产生中断,读取USART_DR寄存器就可以自动将该位清零。

然后我们再串口助手点击发送,就可以看到了我们要发送的数据。

如果程序比较简单的话,那么就推荐查询模式,

(3)中断模式(推荐)

需要用到开启中断的函数,可以看到第一个参数是USART1,第二个参数是选择哪个触发中断,这里我们选RXNE就是接收数据触发中断。

我们选择RXNE触发中断,然后使能。

把结构体变量引出,然后进行修改。

第一个参数选择我们的USART1_IRQN的通道。

然后给中断通道使能。

之后中断优先级我们先给1。

到这里RXNE一旦置1了就会向NVIC申请中断

这是我们中断函数的名字,再启动文件中,我们复制一下。

先判断标志位。

在后面清除一下标志位,这不影响我们函数的功能。

然后定义两个变量。

然后把数据读出来。

因为我们不想在中断函数中进行过多的操作,所以我们自己把标志位读取出来再进行清零。

如果有中断标志位我们就让它返回1,然后把标志位清0,否则返回0。

其实就是再中断里把数据进行了一个转存。对于单字节数据来说可能转存的意义不大,这里主要是演示一下中断函数的操作方法。

放在头文件引用。

主函数换成我们自己的判断。

读取数据也用我们自己的函数。

然后显示一下字符串。

然后加一个数据回传的功能。就可以双向了,把接收到的数据回传给电脑这就是目前程序的全部功能

编译下载,看看最终结果。

给个41,屏幕显示41,串口接收41。这就是我们程序的现象了。

相关推荐
Elias不吃糖1 小时前
eventfd 初认识Reactor/多线程服务器的关键唤醒机制
linux·服务器·c++·学习
宋辰月2 小时前
学习react第三天
前端·学习·react.js
逆小舟2 小时前
【STM32】串口
stm32·单片机·嵌入式硬件
昊喵喵博士3 小时前
直接用 JavaScript 给输入框赋值,Vue 页面input只是纯展示 并 没有触发 vue 的v-model 赋值
笔记
月下倩影时3 小时前
视觉学习篇——机器学习模型评价指标
人工智能·学习·机器学习
重启编程之路3 小时前
python 基础学习socket -UDP编程
python·网络协议·学习·udp
Fantasydg3 小时前
MyBatis学习
java·学习·mybatis
卡提西亚4 小时前
C++笔记-26-类模板
c++·笔记
song8546011344 小时前
锁的初步学习
开发语言·python·学习