如何调试BeagleBone Black上程序停止打印输出的问题
调试方法建议
1. 给轮询循环添加超时机制
你的代码中两个while循环会一直等待ECHO引脚状态变化,一旦传感器无响应(比如检测范围内无物体、接线松动),程序会直接挂死在循环里。给循环加上超时逻辑,既避免挂死,还能输出错误信息定位问题:
while(1) { std::chrono::high_resolution_clock::time_point start,end; long long delay_count=0; // Trigger outGPIO.setValue(HIGH); usleep(10); outGPIO.setValue(LOW); // Poll for Echo HIGH with timeout (1s) auto timeout_start = std::chrono::high_resolution_clock::now(); bool echo_triggered = false; while(inGPIO.getValue() == 0) { usleep(1000); auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - timeout_start).count(); if(elapsed > 1000) { printf("\n[ERROR] Timeout waiting for ECHO HIGH"); goto next_cycle; } start = std::chrono::high_resolution_clock::now(); } echo_triggered = true; // Poll for Echo LOW with timeout (2s, adjust based on sensor max range) timeout_start = std::chrono::high_resolution_clock::now(); while(inGPIO.getValue() == 1) { end = std::chrono::high_resolution_clock::now(); usleep(delay_us); delay_count +=delay_us; auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - timeout_start).count(); if(elapsed > 2000) { printf("\n[ERROR] Timeout waiting for ECHO LOW"); goto next_cycle; } } // Calculate distance long long microseconds = std::chrono::duration_cast<std::chrono::microseconds>(end-start).count(); printf("\nDuration: %lld, delay: %lld dist:%f",microseconds,delay_count,(microseconds+delay_count)*0.034*0.5); next_cycle: usleep(1000*100); }
2. 实时打印GPIO状态,定位挂死节点
在挂死发生前,打印ECHO引脚的实时状态,确认程序卡在哪个阶段:
// 在等待ECHO HIGH的循环中添加 while(inGPIO.getValue() == 0) { int current_val = inGPIO.getValue(); printf("\nWaiting for ECHO HIGH, current value: %d", current_val); usleep(1000); start = std::chrono::high_resolution_clock::now(); } // 在等待ECHO LOW的循环中同理添加打印 while(inGPIO.getValue() == 1) { int current_val = inGPIO.getValue(); printf("\nWaiting for ECHO LOW, current value: %d", current_val); end = std::chrono::high_resolution_clock::now(); usleep(delay_us); delay_count +=delay_us; }
同时可以手动执行cat /sys/class/gpio/gpio45/value,在程序挂起时直接读取引脚状态,验证是传感器无输出还是代码读取异常。
3. 优化GPIO读取逻辑,避免文件IO异常
原代码使用的GPIO库每次调用getValue()都会打开/关闭GPIO值文件,频繁IO操作可能导致文件句柄泄漏或权限问题。改用文件流持续读取:
int main(){ GPIO outGPIO(44), inGPIO(45); outGPIO.setDirection(OUTPUT); inGPIO.setDirection(INPUT); // 预打开ECHO引脚的value文件 std::ifstream echo_pin_file("/sys/class/gpio/gpio45/value"); if(!echo_pin_file.is_open()){ perror("Failed to open ECHO pin file"); return 1; } int delay_us = 50; while(1) { // ... 触发代码不变 ... // 读取ECHO状态时直接从流中读取 int val; echo_pin_file.seekg(0); // 重置文件指针 echo_pin_file >> val; while(val == 0) { usleep(1000); echo_pin_file.seekg(0); echo_pin_file >> val; // ... 超时逻辑不变 ... } // ... 后续逻辑同理修改 ... } }
4. 添加信号处理,捕获挂起时的程序栈
给程序添加信号处理函数,当程序挂起时,通过kill -SIGUSR1 <进程ID>发送信号,打印调用栈和当前状态:
#include <signal.h> #include <execinfo.h> // 把inGPIO设为全局变量,方便信号处理函数访问 GPIO inGPIO(45); void signal_handler(int sig) { void *callstack[10]; size_t stack_size; // 打印调用栈 stack_size = backtrace(callstack, 10); fprintf(stderr, "\nSignal %d received, call stack:\n", sig); backtrace_symbols_fd(callstack, stack_size, STDERR_FILENO); // 打印当前ECHO引脚状态 printf("\nCurrent ECHO pin value: %d", inGPIO.getValue()); exit(1); } int main(){ // 注册信号处理 signal(SIGUSR1, signal_handler); signal(SIGINT, signal_handler); GPIO outGPIO(44); outGPIO.setDirection(OUTPUT); inGPIO.setDirection(INPUT); // ... 原有代码 ... }
编译时需要添加-rdynamic参数,让调用栈显示函数名:
g++ simple.cpp GPIO.cpp -o simple -pthread -rdynamic
5. 硬件层面排查
- 检查传感器供电:BeagleBone GPIO引脚供电能力有限,若传感器需要大电流,需外接独立电源。
- 验证接线:确认TRIG(GPIO44)、ECHO(GPIO45)接线无松动,GND连接可靠。
- 用示波器/逻辑分析仪观测TRIG和ECHO引脚波形,确认触发信号正常,且ECHO信号在检测时能正常返回。
内容的提问来源于stack exchange,提问作者subham sharma
相关产品推荐
相关产品推荐

