STM32——USART原理及应用
STM32——USART原理及应用
USART是STM32微控制器中一个重要的通信外设,用于实现设备间的数据传输。本文将从USART的基本概念、硬件结构、软件配置等多个维度,深入讲解USART的工作原理及应用方法。
1.什么是USART?
1.1 基本概念
USART英文全称:universal asynchronous receiver and transmitter ,翻译过来就是:通用同步异步收/发器。USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX(Transmit Exchange)引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里。简单来说USART就是一个STM32的外设,用于设备之间传输数据,常见的通信外设还有IIC、SPI等。
1.2 USART实物图
1.3USART简约框图
TX(Transmit):传输线路,负责发送数据。连接到其他设备的接收端
RX(Receive):接收线路,负责接收数据。连接到其他设备的发送端
GND(Ground):地线,电路的公共参考点,确保信号的正常传输和电路的正常工作
VCC:电源输入,提供电源给USART模块或相关的电路设备,通常是正电压(如5V或3.3V)
1.5USART发送接收方式
可以通过触发中断发送接收
可以利用DMA搬运发送接收
2.USART硬件部分
2.1USART外设
1.自带波特率发生器,最高达4.5Mbits/s
2.可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
3.可选校验位(无校验/奇校验/偶校验)
4.支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
5.支持DMA
6.数据传输是低位先行
7.USART通信:异步、全双工通信
8.支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
什么是硬件流控制?-- 硬件流控制:比如A设备有个TX向B设备的RX发送数据,A设备发送的快,B设备处理不过来,如果没有硬件流控制,那么B设备只能抛弃新数据或者覆盖原来的数据,如果有硬件流控制,在硬件电路上,会多出一根线,如果B设备没准备好接收,就置高电平,如果准备好了,就置低电平。A接受了B反馈的信号,就只会在B准备好的时候,才发送数据;如果B没准备好,数据就不会发送出去。这就是硬件流控制,可以防止因为B处理慢而导致数据丢失问题。
注:UART只有异步传输功能,所以没有SCLK、nCTS、nRTS功能引脚(这是UART与USART的区别)
2.USART框图
USART框图非常重要,掌握USART框图,就能比较深入的理解USART的工作原理
1线路是USART接收数据的流程:数据从RX接收数据引脚进入→接收移位寄存器(该寄存器的作用是将数据一位一位的移到接收数据寄存器RDR中),利用USART_ReceiveData()函数可以直接读取出接收数据寄存器RDR的数据。
2线路是驱动接收移位寄存器工作:RE→接收控制器→接收移位寄存器
3线路是USART发送数据的流程:数据从发送数据寄存器TDR→发送移位寄存器→TX发送引脚
利用USART_SendData()函数可以将要发送的数据放在TDR发送寄存器中
4线路是驱动发送移位寄存器工作:TE→发送控制器→发送移位寄存器
5与6(TXEIE、RXNEIE)分别是使能发送中断使能、接收中断使能
7(TE、RE)分别是发送使能、接收使能
8与9(TXE、RXNE)分别是发送寄存器空、接收寄存器非空,这两个事件均可申请进入USART中断
发送寄存器空:表明发送寄存器空闲,可通知MCU向TDR写入数据
接收寄存器非空:表明已经接收到了数据,进入中断后可通知MCU读取RDR里的数据
注:发送数据寄存器TDR,接收数据寄存器RDR,这两个寄存器占用同一个地址,在程序上只表现为一个寄存器,就是数据寄存器DR。但实际硬件中是分成了两个寄存器
SW_RX IRDA_OUT IRDA_IN是只能卡与lrDA通信的引脚。
3.USART软件部分
3.1USART基本参数
•波特率:串口通信的速率(如:规定每隔1ms发送1位等)每秒传送码元的个数。
决定了每隔多久发送一位。
•起始位:标志一个数据帧的开始,固定为低电平(空闲时为高电平)
•数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
•校验位:用于数据验证,根据数据位计算得来
•停止位:用于数据帧间隔,固定为高电平
3.2USART串口时序图
3.3USART结构体详解
USART初始化的结构体一般用来配置:USART传输的速率、一帧的长度、是否带校验位、USART模式等等
typedef struct {
uint32_t USART_BaudRate; // 波特率
uint16_t USART_WordLength; // 字长
uint16_t USART_StopBits; // 停止位
uint16_t USART_Parity; // 校验位
uint16_t USART_Mode; // USART模式
uint16_t USART_HardwareFlowControl; // 硬件流控制
} USART_InitTypeDef;
USART_BaudRate: 波特率设置。一般设置为2400、9600、19200、115200。标准库函数会根据设定值计算得到USARTDIV值,从而设置USARTBRR寄存器值。
USART_WordLength: 数据帧字长,可选8位或9位。它设定USARTCR1寄存器的M位的值。如果没有使能奇偶校验控制,一般使用8数据位;如果使能了奇偶校验则一般设置为9数据位。
USART_StopBits: 停止位设置,可选0.5个、1个、1.5个和2个停止位,它设定USARTCR2寄存器的STOP[1:0]位的值,一般我们选择1个停止位。
USART_Parity: 奇偶校验控制选择,可选USARTParityNo(无校验)、USARTParityEven(偶校验)以及USARTParityOdd(奇校验),它设定USARTCR1寄存器的PCE位和PS位的值。
USART_Mode: USART模式选择,有USARTModeRx和USARTModeTx,允许使用逻辑或运算选择两个,它设定USARTCR1寄存器的RE位和TE位。就是发送模式或接受模式。
USART_HardwareFlowControl: 硬件流控制选择,只有在硬件流控制模式才有效,可选有使能RTS、使能CTS、同时使能RTS和CTS、不使能硬件流。
当使用同步模式时需要配置SCLK引脚输出脉冲的属性,标准库使用一个时钟初始化结构体USART_ClockInitTypeDef来设置,该结构体内容也只有在同步模式才需要设置。
3.4 USART结构体配置
void Serial_Init(void)
{
//第一步:开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//第二步:初始化GPIO引脚 USART1的TX引脚复用到PA9,RX引脚复用到PA10
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//第三步:初始化USART,9600波特率,无校验,1位停止位,无流控,只发送模式。
USART_InitTypeDef USART_InitStructure;
//波特率
USART_InitStructure.USART_BaudRate = 9600;
//硬件流控制
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
//串口模式
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
//校验位
USART_InitStructure.USART_Parity = USART_Parity_No;
//停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
//字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
//配置中断
//开启RXNE标志位到NVIC的输出
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//优先级分组
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
//如果RXNE标志位为1,则向NVIC申请中断,就可以在中断中接收数据
USART_Cmd(USART1,ENABLE);
}
3.5USART发送与接收数据
//发送数据的函数,调用这个函数就可以从TX引脚发送一个字节的数据
void Serial_SendByte(uint8_t Byte)
{
//Byte变量写入到TDR(发送数据寄存器)
USART_SendData(USART1,Byte);
//等待,TDR标志位是否为1(标志位是否空闲)
while (USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}
//接收数据
uint8_t Serial_GetRxData(void)
{
return Serial_RxData;
}
//接收中断
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
{
Serial_RxData = USART_ReceiveData(USART1);
Serial_RxFlag = 1;
USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除RXNE,因为USART_DR的读操作可以将RXNE标志位清零,但是防止没有读操作,因此手动清除一次,保险一点。
}
}
//main函数
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Serial.h"
uint8_t RxData;
int main(void)
{
OLED_Init();
Serial_Init();
//OLED_ShowNum(1,1,1,1);
OLED_ShowString(1,1,"RxData:");
while(1)
{
if (Serial_GetRxFlag() == 1) //中断中接收到了数据
{
RxData = Serial_GetRxData(); //将接收的数据转运到RxData
Serial_SendByte(RxData); //发送xData
OLED_ShowHexNum(1,8,RxData,2);
}
}
}
本文原文来自CSDN