-
2021-04-29 11:50:20
按键中断
- 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); }
在中断处理函数中进行了以下操作:
- 判断是上升沿触发还是下降沿触发,通过局部static变量实现
- 关闭EXTI中断
- 软件延时去抖
- 如果按下状态,则配置松开按键中断检测的边沿
- 设置下次中断沿为上升沿或者下降沿标志,即改变局部static变量的值
- 任务调用,向外部提供一个接口调用,通过接口调用可以实现分层设计,同时可以方便的实现不同任务。具体任务调用在接口中进行,不要有阻塞程序
- 实现接口调用函数,在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多按键中断_按键_多路按键中断_多路按键中断
2021-09-10 19:28:31实现多按键间的按键中断,且互不干扰,实测可以重复进入中断 -
多按键中断,多按键中断51,C,C++
2021-09-10 19:28:31实现多按键间的按键中断,且互不干扰,实测可以重复进入中断 -
STM32之按键中断(通用)
2020-09-15 01:27:22对应文章的按键中断的完整例程,简洁易懂,容易记忆,由浅入深,这里非要我凑足50字,我只能说下载不吃亏,吃亏不下载。灯的引脚和中断的引脚根据你的需求替换一下就可以了 -
单片机MSP430G2553--常规按键中断程序.txt
2020-03-15 12:06:42单片机MSP430G2553常规按键中断程序,其他程序比如按键中断、定时器中断、PWM等单片机MSP430G2553系列代码我会陆续上传,敬请期待。 -
关于stm32按键中断总结
2020-07-20 00:06:28本文主要总结了一些关于stm32按键中断的知识,一起来学习一下 -
51单片机按键中断
2018-04-28 01:39:3251单片机按键中断代码,初始化:边沿触发方式下降沿,打开总的中断。 -
STM32F103R6芯片+Proteus仿真+Keil5实现按键中断点亮LED
2020-06-09 12:01:31STM32F103R6芯片+Proteus仿真+Keil5实现按键中断点亮LED 参考书目《STM32嵌入式微控制器快速上手》(第二版)第六章,实例6.5.1(连线图) -
按键中断,按键中断的原理,C/C++
2017-09-18 13:45:03海思的GPIO操作C代码 -
PIC单片机按键中断程序的设计技巧.doc
2018-02-01 10:45:15PIC单片机按键中断程序的设计技巧,内含按键 长按键代码及注释! -
【51程序】基于51单片机的按键中断控制实现不同延时的流水灯
2021-08-17 15:15:42用定时器中断控制流水灯延时时间,用外部中断0控制按键实现切换流水灯延时时间,外部中断1控制实现切换流水灯方向; -
ZigBee-cc2530——裸机按键中断实验
2022-03-21 10:31:09按键S1控制LED0灯的亮灭,按键S2控制LED1灯的亮灭。 附带实验报告 -
单片机MSP430G2553--按键中断控制LED亮灭状态切换.txt
2020-03-15 12:08:41单片机MSP430G2553的按键中断控制LED亮灭状态切换程序,其他程序比如非按键中断、定时器中断、PWM等单片机MSP430G2553系列代码我会陆续上传,敬请期待。 -
led显示按键中断次数
2019-11-25 20:31:11#include #include sbit K=P3^4; sbit P2_2 = P2^2; sbit P2_3 = P2^3; unsigned char Counter; unsigned char table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82, 0xf 8,0x80,0x90};...P0=tabl -
从ARM裸机程序之按键中断方式控制LED
2017-11-12 11:18:47从ARM裸机看驱动之按键中断方式控制LED(一)http://blog.csdn.net/u010872301/article/details/78494383 -
树莓派A20 按键中断驱动程序(包括Makefile 与应用层测试程序)
2017-07-22 10:58:42是风火轮出品的树莓派A20 按键中断驱动程序。程序中包括PI10,PI11两个按键中断,强调一下,这只是一个简单的测试程序,而不是实际产品中使用的驱动程序。 -
009、按键中断识别应用---0~255计数器.7z
2021-06-18 16:29:00009、按键中断识别应用---0~255计数器.7z -
【轻松玩蓝牙】序列之11:按键中断
2020-08-12 14:35:01本节我们主要是从下面4个方面进行讲解:1.准备工作、2.硬件部分、3.软件部分、4.效果展示。 -
STM32F407按键中断,输出到串口实时的状态。
2020-07-29 17:15:31根据4个按键控制LED0.1状态及蜂鸣器状态,通过串口中断输出相应状态。注意:key0为按键1(控制两个灯翻转状态),key1为按键2(控制LED0,即右边小灯),key2为按键3,(控制LED1,即左边小灯)key_up为按键4,... -
基于Stm32CubeMx的板载按键中断测试
2016-10-18 13:12:04基于Stm32CubeMx的板载按键中断测试 -
奋斗STM32开发板按键中断_EXTI_例程讲解-V3.pdf
2020-02-26 22:37:06板子加电后,按动板子上K1-K3按键,可控制对应的LED1-LED3的亮灭,该实验学习了外部中断(EXTI)程序的编制及控制流程。 为了优化外设数目,可以把一些复用功能重新映射到其他引脚上。设置复用重映射和调试I/O配置... -
51单片机程序 时钟及按键中断
2014-04-03 22:40:0451单片机程序 实现时钟及按键中断的功能,通过protues仿真没问题的 -
按键中断程序
2017-07-21 11:27:17STM32装载按键4x4,中断方法!!! -
s3c2440在keil下的按键中断
2018-10-07 22:31:25在移植ADS下的S3C2440的.s文件工程模版下,进行的按键中断程序 -
cc2530按键中断代码及实验步骤
2016-12-02 10:54:43cc2530按键中断代码及实验步骤 -
f103按键中断,PWM输出.zip
2019-10-29 22:37:01STM32F103与L298n电机驱动模块按键控制电机正反转及PWM调速 -
MSP430-GRACE 实战(二):按键中断
2021-12-23 21:03:13MSP430-GRACE 实战(二):按键中断文章目录
MSP430-GRACE 实战(二):按键中断
Grace 是 Graphical Code Engine 的缩写,是 TI 为了方便用户开发 MSP430 提供的图形化代码配置工具,但是目前只有部分 MSP430 的型号支持 Grace,如 G2 系列
本系列文章使用 Grace 配置 MSP430 外设,快速实现功能,帮助大家进一步了解 MSP430
文章侧重点是功能的使用,寄存器原理只有部分的阐述,适合有一定基础同学(不论是 STM32还是MSP430基础),深入的原理需要自行探索
工程中有所有实践 Demo 都有通用步骤,熟悉的同学可以直接跳过
一、开发平台
1.1 硬件平台
MSP430G2553 口袋实验平台:
1.2 软件平台
是 TI 公司推出的集成开发环境:CCS V5.5(Code Composer Studio)
仅 5 和 6 版本支持
二、原理分析
首先看一下按键的接口:
按键对应的接口是 P1.3 接口,读取按键可以使用扫描读取和中断读取,此处我们使用中断读取方式,按键按下时触发中断,编写中断处理函数,在中断中设置对应的标志位,主函数根据标志位进行点灯操作,对 MSP430 来说按键输入中断步骤如下:
- 配置系统时钟,使单片机能稳定运行
- 通过 PxDIR 将 IO 方向设为输入
- 通过写 PxIES,决定中断的边沿是上升沿、下降沿 或两种情况均中断
- 如果是机械按键输入,可以通过 PxREN 启用内部上下拉电阻,根据按键的接法,设定 PxOUT(决定最终是上拉电阻还是下拉电阻)
- 通过配置 PxIE 寄存器开启 IO 中断,通过“_enable_interrupts();”开启总中断
- 在中断子函数中,通过 if 语句查询具体中断的 IO 口
- 根据具体 IO 的输入,编写事件处理函数
- 退出中断前,使用 PxIFG = 0 来清除 IO 中断标志位
其中前5个配置我们都可以使用 Grace 进行配置,后面的则需要我们在代码逻辑中编写
三、GRACE 配置
3.1 新建工程(通用步骤)
点击新建 CCS 工程:
配置工程信息:
工程建立完成:
3.2 配置时钟(通用步骤)
点击 main.cfg 下面的 Device Overview,进去后 Device Overview 颜色会变淡:
我们看到设备界面有个 DVCC 的设置,默认显示 1.8V,这个根据实际单片机供电设置,我这里设置 3.3V,因为 Grace 要知道单片机的实际供电电压,因为在低电压情况下,某些外设不能使用,Grace 会自动屏蔽配置该外设的功能,之后点击 BCS+(Basic Clock System+) 模块配置时钟,点击后弹出 Overview 界面如下:
其中 introduction 是关于该模块的介绍,下面两个则是两个代码使用例子用作参考
在 Overview 旁边有 BasicUser、Power User、Regisiter三个配置项,区别如下:
Grace 的 Basic User 模式配置时钟,可以配置最基础的功能,界面简单,可以瞬间即可完成高速时钟和低速时钟配置:
Power User 是基于 MSP430 的时钟树,列出了关键的分频倍频等寄存器配置接口,方便我们详细开发:
这里配置我们可以根据 MSP430 的时钟树进行配置:
至于 Regisiter 模式则是以图像化的方式配置 BCS+ 的各个寄存器,适合对寄存器非常了解的人进行开发:
这里我们直接选择第一项进行时钟配置,高速时钟选 12M,低速的的话因为没有 32.768K 晶振,配置 12K 就行,配置后如下:
然后我们
Crtl + S
保存一下配置再编译一下,Grace 会自动根据配置,生成代码:到这时钟配置完成
3.3 关闭看门狗(通用步骤)
看门狗实际就是一个定时器,只不过在定时到达时,如果 CPU 没有去操作看门狗寄存器,看门狗就会复位单片机,这里我们没有使用到,但系统自动把它使能了,所以要把它关闭,否则会影响程序正常执行,点击看门狗配置项,取消选 Enable 就行,这里我们开启和关闭其他模块都是一样的操作,勾选 Enable 开启,不勾选则关闭:
3.4 输入中断配置
进入到 GPIO 配置,我们将两个 LED 灯的 GPIO 设置为 输出模式,用来控制 LED 灯:
配置 P1.3 设置为输入模式,设置默认上拉,同时开启下降沿中断
之后点击中断代码生成
点击生成后,我们保存一下,然后编译,Grace 会在 InterruptVectors_init.c 中生成中断回调函数
用户的代码逻辑直接在回调函数内编写就行
四、代码编写
4.1 代码编写位置
Grace 在生成的代码中给用户预留了代码编写位置,用户可以在 Grace 生成的初始化代码中自行添加代码,具体位置在如下的注释之间,这样二次生成代码不会覆盖用户代码:
/* USER CODE START (section: GPIO_graceInit_prologue) */ /* User initialization code */ /* USER CODE END (section: GPIO_graceInit_prologue) */
4.2 按键中断代码
main.c 文件代码
unsigned char state=0; int main(void) { Grace_init(); // Activate Grace-generated configuration // >>>>> Fill-in user code here <<<<< while(1) { if(state == 0) { P1OUT &= ~BIT0; P1OUT &= ~BIT6; }else if(state == 1) { P1OUT |= BIT0; P1OUT &= ~BIT6; }else if(state == 2) { P1OUT &= ~BIT0; P1OUT |= BIT6; }else if(state == 3) { P1OUT |= BIT0; P1OUT |= BIT6; } __delay_cycles(10000); } return (0); }
中断文件代码
/* USER CODE START (section: InterruptVectors_init_c_prologue) */ /* User defined includes, defines, global variables and functions */ extern unsigned char state; /* USER CODE END (section: InterruptVectors_init_c_prologue) */ //............省略中间代码............// #pragma vector=PORT1_VECTOR __interrupt void PORT1_ISR_HOOK(void) { /* USER CODE START (section: PORT1_ISR_HOOK) */ /* replace this comment with your code */ if( (P1IFG&(~P1DIR) ) == BIT3) { state++; state %= 4; } P1IFG=0; /* USER CODE END (section: PORT1_ISR_HOOK) */ }
这里代码逻辑就是中断服务函数中对一个标志位(state)累加,主函数循环根据标志位判断执行点灯,这里我没有加上按键防抖,实际使用需要注意
4.3 程序下载(通用步骤)
代码编写完成后,构建代码,然后连接开发板调试仿真程序:
五、实验现象
实验现象就是按键按下后 LED 根据按下次数有不同的显示情况:
-
STM32按键扫描/按键中断/外部中断
2020-07-14 03:56:50接触STM32有一段时间了,也算是简单入了门,但由于一些原因,今天才来写本应该是入门级的按键相关程序,分为扫描模式和中断模式 -
OLED12864显示屏,12位ADC,按键中断,msp430f5529单片机控制
2016-12-03 21:43:04OLED12864是通过IIC与单片机进行通信的,该代码是基于msp430f5529单片机,运用了单片机中的12位ADC,按键中断和IIC通信部分。 代码功能是:实现手动步进,和ADC检测当前电压最后结果在OLED12864上显示的功能。 程序... -
protues C51单片机 按键中断控制LED灯
2020-06-27 19:11:00在完成交通灯基本功能基础上,当有急救车到达时,两向交通信号为全红,以便让急救车通过。假定急救车通过路口时间为10秒,急救车通过后,交通灯恢复中断前状态。本实验题以按键为中断申请,表示有急救车通过。