You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 23:07:34