STM32快速入门(串口传输之USART)

2024-05-08 13:44

本文主要是介绍STM32快速入门(串口传输之USART),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

STM32快速入门(串口传输之USART)

前言

USART串口传输能实现信息在设备之间的点对点传输,支持单工、半双工、全全双工,一般是有三个引脚:TX、RX、SW_RX(共地)。不需要一根线来同步时钟。最大优点是可以和电脑通信,实现程序调试的功能。

导航

图248 USART框图:

整体框图

图片引自STM32 F1XX系列的中文参考手册。

USART发送和接收的实现细节

第一部分

首先,对于图248的1号矩形框部分。该部分负责数据的发送和接收。(类似人体的四肢

截取了中文手册有关USART的一幅时序图,如下:

时序图1

首先解释一下空闲帧和断开帧:

从图中可以看到,空闲帧包括了停止位。而断开帧是10位低电平,后跟停止位(当m=0时);或者11位低电平,后跟停止位(m=1时)。不可能传输更长的断开帧(长度大于10或者11位)。

发送流程:

  1. 引脚处于空闲状态时,一般是高电平状态。发送使能位被使能:USART_CR1.TE[3] 位被置为1。

  2. (由用户)数据写到发送数据寄存器。在写之前,用户会等待 USART_SR.TXE[7] 被硬件置位,只有该位被置为才说明发送数据寄存器为空,此时写入数据就是安全的,不会造成覆盖的问题。

  3. (以下步骤都是由硬件完成)将发送数据寄存器的内容移到发送移位寄存器,同时将USART_SR.TXE[7] 置位。以示发送数据寄存器为空。

  4. 发送一个起始位。(低电平)

  5. 从最低位开始,左移位将发送移位寄存器的值按位发送到TX引脚(对发送方)。

  6. 如果用户使能了 USART_CR1.PCE[10] ,会根据 USART_CR1.PS[9] 发送一个校验位。

  7. 最后,根据 USART_CR2.STOP[13:12] 的配置发送若干个停止位。(高电平)

  8. 将状态寄存器 USART_SR.TC[6] 置位,表示数据的一帧发送完成。

接收流程:

  1. 接收使能位被使能:USART_CR1.RE[2]

  2. (以下未特别说明,都是由硬件完成)从RX引脚(对接收方)检测到起始位,接收移位寄存器准备接收数据。

  3. 接收移位寄存器从最高位开始,左移位依次按位从RX引脚(对接收方)接收数据。

  4. 接收到停止位。

  5. 如果使能了校验位的话,根据配置进行数据校验。

  6. 校验合格的话,就将接收移位寄存器的值移到接收数据寄存器。

  7. USART_SR.RXNE[5] 置位,表示接收数据寄存器非空,提醒用户接收到数据了。

  8. (由用户)读取接收数据寄存器的数据。

注意:

  • 虽然用户可以操作的寄存器只有一个USART_DR,但是实际上发送和接收数据寄存器在硬件上是各自一个!这样的设计也是双缓冲的实践。

  • 在发送和接收数据之前,用户必须统一设置两端的波特率、校验方式、停止位的数量、字长。否则这四项数据不一致,一定会造成传输错误,导致传输无法进行。其原因从上面的传输流程很容易推断。

有关状态寄存器的位的解释如下:

状态寄存器图1

状态寄存器图2

上面对过载错误位做了一个特写。这是因为我再编码的过程中遇到的一个BUG。排查了半天,原因是当RXNEIE接收中断位使能时,发送方的ORE标志位和RXNE标志位的置位都会触发RXNE事件的中断,当中断处理函数在处理完毕后,只复位RXNE标志,而不管ORE,后续还是会不断的产生中断。所以根据手册(手册其实是有误的),我们需要先读USART_SR,在读USART_DR将ORE标志位清除。(注意!库函数Clear类函数不能清楚ORE位!),这

这里放一张中断请求对应的事件表:

中断事件表

第二部分

对于图248的2号矩形框部分。该部分负责接收和发送的控制,(类似人体大脑。

图中可以看到有很多的控制器、控制寄存器、标志寄存器等。我们可以设置相应的寄存器从而控制收发来实现一些功能。具体寄存器的功能可以参考中文手册,这里不过多赘述。

第三部分

对于图248的3号矩形框部分。该部分负责控制接收和发送的时钟。接收和发送的时钟也称之为波特率,通过波特率,通信双方就能协调其收发的频率(类似人体心脏。

从图248的3号矩形框部分,可知,发送和接受器时钟是相等的。而时钟最开始是来自F_PCLK,送和接受器的时钟是对F_PCLK进行了一个 (16 * USARTDIV) 分频,USARTDIV是一个可调的定点小数。

波特率生成

波特率寄存器

这里解释一下中文手册里面“如何从USART_BRR寄存器值得到USARTDIV”的示例一。 最开始看到这个例子我也是很懵的,什么是定点小数?这是怎么用整数来表示小数的?为什么 <Fraction (USARTDIV) = 12/16 = 0.75> 这里要除以16?原理是这样的:

USART_BRR寄存器里面按定点小数的方式存放USARTDIV的值。只使用了16位,高12位存放小数的整数部分,低4位存放的是小数部分。整数部分很好说,直接存放进去就好了。而小数部分呢,因为小数部分一定是小于1的,所以,它根据低4位所能代表的值,将1划分成了2^4份,也就是16份,每一份占1/16,所以我们要将小数部分表示成4位整数就将小数乘以16并向上取整即可。溢出的话就向整数部分进一。反之,要从4位整数还原小数,就用4位整数乘以1/16。

中文手册总结了一个公式:

波特率 = F_PCLK / (16 * USARTDIV)

通信必须维持相同的波特率。双方各自通过调节USARTDIV,就可以在不同环境下将双方但的波特率调成一样的。

此外,还应该说明的是,公式中,有一个乘以 1 / 16 的操作,这么做的目的是发送接收控制器里面有一个比波特率大16倍的采样频率。采样频率起到很好的滤波效果,它会对每一位进行16次采样。采样对于起始位的探测非常的精妙。并且,对于数据位,中间的8、9、10位会起到决定性作用。

起始位探测:

起始帧探测

首先,我们称对第3、5、7位的采样为第一阶段采样,对第8、9、10的采样为第二阶段采样。

  1. 如果该序列不完整,那么接收端将退出起始位侦测并回到空闲状态(不设置标志位)等待下降沿。

  2. 两个阶段检测的全是0,则确认收到起始位,这时设置RXNE标志位,如果RXNEIE=1,则产生中断。

  3. 如果两阶段中3个采样点上仅有2个是0,那么起始位仍然是有效的,但是会设置NE噪声标志位。如果不能满足这个条件,则中止起始位的侦测过程,接收器会回到空闲状态(不设置标志位)。

  4. 如果两个阶段只有一个阶段中3个采样点上仅有2个是’0’,那么起始位仍然是有效的,但是会设置NE噪声标志位。

数据位噪声探测:

数据采样

对数据位的采样只有一个阶段采样有效,即对8、9、10次采样。

上方图片的下面的表格已经规定了采样的值和状态的映射。读者可以好好的品味一下。

最后,注意因为定点数表示小数是有精度的,所以波特率的计算是存在误差的,具体误差可以查阅中文手册。此外通过中文手册可知F_PCLK有两种情况:

  • PCLK1用于USART2、3、4、5。

  • PCLK2用于USART1

USART发送和接收的配置步骤

USART的配置步骤比较简单。

  1. 通信双方确定好波特率、停止位数、校验方式、字长。

  2. 通过 USART_SR.RXNE[5] 产生的中断(接收数据寄存器非空),去异步接收数据。

  3. 通过直接读写USART_DR寄存器可以实现数据的接收和发送。

  4. 需要的话,可以等待 USART_SR.TC[6] 被硬件置位,来确保发送完成。

  5. 处理中断后,一定要注意彻底清除中断相应的标志位!防止中断假触发!

USART发送和接收的代码

我的开发板硬件连接图如下,所以本实验使用USART1进行串口通信。

硬件图

并且,将PA9、PA10分别配置成推挽复用输出、浮空输入或带上拉输入。

IO复用

GPIO的配置

代码如下:

int fputc(int ch,FILE *p) {//函数默认的,在使用printf函数时自动调用USART_SendData(USART1,(u8)ch);	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);return ch;
}void LunarNVICInit(){NVIC_InitTypeDef NVIC_Cfg;// 配置系统中断分组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// CPU上开启USART的中断NVIC_Cfg.NVIC_IRQChannel = USART1_IRQn;NVIC_Cfg.NVIC_IRQChannelCmd = ENABLE;NVIC_Cfg.NVIC_IRQChannelPreemptionPriority = 2;NVIC_Cfg.NVIC_IRQChannelSubPriority = 2;NVIC_Init(&NVIC_Cfg);}void LunarInitUSART1() {GPIO_InitTypeDef GPIOA9_Cfg, GPIOA10_Cfg;USART_InitTypeDef USART1_Cfg;// PARCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);// 初始化GPIOA9为复用 (发送GPIOA9_Cfg.GPIO_Mode = GPIO_Mode_AF_PP;GPIOA9_Cfg.GPIO_Pin = GPIO_Pin_9;GPIOA9_Cfg.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIOA9_Cfg);// 初始化GPIOA10为复用 (接收GPIOA10_Cfg.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIOA10_Cfg.GPIO_Pin = GPIO_Pin_10;GPIO_Init(GPIOA, &GPIOA10_Cfg);// USART1RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);USART1_Cfg.USART_BaudRate = 115200;USART1_Cfg.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;USART1_Cfg.USART_Parity = USART_Parity_No;USART1_Cfg.USART_StopBits = USART_StopBits_1;USART1_Cfg.USART_WordLength = USART_WordLength_8b;USART1_Cfg.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_Init(USART1, &USART1_Cfg);// 接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);// 打开usartUSART_Cmd(USART1, ENABLE);
}// 中断处理程序
void USART1_IRQHandler(void) {if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {	// 接收数据中断uint16_t data = USART_ReceiveData(USART1);USART_SendData(USART1, data);while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);USART_ClearFlag(USART1, USART_FLAG_TXE);} else {// 其他中断不做处理}// 顺序去读SR和DR清楚ORE位if (USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET){USART_ReceiveData(USART1);// USART_ClearFlag(USART1, USART_FLAG_ORE); // 函数USART_ClearFlag清楚不了USART_FLAG_ORE!!!}
}int main() {// 初始化usartLunarInitUSART1();LunarNVICInit();printf("stm32 启动\n");while(1) {}return 0;}

实验结果就是上位机通过给串口发送字符串,上位机接收框出现回显的效果。


本章完结

这篇关于STM32快速入门(串口传输之USART)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/970484

相关文章

从入门到精通MySQL联合查询

《从入门到精通MySQL联合查询》:本文主要介绍从入门到精通MySQL联合查询,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下... 目录摘要1. 多表联合查询时mysql内部原理2. 内连接3. 外连接4. 自连接5. 子查询6. 合并查询7. 插入查询结果摘要前面我们学习了数据库设计时要满

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决

Linux如何快速检查服务器的硬件配置和性能指标

《Linux如何快速检查服务器的硬件配置和性能指标》在运维和开发工作中,我们经常需要快速检查Linux服务器的硬件配置和性能指标,本文将以CentOS为例,介绍如何通过命令行快速获取这些关键信息,... 目录引言一、查询CPU核心数编程(几C?)1. 使用 nproc(最简单)2. 使用 lscpu(详细信

从入门到精通MySQL 数据库索引(实战案例)

《从入门到精通MySQL数据库索引(实战案例)》索引是数据库的目录,提升查询速度,主要类型包括BTree、Hash、全文、空间索引,需根据场景选择,建议用于高频查询、关联字段、排序等,避免重复率高或... 目录一、索引是什么?能干嘛?核心作用:二、索引的 4 种主要类型(附通俗例子)1. BTree 索引(

Redis 配置文件使用建议redis.conf 从入门到实战

《Redis配置文件使用建议redis.conf从入门到实战》Redis配置方式包括配置文件、命令行参数、运行时CONFIG命令,支持动态修改参数及持久化,常用项涉及端口、绑定、内存策略等,版本8... 目录一、Redis.conf 是什么?二、命令行方式传参(适用于测试)三、运行时动态修改配置(不重启服务

MySQL DQL从入门到精通

《MySQLDQL从入门到精通》通过DQL,我们可以从数据库中检索出所需的数据,进行各种复杂的数据分析和处理,本文将深入探讨MySQLDQL的各个方面,帮助你全面掌握这一重要技能,感兴趣的朋友跟随小... 目录一、DQL 基础:SELECT 语句入门二、数据过滤:WHERE 子句的使用三、结果排序:ORDE

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

Python中OpenCV与Matplotlib的图像操作入门指南

《Python中OpenCV与Matplotlib的图像操作入门指南》:本文主要介绍Python中OpenCV与Matplotlib的图像操作指南,本文通过实例代码给大家介绍的非常详细,对大家的学... 目录一、环境准备二、图像的基本操作1. 图像读取、显示与保存 使用OpenCV操作2. 像素级操作3.