树莓派Pico与4B基于C/C++的USB双向串口通信故障排查
树莓派Pico与4B USB串口双向通信问题修复
核心问题及解决思路
1. 自发送消息循环问题
问题成因:Pico默认USB stdio开启回显功能,导致printf输出的内容会被回送到自身输入缓冲区,触发无限确认循环。
修复:初始化时关闭USB stdio回显与CRLF自动转换:
stdio_set_echo(&stdio_usb, false); stdio_set_translate_crlf(&stdio_usb, false);
2. 缓冲区溢出与乱码问题
原代码存在两个致命bug:
- 未声明
inputString缓冲区,导致写入随机内存,引发崩溃与乱码 - 缓冲区边界判断错误,未预留字符串终止符空间
修复措施:
- 显式声明全局缓冲区(避免栈溢出):
char inputString[MAXLENGTH + 1]; - 严格检查缓冲区边界,预留
\0位置:if (i >= MAXLENGTH - 1) - 过滤无效非ASCII控制字符(可选,根据需求调整)
- 添加字符串终止符,避免后续字符串操作出错
3. 输入残留数据处理
原代码处理完子串后直接清空整个缓冲区,导致未处理的残留数据丢失或引发后续乱码。修复后改为将未处理的剩余内容移到缓冲区开头,等待下一次处理。
修改后的完整代码
C代码
#include "pico/stdlib.h" #include "hardware/pwm.h" #include "hardware/clocks.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <tusb.h> #define MAXLENGTH 128 const uint PROGRAM_SLEEP = 10; uint inputLength = 0; char inputString[MAXLENGTH + 1]; // 全局缓冲区,+1用于存储字符串终止符 void readUSB() { int c; uint i = inputLength; while (true) { c = getchar_timeout_us(0); if (c == PICO_ERROR_TIMEOUT) break; // 过滤无效控制字符,保留换行、分号与可打印ASCII if ((c < 32 && c != '\n' && c != '\r' && c != ';') || c > 126) { continue; } // 检查缓冲区剩余空间,预留终止符位置 if (i >= MAXLENGTH - 1) { printf("overflow\n"); inputLength = 0; memset(inputString, 0, MAXLENGTH); break; } inputString[i] = c; i++; } inputLength = i; inputString[inputLength] = '\0'; // 添加字符串终止符 } void handleSubstr(char *substr, uint length) { // 示例处理逻辑:打印收到的子串 printf("Received: %.*s\n", length, substr); } void processInput() { if (inputLength == 0) return; uint last = 0; for (uint i = 0; i < inputLength; i++) { // 跳过换行/回车符,重置子串起始位置 if (inputString[i] == '\n' || inputString[i] == '\r') { last = i + 1; continue; } if (inputString[i] == ';') { uint length = i - last; if (length > 0) { inputString[i] = '\0'; // 临时终止子串 handleSubstr(inputString + last, length); } last = i + 1; } } // 处理未分割的剩余内容,移到缓冲区开头 if (last < inputLength) { memmove(inputString, inputString + last, inputLength - last); inputLength = inputLength - last; inputString[inputLength] = '\0'; } else { inputLength = 0; memset(inputString, 0, MAXLENGTH); } } int main() { stdio_init_all(); while(!stdio_usb_connected()); // 关闭回显与CRLF转换,避免自读与多余换行 stdio_set_echo(&stdio_usb, false); stdio_set_translate_crlf(&stdio_usb, false); memset(inputString, 0, MAXLENGTH); while(true) { readUSB(); processInput(); sleep_ms(PROGRAM_SLEEP); } }
CMakeLists.txt(保持原配置即可)
add_executable(hello_pwm hello_pwm.c ) # pull in common dependencies and additional pwm hardware support target_link_libraries(hello_pwm pico_stdlib hardware_pwm) # create map/bin/hex file etc. pico_add_extra_outputs(hello_pwm) # enable usb serial pico_enable_stdio_usb(hello_pwm 1) pico_enable_stdio_uart(hello_pwm 0) # add url via pico_set_program_url example_auto_set_url(hello_pwm)
Linux端操作建议
- 避免使用
cat+echo的组合,改用screen或minicom进行串口通信,减少额外字符干扰:screen /dev/ttyUSB0 115200 - 若使用
echo发送消息,添加-n参数避免自动插入换行符:echo -n "test_msg;" > /dev/ttyUSB0
内容的提问来源于stack exchange,提问作者Scheddi
相关产品推荐
相关产品推荐

