硬汉嵌入式论坛

 找回密码
 立即注册
查看: 365|回复: 5
收起左侧

[技术讨论] 51单片机串口解析如何改进

[复制链接]

3

主题

13

回帖

22

积分

新手上路

积分
22
发表于 2025-5-15 21:04:39 | 显示全部楼层 |阅读模式
本人在准备蓝桥杯单片机国赛,目前在学习串口的内容,自己写了代码,pc通过串口给单片机开发板发送信息,单片机接收到后根据信息做出反应。我在单片机解析串口信息这方面有些生疏,想问下各位大佬有什么好的建议或者需要优化的地方,感谢各位。以下是我根据14届单片机国赛题目,将按键控制改成了通过串口控制,以下是题目,关键代码和工程文件,希望各位大佬能不吝赐教。 第十四届国赛.png

14届国赛_串口.7z (45.83 KB, 下载次数: 1)

[C] 纯文本查看 复制代码
void Uart1Server() interrupt 4
{
    if(RI == 1) //串口接收数据
    {

        RI = 0;
        Uart_Rx_Flag = 1; // 标记有完整命令
        Uart_Recv_Tick = 0;

        Uart_Recv[Uart_Recv_Index] = SBUF;
        Uart_Recv_Index++;

        if(Uart_Recv_Index>10)
        {
            Uart_Recv_Index = 0;
            memset(Uart_Recv,0,10);
        }
    }
}

