前言
UART是重要的片上资源,主流单片机基本上都有该功能,通过UART可以扩展出很多的通信接口,如RS232、RS485、LIN,甚至WIFI、蓝牙模组等,可以说只要搞通讯就会涉及到UART。下面和大家分享STM32的UART配置。
1、UART是什么
USART全称universal synchronous asynchronous receiver transmitter通用同步异步接收发送器;速率最高可达4.5Mbits/s,波特率460800;
数据按位顺序发送的串行通信接口简称串口,USART模块是采用串行通信接口最常见的模块,为了方便,就把USART简称为串口;
USART接口通过RX,TX,GND同其他设备相连;当TX引脚被禁止时,该引脚恢复GPIO的配置;当TX引脚使能且未发送数据时,该引脚处于高电平(空闲态);
USART接口的数据字长度可编程,停止位长度可编程;可配置为DMA多缓冲通信;
2、USART的帧格式
串口数据应该遵循USART帧的格式,才能被串口识别;
首先总线需要持续至少一个空闲帧,然后连续发送数据帧,数据帧与数据帧之间有时会有断开帧,断开帧后需要接1-2bit停止位,连接下个数据帧;
断开帧只能为10bit或11bit低电平的帧(CR1_SBK[0]);然后接1或2bit的高电平作为停止位,然后接下一个数据帧;
数据帧的数据字有两种格式,(1)8 bit 数据位;(2)8bit 数据位 + 1 bit 奇偶校验位;
3、USART的寄存器使用
每个USART都有7个自己的寄存器;用来配置该USART的所有功能;
有许多功能诸如硬件流控制,LIN模式,智能卡模式等,由于没用过或是用不上,实在晦涩难懂费时费力,故在此全部跳过;
以下给出了USART作为常用串口收发数据的工作框图,以及相关的寄存器配置;
3.1 工作框图
3.2 相关寄存器配置
1)首先需要配置USART的6个参数:
波特率USART_BRR,字长M,停止位STOP,校验位PCE,PS,PEIE,USART的收发模式TE和RE和硬件流控制CTSIE,CTSE,RTSE;
2)USART提供了8个中断:TXEIE, TCIE, RXNEIE, PEIE, IDLEIE, CTSIE, LBDIE, EIE;8个中断使能均可以进入USART的中断函数,根据需要配置合适的中断使能位为1;通常为RXNEIE位;
3)然后使能接收器RE和发送器TE;
4)然后使能UE中断;
4、USART的代码示例
4.1 标准库提供的常用USART接口
标准库为所有的外设都提供了封装寄存器的API接口函数,文件名为stm32f10x_peripheral.c;
以下为usart外设的常用函数;
//串口USARTx的参数配置初始化函数;
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
//使能串口,(主要是分频器和输出的设置)
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
//使能串口中断,(就是那8个中断,均可以进入中断函数)
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
//都是处理一个字节;
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
//读取SR寄存器的状态,SR的状态都是硬件设置的;
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
//读取SR寄存器和CRx控制寄存器的状态,和上面一个功能相同的;
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
//修改SR寄存器的状态,单功能通讯用不上;
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
4.2 USART1使用代码
#include "usartDemo.h"
u8 USART1_RX_BUF[256]; //接收缓存
u8 USART1_RX_CNT = 0; //接收字节计数
u8 USART1_REV_0D = 0; //收到\r
u8 USART1_REV_0A = 0; //收到\r和\n
//usart1初始化之后,便可以通过串口读写了;
void Usart1_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 0-3;
//USART1外设中断配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
//GPIO初始化 USART1_TX PA9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输出需要配置速率,输入不需要配置速率;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出,<中文..手册>8.1.11外设的GPIO配置
GPIO_Init(GPIOA, &GPIO_InitStructure);
//GPIO初始化 USART1_RX PA10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1初始化
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //CR1中的TE,RE
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//CR1中的RXNEIE中断
USART_Cmd(USART1, ENABLE); //CR1中的UE
}
void USART1_Send_Data(u8 *buf,u16 len)
{
u16 t;
for(t=0;t<len;t++)
{
USART_SendData(USART1,buf[t]);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
//发送字节完成后,TC硬件置1;
} //先读SR,后写DR清除TC位;
USART1_RX_CNT = 0;
USART1_REV_0D = 0;
USART1_REV_0A = 0;
}
void USART1_IRQHandler(void)
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
Res =USART_ReceiveData(USART1); //读DR,硬件清0 RXNE位;
USART1_RX_BUF[USART1_RX_CNT]=Res; //接收数据
USART1_RX_CNT++;
if(Res==0x0d)
USART1_REV_0D = 1;
if(USART1_REV_0D&&(Res==0x0a))
USART1_REV_0A = 1;
}
//RXNE为1,读数据的同时又来了数据,那么新的数据丢失;产生溢出错误,读完数据后RXNE为0,但ORE标志还在;
//RXNE为1,又来了数据,产生接收溢出错误,置位ORE;
if(USART_GetFlagStatus(USART1,USART_FLAG_ORE) == SET)
{
USART_ReceiveData(USART1);
// USART_ClearFlag(USART1,USART_FLAG_ORE);//先读SR,后读DR,可以复位ORE位;应该不用软件清除了;
}
// USART_ClearFlag(USART1,USART_IT_RXNE); //读DR可以清除RXNE,应该不用软件清除了;
}
int main(void)
{
Usart1_Init(460800);
while(1)
{
if(USART1_REV_0A)
{
USART1_Send_Data(USART1_RX_BUF,USART1_RX_CNT);
}
}
}
4.2.1 在前面代码的基础上不使用串口中断,直接通过SR状态位来判断数据的收发;
将上面代码的usart1初始化代码中CR1的RXNEIE配置行注释掉,然后修改main函数如下即可;
int main(void)
{
Usart1_Init(460800);
while(1)
{
if ((USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET))
{
USART1_RX_BUF[USART1_RX_CNT] = USART_ReceiveData(USART1);
if(USART1_RX_BUF[USART1_RX_CNT]==0x0a)
USART1_REV_0A = 1;
USART1_RX_CNT++;
}
if(USART1_REV_0A)
{
USART1_REV_0A = 0;
for(int i=0;i<USART1_RX_CNT;i++)
{
USART_SendData(USART1, USART1_RX_BUF[i]);
while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
}
USART1_RX_CNT=0;
}
}
}
5、总结
USART的功能没想到还挺多的,寄存器看起来就有些费时了,很多概念都是新的,不好理解,直接拉低了效率;于是觉得这样不行,应该用什么看什么,用到再看,学海无涯,精力有限;
另外人家费心费力写好标准库不就是为了帮开发人员省时间吗?了解一下即可,以后没必要深入;
本文代码github:https://github.com/caesura-k/stm32f1_usart
来源:玩转嵌入式
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。