STM32HAL库+按键扫描+定时器间隔时间扫描

一.按键触发方式有很多种:

——只讲解两种模式:外部中断模式,定时器定时扫描模式。

——用到的管脚:PG2管脚——按键;定时器。外部中断。

       两种模式的HAL库配置不一样:

       外部中断模式——按键按下来了一个外部中断,在外部中断回调函数里头编写函数。

       定时器定时扫描模式——每隔多少秒去扫描按键是否按下,就如同每隔一段时间我会去看看           按键是否按下。

——芯片:STM32H723ZGT6型号。
————————————————

二、STM32Cubemax配置——定时器模式

1.先定时器模式

——配置管脚PG2——选择GPIO_Input。

——按键低电平有效——管脚PG2连按键,按键的另外一个脚连接地。

——配置管脚其他参数——

——配置定时器2——

——主频设置多少可以在这里看——主频可以根据自己的设置定时器——这里有个计算公式。

——题外补充知识:

假设你有一个需求,需要一个定时器每0.01秒中断一次。假设微控制器的主时钟频率为 96MHz(即时钟周期为 1/96,000,000 秒),你想计算相应的预分频器值和自动重装载值。

  1. 选择一个合理的预分频器值。为了简化计算,我们可以首先选择一个预分频器值。假设我们选择预分频器值为 9600-1=9599。——(这意味着时钟频率被降低到了10 kHz(也就是96,000,000/9600=10kHZ,96MHz=96,000,000Hz),即每个时钟周期为 0.0001秒(也就是1/10,000秒,1秒等于1000ms,即是0.1ms))。
  2. 使用定时周期公式计算自动重装载值。我们需要定时器每0.01秒溢出一次,即定时周期为 0.01秒。

[ 0.01秒=(9599+1)×({自动重装载值}+1)/96,000,000​]

——公式:定时周期=(预分频器值psc+1)×(自动重装载值Autoreload+1)×时钟周期;

——定时周期——0.01s也就是10ms。

——预分频器值——图片中设置9600-1=9599。——先分频,计算每个时钟周期多久。

——自动重装载值——图片中设置100-1。——每个时钟周期再乘以这个加1,就是你定时的时间。

——时钟周期——主时钟(96MHz)的倒数(1/主时钟频率)——1/96,000,000​。

——通过调整预分频器值和自动重装载值,可以实现不同的定时周期。

——这个选项需要勾出来,允许定时器中断产生全局中断——

——最后生成代码——打开源文件,编辑按键按下的程序部分——

三、代码部分——定时器模式

这个代码有三个数据结构用于按键关联起来。调试的时候主要看这几个机构体的数据变化,调试使用。

————可以通用模型——改变前面管脚配置,和我定义的数组里头管脚就可以正常移植使用。

//主函数main函数里头while(1)前添加一个语句,主函数中需要添加的部分。

  /* USER CODE BEGIN 2 */
  
    // 清除定时器初始化过程中的更新中断标志,避免定时器一启动就中断
    __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);		
    // 使能定时器2更新中断并启动定时器
    HAL_TIM_Base_Start_IT(&htim2);

    bsp_InitKey(); 
  /* USER CODE END 2 */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      Key_Operations();//处理长按和短按的情况
  }
  /* USER CODE END 3 */





//定时器回调函数里头需要编写
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM2)//确定是定时器二中断
	{   
      bsp_KeyScan10ms();     //判断是短按还是长按。   
	}
}






//key.h文件里头的。
#ifndef __KEY_H
#define __KEY_H

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"


typedef enum{
    KEY_NONE = 0,
    
    KEY_1_DOWN,         //按键按下
    KEY_1_UP,           //按键弹起
    KEY_1_LONG,         //按键长按
}KEY_ENUM;

#define KEY_FIFO_SIZE  10
typedef struct {
    uint8_t Buf[KEY_FIFO_SIZE];     /*键值缓冲区*/
    uint8_t Read;                   /*读指针*/
    uint8_t Write;                  /*写指针*/
}KEY_FIFO_T;


/* 按键ID, 主要用于bsp_KeyState()函数的入口参数 */
typedef enum
{
	KID_K1 = 0,
}KEY_ID_E;

