1.USART初始化流程
<1>建立GPIO、USART、NVIC初始化结构体
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
<2>打开对应的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
<3>配置结构体,完成初始化
/*GPIO_InitStructure、NVIC_InitStructure结构体配置 略*/
USART_InitStructure.USART_BaudRate = 115200; //波特率
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; //串口模式
USART_Init(USART1, &USART_InitStructure); //初始化
<4>使能串口接收中断,使能串口
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //中断使能
USART_Cmd(USART2, ENABLE); //定时器使能
2.USART初始化结构体
typedef struct
{
uint32_t USART_BaudRate; /*此成员配置USART通信波特率*/
uint16_t USART_WordLength; /*指定帧中传输或接收的数据位的个数*/
uint16_t USART_StopBits; /*指定传输的停止位数*/
uint16_t USART_Parity; /*指定奇偶模式校验模式*/
uint16_t USART_Mode; /*启用或禁用接收或发送模式*/
uint16_t USART_HardwareFlowControl; /*是否启用硬件流控制模式*/
} USART_InitTypeDef;
/*传输或接收的数据位的个数宏定义*/
#define USART_WordLength_8b ((uint16_t)0x0000)
#define USART_WordLength_9b ((uint16_t)0x1000)
/*停止位的宏定义*/
#define USART_StopBits_1 ((uint16_t)0x0000)
#define USART_StopBits_0_5 ((uint16_t)0x1000)
#define USART_StopBits_2 ((uint16_t)0x2000)
#define USART_StopBits_1_5 ((uint16_t)0x3000)
/*/*奇偶校验位的宏定义*/*/
#define USART_Parity_No ((uint16_t)0x0000)
#define USART_Parity_Even ((uint16_t)0x0400)
#define USART_Parity_Odd ((uint16_t)0x0600)
/*启用或禁用接收或发送的模式*/
#define USART_Mode_Rx ((uint16_t)0x0004)
#define USART_Mode_Tx ((uint16_t)0x0008)
/*硬件流控制宏定义*/
#define USART_HardwareFlowControl_None ((uint16_t)0x0000)
#define USART_HardwareFlowControl_RTS ((uint16_t)0x0100)
#define USART_HardwareFlowControl_CTS ((uint16_t)0x0200)
#define USART_HardwareFlowControl_RTS_CTS ((uint16_t)0x0300)
一般都是配置为9600或者115200的波特率,8位数据位,1位停止位,不使用硬件流控制,启用发送与接收模式,使能接收中断,使能串口。
3.USART发送与中断接收例程
#include "usart.h"
static void usart_1_config(void);
void usatt_1_init(void);
void usart_SendString(uint8_t *str);
static void usart_1_config(void)
{
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_InitStructure.USART_BaudRate = 115200; //波特率
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; //串口模式
USART_Init(USART1, &USART_InitStructure); //初始化
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //中断使能
USART_Cmd(USART2, ENABLE); //串口使能
}
void usatt_1_init(void)
{
usart_1_config();
}
/*发送字符串函数*/
void usart_SendString(u8 *str)
{
u8 i=0;
do{
USART_SendData( USART2, str[i]);/*库函数,用于发送8位的数据*/
while(!USART_GetFlagStatus( USART2, USART_FLAG_TXE));/*等待发送寄存器为空*/
i++;
}while(str[i]!=0);
}
/*串口接收字符串函数*/
void USART2_IRQHandler(void)
{
__IO uint8_t temp;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)/*检测是否接收寄存器是否为空*/
{
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
temp= USART_ReceiveData(USART2);
}
}
注:1、在串口接收中断中一定要清除中断标志位,不然可能造成串口中断一直被响应,造成程序卡死。
2、以上程序仅仅是串口最常见的应用。串口作为一种广泛应用的通信协议,有着广泛的用途。
4.4种接串口中断的接收数据的实现方式
<1>最基本串口中断的接收数据的实现方式
void USART1_IRQHandler(u8 GetData)
{
u8 BackData;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除中断标志.
GetData = UART1_GetByte(BackData); //GetData=USART1->DR;也行
USART1_SendByte(GetData); //发送数据
GPIO_SetBits(GPIOE, GPIO_Pin_8 ); //LED闪烁,接收成功发送完成
delay(100); /*此处延时最好不要用SysTick定时器完成,可以用软件延时*/
GPIO_ResetBits(GPIOE, GPIO_Pin_8 );
}
}
最基本串口中断的接收数据的实现方式,将数据接收完成后又发送出去,接收和发送在中断函数里执行,main函数里无其他要处理的。
优点:简单,适合很少量数据传输。
缺点:无缓存区,并且对数据的正确性没有判断,数据量稍大可能导致数据丢失 。
<2>加了校验帧与缓冲区的串口中断的接收数据的实现方式
void USART2_IRQHandler()
{
if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志
Uart2_Buffer[Uart2_Rx_Num] = USART_ReceiveData(USART2);
Uart2_Rx_Num++;
}
//判断最后接收的数据是否为设定值,确定数据正确性
if((Uart2_Buffer[0] == 0x5A)&&(Uart2_Buffer[Uart2_Rx_Num-1] == 0xA5))
Uart2_Sta=1;
/*如果RXEN标志没有被复位,串口有接收到了一个字符就会发生溢出中断,解决方法是清除*/
if(USART_GetFlagStatus(USART2,USART_FLAG_ORE) == SET) //溢出
{
USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR
USART_ReceiveData(USART2); //读DR
}
}
if( Uart2_Sta )
{
for(Uart2_Tx_Num=0;Uart2_Tx_Num < Uart2_Rx_Num;Uart2_Tx_Num++)
USART2_SendByte(Uart2_Buffer[Uart2_Tx_Num]); //发送数据
Uart2_Rx_Num = 0; //初始化
Uart2_Tx_Num = 0;
Uart2_Sta = 0;
}
注:溢出中断也与死机如影随行,如果出现了溢出中断就意味着百分百丢失了数据。而我们能做的处理无非就是清除标志位防止中断反复触发,从而使整个系统能继续运行下去。至于丢失的数据,已经不可能找回来了。只要接收中断打开,即RXNEIE设置为1,那么ORE中断也自动打开了。
这是加了数据头和数据尾的接收方式,数据头和尾的个数可增加,此处只用于调试之用。中断函数用于接收数据以及判断数据的头尾,第二个函数在main函数里按照查询方式执行。
优点:较简单,采用缓存区接收,对提高数据的正确行有一定的改善 。
缺点:要是第一次数据接收错误,回不到初始化状态,必须复位操作 。
<3>FIFO方式接收串口中断的接收数据的实现方式
void USART2_IRQHandler()
{
if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志.
Uart2_Buffer[Uart2_Rx] = USART_ReceiveData(USART2);
Uart2_Rx++;
Uart2_Rx &= 0x3F; //判断是否计数到最大
}
if(USART_GetFlagStatus(USART2,USART_FLAG_ORE) == SET) //溢出
{
USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR
USART_ReceiveData(USART2); //读DR
}
}
if( Uart2_Tx != Uart2_Rx )
{
USART2_SendByte(Uart2_Buffer[Uart2_Tx]); //发送数据
Uart2_Tx++;
Uart2_Tx &= 0x3F; //判断是否计数到最大
}
采用FIFO方式接收数据,由0x3F可知此处最大接收量为64个,可变,中断函数只负责收,另一函数在main函数里执行,FIFO方式发送。
优点:发送和接收都很自由,中断占用时间少,有利于MCU处理其它。
缺点:对数据的正确性没有判断,一概全部接收。
<4>采用数据包的形式接收串口中断数据的实现方式
void USART2_IRQHandler()
{
if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志
Uart2_Buffer[Uart2_Rx] = USART_ReceiveData(USART2);
Uart2_Rx++;
Uart2_Rx &= 0xFF;
}
if(Uart2_Buffer[Uart2_Rx-1] == 0x5A) //头
Uart2_Tx = Uart2_Rx-1;
if((Uart2_Buffer[Uart2_Tx] == 0x5A)&&(Uart2_Buffer[Uart2_Rx-1] == 0xA5)) //检测到头的情况下检测到尾
{
Uart2_Len = Uart2_Rx-1- Uart2_Tx; //长度
Uart2_Sta=1; //标志位
}
if(USART_GetFlagStatus(USART2,USART_FLAG_ORE) == SET) //溢出
{
USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR
USART_ReceiveData(USART2); //读DR
}
}
if( Uart2_Sta )
{
for(tx2=0;tx2 <= Uart2_Len;tx2++,Uart2_Tx++)
USART2_SendByte(Uart2_Buffer[Uart2_Tx]); //发送数据
Uart2_Rx = 0; //初始化
Uart2_Tx = 0;
Uart2_Sta = 0;
}
数据采用数据包的形式接收,接收后存放于缓存区,通过判断数据头和数据尾(可变)来判断数据的“包”及有效性,中断函数用于接收数据和判断头尾以及数据包长度,另一函数在main函数里执行,负责发送该段数据。
优点:适合打包传输,稳定性和可靠性很有保证,可随意发送,自动挑选有效数据。
缺点:缓存区数据长度要根据“包裹”长度设定,要是多次接收后无头无尾,到有头有尾的那一段数据恰好跨越缓存区最前和最后位置时,可能导致本次数据丢失,不过这种情况几乎没有可能。
5.关于串口USART的相关函数
/*串口初始化*/
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
/*串口使能*/
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
/*串口中断配置*/
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
/*串口DMA使能*/
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);
/*发送8位的数据*/
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
/*接收8位的数据*/
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
/*为获取状态标志位*/
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
/*清除状态标志位*/
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
/*获取中断状态标志位*/
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
/*清除中断状态标志位*/
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
6.关于串口USART的一些补充
1.注意问题:
1.USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);使能了接收中断,那么ORE中断也同时被开启了。
2.ORE中断只能使用USART_GetFlagStatus(USART1, USART_FLAG_ORE) 读到(没有使能USART_IT_ERR中断时)
2.关于printf函数的重定向问题
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
//可百度搜索:半主机模式
#if 1 方法1
#pragma import(__use_no_semihosting) //标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
int _sys_exit(int x) //定义_sys_exit()以避免使用半主机模式
{
x = x;
}
int fputc(int ch, FILE *f) //重定义fputc函数
{
USART1->DR = (u8) ch;
while((USART1->SR&0X40)==0){
};//循环发送,直到发送完毕
return ch;
}
#else
/*使用microLib的方法*/ 需要KEIL打钩!!!!
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t) ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {
}
return ch;
}