[C] 纯文本查看 复制代码
void Uart_Proc(void)
{  
    if(Uart_Recv_Index==0)return;
    if(Uart_Recv_Tick>=10)
    {
        Uart_Recv_Tick=0;
        Uart_Rx_Flag=0;

        if(strcmp(Uart_Recv,"RESET")==0)
        {
            Seg_Mode=0;
            canshu_Distance=40;
            canshu_Temperature=30;
            Factory_JiaoZhun=0;
            Factory_Speed=340;
            Factory_DAC_Limit_10x=10;
            memset(Uart_Recv, 0, 10); // 清空接收数据
            Uart_Recv_Index = 0;
            return;
        }

        if(strcmp(Uart_Recv,"DM")==0)
        {
            Seg_Mode=0;
            memset(Uart_Recv, 0, 10); // 清空接收数据
            Uart_Recv_Index = 0;
            return;
        }else if(strcmp(Uart_Recv,"PM")==0)
        {
            Seg_Mode=1;
            Parameter_Set_Mode=0;
            printf("Please enter the parameters you want to set\r\n");
            printf("SD:Set distance\r\nST:Set temperature\r\n");
            memset(Uart_Recv, 0, 10); // 清空接收数据
            Uart_Recv_Index = 0;
            return;
        }else if(strcmp(Uart_Recv,"FM")==0)
        {
            Seg_Mode=2;
            printf("Please enter the parameters you want to set\r\n");
            printf("SC:Set calibration \r\nSS:Set Speed\r\nSDAC:Set DAC Limit\r\n");
            memset(Uart_Recv, 0, 10); // 清空接收数据
            Uart_Recv_Index = 0;
            return;
        }

        if (Seg_Mode==1)
        {
            if(strcmp(Uart_Recv,"SD")==0)
            {
                Parameter_Set_Mode=0;
                Start_Set_Parameter=1;
                printf("Please Enter The Distance Parameter(10 to 90)\r\n");
                memset(Uart_Recv, 0, 10); // 清空接收数据
                Uart_Recv_Index = 0;
                return;
            }else if(strcmp(Uart_Recv,"ST")==0){
                Parameter_Set_Mode=1;
                Start_Set_Parameter=1;
                printf("Please Enter The Temperature Parameter(0 to 80)\r\n");
                memset(Uart_Recv, 0, 10); // 清空接收数据
                Uart_Recv_Index = 0;
                return;
            }

            if (Start_Set_Parameter)
            {
                unsigned char temp=0;
                unsigned char i=0;
                Start_Set_Parameter=0;
                
                while(isdigit(Uart_Recv[i]))
                {
                    temp=(temp*10+(Uart_Recv[i]-'0'));
                    printf("Uart_Recv[%bd] is %c\r\n",i,Uart_Recv[i]);
                    i++;
                }
                printf("temp is %bd\r\n",temp);
                if (Parameter_Set_Mode)
                {
                    if(temp>=0&&temp<=80)
                    {
                        canshu_Temperature=temp;
                    }else{
                        printf("The Temperature Parameter must be 0 to 80!\r\n");
                    }
                }else{
                    if(temp>=10&&temp<=90)
                    {
                        canshu_Distance=temp;
                    }else{
                        printf("The Distance Parameter must be 10 to 90!\r\n");
                    }
                }
                memset(Uart_Recv, 0, 10); // 清空接收数据
                Uart_Recv_Index = 0;
                return;
            }
        }
        
        if (Seg_Mode==2)
        {
            if(strcmp(Uart_Recv,"SC")==0)
            {
                Factory_Set_Mode=0;
                Start_Set_Factory_Parameter=1;
                printf("Please Enter The calibration value(-90 to 90)\r\n");
                memset(Uart_Recv, 0, 10); // 清空接收数据
                Uart_Recv_Index = 0;
                return;
            }else if(strcmp(Uart_Recv,"SS")==0){
                Factory_Set_Mode=1;
                Start_Set_Factory_Parameter=1;
                printf("Please Enter The Speed value(10 to 9990)\r\n");
                memset(Uart_Recv, 0, 10); // 清空接收数据
                Uart_Recv_Index = 0;
                return;
            }else if(strcmp(Uart_Recv,"SDAC")==0){
                Factory_Set_Mode=2;
                Start_Set_Factory_Parameter=1;
                printf("Please Enter The DAC value(0.1 to 2.0)\r\n");
                memset(Uart_Recv, 0, 10); // 清空接收数据
                Uart_Recv_Index = 0;
                return;
            }

            if(Start_Set_Factory_Parameter)
            {
                unsigned char i=0;
                unsigned int temp1=0;
                Start_Set_Factory_Parameter=0;
                ucLed[0]=1;

                switch(Factory_Set_Mode)
                {
                    case 0:
                        if (Uart_Recv[0]=='-')
                        {
                            
                            signed char temp=0;ucLed[2]^=1;i=1;
                            while (isdigit(Uart_Recv[i]))
                            {
                                temp=(temp*10+(Uart_Recv[i]-'0'));
                                // printf("Uart_Recv[%bd] is %c\r\n",i,Uart_Recv[i]);
                                i++;
                            }
                            // printf("temp is %bd\r\n",temp);
                            if(temp>90)
                            {
                                printf("The calibration value must be -90 to 90!\r\n");
                            }else{
                                Factory_JiaoZhun=-temp;
                                // printf("Factory_JiaoZhun is %bd\r\n",Factory_JiaoZhun);
                            }
                        }else{
                            unsigned char temp=0;
                            while (isdigit(Uart_Recv[i]))
                            {
                                temp=(temp*10+(Uart_Recv[i]-'0'));
                                i++;
                            }
                            if(temp>90)
                            {
                                printf("The calibration value must be -90 to 90!\r\n");
                            }else{
                                Factory_JiaoZhun=temp;
                            }
                        }
                    break;

                    case 1:
                        while (isdigit(Uart_Recv[i]))
                        {
                            temp1=(temp1*10+(Uart_Recv[i]-'0'));
                            i++;
                        }
                        if(temp1<10||temp1>9990)
                        {
                            printf("The Speed value must be 10 to 9990!\r\n");
                        }else{
                            Factory_Speed=temp1;
                        }
                        
                    break;
                    
                    case 2:
                        while (i<Uart_Recv_Index)
                        {
                            if (isdigit(Uart_Recv[i]))
                            {
                                temp1=(temp1*10+(Uart_Recv[i]-'0'));
                            }
                            i++;
                        }
                        if(temp1<1||temp1>20)
                        {
                            printf("The DAC value must be 0.1 to 2.0!\r\n");
                        }else{
                            Factory_DAC_Limit_10x=temp1;
                        }
                        
                    break;
                }
            
                memset(Uart_Recv, 0, 10); // 清空接收数据
                Uart_Recv_Index = 0;
                return;
            }
            
        }
        
        memset(Uart_Recv, 0, 10); // 清空接收数据
        Uart_Recv_Index = 0;
    }
}



