3. 按键中断响应

任务目标

在Contiki-NG中配置GPIO外部中断,使开发板上的用户按键按下时触发中断回调,在回调中翻转LED状态,并利用Contiki的事件驱动机制(process_poll)通知进程执行实际处理。通过本例掌握中断上下文与进程上下文之间的数据传递方式。

硬件与环境

  • 硬件:Zolertia RE-Mote(CC2538)或任意Contiki-NG支持的带按键与LED的开发板
  • 依赖:Contiki-NG 4.x,已配置好工具链(arm-none-eabi-gcc)
  • 引脚映射(RE-Mote):按键S2接GPIO PC0(低有效),LED1接GPIO PC1(高电平点亮)

关键完整代码

创建文件 button-interrupt.c,内容如下:

#include "contiki.h"
#include "dev/gpio-hal.h"
#include "dev/leds.h"
#include <stdint.h>

/* 按键引脚定义(RE-Mote: S2 = PC0) */
#define BUTTON_PORT GPIO_PORT_TO_BASE(GPIO_C_BASE)
#define BUTTON_PIN  GPIO_PIN_MASK(0)

static struct process button_process;

/* 中断回调:置位标志并轮询进程 */
static void button_isr(void)
{
  process_poll(&button_process);
}

PROCESS(button_process, "Button interrupt demo");
PROCESS_THREAD(button_process, ev, data)
{
  PROCESS_BEGIN();

  /* 配置按键引脚为输入,使能内部上拉 */
  gpio_hal_arch_pin_set_input(BUTTON_PORT, BUTTON_PIN);
  gpio_hal_arch_pin_set_pullup(BUTTON_PORT, BUTTON_PIN);

  /* 注册外部中断:下降沿触发(按键按下时拉低) */
  gpio_hal_arch_pin_set_int(BUTTON_PORT, BUTTON_PIN,
                            GPIO_HAL_PIN_INT_EDGE_FALLING,
                            button_isr);

  while(1) {
    PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_POLL);

    /* 按键按下:翻转LED1状态 */
    if(leds_get() & LEDS_RED) {
      leds_off(LEDS_RED);
    } else {
      leds_on(LEDS_RED);
    }
  }

  PROCESS_END();
}

/* 自动启动进程 */
AUTOSTART_PROCESSES(&button_process);

构建与烧录

在Contiki-NG源码根目录下执行:

make TARGET=zoul BOARD=remote button-interrupt.upload

烧录完成后,开发板自动复位运行。

验证方法

  1. 观察LED1(红色)初始状态为熄灭。
  2. 每次按下按键S2并释放,LED1状态应翻转一次(亮→灭→亮)。
  3. 长按按键不释放时LED状态不重复翻转,验证下降沿触发而非电平触发。
  4. 若按键抖动导致一次按下出现多次翻转,可在回调中增加去抖延时(见常见问题)。

通过条件:每次单击按键,LED1状态稳定翻转一次,无漏触发或重复触发。

关键说明

  • 中断回调button_isr中只调用process_poll,不直接操作LED——这是Contiki事件驱动编程的核心模式:中断上下文只做最简操作,实际处理放回进程上下文执行。
  • PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_POLL)使进程仅在收到轮询事件时唤醒,避免忙等待。
  • 若更换开发板,需修改BUTTON_PORT/BUTTON_PIN及LED宏定义,并确认GPIO中断触发沿与硬件电路匹配。

常见问题

  • 按键无响应:检查引脚编号是否正确,确认GPIO中断已在gpio_hal_arch_pin_set_int中使能;部分平台需额外调用gpio_hal_arch_enable_interrupt()。
  • 一次按下翻转多次:机械按键存在抖动,可在button_isr中设置定时器去抖,或使用clock_delay()延时约20ms后再次确认电平。
  • 编译报错找不到gpio-hal.h:确认目标平台为zoul,该头文件位于arch/cpu/cc2538/dev/下,由平台构建系统自动包含。
按键按下 硬件中断触发 ISR回调 process_poll() 进程唤醒 PROCESS_EVENT_POLL 进程上下文执行 翻转LED状态 等待下次中断 进程挂起 中断上下文:只做最简操作(poll),不执行耗时逻辑 进程上下文:处理实际业务(LED翻转),可安全调用阻塞函数