Arduino IDE串口数据更新异常:update函数未触发问题排查
Arduino串口接收函数未触发问题排查与修复
问题描述
编写了一款持续输出数字的Arduino程序,预期串口接收数据时调用update()函数处理字符串并将结果存入数组,但程序始终未正确触发该函数的处理逻辑,请求排查原因。
原代码
const byte buffSize = 60; char inputBuffer[buffSize]; const char startMarker = '<'; const char endMarker = '>'; byte bytesRecvd = 0; boolean readInProgress = false; boolean newDataFromPC = false; char messageFromPC[buffSize] = {0}; uint32_t tmr1; // 计时器变量 int time = 0; int endtest = 0; int starttest = 0; void setup() { Serial.begin(115200); delay(500); } void loop() { if(Serial.available() > 0){ int *workmas = update(); starttest = workmas[0]; time = workmas[1]; endtest = workmas[2]; } int n = starttest; if (millis() - tmr1 >= time && time > 0) { // 计算时间差 tmr1 = millis(); // 重置计时器 int count = 0; while(n < endtest){ n = n + 1; count = count + 1; delay(250); Serial.print("< "); Serial.print(n); Serial.print(" "); Serial.print(count); Serial.println(" >"); } } Serial.print("<"); Serial.print(" 1 "); Serial.println(">"); delay(200); } int *update(){ int arrmas[3]; char x = Serial.read(); if (x == endMarker) { readInProgress = false; newDataFromPC = true; inputBuffer[bytesRecvd] = 0; char * strtokIndx; // strtok()使用的索引 strtokIndx = strtok(inputBuffer,","); // 获取第一部分 arrmas[0] = atoi(strtokIndx); // 转换为整数 strtokIndx = strtok(NULL, ","); // 继续分割 arrmas[1] = atoi(strtokIndx); // 转换为整数 strtokIndx = strtok(NULL, ","); // 继续分割 arrmas[2] = atoi(strtokIndx); // 转换为整数 } if(readInProgress) { inputBuffer[bytesRecvd] = x; bytesRecvd ++; if (bytesRecvd == buffSize) { bytesRecvd = buffSize - 1; } } if (x == startMarker) { bytesRecvd = 0; readInProgress = true; } return arrmas; }
核心问题分析
1. 局部数组返回导致的野指针问题
update()函数中定义的arrmas[3]是局部变量,函数执行完毕后,栈上的该数组会被销毁,返回的指针指向已释放的内存,属于野指针。后续访问workmas[0]、workmas[1]等会触发未定义行为,数据完全不可靠。
2. 串口接收逻辑顺序颠倒
update()中先判断结束标记endMarker,再处理中间字符存储,最后判断起始标记startMarker,这会导致:
- 起始标记被当作普通字符存储
- 结束标记的处理逻辑无法正确触发完整数据的解析
3. 串口数据处理不完整
loop()中仅在Serial.available()>0时调用一次update(),但串口数据可能分批到达,单次调用只能读取一个字节,无法完成多字节数据的拼接与解析。
4. 数据更新逻辑错误
代码定义了newDataFromPC标记,但未用它判断是否完成完整数据接收,而是每次读取字节就强制更新全局变量,导致未完成解析的数据被错误赋值。
修复后的代码
const byte buffSize = 60; char inputBuffer[buffSize]; const char startMarker = '<'; const char endMarker = '>'; byte bytesRecvd = 0; boolean readInProgress = false; boolean newDataFromPC = false; uint32_t tmr1; int time = 0; int endtest = 0; int starttest = 0; // 用全局数组替代局部数组,避免野指针问题 int arrmas[3] = {0}; void setup() { Serial.begin(115200); delay(500); } void loop() { // 循环处理所有可用的串口字节,确保数据不丢失 while(Serial.available() > 0){ update(); } // 只有当完整接收完一组数据后,才更新全局变量 if(newDataFromPC){ starttest = arrmas[0]; time = arrmas[1]; endtest = arrmas[2]; newDataFromPC = false; // 重置标记 } int n = starttest; if (millis() - tmr1 >= time && time > 0) { tmr1 = millis(); int count = 0; while(n < endtest){ n = n + 1; count = count + 1; delay(250); Serial.print("< "); Serial.print(n); Serial.print(" "); Serial.print(count); Serial.println(" >"); } } Serial.print("<"); Serial.print(" 1 "); Serial.println(">"); delay(200); } void update(){ char x = Serial.read(); // 先判断起始标记,初始化接收状态 if (x == startMarker) { bytesRecvd = 0; readInProgress = true; return; } // 再处理中间接收的字符 if(readInProgress) { inputBuffer[bytesRecvd] = x; bytesRecvd ++; if (bytesRecvd >= buffSize - 1) { // 留一个位置存终止符 bytesRecvd = buffSize - 1; } } // 最后判断结束标记,完成数据解析 if (x == endMarker && readInProgress) { readInProgress = false; newDataFromPC = true; inputBuffer[bytesRecvd] = '\0'; // 手动添加字符串终止符 char * strtokIndx; strtokIndx = strtok(inputBuffer,","); arrmas[0] = strtokIndx ? atoi(strtokIndx) : 0; // 增加空指针判断,避免崩溃 strtokIndx = strtok(NULL, ","); arrmas[1] = strtokIndx ? atoi(strtokIndx) : 0; strtokIndx = strtok(NULL, ","); arrmas[2] = strtokIndx ? atoi(strtokIndx) : 0; } }
关键修改点
- 将
arrmas改为全局数组,彻底解决野指针问题 - 调整
update()的接收逻辑顺序:先处理起始标记,再存储中间字符,最后处理结束标记 - 在
loop()中用while循环处理所有串口可用字节,确保数据完整接收 - 仅当
newDataFromPC标记为true时才更新全局变量,保证数据是完整解析后的结果 - 增加
strtok返回空指针的判断,避免非法访问导致崩溃
内容的提问来源于stack exchange,提问作者Андрей Шарапов
相关产品推荐
相关产品推荐

