基于Raspberry Pi 2的RTEMS曼彻斯特编码信号解码故障排查
问题描述
我正在开发一款RTEMS应用,用于解码发射板传输的曼彻斯特编码消息。两块Raspberry Pi 2通过GPIO 4与GND连接,其中一块作为发射端,每10秒发送一组符合IEEE曼彻斯特编码规则的脉冲序列,且首个比特为0。目前尝试通过边沿触发获取信号,但无法打印出信号内容,已调试两天仍未定位问题。
附当前代码:
#define GPIO_PIN 4 #define MAX_EDGES 4096 #define SIGNAL_GAP_MS 100 volatile int edge_count = 0; volatile rtems_interval edge_times[MAX_EDGES]; volatile rtems_interval last_edge_time = 0; volatile uint32_t edge_values[MAX_EDGES]; volatile bool new_signal_ready = false; void handler(void *arg) { rtems_interval current_time = rtems_clock_get_ticks_since_boot(); uint8_t value = rtems_gpio_bsp_get_value(0, GPIO_PIN); rtems_interval time_since_last_edge = current_time - last_edge_time; if (time_since_last_edge > rtems_clock_get_ticks_per_second() * SIGNAL_GAP_MS / 1000) { if (edge_count > 0) { new_signal_ready = true; } edge_count = 0; } if (edge_count < MAX_EDGES) { edge_times[edge_count] = current_time - last_edge_time; edge_values[edge_count] = value; edge_count++; } last_edge_time = current_time; } void init_gpio(void) { rtems_status_code sc; sc = rtems_gpio_bsp_select_input(0, GPIO_PIN, NULL); if (sc != RTEMS_SUCCESSFUL) { printk("Failed to set GPIO %d as input. Error code: %s\n", GPIO_PIN, rtems_status_text(sc)); return; } sc = rtems_gpio_bsp_set_resistor_mode(0, GPIO_PIN, PULL_UP); if (sc != RTEMS_SUCCESSFUL) { printk("Failed to set pull-up resistor for GPIO %d. Error code: %s\n", GPIO_PIN, rtems_status_text(sc)); return; } printk("GPIO %d initialized as input with pull-up.\n", GPIO_PIN); } void interrupt_config(void) { rtems_status_code sc; sc = rtems_gpio_bsp_enable_interrupt(0, GPIO_PIN, BOTH_EDGES); if (sc != RTEMS_SUCCESSFUL) { printk("Failed to enable interrupt on GPIO %d. Error code: %s\n", GPIO_PIN, rtems_status_text(sc)); return; } printk("Interrupt enabled on GPIO %d.\n", GPIO_PIN); sc = rtems_interrupt_handler_install(rtems_gpio_bsp_get_vector(0), "GPIO Interrupt Handler", RTEMS_INTERRUPT_UNIQUE, handler, NULL); if (sc != RTEMS_SUCCESSFUL) { printk("Failed to install interrupt handler. Error code: %s\n", rtems_status_text(sc)); return; } else { printk("Interrupt handler installed.\n"); } } void print_signal(void) { printk("Signal received with %d edges:\n", edge_count); for (int i = 0; i < edge_count; i++) { printk("%d", edge_values[i]); } printk("\nEnd of signal sequence.\n\n"); } void reset_signal_data(void) { edge_count = 0; for (int i = 0; i < MAX_EDGES; i++) { edge_times[i] = 0; edge_values[i] = 0; } } rtems_task Init(rtems_task_argument argument) { rtems_status_code sc; printk("Initializing ...\n"); sc = rtems_gpio_initialize(); if (sc != RTEMS_SUCCESSFUL) { printk("Failed to initialize GPIO. Error code: %s\n", rtems_status_text(sc)); return; } else { printk("GPIO API initialized.\n"); } init_gpio(); interrupt_config(); while (1) { if (new_signal_ready) { print_signal(); new_signal_ready = false; } reset_signal_data(); rtems_task_wake_after(1); } }
问题分析与修复方案
1. 主循环错误的信号重置逻辑
当前主循环每次迭代都会调用reset_signal_data(),直接清空中断正在收集的边沿数据,导致永远无法积累足够的边沿触发new_signal_ready。
修复:仅在处理完信号后再重置数据:
while (1) { if (new_signal_ready) { print_signal(); new_signal_ready = false; reset_signal_data(); // 处理完信号后再重置 } rtems_task_wake_after(1); }
2. 中断处理函数的逻辑顺序错误
首次触发中断时,last_edge_time初始为0,计算出的time_since_last_edge远大于SIGNAL_GAP_MS对应的tick数,直接触发间隙判断并清空edge_count,导致第一个边沿无法被记录。
修复:先记录当前边沿,再判断信号间隙:
void handler(void *arg) { rtems_interval current_time = rtems_clock_get_ticks_since_boot(); uint8_t value = rtems_gpio_bsp_get_value(0, GPIO_PIN); rtems_interval time_since_last_edge = current_time - last_edge_time; // 先记录当前边沿(排除首次触发的初始0值) if (last_edge_time != 0 && edge_count < MAX_EDGES) { edge_times[edge_count] = time_since_last_edge; edge_values[edge_count] = value; edge_count++; } // 判断是否是新信号的起始间隙 if (time_since_last_edge > rtems_clock_get_ticks_per_second() * SIGNAL_GAP_MS / 1000) { if (edge_count > 0) { new_signal_ready = true; } edge_count = 0; } last_edge_time = current_time; }
3. 中断向量获取错误
rtems_gpio_bsp_get_vector(0)参数不正确,应传入GPIO_PIN对应的索引而非固定值0,才能获取正确的GPIO4中断向量:
// 修正中断向量获取 sc = rtems_interrupt_handler_install(rtems_gpio_bsp_get_vector(GPIO_PIN), "GPIO Interrupt Handler", RTEMS_INTERRUPT_UNIQUE, handler, NULL);
4. 多环境下的变量原子性保障
虽然变量加了volatile,但中断与任务并发时,edge_count、new_signal_ready的读写需保证原子性,可使用RTEMS中断禁用/启用宏保护临界区:
以new_signal_ready的读写为例:
// 原子设置标记 rtems_interrupt_level level; rtems_interrupt_disable(level); new_signal_ready = true; rtems_interrupt_enable(level); // 原子读取标记 bool ready = false; rtems_interrupt_disable(level); ready = new_signal_ready; rtems_interrupt_enable(level);
修正后的完整代码
#define GPIO_PIN 4 #define MAX_EDGES 4096 #define SIGNAL_GAP_MS 100 volatile int edge_count = 0; volatile rtems_interval edge_times[MAX_EDGES]; volatile rtems_interval last_edge_time = 0; volatile uint32_t edge_values[MAX_EDGES]; volatile bool new_signal_ready = false; void handler(void *arg) { rtems_interval current_time = rtems_clock_get_ticks_since_boot(); uint8_t value = rtems_gpio_bsp_get_value(0, GPIO_PIN); rtems_interval time_since_last_edge = current_time - last_edge_time; rtems_interrupt_level level; // 首次触发不记录(避免初始0值导致的异常间隙判断) if (last_edge_time != 0) { rtems_interrupt_disable(level); if (edge_count < MAX_EDGES) { edge_times[edge_count] = time_since_last_edge; edge_values[edge_count] = value; edge_count++; } rtems_interrupt_enable(level); } // 判断信号间隙,触发新信号标记 if (time_since_last_edge > rtems_clock_get_ticks_per_second() * SIGNAL_GAP_MS / 1000) { rtems_interrupt_disable(level); if (edge_count > 0) { new_signal_ready = true; } edge_count = 0; rtems_interrupt_enable(level); } last_edge_time = current_time; } void init_gpio(void) { rtems_status_code sc; sc = rtems_gpio_bsp_select_input(0, GPIO_PIN, NULL); if (sc != RTEMS_SUCCESSFUL) { printk("Failed to set GPIO %d as input. Error code: %s\n", GPIO_PIN, rtems_status_text(sc)); return; } sc = rtems_gpio_bsp_set_resistor_mode(0, GPIO_PIN, PULL_UP); if (sc != RTEMS_SUCCESSFUL) { printk("Failed to set pull-up resistor for GPIO %d. Error code: %s\n", GPIO_PIN, rtems_status_text(sc)); return; } printk("GPIO %d initialized as input with pull-up.\n", GPIO_PIN); } void interrupt_config(void) { rtems_status_code sc; sc = rtems_gpio_bsp_enable_interrupt(0, GPIO_PIN, BOTH_EDGES); if (sc != RTEMS_SUCCESSFUL) { printk("Failed to enable interrupt on GPIO %d. Error code: %s\n", GPIO_PIN, rtems_status_text(sc)); return; } printk("Interrupt enabled on GPIO %d.\n", GPIO_PIN); // 修正:传入GPIO_PIN获取对应中断向量 sc = rtems_interrupt_handler_install(rtems_gpio_bsp_get_vector(GPIO_PIN), "GPIO Interrupt Handler", RTEMS_INTERRUPT_UNIQUE, handler, NULL); if (sc != RTEMS_SUCCESSFUL) { printk("Failed to install interrupt handler. Error code: %s\n", rtems_status_text(sc)); return; } else { printk("Interrupt handler installed.\n"); } } void print_signal(void) { printk("Signal received with %d edges:\n", edge_count); for (int i = 0; i < edge_count; i++) { printk("%d", edge_values[i]); } printk("\nEnd of signal sequence.\n\n"); } void reset_signal_data(void) { rtems_interrupt_level level; rtems_interrupt_disable(level); edge_count = 0; for (int i = 0; i < MAX_EDGES; i++) { edge_times[i] = 0; edge_values[i] = 0; } rtems_interrupt_enable(level); } rtems_task Init(rtems_task_argument argument) { rtems_status_code sc; rtems_interrupt_level level; printk("Initializing ...\n"); sc = rtems_gpio_initialize(); if (sc != RTEMS_SUCCESSFUL) { printk("Failed to initialize GPIO. Error code: %s\n", rtems_status_text(sc)); return; } else { printk("GPIO API initialized.\n"); } init_gpio(); interrupt_config(); while (1) { bool ready = false; // 原子读取new_signal_ready rtems_interrupt_disable(level); ready = new_signal_ready; rtems_interrupt_enable(level); if (ready) { print_signal(); // 原子重置标记 rtems_interrupt_disable(level); new_signal_ready = false; rtems_interrupt_enable(level); reset_signal_data(); } rtems_task_wake_after(1); } }
内容的提问来源于stack exchange,提问作者Mysterion
相关产品推荐
相关产品推荐