回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115988
QQ
发表于 2025-5-16 08:30:13 | 显示全部楼层
楼主这个代码里面while循环判断,如果串口发送有丢包或者出错还能正常退出不
回复

使用道具 举报

3

主题

13

回帖

22

积分

新手上路

积分
22
 楼主| 发表于 2025-5-16 13:03:47 | 显示全部楼层
本帖最后由 cien 于 2025-5-16 15:48 编辑
eric2013 发表于 2025-5-16 08:30
楼主这个代码里面while循环判断,如果串口发送有丢包或者出错还能正常退出不

您好,我目前还没有遇到过串口丢包的情况,但是我简单测试了一下发送错误的数据,是可以正常退出的。我问了下DeepSeek,这代码是显然存在一些问题的,遇到一些特殊情况会出错的。感谢您的提问。
关键问题总结
1. 数组越界风险

    中断代码中,Uart_Recv_Index 的索引逻辑存在越界可能:
    c

Uart_Recv[Uart_Recv_Index] = SBUF; // 写入数据
Uart_Recv_Index++;
if(Uart_Recv_Index > 10) {         // 索引超过 10 时重置
    Uart_Recv_Index = 0;
    memset(Uart_Recv, 0, 10);      // 清空数组
}

    如果 Uart_Recv 数组定义为 char Uart_Recv[10],则最大有效索引为 9。当 Uart_Recv_Index 增加到 10 时,Uart_Recv[10] 会越界写入,导致内存损坏。

修复建议:

    将数组定义为 char Uart_Recv[11](包含终止符空间),并修改判断条件为 if(Uart_Recv_Index >= 10)。

    在写入数据后,显式添加终止符 \0:
    c

        Uart_Recv[Uart_Recv_Index] = SBUF;
        Uart_Recv_Index++;
        Uart_Recv[Uart_Recv_Index] = '\0'; // 添加终止符

2. 数据完整性丢失

    中断服务程序在接收超过 10 字节时直接清空数组,导致长命令被截断:
    c

if(Uart_Recv_Index > 10) {
    Uart_Recv_Index = 0;
    memset(Uart_Recv, 0, 10); // 清空数组
}

    若用户发送 "RESET123456"(11 字节),第 11 字节会触发清空操作,导致命令不完整。

修复建议:

    限制最大接收长度为 10 字节,并在达到长度后丢弃后续字节:
    c

        if (Uart_Recv_Index < 10) {
            Uart_Recv[Uart_Recv_Index] = SBUF;
            Uart_Recv_Index++;
        } else {
            // 丢弃超长数据
        }

3. 字符串未终止

    Uart_Proc 中使用 strcmp 比较字符串,但 Uart_Recv 未以 \0 终止:
    c

if(strcmp(Uart_Recv, "RESET") == 0) // 比较未终止的字符串,可能越界

修复建议:

    在中断中每次写入数据后添加终止符:
    c

        Uart_Recv[Uart_Recv_Index] = '\0';

4. 负数解析缺陷

    在解析负数(如校准值)时,若输入为 "-"(仅有负号无数字),代码会错误处理:
    c

if (Uart_Recv[0] == '-') {
    i = 1;
    // 未检查后续字符是否为数字
    while (isdigit(Uart_Recv)) { ... }
}

