【百问网】7天物联网智能家居实战 Day3_2

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值