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
相关产品推荐
相关产品推荐