修复建议:

    添加对负号后是否为数字的校验:
    c

        if (Uart_Recv[0] == '-') {
            if (Uart_Recv_Index == 1 || !isdigit(Uart_Recv[1])) {
                printf("Error: Invalid negative value!\n");
                return;
            }
        }

5. 竞争条件

    中断服务程序 (Uart1Server) 和主循环 (Uart_Proc) 共享 Uart_Recv_Index 和 Uart_Recv,但无保护机制:

        若主循环正在处理 Uart_Recv 时,中断修改了其内容,可能导致数据不一致。

    修复建议:

        在访问共享变量时禁用中断:
        c

        // 主循环中操作 Uart_Recv 前禁用中断
        EA = 0; // 8051 平台禁用全局中断
        // 安全操作 Uart_Recv_Index 和 Uart_Recv
        EA = 1; // 恢复中断

改进后的代码示例
中断服务程序 (Uart1Server)
c

#define UART_BUF_SIZE 10 // 缓冲区大小(含终止符需定义为 11)

char Uart_Recv[UART_BUF_SIZE + 1]; // 缓冲区大小为 11 字节
volatile uint8_t Uart_Recv_Index = 0;

void Uart1Server() interrupt 4 {
    if (RI == 1) {
        RI = 0;
        Uart_Rx_Flag = 1;
        Uart_Recv_Tick = 0;

        if (Uart_Recv_Index < UART_BUF_SIZE) {
            Uart_Recv[Uart_Recv_Index] = SBUF;
            Uart_Recv_Index++;
            Uart_Recv[Uart_Recv_Index] = '\0'; // 显式终止符
        } else {
            // 缓冲区满,丢弃数据或处理错误
        }
    }
}

主循环处理 (Uart_Proc)
c

void Uart_Proc(void) {
    if (Uart_Recv_Index == 0) return;

    // 操作共享变量前禁用中断
    EA = 0;
    uint8_t recv_len = Uart_Recv_Index;
    char recv_buf[UART_BUF_SIZE + 1];
    strncpy(recv_buf, Uart_Recv, UART_BUF_SIZE);
    recv_buf[recv_len] = '\0'; // 确保终止
    EA = 1;

    if (Uart_Recv_Tick >= 10) {
        // 使用 recv_buf 进行安全处理
        if (strcmp(recv_buf, "RESET") == 0) {
            // 处理命令
        }

        // 处理完成后清空缓冲区
        EA = 0;
        Uart_Recv_Index = 0;
        memset(Uart_Recv, 0, UART_BUF_SIZE + 1);
        EA = 1;
    }
}

增强型数字解析逻辑
c

// 示例:解析工厂校准值(支持负数)
case 0: // Factory_Set_Mode=0(校准值)
    int temp = 0;
    int sign = 1;
    int i = 0;

    if (recv_buf[0] == '-') {
        sign = -1;
        i = 1;
        // 检查负号后是否有有效数字
        if (i >= recv_len || !isdigit(recv_buf)) {
            printf("Error: Invalid negative value!\n");
            break;
        }
    }

    while (i < recv_len && isdigit(recv_buf)) {
        temp = temp * 10 + (recv_buf - '0');
        i++;
    }

    temp *= sign;
    if (temp < -90 || temp > 90) {
        printf("Error: Calibration value out of range!\n");
    } else {
        Factory_JiaoZhun = temp;
    }
    break;

总结

    内存安全:确保缓冲区大小足够并显式终止字符串。

    数据完整性:避免截断长数据,合理处理超长输入。

    输入验证:严格校验负数、非数字字符和范围。

    线程安全:通过禁用中断保护共享变量。

    防御性编程:使用 strncpy 和长度限制函数替代不安全操作。

通过上述改进,可显著提升代码的鲁棒性,避免越界、数据损坏和逻辑错误。
回复

使用道具 举报

3

主题

13

回帖

22

积分

新手上路

积分
22
 楼主| 发表于 2025-5-16 20:38:19 | 显示全部楼层
