我是靠谱客的博主 成就小馒头,最近开发中收集的这篇文章主要介绍STM32按键设计二之按键中断按键中断按键中断程序设计效果,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

按键中断

  • STM32使用按键中断需要配置端口IO,EXTI和NVIC。
  • 当按键按下的时候,引脚电平发生转变,同时触发沿触发EXTI中断,进而打断CPU(如果正在执行非中断程序或者中断级别低的程序)正在执行的程序,使程序跳转到EXTI中断中执行。

上篇使用了扫描处理中断,本篇将使用按键中断控制LED灯。按键按下则LED灯点亮,松开按键LED灯灭。【传送门:STM32按键设计一之扫描】

按键中断程序设计

  • 首先,在key.h中定义一个中断模式开启开关的宏定义,可以切换按键扫描和按键中断,使在程序设计的过程中拥有更高的自由度。
/* 定义使用中断模式 */
#define KEY_INTERRUPT_MODE       1
  • 按键状态位定义,同时定义SCAN模式和按键中断模式的枚举值。切换KEY_INTERRUPT_MODE的值0和1就可以切换程序使用SCAN模式或者终端模式。
#if !KEY_INTERRUPT_MODE
/* 用于SCAN模式 ----------- */
ENUM(KEY_STAT)
{
    KEY0_PRESS = 0x01,
    KEY1_PRESS = 0x02,
    WK_UP_PRESS = 0x04
};

#else
/* 按键中断模式 ----------- */
ENUM(KEY0_State)
{
    KEY0_DOWN,  /* 当KEY0按下时,IO口状态为低电平 */
    KEY0_UP
};

#define KEY0_EXTI_IRQn                  EXTI4_IRQn
#define KEY0_EXTIn_IRQHandler           EXTI4_IRQHandler
#define KEY0_EXTI_Line                  EXTI_Line4

#endif
  • key.c中定义按键初始化函数,同时使用于SCAN模式和按键中断模式。
/**
 * @name: KEY_Init
 * @description: 按键初始化函数,用于扫描模式和中断模式
 * @param {*}
 * @return {*}
 */
void KEY_Init(void)
{
    KEY_GPIO_Init();
#if KEY_INTERRUPT_MODE
    KEY_EXTI_Init();
#endif
}

定义GPIO口初始化函数和外部中断函数,在实际项目中采用定义的中断开关进行模式控制,方便使用。

/**
 * @name: KEY_GPIO_Init
 * @description: KEY IO口初始化 
 * @param {*}
 * @return {*}
 */
static void KEY_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_APB2PeriphClockCmd(KEY0_RCC_APB2Periph_CLK | KEY_UP_RCC_APB2Periph_CLK, ENABLE);

    GPIO_InitStruct.GPIO_Pin = KEY0_GPIO_Pin;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(KEY0_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = KEY1_GPIO_Pin;
    GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = KEY_UP_GPIO_Pin;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_Init(KEY_UP_GPIO_Port, &GPIO_InitStruct);
}

#if KEY_INTERRUPT_MODE
/**
 * @name: KEY_EXTI_Init
 * @description: 按键外部中断初始化
 * @param {*}
 * @return {*}
 */
static void KEY_EXTI_Init(void)
{

    EXTI_InitTypeDef EXTI_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
    /* exti 开启afio时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

    EXTI_DeInit();
    /* exti中断IO口配置 */
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
    /* exti配置 */
    EXTI_InitStruct.EXTI_Line = KEY0_EXTI_Line;
    EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStruct);

    EXTI_ClearITPendingBit(KEY0_EXTI_Line);

    /* exti中断nvic配置 */
    NVIC_InitStruct.NVIC_IRQChannel = KEY0_EXTI_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}
  • 定义中断处理函数中需要用到的EXTI中断使能和EXTI中断触发沿配置函数。
    自己定义一个EXTI中断使能函数,在STM32提供的库函数中没有单独的中断开关函数需要使用可以调用EXTI_Init()函数进行,但是EXTI需要操纵的寄存器太多不够简洁,同时也可以参考库函数中比较经典的最后两行位操作进行(库函数的操作更加简洁)。
/**
 * @name: EXTI_ITConfig
 * @description: 开关exti中断
 * @param {u32} EXTI_LINEn exti中断线
 * @param {FunctionalState} state exti中断状态
 * @return {*}
 */
static void EXTI_ITConfig(u32 EXTI_LINEn, FunctionalState state)
{
    if(state)
        EXTI->IMR |= EXTI_LINEn;
    else
        EXTI->IMR &= ~EXTI_LINEn;

}

定义一个给变触发边沿的函数,此函数也能实现对中断的开关

/**
 * @name: KEY_EXTI_Config
 * @description: exti结构体配置
 * @param {u32} EXTI_LINEn  exti线
 * @param {EXTITrigger_TypeDef} EXTITrigger_x   exti触发模式
 * @param {FunctionalState} state  exti中断使能
 * @return {*}
 */
