IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 单片机独立按键模块(含短按,长按,连发功能) -> 正文阅读

[嵌入式]单片机独立按键模块(含短按,长按,连发功能)

? ? ? ?最近看了很多按键扫描的文章,发现各有长处,后来自己花了一点时间做了一个单片机独立按键扫描的模块,此模块优点颇多,支持短按,长按,连发功能,只要配置相关结构体就可以实现这些功能,唯一的缺点是不支持多按键同时按下功能(后续有时间加上)

? ? ? ?先简单介绍下整体思路,首先在Key_Scan.h里面定义一个按键结构体KeyInitStruct,里面包含按键的一些设置

#define Key_Num								 5            //定义按键的个数

//按键连按结构体  	
typedef  struct 
{
  uint8_t	            const Key_Continuous_flg;         //按键是否支持连按功能,0-无连按,1-连按
	uint16_t            const Key_down_TimeSet;           //连按时间设置   
  uint16_t            const Key_down_frequency;         //连按频率设置  	
}Key_DownTypeStruct;


//按键长按结构体
typedef  struct 
{
  uint8_t	            const Key_Longdown_flg;           //按键是否支持长按功能,0-无长按,1-长按
	uint16_t            const Key_L_down_TimeSet;         //长按时间设置  
	void(*Key_longdown_Dut)(void);                        //长按函数指针
}Key_LongDownStruct;


//按键设置结构体
typedef  struct  
{ 
	GPIO_TypeDef*       GPIO_X;                     //GPIO口 
	uint16_t            GPIO_PIN;                   //GPIO引脚
	uint8_t 	          Key_Value;                  //按键不按时引脚电平
	uint8_t	            Key_Down;                   //按键防抖处理后按下标志  0-没按下,1-按下
	Key_LongDownStruct	Key_LongDown;               //按键长按结构体
	Key_DownTypeStruct  Key_DownType;               //按键连按结构体  	
	void(*Key_shortdown_Dut)(void);                 //短按函数指针
}KeyInitStruct ; 

然后在Key_Scan.c定义一个Key的结构体数组

KeyInitStruct Key[Key_Num]={    
	{GPIOC,GPIO_Pin_7,1,0,{1,100,KeySet_Longdown_Dut},{1,75,10},KeySet_shortdown_Dut},  //SET按键    
	{GPIOC,GPIO_Pin_9,1,0,{0,500},{1,75,5},KeyOPEN_shortdown_Dut},	          //OPEN按键
	{GPIOC,GPIO_Pin_8,1,0,{0,500},{0,50,5},KeySTOP_shortdown_Dut},	          //STOP按键
	{GPIOC,GPIO_Pin_6,1,0,{0,500},{1,75,5},KeyCLOSE_shortdown_Dut},           //CLOSE按键
	{GPIOC,GPIO_Pin_14,1,0,{1,400,KeyLearn_Longdown_Dut},{0,50,5},KeyLearn_shortdown_Dut}, //LEARN按键
};

在Key_Scan.c放入按键功能函数,将此函数放在20ms定时器里面,该函数一般不用更改(假如用户要在按键松开实现功能,可把对应函数移动到程序松开位置)

/******************************************************************************
 Function    :void Key_Scan(void)

 ------------------------------------------------------------------------------
 Description :按键扫描函数

 -----------------------------------------------------------------------------
 Returnvalue :

 -----------------------------------------------------------------------------
 Parameters  :

 -----------------------------------------------------------------------------
 Date        :

 -----------------------------------------------------------------------------
 Remarks     :
              
 ******************************************************************************/
void Key_Scan(void)
{
	uint8_t i;
	for(i=0;i<Key_Num;i++)
	{
		if(Key[i].Key_Value ==1)                   //硬件电路是否上拉
		Key[i].Key_Down = 1-GPIO_ReadInputDataBit(Key[i].GPIO_X,Key[i].GPIO_PIN);  //硬件电路上拉,按键按下则为0
    else
		Key[i].Key_Down = GPIO_ReadInputDataBit(Key[i].GPIO_X,Key[i].GPIO_PIN);         
	}
}
	

/******************************************************************************
 Function    :void Key_Function(void)

 ------------------------------------------------------------------------------
 Description :按键功能函数,放在20ms定时器

 -----------------------------------------------------------------------------
 Returnvalue :

 -----------------------------------------------------------------------------
 Parameters  :

 -----------------------------------------------------------------------------
 Date        :

 -----------------------------------------------------------------------------
 Remarks     :
              
 ******************************************************************************/