我按照DeepSeek的建议修改了一下。工程文件附上了。

[C] 纯文本查看 复制代码
#define UART_BUF_SIZE 10 // 缓冲区大小(含终止符需定义为 11)

xdata unsigned char Uart_Recv[UART_BUF_SIZE+1];//串口接收数据储存数组 默认10个字节 若接收数据较长 可更改最大字节数
unsigned char Uart_Recv_Index;//串口接收数组指针
unsigned char Uart_Recv_Tick;
bit Uart_Rx_Flag;

// 处理 RESET 命令
void handleResetCommand() {
Seg_Mode = 0;
canshu_Distance = 40;
canshu_Temperature = 30;
Factory_JiaoZhun = 0;
Factory_Speed = 340;
Factory_DAC_Limit_10x = 10;
}


// 处理 DM 命令
void handleDMCommand() {
Seg_Mode = 0;
}

// 处理 PM 命令
void handlePMCommand() {
Seg_Mode = 1;
Parameter_Set_Mode = 0;
printf("Please enter the parameters you want to set\r\n");
printf("SD:Set distance\r\nST:Set temperature\r\n");
}

// 处理 FM 命令
void handleFMCommand() {
Seg_Mode = 2;
printf("Please enter the parameters you want to set\r\n");
printf("SC:Set calibration \r\nSS:Set Speed\r\nSDAC:Set DAC Limit\r\n");
}

// 解析并设置普通参数(温度/距离)
void parseAndSetParameter(const char *recv_buf) {
unsigned char temp = 0;
unsigned char i = 0;

while (isdigit(recv_buf[i])) {
temp = temp * 10 + (recv_buf[i] - '0');
printf("recv_buf[%bd] is %c\r\n", i, recv_buf[i]);
i++;
}

printf("temp is %bd\r\n", temp);
if (Parameter_Set_Mode) {
if (temp >= 0 && temp <= 80) {
canshu_Temperature = temp;
} else {
printf("Temperature must be 0-80!\r\n");
}
} else {
if (temp >= 10 && temp <= 90) {
canshu_Distance = temp;
} else {
printf("Distance must be 10-90!\r\n");
}
}
}

// 解析并设置工厂参数(校准/速度/DAC)
void parseAndSetFactoryParameter(const char *recv_buf,unsigned char recv_len) {
unsigned char i = 0;
unsigned int temp1 = 0;
signed int temp = 0;
signed char sign=1;

switch (Factory_Set_Mode) {
case 0: // 校准值
if (recv_buf[0] == '-') {
sign=-1;
i = 1;

if(i>=recv_len||!isdigit(recv_buf[i]))
{
printf("Error: Invalid negative value!\n");
break;
}
}

while (i<recv_len&&isdigit(recv_buf[i]))
{
temp = temp * 10 + (recv_buf[i] - '0');
i++;
}

temp *= sign;
if (temp < -90 || temp > 90) {
printf("Error: Calibration value out of range!\n");
} else {
Factory_JiaoZhun = temp;
}
break;

case 1: // 速度
while (isdigit(recv_buf[i])) {
temp1 = temp1 * 10 + (recv_buf[i] - '0');
i++;
}
if (temp1 < 10 || temp1 > 9990) {
printf("Speed value out of range!\r\n");
} else {
Factory_Speed = temp1;
}
break;

case 2: // DAC限制
while (i < strlen(recv_buf)) {
if (isdigit(recv_buf[i])) {
temp1 = temp1 * 10 + (recv_buf[i] - '0');
}
i++;
}
if (temp1 < 1 || temp1 > 20) {
printf("DAC value out of range!\r\n");
} else {
Factory_DAC_Limit_10x = temp1;
}
break;
}
}

