STM32MP157实验(三)——按键扫描和中断
文章目录
按键扫描
设计需求
通过按键扫描的方式实现,按下KEY_USER1(KEY1)按键,点亮LED_GREEN,再次按下熄灭LED_GREEN;按下KEY_USER2(KEY2)按键,点亮LED_YELLOW,再次按下熄灭LED_YELLOW.
基础知识
前面LED灯是控制GPIO输出,而按键则是读取GPIO电平,从而获知按键是否按下。
按键检测一般有两种:按键扫描(类似于轮询)和按键中断(中断检测)。
按键扫描:是间隔很短时间反复查询GPIO状态,从而的值是否有按键动作,这种方式简单,但是比较耗费资源。按键中断则是通过按键产生中断信号,从而实现按键的检测,这种方式需要使用到中断机制,需要对MCU有一定的了解。后面的按键中断实验将会详细介绍
按键一般占用一个GPIO口,通过检测该GPIO的电平变化得知按键操作,我们查看按键原理图如下
通过原理图我们可以分析出,当按键没有按下的时候,3.3V的VDD通过电阻直接连在我们的GPIO口上(PG2),那么我们的MCU读取到的PG2GPIO口的电平就是高电平。当我们的按键按下,左边电路导通,那么我们的GPIO口的电平就是低电平,MCU读取到的电平就是低电平。
我们常用的按键都是机械触点式按键,机械式按键在按下或释放过程中,由于机械弹性作用的影响,会伴随着机械抖动,如下所示:
抖动的时长与机械开关特性相关,一般为5ms-10ms。在这个抖动过程中,会产生多次高低电平,所以为了确定电平的稳定性,我们需要截取稳定的电平断,所以我们需要进行按键的消抖。按键消抖可以硬件上处理,即在硬件旁并联电容,吸收抖动的电平。也可以软件处理,即通过延时,避开抖动。
硬件设计
开发板上有4个按键,其中两个是复位按键和唤醒按键,这次实验不会介绍,后面介绍。另外两个按键就是我们的上面的KEY1和KEY2.KEY1接在了MCU的PG3脚,并且并联了一个C34电容用于硬件的消抖,还并联了一个TVS二极管防静电。同理,KEY2接在了PG2上。
STM32CubeIDE设计
MX设置
注意这里的红绿两个灯也需要配置,就像第一个实验那样
代码设计
driver_led.h
#ifndef DRIVER_LED_H_
#define DRIVER_LED_H_
#include "main.h"
#include "stm32mp1xx_hal.h"
#define LED_GREEN_ON() HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_RESET)
#define LED_GREEN_OFF() HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_SET)
#define LED_YELLOW_ON() HAL_GPIO_WritePin(LED_YELLOW_GPIO_Port, LED_YELLOW_Pin, GPIO_PIN_RESET)
#define LED_YELLOW_OFF() HAL_GPIO_WritePin(LED_YELLOW_GPIO_Port, LED_YELLOW_Pin, GPIO_PIN_SET)
extern void DemoLedInit(void);
extern void LedBlinking(void);
#endif /* DRIVER_LED_H_ */
driver_key.h
#ifndef DRIVER_KEY_H_
#define DRIVER_KEY_H_
#include "driver_led.h"
#include "main.h"
#include "stm32mp1xx_hal.h"
#define PUSH_DOWN GPIO_PIN_RESET
#define PUSH_UP GPIO_PIN_SET
#define KEY1_READ HAL_GPIO_ReadPin(KEY_USER1_GPIO_Port,KEY_USER1_Pin)
#define KEY2_READ HAL_GPIO_ReadPin(KEY_USER2_GPIO_Port,KEY_USER2_Pin)
extern void Scan_key1(void);
extern void Scan_key2(void);
#endif /* DRIVER_KEY_H_ */
driver_key.c
#include "driver_key.h"
#include <stdbool.h>
static bool key1_flag=false;
static bool key2_flag=false;
void Scan_key1(void){
if(KEY1_READ==PUSH_DOWN){//如果按键按下
HAL_Delay(5);//延迟5秒,消抖
if(KEY1_READ==PUSH_DOWN){
key1_flag=!key1_flag;
if(key1_flag){
LED_GREEN_ON();//改变绿灯的状态
}else{
LED_GREEN_OFF();
}
}
}
}
void Scan_key2(void){
if(KEY2_READ==PUSH_DOWN){
HAL_Delay(5);
if(KEY2_READ==PUSH_DOWN){
key2_flag=!key2_flag;
if(key2_flag){
LED_YELLOW_ON();
}else{
LED_YELLOW_OFF();
}
}
}
}
main.c
int main(void)
{
/* USER CODE BEGIN 1 */
static uint32_t sys_freq=0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
if(IS_ENGINEERING_BOOT_MODE())
{
/* Configure the system clock */
SystemClock_Config();
}
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
sys_freq=HAL_RCC_GetSystemCoreClockFreq();
if(!sys_freq){
return -1;
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
Scan_key1();
Scan_key2();
}
/* USER CODE END 3 */
}
实验结果
按下和松开KEY2,黄灯进行熄灭。同理KEY1
按键中断
设计需求
通过按键中断的方式实现,按下KEY2,点亮黄灯,再次按下,熄灭黄灯。同理按下KEY1,控制绿灯
基础知识
-
中断的基本概念
正常情况下,微处理器根据代码内容,按顺序执行指令。执行过程中,如果遇到其他紧急的事件需要处理,则先暂定当前任务,执行紧急事件,待紧急事件处理完后,再恢复到刚才暂定的地方继续执行。这个紧急事件就叫做中断。中断的执行流程如下图
-
中断的分类
CPU在执行指令时,检测到非法指令,比如除0,地址越界访问等,会产生中断,这种中断属于内部中断,也叫系统异常。由CPU外部设备引起的外部事件,比如GPIO中断,USART中断等,这属于外部中断。
ARM CM4内核可以支持256个中断(2^8=256,说明是用一个8至少8位的寄存器来表示中断)16个内部中断和240个外部中断。**对于stm32mp157的M4,没有用到CM4内核的所有资源,只是用到了一部分,只有10个内部中断和150个外部中断,总计160个中断。**如下图
-
中断优先级
M4拥有这么多中断,当这些中断同时发生时,CPU应该怎么来处理呢?因此就有了中断优先级的概念。中断优先级数越小,优先级越高。
抢占优先级:M4支持嵌套中断,所以当CPU在进行一个中断的时候,如果来了一个优先级更高的中断,CPU就立马取执行优先级更高的中断
响应优先级:响应优先级就是当优先级相同的中断同时发生,谁的响应优先级更高,就先执行谁,另一个则等待上一个执行完了在被执行。 -
中断优先级的分组
这是需要我们根据实际情况进行设计的
-
GPIO的中断
STM32MP157有PA-PI、PZ共10组GPIO,每组GPIO又有0~15共16个GPIO口。数字编号相同的GPIO(PA0,PB0.PC0…)共享一个中断源。如下图
硬件设计
就是按键的原理图,同上面的扫描实验
STM32CubeIDE设计
MX设置
代码设计
GPIO_init.c
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOG_CLK_ENABLE();//时钟使能
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_RESET);//初始化绿灯
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED_YELLOW_GPIO_Port, LED_YELLOW_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : KEY_USER3_Pin KEY_USER2_Pin */
GPIO_InitStruct.Pin = KEY_USER3_Pin|KEY_USER2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;//上升沿触发中断
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
/*Configure GPIO pin : LED_GREEN_Pin */
GPIO_InitStruct.Pin = LED_GREEN_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(LED_GREEN_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : LED_YELLOW_Pin */
GPIO_InitStruct.Pin = LED_YELLOW_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(LED_YELLOW_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI2_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI2_IRQn);
HAL_NVIC_SetPriority(EXTI3_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
}
头文件
/*
* driver_key_int.h
*
* Created on: Jan 1, 2022
* Author: lenovo
*/
#ifndef DRIVER_KEY_INT_H_
#define DRIVER_KEY_INT_H_
#include "main.h"
#include "stm32mp1xx_hal.h"
#define LED_GREEN_ON() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10,GPIO_PIN_RESET);
#define LED_GREEN_OFF() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10,GPIO_PIN_SET);
#define LED_YELLOW_ON() HAL_GPIO_WritePin(GPIOG,GPIO_PIN_8,GPIO_PIN_RESET);
#define LED_YELLOW_OFF() HAL_GPIO_WritePin(GPIOG,GPIO_PIN_8,GPIO_PIN_SET);
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin);
#endif /* DRIVER_KEY_INT_H_ */
.c文件
/*
* driver_key_int.c
*
* Created on: Jan 1, 2022
* Author: lenovo
*/
#include "driver_key_int.h"
#include <stdbool.h>
static bool key1_flag=false;
static bool key2_flag=false;
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin){
switch(GPIO_Pin){
case KEY_USER1_Pin:
{
key1_flag=!key1_flag;
if(key1_flag){
LED_GREEN_ON();
}else{
LED_GREEN_OFF();
}
break;
}
case KEY_USER2_Pin:
{
key2_flag=!key2_flag;
if(key2_flag){
LED_YELLOW_ON();
}else{
LED_YELLOW_OFF();
}
break;
}
break;
}
}
总结
加油