/*
	按键滤波时间50ms, 单位10ms。
	只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件
	即使按键电路不做硬件滤波,该滤波机制也可以保证可靠地检测到按键事件
*/
#define KEY_FILTER_TIME   5
#define KEY_LONG_TIME     100			/* 单位10ms, 持续1秒,认为长按事件 */

typedef struct {
    uint8_t (*IsKeyDownFunc)(void);                 /*按键是否松手*/
    uint8_t Count;                                  /*滤波计数器*/
    uint16_t LongCount; /* 长按计数器 */
    uint16_t LongTime; /* 按键按下持续时间, 0 表示不检测长按 */    
    uint8_t State;                                  /* 按键当前状态(按下还是弹起) */
    uint8_t RepeatSpeed;                            /* 连续按键周期 */
    uint8_t RepeatCount;                            /* 连续按键计数器 */
}KEY_T;


void bsp_InitKey(void);
void Key_Operations(void);
void bsp_KeyScan10ms(void);

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */







//key.c文件里头的
#include "key.h"

#define LONG_PRESS_DURATION 100 // 定义长按的时间阈值,这里假设1000个计时器周期
#define KEY_BUTTON_PIN GPIO_PIN_2 // 假设按键连接到GPIO_PIN_2



#define HARD_KEY_NUM	    1	   						/* 实体按键个数 */
#define KEY_COUNT           1                           /*按键数量*/
KEY_FIFO_T Key_FIFO;
KEY_T KEY_Filter[HARD_KEY_NUM];
/* 依次定义GPIO */
typedef struct
{
	GPIO_TypeDef* gpio;
	uint16_t pin;
	uint8_t ActiveLevel;	/* 激活电平 */
}X_GPIO_T;

/* GPIO和PIN定义 */
static const X_GPIO_T s_gpio_list[HARD_KEY_NUM] = {
	{GPIOG, GPIO_PIN_2, 0},		/* KEY1 ,低电平表示按下*/
};

extern TIM_HandleTypeDef htim2;


 
void key_handler(void);
 
 
/*
*********************************************************************************************************
*	函 数 名: KeyPinActive
*	功能说明: 判断按键是否按下
*	形    参: 
            @_id:按键编号 
*	返 回 值: 返回值1 表示按下(导通),0表示未按下(释放)
*********************************************************************************************************
*/
static uint8_t KeyPinActive(uint8_t _id)
{
    uint8_t state;
    if((s_gpio_list[_id].gpio->IDR & s_gpio_list[_id].pin) == 0) 
    {
        state = 0;  //低电平
    }
    else 
    {
        state = 1;  //高电平
    }
    
    if(s_gpio_list[_id].ActiveLevel == state)
    {
        return 1;//按下 
    }else 
    {
        return 0;//未按下
    }
}
/*
*********************************************************************************************************
*	函 数 名: IsKeyDownFunc
*	功能说明: 判断按键是否按下。单键和组合键区分。单键事件不允许有其他键按下。
*	形    参: 无
*	返 回 值: 返回值1 表示按下(导通),0表示未按下(释放)
*********************************************************************************************************
*/
static uint8_t IsKeyDownFunc(uint8_t _id)
{
    /*实体按键*/
    if(_id < HARD_KEY_NUM)
    {
        uint8_t i,count = 0,save =255;
        /*判断有几个按键按下*/
        for(i = 0;i < HARD_KEY_NUM;i++)
        {
            if(KeyPinActive(i))
            {
                count++;
                save = i;
            }
        }
        if(count == 1 && save == _id)
        {
            return 1;//只有一个按键按下,且该按键正确才有效
        }
        
        return 0;
    }
    return 0;
}

void bsp_InitKeyVar(void) //初始化按键变量
{
   /* 对按键FIFO读写指针清零 */
	Key_FIFO.Read = 0;
	Key_FIFO.Write = 0;
    
    for(int i=0;i<KEY_COUNT;i++)
    {
        KEY_Filter[i].LongTime = KEY_LONG_TIME;
        KEY_Filter[i].Count = KEY_FILTER_TIME / 2;   
        KEY_Filter[i].State = 0;							/* 按键缺省状态,0为未按下 */
		KEY_Filter[i].RepeatSpeed = 0;						/* 按键连发的速度,0表示不支持连发 */
		KEY_Filter[i].RepeatCount = 0;						/* 连发计数器 */
    }
}