// 处理 Seg_Mode=1 的参数设置
void handleSegMode1(const char *recv_buf) {
if (strcmp(recv_buf, "SD") == 0) {
Parameter_Set_Mode = 0;
Start_Set_Parameter = 1;
printf("Please Enter The Distance Parameter(10 to 90)\r\n");
} else if (strcmp(recv_buf, "ST") == 0) {
Parameter_Set_Mode = 1;
Start_Set_Parameter = 1;
printf("Please Enter The Temperature Parameter(0 to 80)\r\n");
}else if (Start_Set_Parameter) {
parseAndSetParameter(recv_buf);
Start_Set_Parameter = 0;
}
}

// 处理 Seg_Mode=2 的工厂参数设置
void handleSegMode2(const char *recv_buf,unsigned char recv_len) {
if (strcmp(recv_buf, "SC") == 0) {
Factory_Set_Mode = 0;
Start_Set_Factory_Parameter = 1;
printf("Please Enter The calibration value(-90 to 90)\r\n");
} else if (strcmp(recv_buf, "SS") == 0) {
Factory_Set_Mode = 1;
Start_Set_Factory_Parameter = 1;
printf("Please Enter The Speed value(10 to 9990)\r\n");
} else if (strcmp(recv_buf, "SDAC") == 0) {
Factory_Set_Mode = 2;
Start_Set_Factory_Parameter = 1;
printf("Please Enter The DAC value(0.1 to 2.0)\r\n");
}else

if (Start_Set_Factory_Parameter) {
parseAndSetFactoryParameter(recv_buf,recv_len);
Start_Set_Factory_Parameter = 0;
}
}



void Uart_Proc(void)
{
unsigned char recv_len = Uart_Recv_Index;
xdata char recv_buf[UART_BUF_SIZE + 1];

if (Uart_Buffer_Overflow)
{
printf("Error: UART buffer overflow!\r\n");
Uart_Buffer_Overflow = 0; // 清除标志
}


ES = 0; // 仅禁用串口中断
strncpy(recv_buf, Uart_Recv, UART_BUF_SIZE);
recv_buf[recv_len] = '\0';
ES = 1; // 恢复串口中断

if(Uart_Recv_Index==0)return;
if(Uart_Recv_Tick>=10)
{
Uart_Recv_Tick=0;
Uart_Rx_Flag=0;

// 4. 统一处理命令解析
if (strcmp(recv_buf, "RESET") == 0) {
handleResetCommand();
} else if (strcmp(recv_buf, "DM") == 0) {
handleDMCommand();
} else if (strcmp(recv_buf, "PM") == 0) {
handlePMCommand();
} else if (strcmp(recv_buf, "FM") == 0) {
handleFMCommand();
} else if (Seg_Mode == 1) {
handleSegMode1(recv_buf);
} else if (Seg_Mode == 2) {
handleSegMode2(recv_buf,recv_len);
}

// 清空接收缓冲区(原子操作)
ES = 0;
Uart_Recv_Index = 0;
memset(Uart_Recv, 0, UART_BUF_SIZE);
ES = 1;
}
}


void Uart1Server() interrupt 4
{
if(RI == 1) //串口接收数据
{

RI = 0;
Uart_Rx_Flag = 1; // 标记有完整命令
Uart_Recv_Tick = 0;

if(Uart_Recv_Index<UART_BUF_SIZE)
{
Uart_Recv[Uart_Recv_Index] = SBUF;
Uart_Recv_Index++;
Uart_Recv[Uart_Recv_Index]='\0';
}else{
Uart_Buffer_Overflow = 1; // 标记溢出
Uart_Recv_Index = 0; // 重置索引,准备接收新数据
memset(Uart_Recv, 0, UART_BUF_SIZE + 1); // 清空缓冲区(可选)
}
}
}

14届国赛_串口.7z

56.57 KB, 下载次数: 0

回复

使用道具 举报

0

主题

30

回帖

30

积分

新手上路

积分
30
发表于 2025-5-19 10:01:35 | 显示全部楼层

建议解析和硬件有关代码分开,譬如我这个函数,用来解析 cdbus 数据包(三字节头:原地址、目标地址、数据长度,然后是数据,最后是 2 字节 crc 结尾)

