(蓝桥杯)STM32G431RBT6(按键中/模块化)
一、CubeMx配置
看上一篇CSDN有讲解
二、Keil5配置
1、新建空白页
2、ctrl+s保存、添加文件.c
3、添加文件/.h
4、在目录栏里面添加key.c
三、模块化代码
1、报错原因
update_key_status();
(1)是因为原来的stm32g4xx_it.c文件里面没有包含“key.h”
#include "key.h"
(2)main文件里面也要包含
2、key.c代码
#include "key.h"
uint16_t key_delay_filter_count[4]={0,0,0,0};
key_status keys_status[4];//一共有四个按键,所以需要一个数组
void update_key_status(void)//第一个按键的状态
{
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==0)
{
key_delay_filter_count[0]<<=1;
key_delay_filter_count[0]|=0x0001;
if(key_delay_filter_count[0] >0x03FF)
{
if(keys_status[0]==key_released)
keys_status[0]=key_pressed;
}
}
else
{
key_delay_filter_count [0]>>=1;
if(key_delay_filter_count[0]==0)
{
keys_status[0]=key_released;
}
}
}
uint16_t get_key_status(uint8_t key_num)
{
return keys_status[key_num];
}
void do_it_once(uint8_t key_num)
{
keys_status[key_num]=key_waiting_for_release ;
}
3、key.h代码
#ifndef __KEY_H__
#define __KEY_H__
#include "main.h"//这一步要包含,否则最起码类型的定义可能跳转不出来
typedef enum
{
key_released=0U,
key_pressed,
key_waiting_for_release
}key_status;
extern key_status keys_status[4];
void update_key_status(void);
uint16_t get_key_status(uint8_t key_num);
void do_it_once(uint8_t key_num);
#endif
4、stm32g4xx_it.c代码
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
update_key_status();
/* USER CODE END SysTick_IRQn 1 */
}
5、main代码
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(get_key_status(0)==key_pressed)
{
HAL_GPIO_TogglePin (LED1_GPIO_Port ,LED1_Pin );
do_it_once(0);
}
}
6、逻辑分析
(1)首先程序进入的是void SysTick_Handler(void)这个中断函数,这个中断函数里面包含的就是update_key_status();这个更新的状态函数,然后我们跳转到这个函数的具体程序
(2)update_key_status();这个函数就是在我们的key.c里面。
它的逻辑就是,一旦检测到我们按键按下的信号,就开始进行一个按键消抖的计时,一旦大于10ms,就确认为按键按下(这一步可以忽略认为按键按下的信号)。
if(keys_status[0]==key_released)这一句的意思就是,我按键原来的状态如果是松开的状态,然后就把我的按键状态改为keys_status[0]=key_pressed;所以下面的else只是为了给我的按键赋值一个状态,然后当我的按键按下,我再给他赋值为另外一个状态。
(3)然后跳转到我们的main函数
if(get_key_status(0)==key_pressed);这个函数可以继续看看我们的key.c里面对get_key_status(uint8_t key_num)的函数的理解
就是相当于我们给get_key_status赋一个值,例如(0),然后它的按键状态的名字就是keys_status[0]。当我们按键按下,此时的keys_status[0]=keys_status[0]=key_pressed;然后我的main函数里面就是判断我的按键是否按下。
按键按下之后就开始翻转我们的电平,对应的LED灯的电平
此时还进入了一个函数do_it_once(0),此时还是跳转到key.c里面。意思就是我给do_it_once输入一个值,例如(0),就变成keys_status[0]=key_waiting_for_release。此时我的按键状态就不再是key_pressed就不能够继续翻转我的电平,且此时就算进入我的中断函数,因为我只按下还为松开,所以我的按键状态也就没有变成key_released,所以此时就算每隔1ms进入中断函数,也不能改变我的按键状态key_waiting_for_release,直到我松开手再次按下才能翻转我的电平。我松开手之后我的电平状态就变成了key_released,因为此时没有给这个状态赋其它程序,所以它不会改变我之前的LED灯的现象
(4)头文件之间包含的关系及key.h的作用
main函数里面包含key.h和stm32g4xx_it.c和里面包含key.h还有key.c里面要包含key,h都是因为这两个文件里面都要用到key.h里面包含的函数。
key.h里面包含“main.h”是因为它也需要main.h里面的函数。而且key.h里面包含了,我的key.c里面也可以用main.c里面的函数。
所以key.h里面定义的枚举类型,在其它函数里面都不需要声明就直接用,就是因为ley.h里面已经写好了,甚至是在main函数里面的外部声明 extern 都有。
(5)总结
以上的这些分析其实跟上一篇(按键上)的逻辑是一样的,这样写的好处,就是模块化分开来了,不至于在SysTick_Handler这里面所有的函数阻塞住。
四、一个按键控制一个LED灯的代码
(1)key.c代码
#include "key.h"
uint16_t key_delay_filter_count[4]={0,0,0,0};
key_status keys_status[4];//一共有四个按键,所以需要一个数组
void update_key_status1(void)//第一个按键的状态
{
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==0)
{
key_delay_filter_count[0]<<=1;
key_delay_filter_count[0]|=0x0001;
if(key_delay_filter_count[0] >0x03FF)
{
if(keys_status[0]==key_released)
keys_status[0]=key_pressed;
}
}
else
{
key_delay_filter_count [0]>>=1;
if(key_delay_filter_count[0]==0)
{
keys_status[0]=key_released;
}
}
}
void update_key_status2(void)//第一个按键的状态
{
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==0)
{
key_delay_filter_count[1]<<=1;
key_delay_filter_count[1]|=0x0001;
if(key_delay_filter_count[1] >0x03FF)
{
if(keys_status[1]==key_released)
keys_status[1]=key_pressed;
}
}
else
{
key_delay_filter_count [1]>>=1;
if(key_delay_filter_count[1]==0)
{
keys_status[1]=key_released;
}
}
}
void update_key_status3(void)//第一个按键的状态
{
if(HAL_GPIO_ReadPin(KEY3_GPIO_Port,KEY3_Pin)==0)
{
key_delay_filter_count[2]<<=1;
key_delay_filter_count[2]|=0x0001;
if(key_delay_filter_count[2] >0x03FF)
{
if(keys_status[2]==key_released)
keys_status[2]=key_pressed;
}
}
else
{
key_delay_filter_count [2]>>=1;
if(key_delay_filter_count[2]==0)
{
keys_status[2]=key_released;
}
}
}
void update_key_status4(void)//第一个按键的状态
{
if(HAL_GPIO_ReadPin(KEY4_GPIO_Port,KEY4_Pin)==0)
{
key_delay_filter_count[3]<<=1;
key_delay_filter_count[3]|=0x0001;
if(key_delay_filter_count[3] >0x03FF)
{
if(keys_status[3]==key_released)
keys_status[3]=key_pressed;
}
}
else
{
key_delay_filter_count [3]>>=1;
if(key_delay_filter_count[3]==0)
{
keys_status[3]=key_released;
}
}
}
uint16_t get_key_status(uint8_t key_num)
{
return keys_status[key_num];
}
void do_it_once(uint8_t key_num)
{
keys_status[key_num]=key_waiting_for_release ;
}
(2)key.h代码
#ifndef __KEY_H__
#define __KEY_H__
#include "main.h"//这一步要包含,否则最起码类型的定义可能跳转不出来
typedef enum
{
key_released=0U,
key_pressed,
key_waiting_for_release
}key_status;
extern key_status keys_status[4];
void update_key_status1(void);
void update_key_status2(void);
void update_key_status3(void);
void update_key_status4(void);
uint16_t get_key_status(uint8_t key_num);
void do_it_once(uint8_t key_num);
#endif
(3)stm32g4xx_it.c代码
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
update_key_status1();
update_key_status2();
update_key_status3();
update_key_status4();
/* USER CODE END SysTick_IRQn 1 */
}
(4)main代码
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(get_key_status(0)==key_pressed)
{
HAL_GPIO_TogglePin (LED1_GPIO_Port ,LED1_Pin );
do_it_once(0);
}
if(get_key_status(1)==key_pressed)
{
HAL_GPIO_TogglePin (LED2_GPIO_Port ,LED2_Pin );
do_it_once(1);
}
if(get_key_status(2)==key_pressed)
{
HAL_GPIO_TogglePin (LED3_GPIO_Port ,LED3_Pin );
do_it_once(2);
}
if(get_key_status(3)==key_pressed)
{
HAL_GPIO_TogglePin (LED4_GPIO_Port ,LED4_Pin );
do_it_once(3);
}
}
(5)逻辑分析
其实大概的逻辑分析跟上述相同,唯一的区别点就在于,我给不同的按键(KEY1、KEY2等)按键按下给了不同的按键按下状态的名字(keys_status[0]、keys_status[1]等),给了不同状态的名字,所以不同的按键按下能执行相对应的程序。
五、while循环里面只有一个函数
(1)key.c代码
#include "key.h"
uint16_t key_delay_filter_count[4]={0,0,0,0};
key_status keys_status[4];//一共有四个按键,所以需要一个数组
void update_key_status1(void)//第一个按键的状态
{
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==0)
{
key_delay_filter_count[0]<<=1;
key_delay_filter_count[0]|=0x0001;
if(key_delay_filter_count[0] >0x03FF)
{
if(keys_status[0]==key_released)
keys_status[0]=key_pressed;
}
}
else
{
key_delay_filter_count [0]>>=1;
if(key_delay_filter_count[0]==0)
{
keys_status[0]=key_released;
}
}
}
void update_key_status2(void)//第一个按键的状态
{
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==0)
{
key_delay_filter_count[1]<<=1;
key_delay_filter_count[1]|=0x0001;
if(key_delay_filter_count[1] >0x03FF)
{
if(keys_status[1]==key_released)
keys_status[1]=key_pressed;
}
}
else
{
key_delay_filter_count [1]>>=1;
if(key_delay_filter_count[1]==0)
{
keys_status[1]=key_released;
}
}
}
void update_key_status3(void)//第一个按键的状态
{
if(HAL_GPIO_ReadPin(KEY3_GPIO_Port,KEY3_Pin)==0)
{
key_delay_filter_count[2]<<=1;
key_delay_filter_count[2]|=0x0001;
if(key_delay_filter_count[2] >0x03FF)
{
if(keys_status[2]==key_released)
keys_status[2]=key_pressed;
}
}
else
{
key_delay_filter_count [2]>>=1;
if(key_delay_filter_count[2]==0)
{
keys_status[2]=key_released;
}
}
}
void update_key_status4(void)//第一个按键的状态
{
if(HAL_GPIO_ReadPin(KEY4_GPIO_Port,KEY4_Pin)==0)
{
key_delay_filter_count[3]<<=1;
key_delay_filter_count[3]|=0x0001;
if(key_delay_filter_count[3] >0x03FF)
{
if(keys_status[3]==key_released)
keys_status[3]=key_pressed;
}
}
else
{
key_delay_filter_count [3]>>=1;
if(key_delay_filter_count[3]==0)
{
keys_status[3]=key_released;
}
}
}
void key(void)
{
if(get_key_status(0)==key_pressed)
{
HAL_GPIO_TogglePin (LED1_GPIO_Port ,LED1_Pin );
do_it_once(0);
}
if(get_key_status(1)==key_pressed)
{
HAL_GPIO_TogglePin (LED2_GPIO_Port ,LED2_Pin );
do_it_once(1);
}
if(get_key_status(2)==key_pressed)
{
HAL_GPIO_TogglePin (LED3_GPIO_Port ,LED3_Pin );
do_it_once(2);
}
if(get_key_status(3)==key_pressed)
{
HAL_GPIO_TogglePin (LED4_GPIO_Port ,LED4_Pin );
do_it_once(3);
}
}
/*
这一段程序的含义是
首先进入中断函数,中断函数里面跳转到key.c
当我的按键按下,然后我的按键状态[1]就变成按下,然后在while循环里面执行的就是key()
kye()这个函数的意思就是,看我get_key_status对应的是哪个数字,然后就是对应的哪个按键状态,然后就开始翻转电平
*/
uint16_t get_key_status(uint8_t key_num)
{
return keys_status[key_num];
}
void do_it_once(uint8_t key_num)
{
keys_status[key_num]=key_waiting_for_release ;
}
(2)key.h代码
#ifndef __KEY_H__
#define __KEY_H__
#include "main.h"//这一步要包含,否则最起码类型的定义可能跳转不出来
typedef enum
{
key_released=0U,
key_pressed,
key_waiting_for_release
}key_status;
extern key_status keys_status[4];
void update_key_status1(void);
void update_key_status2(void);
void update_key_status3(void);
void update_key_status4(void);
void key(void);
uint16_t get_key_status(uint8_t key_num);
void do_it_once(uint8_t key_num);
#endif
(3)stm32g4xx_it.c代码
头文件还是要包含"key.h"
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
update_key_status1();
update_key_status2();
update_key_status3();
update_key_status4();
/* USER CODE END SysTick_IRQn 1 */
}
(4)main代码
头文件还是要包含"key.h"
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
key();
}
六、学习资料来源
(1)B站
【STM32G431RBT6_STM32CubeMX_Keil_按键篇_中-哔哩哔哩】 https://b23.tv/WwbEEEk
(2)Keil5代码
文件包:KEY2
(“按键上”的代码时KEY1)
作者:学习日记hhh