一.问题提出
最近在学习STM32串口通信的时候,对照正点原子的代码,自己写了一个简易的串口通信的协议来练练手,代码长这样:
主程序
#include "HeadFile.h"
int main(void)
{
HardwareInit();
while(1)
{
if(USART1_state==2)
{
u8 i;
printf("get: ");
for(i=0;i<counter;i++)
{
printf("%c",USART_RX_BUF[i]);
}
printf("\n");
counter=0;
USART1_state=0;
}
}
}
串口配置(正点原子的代码,没有改动)
void ConfigureUART1(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.10
//Usart1 NVIC 配置
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); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound; //串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
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); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
}
串口中断函数
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
printf("Interrupt entered\n\n");
Res =USART_ReceiveData(USART1); //读取接收到的数据
if(Res==0xAA)
{
USART1_state=1;
printf("STATE1 ENTERED\n\n");
}
else
{
if(USART1_state==1&&Res!=0xFC)
{
USART_RX_BUF[counter++]=Res;
printf("READING char:%c\n",Res);
}
if(Res==0xFC)
{
USART1_state=2;
printf("STATE2 ENTERED\n\n");
}
}
}
}
原因分析:
按照我的预期,这个简单的串口协议解析程序应该是这样工作的:
- 电脑通过串口助手使用CH340连接单片机发送一串数据,比如: 0xAA 0xBB 0xCC 0xDD 0xFC
- 单片机收到数据并处理,将处理过程通过串口打印出来
- 如果解析成功,会向上位机把数据串echo出来
然而,它是这样工作的(发送的是0xAA 0xB5 0xC3 0xFC):
正常状态下,应该会返回:“get:得”
这意味着程序顺利的进入了串口中断,收到了帧头,却没有解析后面的数据,确切来说,根本没有进入else部分.程序只解析到了0xAA,其后的数据被丢失.
利用串口助手多字符串发送工具一个字符一个字符的发送数据,每个字符之间间隔1ms:
发现程序正常,解析成功.
思考人生…看来不是协议本身的问题…
查阅了资料,有建议说串口中断接收并存储数据之后就不要干其他事情,否则中断函数耗时过多,容易使数据丢失,导致解析失败.意识到debug用的printf会占用大量的处理时间,可能是导致程序只能读取识别帧头的原因.
将所有串口中断内printf删掉,编译烧录,发送数据,解析成功…
解决方案:
串口中断里,不要加入任何数据解析之外的逻辑
比如:延时,串行输出,浮点数计算…如果硬是要debug,建议用keil或者在中断里点个不同颜色的灯表示运行状态
另外,用printf函数debug是一个坏习惯…今天我又给我这个喜欢在代码里面加一堆printf来debug的人上了一课.