串口接收到数据,无论接收到多少字节,直接喂给这个函数(如果是 ring buffer 回环到 buffer 开头,则分两次喂)

这个函数检测到合法的包,就会储存到 rx_frame 这个链表上,用户判断这个链表有没有数据包即可

[C] 纯文本查看 复制代码

void cduart_rx_handle(cduart_dev_t *dev, const uint8_t *buf, unsigned len)
{
    unsigned max_len;
    unsigned cpy_len;
    const uint8_t *rd = buf;

    while (true) {
        cd_frame_t *frame = dev->rx_frame;

        if (dev->rx_byte_cnt != 0 && get_systick() - dev->t_last > CDUART_IDLE_TIME) {
            printf("bus: timeout [%02x %02x %02x] %d, %d\n",
                    frame->dat[0], frame->dat[1], frame->dat[2], dev->rx_byte_cnt, dev->rx_drop);
            for (int i = 0; i < dev->rx_byte_cnt; i++)
                printf("%02x ", frame->dat[i]);
            printf("\n");
            dev->rx_byte_cnt = 0;
            dev->rx_crc = 0xffff;
            dev->rx_drop = false;
        }

        if (!len || rd == buf + len)
            return;
        max_len = buf + len - rd;
        dev->t_last = get_systick();

        if (dev->rx_byte_cnt < 3)
            cpy_len = min(3 - dev->rx_byte_cnt, max_len);
        else
            cpy_len = min(frame->dat[2] + 5 - dev->rx_byte_cnt, max_len);

        if (!dev->rx_drop)
            memcpy(frame->dat + dev->rx_byte_cnt, rd, cpy_len);
        dev->rx_byte_cnt += cpy_len;

        if (dev->rx_byte_cnt == 3 && frame->dat[2] > CD_FRAME_SIZE - 5) {
            printf("bus: drop [%x %x %x]\n", frame->dat[0], frame->dat[1], frame->dat[2]);
            dev->rx_drop = true;
        }

        if (!dev->rx_drop)
            dev->rx_crc = CDUART_CRC_SUB(rd, cpy_len, dev->rx_crc);
        rd += cpy_len;

        if (dev->rx_byte_cnt == frame->dat[2] + 5) {
            if (!dev->rx_drop) {
                if (dev->rx_crc != 0) {
                    printf("bus: !crc [%x %x %x]\n", frame->dat[0], frame->dat[1], frame->dat[2]);

                } else {
                    cd_frame_t *frm = cd_list_get(dev->free_head);
                    if (frm) {
                        cd_list_put(&dev->rx_head, dev->rx_frame);
                        dev->rx_frame = frm;
                    } else {
                        printf("bus: rx lost\n");
                    }
                }
            }
            dev->rx_byte_cnt = 0;
            dev->rx_crc = 0xffff;
            dev->rx_drop = false;
        }
    }
}



完整代码和头文件以及使用示范,见这个文件和所属项目:
https://github.com/dukelec/cdbus ... /utils/cdbus_uart.c [.h]



回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2025-5-23 13:20:45 | 显示全部楼层
单字节单字节收, 找符合条件的数据包包头, 包头匹配错误就重新收单字节直到找到包头.包头可以是两个字节, 也可以是一个字节, 看你怎么定义的通信协议,找到包头之后,如果是定长数据包, 可以直接接收剩余的数据, 检测最后一位数据包尾是否符合要求.51单片机不知道有没有硬件crc校验的功能, 如果有, 数据检测可以用硬件crc校验.如果没有就尽量不要用crc校验数据了,因为51单片机好像是16个机器周期才跑一条指令, 框框算CRC会拖慢程序运行速度的,这种单片机的校验数据最好是在接收到完整的数据包之后可以校验一下, 如果进来一个字节校验一次, 你的单片机运行速度会被拖慢太多了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-6-1 18:16 , Processed in 0.267027 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表