You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
    }
}

修复说明

  1. 修正串口接收逻辑:增加rec_index记录接收位置,收到换行符时添加字符串结束符,触发解析操作,避免数组越界。
  2. 调整解析时机:将解析逻辑移至串口接收完成时执行,确保有有效数据才解析。
  3. 删除无效代码:移除u数组及覆盖解析结果的代码。
  4. 优化倒计时逻辑:修正条件判断,确保时间未全0时正常倒计时,逻辑更清晰。
  5. 添加异常判断:解析时检查strtok返回的指针是否为NULL,避免非法访问。

内容的提问来源于stack exchange,提问作者Zag

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 21:27:02