void Key_Function(void)
{
	uint8_t i;
	static uint8_t Pre_Key_St[Key_Num] = {0};          //定义一个按键上次状态的变量
	static uint16_t Key_St_cnt[Key_Num]={0};           //按键按下计数变量

	Key_Scan();
	for(i=0;i<Key_Num;i++)
	{
		if( Key[i].Key_LongDown.Key_Longdown_flg==0 )    //无长按功能
		{
			if( Key[i].Key_DownType.Key_Continuous_flg==0 )//无连按功能
			{				
				if( Pre_Key_St[i] ^ Key[i].Key_Down )        //按键上一次与现在状态不一致,说明按键要么按下,要么松开
				{
					 Pre_Key_St[i] = Key[i].Key_Down;          //这条语句很关键,自己体会,无法言语

						if(Key[i].Key_Down)                      //按键按下
						{                              
							Key[i].Key_shortdown_Dut();
						}
						else                                     //按键松开
						{
																										 //可以在此添加按键松开的函数
						}
				}
				else
				{
				 Pre_Key_St[i] = Key[i].Key_Down;;     
				}
			}
			else                                           //连按功能
			{
				if( Pre_Key_St[i] ^ Key[i].Key_Down )        //按键上一次与现在状态不一致,说明按键要么按下,要么松开
				{
					 Pre_Key_St[i] = Key[i].Key_Down;          //这条语句很关键,自己体会,无法言语

						if(Key[i].Key_Down)                      //按键按下
						{                              

							Key[i].Key_shortdown_Dut();
						}
						else                                     //按键松开
						{
																										 //可以在此添加按键松开的函数
						}
				}
				else
				{
				 Pre_Key_St[i] = Key[i].Key_Down;    
				}				
				if(Key[i].Key_Down)                      
				{
					Key_St_cnt[i]++;                            //按键按下开始计数
				}
				else   
				{       
					Key_St_cnt[i] = 0;                          //松开计数清零
				}
				if(Key_St_cnt[i]>Key[i].Key_DownType.Key_down_TimeSet)              //按键按下时间大于连按时间设置
				{
					if((Key_St_cnt[i]-Key[i].Key_DownType.Key_down_TimeSet)%Key[i].Key_DownType.Key_down_frequency==0) //进入函数的频率
					{

						Key[i].Key_shortdown_Dut();
					}
				}
			}
	  }
		else                                           //有长按功能
		{
			if(Key[i].Key_Down)
			{
				Key_St_cnt[i]++;
			}
			if(Key_St_cnt[i]>0 && Key_St_cnt[i]<Key[i].Key_LongDown.Key_L_down_TimeSet)               //短按
			{
				if(Key[i].Key_Down == 0)                                                                //短按松开
				{

					Key[i].Key_shortdown_Dut();
					Key_St_cnt[i] = 0;
				}
			}
		  else if(Key_St_cnt[i]>=Key[i].Key_LongDown.Key_L_down_TimeSet)
			{
				if( Pre_Key_St[i] ^ Key[i].Key_Down )        					 //按键上一次与现在状态不一致,说明按键要么按下,要么松开
				{
					 Pre_Key_St[i] = Key[i].Key_Down;         				   //这条语句很关键,自己体会,无法言语

						if(Key[i].Key_Down)                      					 //按键按下
						{                               
							Key[i].Key_LongDown.Key_longdown_Dut();
						}
						else                                    					 //按键长按松开
						{
							Key_St_cnt[i] = 0;										 					 //按键长按松开计数清零
						}
				}
				else
				{
				 Pre_Key_St[i] = Key[i].Key_Down;    
				}				
			}
		}
  }
}

最后是按键对应的功能函数,用户只要在此更改自己所要实现的功能就行

void KeySet_shortdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeyOPEN_shortdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeySTOP_shortdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeyCLOSE_shortdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeyLearn_shortdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeySet_Longdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeyLearn_Longdown_Dut(void)
{
//在此添加按键所对应的功能
}

总结:用户只要配置按键结构体函数,定义按键个数Key_Num ,以及更改对应函数的功能。就可以实现所想要的功能,非常灵活,移植度高。下次有时间在做个按键与数码管二三级菜单设置模块。??

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-02 11:32:57  更:2021-09-02 11:33:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:32:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码