Goal
LED四种模式:
- LED0亮,LED1灭
- LED0灭,LED1亮
- LED0和LED1同时亮
- LED0亮,LED1灭,200ms后,LED0灭,LED1亮,如此循环
1. KEY0按键第一次按下时,LED按模式一显示,第二次按下时,LED模式+1,如此循环
2. KEY1按键按下时,取消KEY0的功能,LED0和LED1灯都灭
Background
嵌套向量中断控制器(NVIC)
外部中断/事件控制器(EXTI)
外部中断/事件控制器包含多达23个用于产生事件/中断请求的边沿检测器。每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或边沿触发)。简而言之,中断的作用为,当原程序执行时,按下中断按键,程序去执行中断后需执行的任务,执行完后回到原程序接着执行原本内容。
【注意:在中断函数中不可使用HAL_Delay();该语句会讲程序的时钟系统打乱。】
图 1 外部中断/事件控制器框图
Experiment Steps
- 配置IDE中用于生成代码的参数
按照实验1:点亮LED中步骤(上一篇文章),分别配置好寄存器。
在STM32F4ZG核心板原理图中,找到用户自定义按键,如图2。
图2 用户自定义按键
实验目标1要求使用PB9(KEY0)为输入端口控制LED模式切换,由图2可得设置GPIO_Input模式,图中显示并无上拉、下拉电阻,则端口配置为No pull-up and no pull-down(也可以配置为上拉)。
图3 PB9输入端口配置参数
实验目标2要求使用PB8(KEY1)为中断端口,由图2得当开关断开时PB8标识处为高电平,当开关闭合电路导通而PB8标识处接地为低电平,高电平变为低电平为下降沿触发,因此该中断端口选择External Interrupt Mode with Falling edge trigger detection并且因为没有上、下拉电阻,端口也配置为无上下拉电阻。还需在嵌套向量中断控制器中勾选EXTI line[9:5] interrupts使其启用。对PB8中断端口的配置如图4。
图4 PB8中断端口皮配置参数
- 实验①:按键切换LED闪烁模式
【在上次写的LED实验报告基础上完成该实验】
编程思维:
每按下一次KEY0按键,程序分别进入0,1,2,3,4四种LED闪烁的模式,用flag和!flag标识每次进入while(1)循环一次,并在按键按下后改变用于标记处于第几个LED闪烁模式的变量b。
编程代码:
首先在main.h中加入typedef enum {FALSE = 0,TRUE = 1} bool;以激活布尔函数的使用。
其次在main.c中设置全局变量b并且在int main(void){ }中进行编程如下(其他部分不改变):
【注意:将代码写在/* USER CODE BEGIN */和/* USER CODE END*/之间不会被重新generate code的时候覆盖掉】
#include "main.h"
#include "gpio.h"
void SystemClock_Config(void);
int b = 0;
int main(void)
{
bool flag = FALSE;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
while (1)
{
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == GPIO_PIN_RESET){
if(!flag){
if(b == 4){
b = 1;
}
else{
b++;
}
flag = TRUE;
}
}
else{
if(flag){
flag = FALSE;
}
}
if(b == 0){
HAL_GPIO_WritePin(led0_GPIO_Port, led0_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(led1_GPIO_Port, led1_Pin, GPIO_PIN_SET);
}
else if(b == 1){
HAL_GPIO_WritePin(led0_GPIO_Port, led0_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(led1_GPIO_Port, led1_Pin, GPIO_PIN_SET);
}
else if(b == 2){
HAL_GPIO_WritePin(led0_GPIO_Port, led0_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(led1_GPIO_Port, led1_Pin, GPIO_PIN_RESET);
}
else if(b == 3){
HAL_GPIO_WritePin(led0_GPIO_Port, led0_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(led1_GPIO_Port, led1_Pin, GPIO_PIN_RESET);
}
else if(b == 4){
HAL_GPIO_WritePin(led0_GPIO_Port, led0_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(led1_GPIO_Port, led1_Pin, GPIO_PIN_SET);
HAL_Delay(200);
HAL_GPIO_WritePin(led0_GPIO_Port,led0_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(led1_GPIO_Port,led1_Pin,GPIO_PIN_RESET);
HAL_Delay(200);
}
}
}
- 实验②:设置中断函数实验报告
编程思维:
需要再KEY1按键按下后,取消KEY0功能,让led0和led1同时灭。
在stm32f4xx_hal_gpio.c中看到函数void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin),其中有HAL_GPIO_EXTI_Callback(GPIO_Pin);(还看到IQRHandler中有一句__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);作用为执行后清空中断);而紧接着看到函数__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin),函数前有个__weak,__weak的意思是这个函数为弱函数,我们只需要在main.c中写一个void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin),便可覆盖弱函数,弱函数只在没有自己写入的强函数下执行。
编程代码:
在以上代码中int b = 0;和int main(void)中的位置加入以下函数即可。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY1_Pin){
b=0;
}
}
Result
实验①:
随着KEY0的按下,D1作为LED0,D2作为LED1。在不按下时,LED0和LED1同时灭;在按下第一次时,LED0亮,LED1灭;在按下第二次时,LED0灭,LED1亮;在按下第三次时,LED0和LED1同时亮;在按下第四次时,LED0亮,LED1灭,200ms后,LED0灭,LED1亮,如此循环。在随着按键的按下,这四种模式也一直循环。(图5只选取了前三种模式,第四种模式为动态闪烁不方便截图)
实验②:
KEY1按下,中断原程序运行,回到b = 0模式,也即LED0和LED1同时灭。只有当KEY0再按下便再从b = 0模式重新执行实验①内容。
图5 实验①的三种模式闪烁情况