void bsp_InitKey(void)
{
    bsp_InitKeyVar();
}


void bsp_PutKey(uint8_t KeyValue)
{
    Key_FIFO.Buf[Key_FIFO.Write] = KeyValue;
    if(++Key_FIFO.Write >= KEY_FIFO_SIZE){
         Key_FIFO.Write=0;
    }
}

int bsp_GetKey(void)
{
    uint8_t ret;
    if(Key_FIFO.Read == Key_FIFO.Write)
        return KEY_NONE;
    else{
        ret = Key_FIFO.Buf[Key_FIFO.Read];
        if(++Key_FIFO.Read >= KEY_FIFO_SIZE){
             Key_FIFO.Read=0;
        }
        return ret;
    }
}
/*
*********************************************************************************************************
*	函 数 名: bsp_GetKeyState
*	功能说明: 读取按键的状态
*	形    参:  _ucKeyID : 按键ID,从0开始
*	返 回 值: 1 表示按下, 0 表示未按下
*********************************************************************************************************
*/
uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID)
{
	return KEY_Filter[_ucKeyID].State;
}
/*
*********************************************************************************************************
*	函 数 名: bsp_DetectKey
*	功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。
*	形    参: IO的id, 从0开始编码
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_DetectKey(uint8_t i)
{
    KEY_T *pBtn;
    
    pBtn = &KEY_Filter[i];//滤波器
    if(IsKeyDownFunc(i))
    {
        if(pBtn->Count < KEY_FILTER_TIME)
        {
            pBtn->Count = KEY_FILTER_TIME;
        }
        else if(pBtn->Count < 2 * KEY_FILTER_TIME)
		{
			pBtn->Count++;
		}
        else 
        {
            if (pBtn->State == 0)
			{
				pBtn->State = 1;

				/* 发送按钮按下的消息 */
				bsp_PutKey((uint8_t)(3 * i + 1));
			}
            if (pBtn->LongTime > 0)  //长按
			{
				if (pBtn->LongCount < pBtn->LongTime)
				{
					/* 发送按钮持续按下的消息 */
					if (++pBtn->LongCount == pBtn->LongTime)
					{
						/* 键值放入按键FIFO */
						bsp_PutKey((uint8_t)(3 * i + 3));
					}
				}
			}
        }
    }
    else
	{
		if(pBtn->Count > KEY_FILTER_TIME)
		{
			pBtn->Count = KEY_FILTER_TIME;
		}
		else if(pBtn->Count != 0)
		{
			pBtn->Count--; 
		}
		else
		{
			if (pBtn->State == 1)
			{
				pBtn->State = 0;

				/* 发送按钮弹起的消息 */
				bsp_PutKey((uint8_t)(3 * i + 2));
			}
		}

		pBtn->LongCount = 0;
		pBtn->RepeatCount = 0;
	}
}
/*
*********************************************************************************************************
*	函 数 名: bsp_KeyScan10ms
*	功能说明: 扫描所有按键。非阻塞,被systick中断周期性的调用,10ms一次
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_KeyScan10ms(void)
{
	uint8_t i;

	for (i = 0; i < KEY_COUNT; i++)
	{
		bsp_DetectKey(i);
	}
}

void Key_Operations(void)
{
    uint8_t state =bsp_GetKey();
    switch(state)
    {
        case KEY_1_DOWN:			    /* K1键按下 */
            key_handler();
            break;
        case KEY_1_LONG:				/* K1键长按 */
//            key_handler();
            break;
        default:
            /* 其它的键值不处理 */
            break;
    }
}

——这比消抖来的更加好,调试期间使用的外部中断模式,试了很多次都是按一下发一次,会出现多次触发外部中断,长按的情况也会有出现多发情况。

——以后有机会出一版外部中断处理按键长按和短按的更加好的例子,这个有许多是参考网上说的,做了修改。HAL库cubemax配置部分暂时留到一起写。这个定时器控制具体细节原理后期有机会讲一讲。

以上仅仅属于本人学习心得,可供学习参考,禁止商用~

  • 42
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值