目前共有2篇帖子。
【程序】STM32F107通過外部中斷來響應按鍵
1樓 巨大八爪鱼 2017-6-8 23:03
/* 單片機: STM32F107VCT6
** 系統時鐘: 72MHz
** 外部晶振: 25MHz
**/
#include <stm32f10x.h>

#define KEY_SYNC 0 // 若id!=0, 則認為是可能按下了按鍵id; 否則沒有按鍵按下
#define KEY_DOWN 1 // 經過SysTick採樣後才認為按鍵真的按下了
#define KEY_PROCESSING 2 // 開始處理按鍵
#define KEY_PROCESSED 3 // 按鍵處理完畢
#define KEY_UP 4 // 按鍵已鬆開 (沒有新按鍵按下時將保持這個狀態, key.id的值不會丟失)

// 0->1為不定長延時
// 1->2為15ms延時
// 3->4為n個15ms的延時, 直到按鍵鬆開

EXTI_InitTypeDef exti;

struct key_state
{
    uint8_t id;
    uint8_t step;
} key;

int main(void)
{
    GPIO_InitTypeDef gpio;
    NVIC_InitTypeDef nvic;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE, ENABLE);
    
    /* PE13~15為低電平點亮的LED燈 */
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_SetBits(GPIOE, gpio.GPIO_Pin); // 熄滅LED
    GPIO_Init(GPIOE, &gpio);
    
    /* PE10~12為低電平有效的按鍵 */
    gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    gpio.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
    GPIO_Init(GPIOE, &gpio);
    
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource10);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource11);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource12);
    SysTick_Config(0x107ac0); // 採樣用的定時器, 數值=72000000Hz*0.015s=1080000ticks=0x107ac0
    
    // 允許執行外中斷處理函數
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    nvic.NVIC_IRQChannel = EXTI15_10_IRQn;
    nvic.NVIC_IRQChannelCmd = ENABLE;
    nvic.NVIC_IRQChannelPreemptionPriority = 0; // SysTick中斷不可搶佔按鍵中斷
    nvic.NVIC_IRQChannelSubPriority = 1; // 先處理SysTick後處理按鍵中斷
    NVIC_Init(&nvic);
    
    // 開外中斷
    exti.EXTI_Line = EXTI_Line10 | EXTI_Line11 | EXTI_Line12;
    exti.EXTI_LineCmd = ENABLE;
    exti.EXTI_Mode = EXTI_Mode_Interrupt;
    exti.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_Init(&exti);
    
    while (1)
    {
        // 按鍵處理
        if (key.step == KEY_PROCESSING)
        {
            GPIOE->ODR ^= GPIO_Pin_12 << key.id; // 根據按鍵號切換指定LED燈的狀態
            key.step = KEY_PROCESSED;
        }
    }
}

void EXTI15_10_IRQHandler(void)
{
    key.id = 0;
    key.step = KEY_SYNC;
    if (EXTI_GetFlagStatus(EXTI_Line10) == SET)
    {
        EXTI_ClearFlag(EXTI_Line10);
        key.id = 1;
    }
    if (EXTI_GetFlagStatus(EXTI_Line11) == SET)
    {
        EXTI_ClearFlag(EXTI_Line11);
        key.id = 2;
    }
    if (EXTI_GetFlagStatus(EXTI_Line12) == SET)
    {
        EXTI_ClearFlag(EXTI_Line12);
        key.id = 3;
    }
    
    // 關外中斷
    exti.EXTI_LineCmd = DISABLE;
    EXTI_Init(&exti);
}

void SysTick_Handler(void)
{
    if (key.id == 0)
        return;
    
    if (key.step < KEY_PROCESSING)
    {
        if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_9 << key.id) == 0)
            key.step++;
        else
        {
            key.step = KEY_SYNC; // 按鍵提前鬆開, 無效
            key.id = 0;
            exti.EXTI_LineCmd = ENABLE;
            EXTI_Init(&exti);
        }
    }
    else if (key.step == KEY_PROCESSED)
    {
        // 等待按鍵釋放
        if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_9 << key.id) != 0)
        {
            key.step = KEY_UP;
            exti.EXTI_LineCmd = ENABLE;
            EXTI_Init(&exti);
        }
    }
}

2樓 巨大八爪鱼 2017-6-8 23:35
由於按鍵釋放後有很大幾率發生抖動,所以實際上step保持在KEY_UP上的時間非常短。
由於按鍵抖動時低電平的時間不能保持15~30ms,所以不會重複觸發按鍵處理程序。

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
 
 
©2010-2024 Arslanbar [手機版] [桌面版]
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。