使用固件库实现按键控制LED
提示:以下是本篇文章正文内容
从按键的原理图可知,这些按键在没有被按下的时候,GPIO 引脚的输入状态为低电平(按键所在的电路不通,引脚接地),当按键按下时,GPIO 引脚的输入状态为高电平(按键所在的电路导通,引脚接到电源)。只要我们检测引脚的输入电平,即可判断按键是否被按下。
电容的作用:利用电容充放电的延时,消除了波纹,起到消抖功能。
在“工程”上新建“bsp_key.c”及“bsp_key.h”文件,这些文件不属于STM32 标准库的内容,是由我们自己根据应用需要编写的,编程步骤如下:
在编写应用程序的过程中,要考虑更改硬件环境的情况,即提高可移植性。
这个时候一般把硬件相关的部分使用宏来封装,若更改了硬件环境,只修改这些硬件相关的宏即可,这
些定义一般存储在头文件,即本例子中的bsp_key.h文件中
代码如下:
1 // 引脚定义
2 #define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
3 #define KEY1_GPIO_PORT GPIOA
4 #define KEY1_GPIO_PIN GPIO_Pin_0
5
6 #define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
7 #define KEY2_GPIO_PORT GPIOC
8 #define KEY2_GPIO_PIN GPIO_Pin_13
以上代码根据按键的硬件连接,把检测按键输入的 GPIO 端口、GPIO 引脚号以及GPIO 端口时钟封装起来了。
利用上面的宏,编写按键的初始化函数
1 void Key_GPIO_Config(void)
2 {
3 GPIO_InitTypeDef GPIO_InitStructure;
4
5 /*开启按键端口的时钟*/
6 RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE);
7
8 //选择按键的引脚
9 GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
10 // 设置按键的引脚为浮空输入
11 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
12 //使用结构体初始化按键
13 GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
14
15 //选择按键的引脚
16 GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;
17 //设置按键的引脚为浮空输入
18 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
19 //使用结构体初始化按键
20 GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);
21 }
函数执行流程如下:
(1) 使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置。
(2) 调用库函数 RCC_APB2PeriphClockCmd 来使能按键的 GPIO 端口时钟。
(3) 向 GPIO 初始化结构体赋值,把引脚初始化成浮空输入模式。
(4) 使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始化。
(5) 使用同样的初始化结构体,只修改控制的引脚和端口,初始化其它按键检测时使用的GPIO 引脚。
初始化按键后,就可以通过检测对应引脚的电平来判断按键状态了:
1 /** 按键按下标置宏
2 * 按键按下为高电平,设置 KEY_ON=1, KEY_OFF=0
3 * 若按键按下为低电平,把宏设置成 KEY_ON=0 ,KEY_OFF=1 即可
4 */
5 #define KEY_ON 1
6 #define KEY_OFF 0
7
8 /**
9 * @brief 检测是否有按键按下
10 * @param GPIOx:具体的端口, x 可以是(A...G)
11 * @param GPIO_PIN:具体的端口位, 可以是 GPIO_PIN_x(x 可以是 0...15)
12 * @retval 按键的状态
13 * @arg KEY_ON:按键按下
14 * @arg KEY_OFF:按键没按下
15 */
16 uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
17 {
18 /*检测是否有按键按下 */
19 if (GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON ) {
20 /*等待按键释放 */
21 while (GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);
22 return KEY_ON;
23 } else
24 return KEY_OFF;
25 }
在这里我们定义了一个 Key_Scan 函数用于扫描按键状态。GPIO 引脚的输入电平可通过 读 取 IDR 寄存器对应的数据位来感知。
STM32 标准库提供了库函数GPIO_ReadInputDataBit 来获取位状态,该函数输入 GPIO 端口及引脚号,函数返回该引脚的电平状态,高电平返回 1,低电平返回 0。
Key_Scan 函数中以 GPIO_ReadInputDataBit 的返回值与自定义的宏“KEY_ON”对比,若检测到按键按下,则使用 while 循环持续检测按键状态,直到按键释放,按键释放后 Key_Scan 函数返回一个“KEY_ON”值;若没有检测到按键按下,则函数直接返回“KEY_OFF”。
接下来我们使用主函数编写按键检测流程
1 /**
2 * @brief 主函数
3 * @param 无
4 * @retval 无
5 */
6 int main(void)
7 {
8 /* LED 端口初始化 */
9 LED_GPIO_Config();
10
11 /*初始化按键*/
12 Key_GPIO_Config();
13
14 /* 轮询按键状态,若按键按下则反转 LED */
15 while (1) {
16 if ( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON ) {
17 /*LED1 反转*/
18 LED1_TOGGLE;
19 }
20
21 if ( Key_Scan(KEY2_GPIO_PORT,KEY2_PIN) == KEY_ON ) {
22 /*LED2 反转*/
23 LED2_TOGGLE;
24 }
25 }
26 }
代码中初始化 LED 灯及按键后,在 while 函数里不断调用 Key_Scan 函数,并判断其返回值,若返回值表示按键按下,则反转 LED 灯的状态。
把编译好的程序下载到开发板并复位,按下按键可以控制 LED 灯亮、灭状态。
代码中的“\”是 C 语言中的续行符语法,表示续行符的下一行与续行符所在的代码是同一行。代码中因为宏定义关键字“#define”只是对当前行有效,所以我们使用续行符来连接起来,应用续行符的时候要注意,在“\”后面不能有任何字符(包括注释、空格),只能直接回车。
应用:#define LED_G(a) if(a)
GPIO_RestBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN);
else GPIO_SetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN);
使用续行就达到了宏定义是一行分成几行写的作用。
^异或 ,C语言中二进制运算符
功能:与1 异或改变,与0异或不变
应用:
GPIOB->ODR ^= GPIO_Pin_0;
ODR的第一位和1相异或,做到改变状态,输出高低电平的效果。