MCU倒计时器串口接收字符串解析失败,数码管显示00-00-00求助
问题排查:MCU倒计时器串口数据解析失败导致数码管显示异常
问题描述
本人为MCU C语言开发新手,正在开发一款MCU倒计时器,需求为通过串口接收PC端C#程序发送的"hou-min-sec"格式字符串(hou、min、sec为变量),将其解析为hour、minute、second整数变量用于倒计时。但目前MCU接收数据后,动态数码管始终显示00-00-00,尝试使用strtok函数解析字符串未生效,恳请协助排查解决该问题。
原代码
#include "reg52.h" #include "string.h" #include "stdlib.h" #define uchar unsigned char typedef unsigned int u16; typedef unsigned char u8; sbit LSA=P2^2; sbit LSB=P2^3; sbit LSC=P2^4; char rec_data[12] = "\0"; char str[12] = "\0"; u8 u[3]; u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; u8 sec,min,hou; u8 DisplayData[8]; u8 count = 0; void delay(u16 i) { while(i--); } void Timer0Init() { TMOD|=0X01; TH0=(65536-50000)/256; TL0=(65536-50000)%256; ET0=1; EA=1; TF0 = 1; TR0=1; } void Timer0(void) interrupt 1 { TH0=(65536-50000)/256; TL0=(65536-50000)%256; count++; if(count == 20) { count = 0; if (hou >= 0 || min >= 0 && sec != 0) { if(sec > 0) { sec = sec - 1; } if (min != 0 && sec == 0) { min = min - 1; sec = 59; } if (hou != 0 && min == 0 && sec == 0) { hou = hou - 1; min = 59; sec = 59; } } if (hou == 0 && min == 0 && sec == 0) { hou = 0; min = 0; sec = 0; } } void DigDisplay() { u8 i; for(i=0;i<8;i++) { switch(i) { case(0): LSA=1;LSB=1;LSC=1; break; case(1): LSA=0;LSB=1;LSC=1; break; case(2): LSA=1;LSB=0;LSC=1; break; case(3): LSA=0;LSB=0;LSC=1; break; case(4): LSA=1;LSB=1;LSC=0; break; case(5): LSA=0;LSB=1;LSC=0; break; case(6): LSA=1;LSB=0;LSC=0; break; case(7): LSA=0;LSB=0;LSC=0; break; } P0=DisplayData[i]; delay(100); P0=0x00; } } void datapros() { DisplayData[0]=smgduan[hou/10]; DisplayData[1]=smgduan[hou%10]; DisplayData[2]=0x40; DisplayData[3]=smgduan[min/10]; DisplayData[4]=smgduan[min%10]; DisplayData[5]=0x40; DisplayData[6]=smgduan[sec/10]; DisplayData[7]=smgduan[sec%10]; } void UartInit(u8 baud) { TMOD|=0X20; SCON=0X50; PCON=0X80; TH1=baud; TL1=baud; TR1 = 1; EA=1; ES=1; } void uart() interrupt 4 { if(RI == 1) { rec_data[12]=SBUF; RI = 0; } RI = 0; SBUF=rec_data[12]; while(!TI); TI=0; } void Select() { const char del[2] = "-"; char *p; p = strtok(rec_data, del); hou = atoi(p); p = strtok(NULL, del); min = atoi(p); p = strtok(NULL, del); sec = atoi(p); } void main() { UartInit(0XFA); Timer0Init(); Select(); hou = u[0]; min = u[1]; sec = u[2]; while(1) { datapros(); DigDisplay(); } }
核心问题排查
- 串口接收逻辑错误:
rec_data数组长度为12,有效下标是0~11,代码中直接赋值rec_data[12]=SBUF会导致数组越界,破坏内存;且当前仅接收单个字符,未完整接收整个"hou-min-sec"格式字符串,也未添加字符串结束符\0。 - 解析函数调用时机错误:
main初始化阶段就调用Select(),此时串口还未收到任何数据,rec_data为空,解析出的时间值均为0。 - 解析结果被覆盖:调用
Select()后,代码又执行hou = u[0]; min = u[1]; sec = u[2];,而u数组未初始化,值为随机垃圾值,直接覆盖了解析结果。 - 倒计时条件逻辑错误:
if (hou >= 0 || min >= 0 && sec != 0)因运算符优先级问题,实际逻辑为hou >=0 || (min >=0 && sec !=0),会导致hou=0但min/sec有值时无法触发倒计时,逻辑不严谨。 - 解析未做异常判断:若
rec_data格式不正确,strtok会返回NULL,此时atoi(NULL)返回0,未做容错处理。
修复后的代码示例
#include "reg52.h" #include "string.h" #include "stdlib.h" #define uchar unsigned char typedef unsigned int u16; typedef unsigned char u8; sbit LSA=P2^2; sbit LSB=P2^3; sbit LSC=P2^4; char rec_data[12] = "\0"; u8 rec_index = 0; // 接收字符索引 u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; u8 sec=0,min=0,hou=0; u8 DisplayData[8]; u8 count = 0; void delay(u16 i) { while(i--); } void Timer0Init() { TMOD|=0X01; TH0=(65536-50000)/256; TL0=(65536-50000)%256; ET0=1; EA=1; TR0=1; } void Timer0(void) interrupt 1 { TH0=(65536-50000)/256; TL0=(65536-50000)%256; count++; if(count == 20) { count = 0; // 修正倒计时条件:只要时间未全0就执行倒计时 if (!(hou == 0 && min == 0 && sec == 0)) { if(sec > 0) { sec--; } else { if(min > 0) { min--; sec = 59; } else { if(hou > 0) { hou--; min = 59; sec = 59; } } } } } } void DigDisplay() { u8 i; for(i=0;i<8;i++) { switch(i) { case(0): LSA=1;LSB=1;LSC=1; break; case(1): LSA=0;LSB=1;LSC=1; break; case(2): LSA=1;LSB=0;LSC=1; break; case(3): LSA=0;LSB=0;LSC=1; break; case(4): LSA=1;LSB=1;LSC=0; break; case(5): LSA=0;LSB=1;LSC=0; break; case(6): LSA=1;LSB=0;LSC=0; break; case(7): LSA=0;LSB=0;LSC=0; break; } P0=DisplayData[i]; delay(100); P0=0x00; } } void datapros() { DisplayData[0]=smgduan[hou/10]; DisplayData[1]=smgduan[hou%10]; DisplayData[2]=0x40; DisplayData[3]=smgduan[min/10]; DisplayData[4]=smgduan[min%10]; DisplayData[5]=0x40; DisplayData[6]=smgduan[sec/10]; DisplayData[7]=smgduan[sec%10]; } void UartInit(u8 baud) { TMOD|=0X20; SCON=0X50; PCON=0X80; TH1=baud; TL1=baud; TR1 = 1; EA=1; ES=1; } void uart() interrupt 4 { if(RI == 1) { char ch = SBUF; RI = 0; // 假设PC端发送字符串以换行符'\n'结尾 if(ch != '\n' && rec_index < 11) // 留一个位置放'\0' { rec_data[rec_index++] = ch; } else { rec_data[rec_index] = '\0'; // 添加字符串结束符 rec_index = 0; // 重置索引,准备下一次接收 // 接收完成后立即解析 const char del[2] = "-"; char *p = strtok(rec_data, del); if(p != NULL) hou = atoi(p); p = strtok(NULL, del); if(p != NULL) min = atoi(p); p = strtok(NULL, del); if(p != NULL) sec = atoi(p); } // 回显数据 SBUF=ch; while(!TI); TI=0; } } void main() { UartInit(0XFA); Timer0Init(); while(1) { datapros(); DigDisplay(); } }
修复说明
- 修正串口接收逻辑:增加
rec_index记录接收位置,收到换行符时添加字符串结束符,触发解析操作,避免数组越界。 - 调整解析时机:将解析逻辑移至串口接收完成时执行,确保有有效数据才解析。
- 删除无效代码:移除
u数组及覆盖解析结果的代码。 - 优化倒计时逻辑:修正条件判断,确保时间未全0时正常倒计时,逻辑更清晰。
- 添加异常判断:解析时检查
strtok返回的指针是否为NULL,避免非法访问。
内容的提问来源于stack exchange,提问作者Zag
相关产品推荐
相关产品推荐