static void KEY_EXTI_Config(u32 EXTI_LINEn, EXTITrigger_TypeDef EXTITrigger_x, FunctionalState state)
{
    EXTI_InitTypeDef EXTI_InitStruct;

    EXTI_InitStruct.EXTI_Line = EXTI_LINEn;
    EXTI_InitStruct.EXTI_Trigger = EXTITrigger_x;
    EXTI_InitStruct.EXTI_LineCmd = state;
    EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_Init(&EXTI_InitStruct);
}
  • 中断处理函数
/**
 * @name: KEY0_EXTIn_IRQHandler
 * @description: exti中断处理函数
 * @param {*}
 * @return {*}
 */
void KEY0_EXTIn_IRQHandler(void)
{
    /* 局部静态变量,用于触发沿判断 */
    static uint8_t is_rising = 0;

    if(!is_rising)
    {
        /* Falling Trigger */
        EXTI_ITConfig(KEY0_EXTI_Line, DISABLE); /* 关EXTI线中断 */
        delay_ms(10);   /* 去抖 */
        if(KEY0 == KEY0_DOWN)   /* 是否按下 */
        {
            /* 配置下次上升沿触发 */
            KEY_EXTI_Config(KEY0_EXTI_Line, EXTI_Trigger_Rising, ENABLE);
            is_rising = 1;

            KEY0_Down_callback();
        }
        else
        {
            EXTI_ITConfig(KEY0_EXTI_Line, ENABLE);
        }
    }
    else
    {
        /* Rising Trigger */
        EXTI_ITConfig(KEY0_EXTI_Line, DISABLE); /* 关EXTI线中断 */
        delay_ms(10); /* 去抖 */
        if(KEY0 == KEY0_UP) /* 是否松开 */
        {
            /* 配置下次下降沿触发 */
            KEY_EXTI_Config(KEY0_EXTI_Line, EXTI_Trigger_Falling, ENABLE);
            is_rising = 0;

            KEY0_Up_callback();
        }
        else
        {
            EXTI_ITConfig(KEY0_EXTI_Line, ENABLE);
        }
    }
    EXTI_ClearITPendingBit(KEY0_EXTI_Line);
}

在中断处理函数中进行了以下操作:

  1. 判断是上升沿触发还是下降沿触发,通过局部static变量实现
  2. 关闭EXTI中断
  3. 软件延时去抖
  4. 如果按下状态,则配置松开按键中断检测的边沿
  5. 设置下次中断沿为上升沿或者下降沿标志,即改变局部static变量的值
  6. 任务调用,向外部提供一个接口调用,通过接口调用可以实现分层设计,同时可以方便的实现不同任务。具体任务调用在接口中进行,不要有阻塞程序
  • 实现接口调用函数,在app.c文件中定义如下代码:
/* 具体任务 */
void LED_Callback(void)
{
    LED1 = !LED1;
}

/* 任务回调函数 */
void KEY0_Down_callback(void)
{
    LED_Callback();
    /* 本篇或者之前的,在中断中进行打印只为了测试 */
    printf("led toggle in key down interrupt ! n");
}

/* 任务回调函数 */
void KEY0_Up_callback(void)
{
    LED_Callback();
    /* 本篇或者之前的,在中断中进行打印只为了测试 */
    printf("led toggle in key up interrupt ! n");
}

这样就可以实现按键中断实现简单的按下按键点亮LED,松开关闭LED。

效果

在main.c中初始化,并调用:

int main(void)
{
	uint8_t key_sta = 0;
	uint32_t t = 0;

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	initSysTick();
	Usart1_Init(115200);
	LED1_Init();
	LED2_Init();
	KEY_Init();

	printf("Init Hardware OK ... n");

	for(;;)
	{
		t++;
		if(t >= 2000)
			t = 0;

#if !KEY_INTERRUPT_MODE	/* 仅用于测试 */
		/* 按键开关程序 */
		key_sta = KEY_Scan(0);
		if(key_sta == KEY0_PRESS)
		{
			LED1_Open();
			printf("key0 press! n");
		}
		if(key_sta == KEY1_PRESS)
		{
			LED1_Close();
			printf("key1 press! n");
		}
#endif

		/* 系统正常指示灯 */
		if(0 == t % 100)	
		{
			LED2_Toggle();
		}
		delay_ms(10);
	}
}

按键中断测试结果

  • 需要宏定义中KEY_INTERRUPT_MODE置为1
    在这里插入图片描述

按键扫描模式测试结果

  • 需要宏定义中KEY_INTERRUPT_MODE置为0
    在这里插入图片描述


喜欢请点个赞哦,谢谢!欢迎指正

最后

以上就是成就小馒头为你收集整理的STM32按键设计二之按键中断按键中断按键中断程序设计效果的全部内容,希望文章能够帮你解决STM32按键设计二之按键中断按键中断按键中断程序设计效果所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(71)

评论列表共有 0 条评论

立即
投稿
返回
顶部