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

STM32串口解析带格式字符串控制LED失效问题求助

STM32串口解析带格式字符串控制LED失效问题求助

大家好,我现在遇到一个STM32串口解析的棘手问题,希望各位大佬能帮忙排查下。

问题背景

我用Python通过虚拟串口给STM32发送格式为(0,77,88,55)的字符串,第一个数字是随机生成的0或1,后面三个数值固定。之前单独发送0/1字符时,LED能正常开关,但改成这种带括号和逗号的格式后,解析出来的第一个值完全没法控制LED,毫无反应。

相关代码

Python发送端代码

import random
import serial
import time

last_time = 0
test_list = [0,1]
serialSTM32 = serial.Serial('COM22',9600,writeTimeout=0)

while True:
    if (time.time() - last_time) > 2:
        random_num = random.choice(test_list)
        string = "(" + str(random_num) + "," + "77" + "," + "88" + "," + "55" + ")"
        serialSTM32.write(string.encode('utf-8'))
        print(string)
        last_time = time.time()
    serialSTM32.flushInput()

STM32 usbd_cdc_if.c 接收部分

uint8_t bufferVariable = 0;

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
    USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
    USBD_CDC_ReceivePacket(&hUsbDeviceFS);
    bufferVariable = Buf[0];
    return (USBD_OK);
}

STM32 main.c 解析与控制代码

#define buffer_size 40

char input_buffer[buffer_size];
const char start_marker = '(';
const char end_marker = ')';
uint8_t bytes_received = 0;
uint8_t read_in_progress = 0;

char* grab_value(char *data, char separator, int index) {
    int found = 0;
    int string_index[] = { 0, -1 };
    int maximum_index = strlen(data) - 1;

    for (int i = 0; i <= maximum_index && found <= index; i++) {
        if (data[i] == separator || i == maximum_index) {
            found++;
            string_index[0] = string_index[1] + 1;
            string_index[1] = (i == maximum_index)? i + 1 : i;
        }
    }

    if (found > index) {
        data[string_index[1]] = '\0';
        return &data[string_index[0]];
    } else {
        return NULL;
    }
}

int first, second, third, fourth;
extern uint8_t bufferVariable;

int main(void) {
    while (1) {
        if (bufferVariable == end_marker) {
            read_in_progress = 0;
            input_buffer[bytes_received] = '\0';
        }

        if (read_in_progress) {
            input_buffer[bytes_received++] = bufferVariable;
            if (bytes_received == buffer_size) {
                bytes_received = buffer_size - 1;
            }
        }

        if (bufferVariable == start_marker) {
            bytes_received = 0;
            read_in_progress = 1;
        }

        char *str_first = grab_value(input_buffer, ',', 0);
        char *str_second = grab_value(input_buffer, ',', 1);
        char *str_third = grab_value(input_buffer, ',', 2);
        char *str_fourth = grab_value(input_buffer, ',', 3);

        if (str_first) {
            first = atoi(str_first);
        }
        if (str_second) {
            second = atoi(str_second);
        }
        if (str_third) {
            third = atoi(str_third);
        }
        if (str_fourth) {
            fourth = atoi(str_fourth);
        }

        printf("first: %d\n", first);

        if (first == '0') {
            HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_SET);
        } else if (first == '1') {
            HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_RESET);
        }
    }
}

我排查到的问题点及解决方案

1. 最致命的逻辑错误:整数与字符常量比较

first是通过atoi转换得到的整数(0或1),但判断LED状态时却和字符常量'0'、'1'比较(它们的ASCII值是48和49),永远不会匹配!

修复代码:

if (first == 0) {
    HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_SET);
} else if (first == 1) {
    HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_RESET);
}

2. 串口接收字节丢失问题

原来的CDC_Receive_FS只保存了接收缓冲区的第一个字节Buf[0],Python发送的一串字符会被拆分成多个字节接收,后续字节会覆盖bufferVariable,导致缓冲区只存了最后一个字节,完全丢失中间数据。

修复方案:改用环形缓冲区保存所有接收字节
在usbd_cdc_if.c中修改:

#define RX_BUF_SIZE 64
uint8_t rx_buffer[RX_BUF_SIZE];
uint8_t rx_head = 0;
uint8_t rx_tail = 0;

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
    USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
    USBD_CDC_ReceivePacket(&hUsbDeviceFS);
    
    // 将所有接收字节存入环形缓冲区
    for(uint32_t i=0; i<*Len; i++){
        rx_buffer[rx_head] = Buf[i];
        rx_head = (rx_head + 1) % RX_BUF_SIZE;
    }
    return (USBD_OK);
}

在main.c中修改接收逻辑:

extern uint8_t rx_buffer[];
extern uint8_t rx_head;
extern uint8_t rx_tail;

int main(void) {
    while (1) {
        // 读取环形缓冲区中的所有未处理字节
        while(rx_tail != rx_head){
            uint8_t received_byte = rx_buffer[rx_tail];
            rx_tail = (rx_tail + 1) % RX_BUF_SIZE;
            
            if (received_byte == end_marker) {
                read_in_progress = 0;
                input_buffer[bytes_received] = '\0';
                
                // 收到结束符后再解析,避免处理不完整数据
                char *str_first = grab_value(input_buffer, ',', 0);
                if (str_first) {
                    first = atoi(str_first);
                    printf("first: %d\n", first);
                    // 在这里执行LED控制逻辑
                    if (first == 0) {
                        HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_SET);
                    } else if (first == 1) {
                        HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_RESET);
                    }
                }
                bytes_received = 0; // 重置缓冲区
            }

            if (read_in_progress) {
                if(bytes_received < buffer_size -1){ // 预留位置存字符串结束符
                    input_buffer[bytes_received++] = received_byte;
                }
            }

            if (received_byte == start_marker) {
                bytes_received = 0;
                read_in_progress = 1;
            }
        }
    }
}

3. grab_value函数的副作用

原函数会修改传入的input_buffer(将分隔符替换为'\0'),多次调用后会破坏原数据,导致后续解析失败。

修复方案:使用临时缓冲区处理

char* grab_value(char *data, char separator, int index) {
    static char temp_buffer[buffer_size]; // 静态缓冲区保存临时数据
    strncpy(temp_buffer, data, buffer_size);
    temp_buffer[buffer_size-1] = '\0'; // 确保字符串安全结束
    
    int found = 0;
    int string_index[] = { 0, -1 };
    int maximum_index = strlen(temp_buffer) - 1;

    for (int i = 0; i <= maximum_index && found <= index; i++) {
        if (temp_buffer[i] == separator || i == maximum_index) {
            found++;
            string_index[0] = string_index[1] + 1;
            string_index[1] = (i == maximum_index)? i + 1 : i;
        }
    }

    if (found > index) {
        temp_buffer[string_index[1]] = '\0';
        return &temp_buffer[string_index[0]];
    } else {
        return NULL;
    }
}

总结

先修改LED判断的整数比较逻辑,这是最直接的错误;然后修复串口接收的字节丢失问题,确保所有发送的字符都被保存;最后调整解析时机,只在收到完整的字符串后再执行解析和控制逻辑,应该就能解决LED不响应的问题了。

备注:内容来源于stack exchange,提问作者Some Student

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 15:27:44