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
烧录完成后,开发板自动复位运行。
验证方法
- 观察LED1(红色)初始状态为熄灭。
- 每次按下按键S2并释放,LED1状态应翻转一次(亮→灭→亮)。
- 长按按键不释放时LED状态不重复翻转,验证下降沿触发而非电平触发。
- 若按键抖动导致一次按下出现多次翻转,可在回调中增加去抖延时(见常见问题)。
通过条件:每次单击按键,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/下,由平台构建系统自动包含。