LED和KEY驱动
Day3-二、LED和KEY驱动
(一)、基本原理
LED&KEY驱动比较简单,不再赘述,详见代码注释。
重点在于KeyShakeProcess_Callback(void)这个按键消抖回调函数。
(二)、代码实现
1、driver_led_key.c
#include "driver_led_key.h"
#include "driver_buffer.h"
/* 保存按键信息的环形缓冲区 */ //定义一个RingBuffer类型的结构体
static RingBuffer KeyBuffer;
/* 保存按键信息的环形缓冲区 */ //定义一个ptRingBuffer类型的结构体指针
//static ptRingBuffer pKeyBuffer;
/* 记录按键触发的系统时刻 */
volatile static uint32_t KeyTrigerTime = 0;
/*
* 函数名:Driver_LED_Init
* 功能:初始化LED的GPIO
* 输入参数:无
* 输出参数:无
* 返回值:0-成功;-1-失败;
*/
int Driver_LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(LED_PORT,&GPIO_InitStruct);
return 0;
}
/*
* 函数名:Driver_LED_WriteStatus
* 功能:控制LED的状态
* 输入参数:status:0-灭;1-亮
* 输出参数:无
* 返回值:0-成功;-1-失败;
*/
int Driver_LED_WriteStatus(uint8_t status)
{
LED(status);
return 0;
}
/*
* 函数名:Driver_Key_Init
* 功能:初始化按键的GPIO、使能其外部中能
* 输入参数:无
* 输出参数:无
* 返回值:0-成功;-1-失败;
*/
int Driver_KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
Driver_Buffer_Init(&KeyBuffer,sizeof(KeyEvent)<<4);//初始化保存按键信息的环形缓冲区,大小为4个按键信息的空间大小
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = KEY_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;//双边沿触发
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(KEY_PORT,&GPIO_InitStruct);
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
return 0;
}
/*
* 函数名:EXTI0_IRQHandler
* 功能:外部中断0的中断服务函数
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
void EXTI0_IRQHandler(void)
{//PA0检测到上升沿或下降沿都会进入该中断服务函数,在这里调用GPIO外部中断函数HAL_GPIO_EXTI_IRQHandler();
HAL_GPIO_EXTI_IRQHandler(KEY_PIN);
//在这个函数中除了中断标志位然后又调用了一个HAL_GPIO_EXTI_Callback(GPIO_Pin)函数。
//根据函数名Callback也能看出来这里才是真正执行具体功能的函数,该函数需要用户文件中重新实现该函数
}
/*
* 函数名:HAL_GPIO_EXTI_Callback
* 功能:GPIO外部中断的回调函数,处理触发中断的GPIO的事务,此处更新按键的触发时刻
* 输入参数:GPIO_Pin-触发外部中断的GPIO引脚号
* 输出参数:无
* 返回值:无
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{//换句话说,按键按下或松开时都会执行该函数,更新当前的触发时间,供后续消抖处理
KeyTrigerTime = HAL_GetTick() + 50;
}
/*
* 函数名:KeyShakeProcess_Callback
* 功能:处理按键消抖的回调函数,会在Systick的1ms中断服务函数中调用
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
void KeyShakeProcess_Callback(void)
{
KeyEvent nKeyEvent = {0};//初始化表示按键信息的结构体。
static uint32_t press_time = 0;//按下时刻的tick时间
static uint32_t release_time = 0;//松开时刻的tick时间
uint32_t tick = HAL_GetTick();
if(tick == KeyTrigerTime)
{ //因为整个函数是放在SysTick_Handler(void)中的,每1ms进入一次这个中断,tick值加1
//当tick值增长到KeyTrigerTime时,也即通过了消抖判断,真正地按下/松开了,才继续后续的判断
//换句话说,就是当有按键按下时,KeyTrigerTime为此时的HAL_GetTick()值加上用于消抖延迟的50ms。
//而由于tick=HAL_GetTick();那么tick值随着滴答中断不断地增长,并且每次滴答中断里都会判断tick值是否等于KeyTrigerTime,
//如果没有按键按下的话,KeyTrigerTime = 0初始值。不满足判断条件,什么都不做。
//如果有按键按下,按下的瞬间,触发EXTI0中断,KeyTrigerTime = 此时HAL_GetTick()值+50,此时的tick值也等于HAL_GetTick(),即 KeyTrigerTime比tick大50,仍然判断tick是否与KeyTrigerTime相等,显然不相等。
//而滴答中断每1ms都会使tick加1,tick在不断增长,而KeyTrigerTime的值在按下的瞬间就已经确定,是定值了。
//不过,这也就达到了消抖的目的,即在按键按下或松开的那一瞬间不去读按键IO口的电平,而是在延迟50个tick之后,50个tick之后,tick增加到与 KeyTrigerTime相等,满足判断条件,这之后再读取按键IO口的电平,从而达到了消抖的目的。
if(KEY_STATE() == 0) // press
{
press_time = tick; //如果按键按下,记录按下时的tick值
}
else // release
{
release_time = tick; //如果按键松开,记录松开时的tick值
}
// if(release_time != 0 && release_time < press_time)
// {
// release_time = press_time = 0; //如果松开时刻的tick值不为0 且 松开时刻的tick值小于按下时刻的tick值,说明
// }
if(press_time != 0 && release_time != 0)
{
nKeyEvent.num = 1;
nKeyEvent.time = release_time - press_time; //给存储按键信息的结构体的成员赋值
release_time = press_time = 0; //并清空松开和按下时刻的tick值
Driver_Buffer_WriteBytes(&KeyBuffer, (uint8_t*)&nKeyEvent.num, sizeof(KeyEvent));//将按键的序号写到环形缓冲区
}
}
}
/*
* 函数名:Driver_Key_Read
* 功能:读取按键信息,可以是多次按键信息
* 输入参数:buf-保存按键信息的指针;len-读取按键信息的个数
* 输出参数:无
* 返回值:0-成功;-1-失败;
*/
int Driver_Key_Read(uint8_t *buf,uint16_t len) //uint8_t len?
{
// len要大于0且不能小于按键属性结构体的大小且是其整数倍
if(len==0 || len<sizeof(KeyEvent) || (len %sizeof(KeyEvent))!=0) return -1;
if(buf == NULL) return -1;
if(Driver_Buffer_ReadBytes(&KeyBuffer, buf, len) == len)//读环形缓冲区
//因为Driver_Buffer_ReadBytes()返回的是成功读出到buf里的字节数,
//如果成功读出的字节数与要读出的字节数len相等,表示读取按键信息成功.
return 0;
return -1;//否则返回-1,表示失败。
}
2、driver_led_key.h
#ifndef __DRIVER_LED_KEY_H
#define __DRIVER_LED_KEY_H
#include "stm32f1xx_hal.h"
/*led - PA1*/
#define LED_PORT GPIOA
#define LED_PIN GPIO_PIN_1
#define LED(STATUS) HAL_GPIO_WritePin(LED_PORT,LED_PIN,STATUS?GPIO_PIN_RESET:GPIO_PIN_SET)
#define LED_SHINE() HAL_GPIO_TogglePin(LED_PORT,LED_PIN)
/*key - PA0*/
#define KEY_PORT GPIOA
#define KEY_PIN GPIO_PIN_0
#define KEY_STATE() HAL_GPIO_ReadPin(KEY_PORT,KEY_PIN)
int Driver_LED_Init(void);
int Driver_LED_WriteStatus(uint8_t status);
int Driver_KEY_Init(void);
void EXTI0_IRQHandler(void);
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
int Driver_Key_Read(uint8_t *buf,uint16_t len);
void KeyShakeProcess_Callback(void);
typedef struct
{
unsigned short num; //2 Bytes
unsigned short time; //2 Bytes
}KeyEvent;
#endif