3. 外部中断按键

任务目标

在STM32上使用RT-Thread,将按键配置为外部中断模式。按键按下时触发中断服务函数,在ISR中通过rt_sem_release释放信号量,唤醒一个LED控制线程,实现按键控制LED翻转。通过本案例掌握RT-Thread中断与线程间通信的基本方法。

硬件与工程配置

  • 硬件:STM32F407开发板(PA0接按键到GND,内部上拉;PD2接LED阳极,阴极经电阻到GND)
  • RT-Thread版本:v5.0+(使用标准版或Nano均可)
  • 需在board.h中确认已定义BSP_USING_GPIO,并在rtconfig.h中使能RT_USING_SEMAPHORE(默认开启)
  • 使用RT-Thread Studio或Keil工程,已包含gpio驱动框架

实现原理

按键按下产生下降沿中断,ISR中只做“释放信号量”这一件事(中断中不允许阻塞操作)。LED控制线程等待信号量,获取到后翻转LED电平。这样中断处理时间极短,耗时操作全部放在线程上下文中完成。

完整代码

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>

#define KEY_PIN    GET_PIN(A, 0)   /* PA0 按键 */
#define LED_PIN    GET_PIN(D, 2)   /* PD2 LED */

static rt_sem_t key_sem = RT_NULL;

/* LED控制线程 */
static void led_thread_entry(void *parameter)
{
    rt_uint32_t level = 0;
    while (1)
    {
        /* 等待信号量,超时时间设为永久等待 */
        if (rt_sem_take(key_sem, RT_WAITING_FOREVER) == RT_EOK)
        {
            level = !level;
            rt_pin_write(LED_PIN, level);
        }
    }
}

/* 外部中断回调函数(中断上下文) */
static void key_isr(void *args)
{
    /* 释放信号量,唤醒LED线程 */
    rt_sem_release(key_sem);
}

static int exti_key_sample(void)
{
    /* 初始化LED引脚为输出 */
    rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);
    rt_pin_write(LED_PIN, PIN_LOW);

    /* 初始化按键引脚为上拉输入 */
    rt_pin_mode(KEY_PIN, PIN_MODE_INPUT_PULLUP);

    /* 创建二值信号量,初始值为0 */
    key_sem = rt_sem_create("key_sem", 0, RT_IPC_FLAG_PRIO);
    if (key_sem == RT_NULL)
    {
        rt_kprintf("create semaphore failed\n");
        return -1;
    }

    /* 绑定中断回调,下降沿触发 */
    rt_pin_attach_irq(KEY_PIN, PIN_IRQ_MODE_FALLING, key_isr, RT_NULL);
    rt_pin_irq_enable(KEY_PIN, PIN_IRQ_ENABLE);

    /* 创建LED控制线程 */
    rt_thread_t tid = rt_thread_create("led_thread",
                                       led_thread_entry,
                                       RT_NULL,
                                       512,
                                       10,
                                       20);
    if (tid != RT_NULL)
    {
        rt_thread_startup(tid);
    }

    rt_kprintf("EXTI key demo started, press the key to toggle LED\n");
    return 0;
}

/* 使用MSH命令调用示例 */
MSH_CMD_EXPORT(exti_key_sample, exti key sample);

编译与运行

  1. 将上述代码保存为exti_key_sample.c,放入工程的applications目录
  2. 编译下载后,在MSH控制台输入命令:
msh> exti_key_sample

系统会打印EXTI key demo started,表示初始化成功。

验证方法

  • 按下按键(PA0接地),每次按下并释放时LED(PD2)电平应翻转一次(亮→灭→亮)
  • 在MSH中输入list_thread可看到led_thread处于suspend状态(等待信号量);按下按键瞬间该线程变为ready状态执行翻转操作
  • 输入list_sem可看到key_sem信号量,初始计数值为0,按键按下后短暂变为1(随后被线程获取回0)

通过标准:每次按键按下,LED状态翻转一次,且系统无崩溃、无卡死,MSH命令响应正常。

关键说明

  • 中断回调key_isr中只做信号量释放,不执行任何延时或打印操作,保证中断服务时间最短
  • 按键未按下时引脚为高电平(内部上拉),按下接地产生下降沿,因此使用PIN_IRQ_MODE_FALLING触发
  • 若按键接法相反(按下接高电平),需改用PIN_IRQ_MODE_RISING触发模式
  • 信号量创建为二值信号量(初始值0,最大值1),可避免按键快速连按时信号量堆积导致LED状态与按键次数不一致