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。这就是我们程序的现象了。

相关推荐
柒柒钏9 小时前
PyTorch学习总结(一)
人工智能·pytorch·学习
自不量力的A同学10 小时前
FreeFileSync 14.6 发布
笔记
可可苏饼干10 小时前
ELK(Elastic Stack)日志采集与分析
linux·运维·笔记·elk
s1ckrain11 小时前
数字逻辑笔记—组合逻辑电路
笔记·fpga开发·嵌入式
ℳ๓. Sweet11 小时前
【从零开发STM32(HAL版)】一、开发硬件环境准备
stm32·单片机·嵌入式硬件
一条破秋裤11 小时前
零样本学习指标
深度学习·学习·机器学习
喜欢踢足球的老罗11 小时前
Qoder AI IDE深度体验:用Repo Wiki与AskModel重塑开源库学习范式
人工智能·学习·qoder
可可苏饼干12 小时前
NoSQL 与 Redis
数据库·redis·笔记·学习·nosql
重生之我在番茄自学网安拯救世界12 小时前
网络安全中级阶段学习笔记(一):DVWA靶场安装配置教程与网络空间搜索语法
笔记·学习·网络安全·靶场·dvwa·fofa·google hack
梓德原12 小时前
【总结】STM32 SPI DMA 的使用
stm32·单片机·嵌入式硬件