中级会员
 
- 积分
- 235
- 金钱
- 235
- 注册时间
- 2018-5-2
- 在线时间
- 83 小时
|
本帖最后由 safu枫 于 2018-5-4 09:07 编辑
在51和STM32单片机上使用面向对象的编程,第二篇:强大的按键能爆炸学完点亮一盏灯了,我们接下来学习如何通过按键点亮一盏灯;
首先我们还是介绍传统的按键处理函数
[mw_shl_code=c,true]void key_scanf()
{
if(key1 == key_down)
{
.......
}
if(key2 == key_down)
{
.........
}
........
}[/mw_shl_code]
我们先来分析这种结构的问题
首先,很多按键全部堆在一起,里面编写处理函数,逻辑上很混乱,其次如果我想要实现长按键处理 可能就得加全局变量,或者我想要禁止某个按键 失能某个按键 怎么办?如果按键有十几个
但是大部分功能都相似又怎么办?
突然按键的功能要修改,然后过几天客户说又要修改回来。。。以上总总问题都是我遇到过的
所以我现在这样写按键程序:
首先我们还是得有抽象化编程的思路,先想好 我这个按键类需要有什么样的方法,有什么样的属性,然后根据这些创建一个结构体,或者叫创建一个类
1:和LED一样我们需要有设置端口的方法,和记录端口的变量(属性);
2:我想有一个 短按键处理和一个长按 按键处理,所以需要有相关的属性变量;
3:有一个能设置该按键使能的方法和相关属性
。。。。
反正大家根据这种思路去想去编写,然后就是这里本篇不会把所有功能和代码介绍,本篇最主要的是想介绍面向对象中多态的使用
开始吧,我们先创建一个结构体
[mw_shl_code=c,true]typedef struct CLASS_KEY{
void (* const KEYShortHand)(struct CLASS_KEY * p); //KEY短按 按键处理函数
void (* const KEYLongHand)(struct CLASS_KEY * p); //KEY1长按 按键处理函数
void (* const SetShortEventCMD)(struct CLASS_KEY * p,unsigned char cmd); //设置使能 或者 失能
void (* const SetLongEventCMD)(struct CLASS_KEY * p,unsigned char cmd ); //设置使能 或者 失能
void (* const SetKeyGPIOCMD)(struct CLASS_KEY * p, GPIO_TypeDef* GPIOx, u16 GPIO_Pin); //设置端口函数
GPIO_TypeDef* GPIOx;
u16 GPIO_Pin;
unsigned short int KEY_Times; //用于记录按键 按下的时间 用来判断区别长按 和短按
unsigned char KEY_ShortEvent_EN; //使能标志位
unsigned char KEY_LongEvent_EN;
}typ_CLASS_KEY;[/mw_shl_code]然后我们来编写一下 结构体中所需的函数
首先是设置端口,这一点 和上一篇有些不同,我们会用到一个链表结构
[mw_shl_code=c,true]void SetKeyGPIO(struct CLASS_KEY * p,GPIO_TypeDef* GPIOx,u16 GPIO_Pin)
{
//KEY
GPIO_InitTypeDef GPIO_InitStructure;
p->GPIO_Pin = GPIO_Pin;
p->GPIOx = GPIOx;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOx, &GPIO_InitStructure);
key_list.AddKEY_List(p);
}[/mw_shl_code]
[mw_shl_code=c,true]void KEY1_ShortPrintfHand(struct CLASS_KEY * p)//按键1短按 按键处理函数
{
if(p->KEY_ShortEvent_EN)
{
if((p->KEY_Times >= 1) && ((p->KEY_Times <= 49)))
{
printf("KEY1短按事件\r\n");
}
}
}
void KEY1_LongPrintfHand(struct CLASS_KEY * p)//按键1长按 按键处理函数
{
if(p->KEY_LongEvent_EN)
{
if(p->KEY_Times>=50)
{
printf("KEY1长按事件\r\n");
}
}
}[/mw_shl_code][mw_shl_code=c,true]void SetShortEventCMD(struct CLASS_KEY * p,unsigned char cmd)
{
p->KEY_ShortEvent_EN = cmd;
}
void SetLongEventCMD(struct CLASS_KEY * p,unsigned char cmd)
{
p->KEY_LongEvent_EN = cmd;
}[/mw_shl_code]
宏定义:
[mw_shl_code=c,true]#define NewKey(pKEY_ShortHand,pKEY_LongHand) { \
pKEY_ShortHand, \
pKEY_LongHand, \
SetShortEventCMD, \
SetLongEventCMD, \
SetKeyGPIO, \
0, \
0, \
0, \
1, \
1 \
} [/mw_shl_code]
好了 我们编写完相关函数后 就来定义这个结构体 给它赋值 (实例化这个类)
[mw_shl_code=applescript,true]typ_CLASS_KEY key1 = NewKey(KEY1_ShortPrintfHand,KEY1_LongPrintfHand);
key1.SetKeyGPIOCMD(&key1, GPIOE, GPIO_Pin_3);
[/mw_shl_code]
这里 我们先要编写一个按键扫描函数,由于我想要一劳永逸,这里会用到链表结构,就是在定义按键端口的时候会把这个对象添加到链表结构中去,所以增减按键就不用去动扫描函数了,是不是贼棒。。
上代码:
先定义一个链表节点 的结构体
[mw_shl_code=c,true]typedef struct CLASS_KEY_List
{
void (* const AddKEY_List)(struct CLASS_KEY * p);
typ_CLASS_KEY * _KEY_ListNode;
struct CLASS_KEY_List * _KEY_ListNext;
}typ_CLASS_KEY_List;[/mw_shl_code]
接下来是 扫描函数
[mw_shl_code=c,true]void KEY_Scanf()
{
typ_CLASS_KEY_List * p_temp;
p_temp = &key_list;
while(p_temp != 0)
{
if(GPIO_ReadInputDataBit(p_temp->_KEY_ListNode->GPIOx, p_temp->_KEY_ListNode->GPIO_Pin) == 0)
{
delay_ms(20);
if(GPIO_ReadInputDataBit(p_temp->_KEY_ListNode->GPIOx, p_temp->_KEY_ListNode->GPIO_Pin) == 0)
{
while(!GPIO_ReadInputDataBit(p_temp->_KEY_ListNode->GPIOx, p_temp->_KEY_ListNode->GPIO_Pin))
{
delay_ms(20);
p_temp->_KEY_ListNode->KEY_Times ++;
}
p_temp->_KEY_ListNode->KEYShortHand(p_temp->_KEY_ListNode);
p_temp->_KEY_ListNode->KEYLongHand(p_temp->_KEY_ListNode);
p_temp->_KEY_ListNode->KEY_Times = 0;
}
}
p_temp = p_temp->_KEY_ListNext;
}
}[/mw_shl_code]
最后把按键扫描函数 放在大循环里面 轮询就好了。。。。。
最后我们分析一下,这样写的好处:逻辑清楚,每个按键处理函数单独写出来
我每增加一个按键不需要去修改扫描函数
如果有功能类似的按键处理函数直接套用
如果按键如果函数随时需要改动只需要修改按键处理函数指针就好,这和后面我要介绍的状态机模式一样,比如当我按下第一次按键的时候我是打开灯光,当我第二次按下的时候我是炸毁房子。。。哈哈我这里介绍一下炸毁房子按键的写法这也就是 面向对象里面的多态
[mw_shl_code=c,true]void KEY1_ShortPrintfHand(struct CLASS_KEY * p)
{
if(p->KEY_ShortEvent_EN)
{
if((p->KEY_Times >= 1) && ((p->KEY_Times <= 49)))
{
printf("KEY1短按处理事件,打开灯光\r\n");
p->KEYShortHand = 爆炸函数; //这里把该按键的短按 按键处理函数替换成爆炸函数 当下次按下按键的时候 就会调用爆炸函数 而不是现在这个 到时候就会爆炸.....
}
}
}[/mw_shl_code]
有人反映这样使用链表内存开销比较大,而且对于新手也不太容易理解和操作,这里可以改成数组来存储 按键对象(按键结构体),然后只需要遍历数组就好了,只不过每次需要多操作几步
需要改变数组元素 大小,当然使用宏定义 也非常方便
|
-
-
KEY.rar
5.35 MB, 下载次数: 229
第二篇按键源码工程
|