六、 USART 同步/异步串口
6.1 基本介绍
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)是STM32内部集成的硬件外设,用于实现通用同步/异步串行通信。USART不仅可以作为全双工通信接口,还支持多种通信模式,包括同步和异步通信。这使得STM32能够与其他设备(如单片机、电脑、模块等)进行高效的数据传输。
USART与UART的区别
- USART:支持同步和异步通信,功能更为强大。
- UART(Universal Asynchronous Receiver/Transmitter):仅支持异步通信,是USART的一个子集。
在异步通信模式下,USART和UART的功能基本相同,因此当进行异步通信时,两者可以互换使用。
6.2 USART的通信方式
USART采用异步串行全双工通信方式,即通信双方能够同时进行数据的发送和接收。在通信过程中,USART通过TX(发送)和RX(接收)两个引脚实现数据的传输。
数据帧格式
USART的数据帧格式包括起始位、数据位、校验位(可选)和停止位。常见的数据帧格式如下:
- 起始位:1位,固定为低电平,用于标志数据帧的开始。
- 数据位:通常为8位或9位,表示实际传输的数据。
- 校验位:可选,用于数据验证,可以是奇校验、偶校验或无校验。
- 停止位:0.5位、1位、1.5位或2位,用于标志数据帧的结束。
6.3 USART的配置
串口控制器框图
STM32的USART控制器包括发送部分、接收部分、波特率生成部分和通信协议控制部分。发送部分负责将内核写入的数据通过TX引脚发送出去;接收部分负责从RX引脚接收数据并存储到内核中。波特率生成部分用于生成通信所需 的时钟信号,确保双方通信速率一致。
寄存器描述
- 状态寄存器(USART_SR):指示USART控制器的运行状态,如发送完成(TC)和接收数据寄存器非空(RXNE)等。
- 数据寄存器(USART_DR):包括发送数据寄存器(只读)和接收数据寄存器(只写),用于数据的读写操作。
- 波特率寄存器(USART_BRR):用于设置USART的波特率,通过计算并写入相应的值来控制通信速率。
- 控制寄存器1(USART_CR1):用于配置USART的基本功能,如字长、发送使能、接收使能等。
- 控制寄存器2(USART_CR2):用于配置USART的高级功能,如停止位长度等。
程序设计思路
- 初始化GPIO:将TX和RX引脚配置为复用推挽输出和浮空输入(或上拉输入)。
- 初始化USART:配置USART的波特率、字长、停止位、校验位等参数。
- 编写发送和接收函数:实现数据的发送和接收功能。
6.4 示例代码
非中断初始化函数
c
#include "myserial.h"
// usart1 PA9\TX PA10\RX
void MySerial_Init(void)
{
// 使能RCC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);
// 配置GPIO,输出为复用输出,输入为浮空输入或者为上拉输入
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//初始化USART
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 9600;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
USART_InitStruct.USART_Parity = USART_Parity_No;// 不开启奇偶校验位
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 一位停止位
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART_InitStruct);
// 使能串口
USART_Cmd(USART1,ENABLE);
}
串口发送函数
c
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1,Byte);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}
// 发送数组
void Serial_SendArray(uint8_t *Array,uint16_t Length)
{
uint16_t i;
for(i = 0; i < Length;i++)
{
Serial_SendByte(Array[i]);
}
}
// 发送字符串
void Serial_SendString(char *String)
{
uint16_t i;
for(i = 0;String[i] != 0;i++)
{
Serial_SendByte(String[i]);
}
}
// 发送数字字符串
uint32_t Serial_Pow(uint32_t x,uint32_t y)
{
uint32_t result = 1;
while(y--)
{
result *= x;
}
return result;
}
void Serial_SendNumber(uint32_t Number,uint8_t Length)
{
uint16_t i;
for(i = 0; i < Length;i++)
{
Serial_SendByte(Number/Serial_Pow(10,Length - i - 1) % 10 + '0');
}
}
// 改写fputc函数
int fputc(int ch,FILE *f)
{
Serial_SendByte(ch);
return ch;
}
// 可变参数的利用
void Serial_Printf(char *format,...)
{
char String[100];
va_list arg; // 参数列表变量
va_start(arg,format); // 从format之后开始接收参数列表,存放在arg
vsprintf(String,format,arg);
va_end(arg);
Serial_SendString(String);
}
中断初始化函数
c
void MySerial_Init(void)
{
// 使能RCC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);
// 配置GPIO,输出为复用输出,输入为浮空输入或者为上拉输入
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//初始化USART
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 9600;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
USART_InitStruct.USART_Parity = USART_Parity_No;// 不开启奇偶校验位
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 一位停止位
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART_InitStruct);
// 开启中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
// 配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
// 使能串口
USART_Cmd(USART1,ENABLE);
}
中断接收函数
c
uint8_t Serial_RxData;
uint8_t Serial_RxFlag = 0;
uint8_t Serial_GetRxData(void)
{
return Serial_RxData;
}
uint8_t Serial_GetRxFlag(void)
{
if(Serial_RxFlag)
{
Serial_RxFlag = 0;
return 1;
}else
{
return 0;
}
}
void USART1_IRQHandler(void)
{
if(USART_GetFlagStatus(USART1,USART_IT_RXNE) == SET)
{
Serial_RxData = USART_ReceiveData(USART1);
Serial_RxFlag = 1;
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
接收数据包
模拟状态机的原理,实现接收固定长度的数字包的接收功能
c
void USART1_IRQHandler(void)
{
static uint8_t RxStatus = 0;
static uint8_t PRxdata = 0;
if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
{
Serial_RxData = USART_ReceiveData(USART1);
switch(RxStatus)
{
case 0:
if(Serial_RxData == 0xFF)
{
PRxdata = 0;
RxStatus++;
}
break;
case 1:
Serial_RxPacket[PRxdata] = Serial_RxData;
PRxdata++;
if(PRxdata >= 4)
RxStatus++;
break;
case 2:
if(Serial_RxData == 0xFE)
{
RxPacketFlag = 1;
RxStatus = 0;
}
break;
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
文本数据包的接收
该数据包需要以@符号开头,结尾检测\r\n
c
void USART1_IRQHandler(void)
{
static uint8_t RxStatus = 0;
static uint8_t PRxdata = 0;
if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
{
Serial_RxData = USART_ReceiveData(USART1);
switch(RxStatus)
{
case 0:
if(Serial_RxData == '@' && RxPacketFlag == 0)
{
RxStatus++;
PRxdata = 0;
memset(Serial_RxPacket,'\0',sizeof(Serial_RxPacket)/sizeof(Serial_RxPacket[0]));
}
break;
case 1:
Serial_RxPacket[PRxdata] = Serial_RxData;
PRxdata++;
if(Serial_RxData == '\r')
RxStatus++;
break;
case 2:
if(Serial_RxData == '\n')
{
RxPacketFlag = 1;
Serial_RxPacket[PRxdata] = '\0';
RxStatus = 0;
}
break;
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}