OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5949|回复: 6

在51和STM32单片机上使用面向对象的编程,第二篇:强大的按键能爆炸

[复制链接]

8

主题

61

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
235
金钱
235
注册时间
2018-5-2
在线时间
83 小时
发表于 2018-5-3 16:56:00 | 显示全部楼层 |阅读模式
本帖最后由 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

第二篇按键源码工程

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

25

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
390
金钱
390
注册时间
2011-11-14
在线时间
130 小时
发表于 2018-5-3 18:17:12 | 显示全部楼层
好思路,不过内存开销有点大
回复 支持 反对

使用道具 举报

20

主题

450

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
4938
金钱
4938
注册时间
2017-7-6
在线时间
722 小时
发表于 2018-5-3 18:32:02 | 显示全部楼层
队列 状态机 处理方法也不错
我是小白,很白很白的小白!!!
回复 支持 反对

使用道具 举报

8

主题

61

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
235
金钱
235
注册时间
2018-5-2
在线时间
83 小时
 楼主| 发表于 2018-5-4 09:04:59 | 显示全部楼层
ifus 发表于 2018-5-3 18:17
好思路,不过内存开销有点大

这里为了减小内存开销,可以使用 数组来存储按键对象(按键结构体),在51上我就是这样实现的
回复 支持 反对

使用道具 举报

5

主题

53

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
271
金钱
271
注册时间
2014-8-7
在线时间
59 小时
发表于 2018-5-10 00:27:19 | 显示全部楼层
没学过C++的人,不好理解
回复 支持 反对

使用道具 举报

40

主题

259

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
459
金钱
459
注册时间
2016-5-19
在线时间
192 小时
发表于 2018-7-27 15:57:12 | 显示全部楼层
写的很好。可以去开发操作系统了
回复 支持 反对

使用道具 举报

6

主题

43

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
294
金钱
294
注册时间
2013-9-9
在线时间
92 小时
发表于 2020-12-9 10:18:10 | 显示全部楼层
不错的思路,可以参考一下
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-23 15:29

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表