STM32学习笔记系列:第三章 串口通信(UART)详解
由于现在学习的是以寄存器库为基础来实现功能,所以以后的知识点和代码都是以寄存器库来配置的,还有目前现在用的板子是STM32F407VET6,该笔记只是自己的见解和理解,大佬勿喷。
4.1 通信协议的介绍
UART串口:通信协议(通信接口)
什么是通信协议:一个已经固定规则,两个或者两个以上的器件想要正常通信,器件之间必须要遵循规则,这个规则就是通信协议
通信协议特点:(通信协议名词补充)
1.同步与异步
同步:步调一致(两个器件之间通信速度相同) 异步:通信速度不一样(两个器件之间通信速度不相同)
同步:两个设备/器件之间步调一致,两个设备之间有一根时钟线进行连接,同步两个器件之间的通信速度、决定两个器件之间数据传输方式(IIC、SPI等)
异步:两个设备/器件之间步调不一致,没有时钟线相连,需要保证两个器件之间的通信速度相同,两个设备/器件之间才能正常,需要分别规定两个器件之间通信速度,保证两个器件之间通信速度相同
(UART异步通信 — 波特率)
总结:同步通信:有时钟线连接,并且时钟线可以控制两个设备之间的速度,让速度保持一致
异步通信:没有时钟线连接,需要用户自行规定两个设备的数据传输速度
2.单工、半双工、全双工
单工、半双工、全双工:针对两个设备/器件之间数据发送(数据线数量、传输数据方向有关)
单工:两个器件/设备之间,在相同环境下,器件A与器件B在整一个传输过程中,器件A只能发送数据到器件B,传输方向单一(数据传输方向为单向传输)
半双工:两个器件/设备之间,在相同环境下,器件A与器件B在整一个传输过程中,在这一时刻器件A发送数据到器件B,下一时刻器件B也能发送数据器件A,半双工传输方向可以改变,在特定条件下,数据传输方向需要进行切换,传输方向可变
总结:单工、半双工它们只有一根数据线
全双工:两个器件/设备之间,在相同环境下,器件A与器件B在整一个传输过程中,器件A既能发送数据到器件B ,器件B也能发送数据到器件A,会有两根数据线,一根数据线负责发送数据、宁外一根负责接收
说明:一般全双工通信都会有发送端(TX/TXD)和接收端(RX/RXD)
会有两根数据线负责数据传输 发送要比接收先
3.串行与并行
数字量由0/1组成,数据一般以字节为单位,1byte = 8bit
串行:独木桥,人需要按照顺序一个接一个通过,数据需要一个一个进行发送(不能同时发送多个数据)
优点:简单,代码好写并不容易出错
缺点:传输速度较慢
并行:高速,车道,能同时并排走很多辆车,数据可以同时发送多个
优点:传输速度快
缺点:较复杂、代码不好写并数据容易出错
4.现场总线/板级总线
现场总线:工业控制现场总线(可远距离传输数据) — 长距离传输
如:485总线—千米级别 UART
can总线—十千米级
板级总线:芯片之间通信(距离长会被干扰) — 短距离传输
如:IIC/SPI/8080并行
5.有线通信/无线通信
有线:可靠性强 传输距离远:线的长度
UART、IIC、SPI、CAN等
无线:方便 传输距离短
GSM(2G) 蓝牙(BLE) 433(500m) 2.4G wifi频宽:2.4Ghz 5Ghz Lora(低功耗)
6.通信配置方式:UART
配置控制器通信:(串口控制器 — 实现MCU与PC之间通信) 配置USART控制器 配置USART寄存器
根据寄存器去配置器件 不需要去理解时序图
优点:简单 UART 缺点:移植性较差
IO模拟通信:(IIC — IO模拟方式去写代码 SPI UART)
需要用户去理解时序图(将时序图翻译成代码) — 初学者比较难
优点:移植性强
4.2 UART概述
4.2.1什么是UART& UART的作用
UART: 通用串行数据总线
例子:wifi与MCU通信、PC电脑与MCU通信、传感器与MCU通信、MCU与MCU通信
注意:通信是产品的基础,也是核心
UART是一种通讯协议,一个标准的通信协议,这是一个固定的通信协议,我们需要遵循这个通讯协议,才能进行器件间的通信
4.2.2UART通信原理
串口拓扑图:
-
UART通信方式:异步全双工串行通信
4.3.3 UART数据帧格式(重点)
数据帧:UART完整一帧数据
数据帧:起始位 + 数据位 + 校验位 + 停止位
起始位:占1bit,表示一帧数据的开始
数据位: 占5 – 8bit(可选)
校验位:用来校验发送或者接收的数据
UART的校验方式:奇偶校验 –> 奇校验 / 偶校验 在UART不会去使用
停止位:占0.5bit – 2bit(可选),表示一帧数据结束
假设器件A需要接收数据:0101 1110,使用奇校验 1001 1110
①校验数据里面'1'的个数 5个
②如数据中‘1’的个数为奇数的话,校验则返回‘0’,如数据中‘1’的个数为偶数,校验则返回‘1’
偶校验同理
一般UART通信不使用奇偶校验:
①验证错误率低
②无法知道错误的数据位
③减少代码流程
4.3.4 UART四要素(非常重要)
只要清楚四要素的作用和学会如何配置四要素,就能完成UART初始化代码
需要再哪里找到四要素: PC端上位机
波特率:决定数据传输速度 在STM32中常用9600 115200
9600:一秒传输9600个比特位(1s传输9600bit数据)
停止位:XCOM中1 – 1.5bit可选,在STM32中1bit、2bit可选
数据位:传输数据的长度,8bit
校验位:奇偶校验 ,一般不使用
4.3 STM32F407VET6的UART
STM32串口控制器:USART — 片上外设
芯片片上外设介绍 —》请参考中文参考手册
UART :异步串口 –> USART:同步异步串口
4.3.1 简介
通用同步异步收发器 (USART) 能够灵活地与外部设备进行全双工数据交换,满足外部设备对工业标准 NRZ 异步串行数据格式的要求。满足:异步串行全双工通信 ,USART 通过小数波特率发生器提供了多种波特率。(计算)
它支持同步单向通信和半双工单线通信;还支持 LIN( 局域互连网络)、智能卡协议与 IrDA(红外线数据协会) SIR ENDEC 规范,以及调制解调器操作 (CTS/RTS)。而且,它还支持多处理器通信。
通过配置多个缓冲区使用 DMA 可实现高速数据通信。 DMA数据搬运工
4.3.2串口控制器框图(重要)
中文参考手册 第26章 功能说明
控制器引脚部分: TX:数据发送口(GPIO口) RX:数据接收口 (GPIO口)
nCTS:
清除以发送(Clear To Send),n 表示低电平有效。如果使能 CTS 流控制,
发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,
如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。
nRTS:
请求以接收(Request To Send),n 表示低电平有效。如果使能 RTS 流控制,
当 USART 接收器准备好接收新数据时就会将 nRTS 变成低电平;当接收寄存器已满时,
nRTS 将被设置为高电平。该引脚只适用于硬件流控制。
nCTS和nRTS这两个管脚会占用IO口,一般不使用硬件流传输控制
数据传输框图部分
发送数据过程:
写入到发送数据寄存器(TDR)里面,数据进入到发送数据寄存器后自动载入发送位移寄存器,再通过发送位移寄存器将数据一位一位发送到外界 USART1->DR = data;
接收数据过程:
接收位移寄存器接收一位一位数据,接收位移寄存器将一位一位数据自动载入接收数据寄存器里面,如果想获取接收到的数据,则读取接收数据寄存器里面的值 , data = USART1->DR;
DR(数据寄存器) :
因为数据寄存器包含两个寄存器,一个用于发送 (TDR),一个用于接收 (RDR),因此它具有双重功能(读和写)
当发送数据时,TDR开始工作 USART1->DR = data;
当接收数据时,RDR开始工作 data = USART1->DR;
通信协议控制部分:
看寄存器的描述 — 中文参考手册
波特率框图部分:
波特率计算: 需要将波特率计算出来并把结果写入USART_BRR寄存器
如需要设置波特率,则需要设置DIV_F和DIV_M,利用公式计算DIV_F和DIV_M
Tx/Rx波特率:已知条件
fck:总线的频率 USART1需要对应总线频率 — APB2 84Mhz 已知条件
OVER8:代表过采样 已知 0 :16倍过采样 / 1:8倍过采样
USARTDIV:未知量
USARTDIV = Fck / 8*(2 – OVER8) / 波特率
DIV_F:相当于USARTDIV小数部分(注意并不是直接把小数提取)
DIV_F = (USARTDIV – DIV_M)*8*(2 – OVER8)
DIV_M:相当于USARTDIV的整数部分(提取整数)
小数部分计算公式:
DIV_F = (USARTDIV – DIV_M)*8*(2 – OVER8)
4.4 串口控制器相关寄存器
状态寄存器 : 涉及标志位
控制寄存器:配置某个器件寄存器(初始化外设)
状态寄存器 (USART_SR)
位 7 TXE:发送数据寄存器为空 (Transmit data register empty)
当 TDR 寄存器的内容已传输到移位寄存器时,该位由硬件置 1。如果 USART_CR1 寄存器
中 TXEIE 位 = 1,则会生成中断。通过对 USART_DR 寄存器执行写入操作将该位清零。
0:数据未传输到移位寄存器 ,表示此时不可以发送数据
1:数据传输到移位寄存器,表示此时可以发送数据
硬件:自动置1
软件:手动置1
位 6 TC:发送完成 (Transmission complete)
如果已完成对包含数据的帧的发送并且 TXE 置 1,则该位由硬件置 1。如果 USART_CR1 寄存
器中 TCIE = 1,则会生成中断。该位由软件序列清零(读取 USART_SR 寄存器,然后写入
USART_DR 寄存器)。TC 位也可以通过向该位写入‘0’来清零。建议仅在多缓冲区通信
时使用此清零序列。
0:传送未完成
1:传送已完成
代表发送数据完成,此位自动置1
位 5 RXNE:读取数据寄存器不为空 (Read data register not empty)
当 RDR 移位寄存器的内容已传输到 USART_DR 寄存器时,该位由硬件置 1。如果
USART_CR1 寄存器中 RXNEIE = 1,则会生成中断。通过对 USART_DR 寄存器执行读入
操作将该位清零。RXNE 标志也可以通过向该位写入零来清零。建议仅在多缓冲区通信时使
用此清零序列。
0:未接收到数据
1:已准备好读取接收到的数据
数据寄存器 (USART_DR)
波特率寄存器 (USART_BRR)
控制寄存器 1 (USART_CR1)
位 15 OVER8:过采样模式 (Oversampling mode)
0:16 倍过采样
1:8 倍过采样
位 13 UE:USART 使能 (USART enable)
该位清零后,USART 预分频器和输出将停止,并会结束当前字节传输以降低功耗。此位由软
件置 1 和清零。
0:禁止 USART 预分频器和输出
1:使能 USART
注意:器件使能一般在硬件初始化最后
位 12 M:字长 (Word length)
该位决定了字长。该位由软件置 1 或清零。
0:1 起始位,8 数据位,n 停止位
1:1 起始位,9 数据位,n 停止位
第9位:校验位
位 3 TE:发送器使能 (Transmitter enable)
该位使能发送器。该位由软件置 1 和清零。
0:禁止发送器
1:使能发送器
位 2 RE:接收器使能 (Receiver enable)
该位使能接收器。该位由软件置 1 和清零。
0:禁止接收器
1:使能接收器并开始搜索起始位
控制寄存器 2 (USART_CR2)
位 13:12 STOP:停止位 (STOP bit)
这些位用于编程停止位。
00:1 个停止位
01:0.5 个停止位
10:2 个停止位
11:1.5 个停止位
USART属于片上外设
如果片上外设需要与外界建立通信需要借助GPIO
USART –> PC
4.5 IO口的复用功能如何配置
复用
USART:片上外设 –》 IO 需要跟外界通信
USART1_TX USART1_RX — 复用到对应IO
说明:
每个IO口都有自己固定的复用功能,并不是每个IO口都可以复用任何功能
如何知道IO口具体的复用功能————查表(数据手册)
如何配置复用功能
①确定IO(根据具体的复用功能确定IO)
根据自己需要的复用功能去查看哪个管脚支持这个复用功能(STM32F407VET6) —— 通常通过原理图就可以确定/数据手册
USART1_TX — PA9 — 复用模式
USART1_RX — PA10 — 复用模式
②确定低位复用寄存器和高位复用寄存器的位
看复用功能高位寄存器 PA9:[7-4] PA10:[11-8]
③确定标识
中文参考手册 7.3.2 GPIO引脚复用器和映射
USART1对应标识为AF7
PA9:[7-4] –> AF7
④确定数值 AF7 : 7
去查看具体寄存器下面的说明(中文参考手册)
GPIOA-> AFRH |= 7 << 4; //将PA9复用到USART1
GPIOA->AFRH |= 7 << 8; //将PA10复用到USART1
注意:低位复位寄存器和高位复位寄存器调用方式
例子:
USART1_TX
确定IO:PA9
确定复用寄存器的位: 4 – 7
确定标识:AF7
确定写入值: 7
GPIOA-> AFRH |= 7 << 4; //将PA9复用到USART1
4.6 USAR初始化
4.6.1 软件设计
USART1初始化函数:
步骤:①分析原理图
USART1_TX — PA9 — 复用模式
USART1_RX — PA10 — 复用模式
②代码流程
USART1初始化函数
{
//打开GPIOA的时钟 — AHB1
//配置PA9和PA10位复用模式
//配置复用关系(复用功能寄存器)
//打开USART1时钟 — APB2
//初始化USART1
//CR1
//CR2
//利用公式计算DIV_M、DIV_F
//将DIV_M、DIV_F写入BRR寄存器
//使能USART1
}
4.6.2 printf重定向方法
printf —> fputc —> stdio.h
printf(“hello world”);
步骤:
①包含#include “stdio.h”,点击编译
②Ctrl键 + F进入搜索替换工具
③找到fputc函数的外部声明
④调用fputc函数进行修改
注意:STM32中printf换行需要”\r\n”
4.6.3 代码设计
usart.c
#include "usart1.h"
/*
函数功能:USART1初始化
返回值:void
形参:u32 bps 波特率
作者:jerry
版本:1.0
函数说明:
USART1_TX --- PA9 --- 复用模式
USART1_RX --- PA10 --- 复用模式
低位寄存器:GPIOA->AFR[0]
高位寄存器:GPIOA->AFR[1]
*/
void Usart1_Init(u32 bps)
{
float USARTDIV = 0;
u32 DIV_M ,DIV_F;
//1、打开GPIOA、USART1时钟
RCC->AHB1ENR |= (1 << 0);
RCC->APB2ENR |= (1 << 4);//打开USART1时钟
//2、PA9、PA10初始化
GPIOA->MODER &=~(0xf << 18);//清零
GPIOA->MODER |= (0xa << 18);//将PA9和PA10配置为复用模式
//3、配置复用关系(IO映射)
GPIOA->AFR[1] |= 7 << 4; //将PA9复用到USART1
GPIOA->AFR[1] |= 7 << 8; //将PA10复用到USART1
//4、USART1初始化
USART1->CR1 &=~ (1 << 15); //16倍过采样:OVER8 = 0
USART1->CR1 &=~ (1 << 12); //数据长度为8bit
USART1->CR1 |= (1 << 3); //发送器使能
USART1->CR1 |= (1 << 2); //接收器使能
USART1->CR2 &=~(3 << 12); //停止位为1bit
//5、计算波特率
USARTDIV = 84000000 / 16.0 / bps;
DIV_M = (u32)USARTDIV; //整数部分
DIV_F = (USARTDIV-DIV_M) * 16; //小数部分
USART1->BRR |= DIV_M << 4 | DIV_F; //设置BRR寄存器
//6、使能接收中断和空闲中断
USART1->CR1 |= (1 << 5); //使能接收中断 RXNE
USART1->CR1 |= (1 << 4); //空闲接收中断 IDLE
//7、配置NVIC
//计算编码值 占先:1 次级:1
u32 pri=NVIC_EncodePriority (5, 1, 1);
//设置优先级
NVIC_SetPriority(USART1_IRQn, pri);
//使能中断源
NVIC_EnableIRQ(USART1_IRQn);
//使能USART1
USART1->CR1 |= (1 << 13); //使能USART1
}
/*
函数功能:USART1发送1byte数据函数
返回值:void
形参:u8 data 发送1byte数据
作者:jerry
版本:1.0
函数说明:
①等待数据可以发送 --- 标志位(SR寄存器)等待1
②发送数据
*/
void usart1_send_byte(u8 data)
{
//①等待数据可以发送 --- 标志位(SR寄存器)等待1
while(!(USART1->SR & (1 << 7)));
//②发送数据
USART1->DR = data;
}
/*
函数功能:USART1接收1byte数据函数
返回值:u8 接收到的1byte数据
形参:void
作者:jerry
版本:1.0
函数说明:
①等待数据可以接收 --- 接收到数据后置一
②接收数据
*/
u8 usart1_rec_byte(void)
{
//①等待数据可以接收 --- 接收到数据后置一
while(!(USART1->SR & 1 << 5));//没有接收到数据时等待(0),接收到数据后跳出(1)
//②接收数据
return USART1->DR;
}
/*
printf重定向 printf("1234567"); ---> XCOM
注意:printf重定向函数,不需要声明和调用
*/
int fputc(int c, FILE * stream)
{
usart1_send_byte(c);
return c;
}
usart.h
#ifndef _USART1_H
#define _USART1_H
#include "stm32f4xx.h"
#include "stdio.h"
void Usart1_Init(u32 bps);
void usart1_send_byte(u8 data);
u8 usart1_rec_byte(void);
#endif
以上就是我对USAT的一点拙见,由于我现在用的板子是STM32F407VET6,所以后面的代码和图片都是基于这块板子的。后面会继续更新相关我的STM32的学习之路。
作者:繁华烟雨