纯C语言按键驱动,使用软件查询实现

https://github.com/jiejieTop/ButtonDrive

调试打印个人信息——__DEBUG__

调试信息PRINT_DEBUG_ENABLE

错误信息PRINT_ERR_ENABLE

个人信息PRINT_INFO_ENABLE

Printf在单片机上可以用串口打印出来。

 

#define PRINT_DEBUG_ENABLE 0 /* 打印调试信息 */

#define PRINT_ERR_ENABLE 0   /* 打印错误信息 */

#define PRINT_INFO_ENABLE 0 /* 打印个人信息 */

 

 

#if PRINT_DEBUG_ENABLE

#define PRINT_DEBUG(fmt, args...)  do{(printf("\n[DEBUG] >> "), printf(fmt, ##args));}while(0)     

#else

#define PRINT_DEBUG(fmt, args...)      

#endif

 

#if PRINT_ERR_ENABLE

#define PRINT_ERR(fmt, args...)  do{(printf("\n[ERR] >> "), printf(fmt, ##args));}while(0)     

#else

#define PRINT_ERR(fmt, args...)        

#endif

 

#if PRINT_INFO_ENABLE

#define PRINT_INFO(fmt, args...)  do{(printf("\n[INFO] >> "), printf(fmt, ##args));}while(0)     

#else

#define PRINT_INFO(fmt, args...)        

#endif

 

 

断言

这是编译器内置宏,这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息。ANSI C标准中几个标准预定义宏:

__LINE__:在源代码中插入当前源代码行号;

__FILE__:在源文件中插入当前源文件名;

__DATE__:在源文件中插入当前的编译日期

__TIME__:在源文件中插入当前编译时间;

__STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;

__cplusplus:当编写C++程序时该标识符被定义。

 

/* 断言 Assert */

#define AssertCalled(char,int) printf("\nError:%s,%d\r\n",char,int)

#define ASSERT(x)   if((x)==0)  AssertCalled(__FILE__,__LINE__)

 

typedef enum

{

ASSERT_ERR = 0, /* 错误 */

ASSERT_SUCCESS = !ASSERT_ERR /* 正确 */

} Assert_ErrorStatus;

按键创建

第一步,判断输入按键结构体地址是否存在

第二步,清空按键结构体内存,参考附录

第三步,初始化按键结构体

第四步,加入到按键列表中

使用链表存放按键,重要的是 NEXT*

/************************************************************

* @brief   按键创建

* @param   name : 按键名称

* @param   btn : 按键结构体

* @param   read_btn_level : 按键电平读取函数,需要用户自己实现返回uint8_t类型的电平

* @param   btn_trigger_level : 按键触发电平

  ***********************************************************/

void Button_Create(const char *name,

                  Button_t *btn,

                  uint8_t(*read_btn_level)(void),

                  uint8_t btn_trigger_level)

{

  if( btn == NULL) //输入储存结构体的变量的地址为空

  { //一个python的断言用这两句输出

    PRINT_ERR("struct button is null!"); //输出错误信息,主要错误信息

    ASSERT(ASSERT_ERR); //输出断言信息,定位错误位置

  }

  

  memset(btn, 0, sizeof(struct button));  //清除结构体信息,建议用户在之前清除

 

  StrnCopy(btn->Name, name, BTN_NAME_MAX); /* 创建按键名称 */

  

  

  btn->Button_State = NONE_TRIGGER;            //按键状态

  btn->Button_Last_State = NONE_TRIGGER;       //按键上一次状态

  btn->Button_Trigger_Event = NONE_TRIGGER;    //按键触发事件

  btn->Read_Button_Level = read_btn_level;     //按键读电平函数

  btn->Button_Trigger_Level = btn_trigger_level;   //按键触发电平

  btn->Button_Last_Level = btn->Read_Button_Level(); //按键当前电平

  btn->Debounce_Time = 0;

  

  PRINT_DEBUG("button create success!");

  

  Add_Button(btn);          //创建的时候添加到单链表中

  

  Print_Btn_Info(btn);     //打印信息

 

}

按键删除

/************************************************************

  * @brief    删除一个已经创建的按键

* @param   btn : 按键结构体

  * @return   NULL

 ***********************************************************/

void Button_Delete(Button_t *btn)

{ //一个*其实和变量没区别,两个*是地址

  struct button** curr; //这里**是申请二级指针

  for(curr = &Head_Button; *curr;) //*curr是结构体地址,遍历完就等于NULL

  {

    struct button* entry = *curr;

    if (entry == btn)

    {

      *curr = entry->Next; //这里就是删除,直接链接到下一个

    }

    else

    {

      curr = &entry->Next; //这里就是遍历

    }

  }

}

//书上

/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */

/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */

Status ListDelete(LinkList *L,int i,ElemType *e)

{

int j;

LinkList p,q;

p = *L;

j = 1;

while (p->next && j < i) /* 遍历寻找第i个元素 */

{

        p = p->next;

        ++j;

}

if (!(p->next) || j > i)

    return ERROR;           /* 第i个元素不存在 */

q = p->next;

p->next = q->next; /* 将q的后继赋值给p的后继 */

*e = q->data;               /* 将q结点中的数据给e */

free(q);                    /* 让系统回收此结点,释放内存 */

return OK;

}

按键触发事件与回调函数映射链接起来

/************************************************************

  * @brief   按键触发事件与回调函数映射链接起来

  * @param   btn : 按键结构体

  * @param   btn_event : 按键触发事件

  * @param   btn_callback : 按键触发之后的回调处理函数。需要用户实现

  * @return  NULL

  ***********************************************************/

void Button_Attach(Button_t *btn,Button_Event btn_event,Button_CallBack btn_callback)

{

  if( btn == NULL) //没有按键实体,报警

  {

    PRINT_ERR("struct button is null!");

    //ASSERT(ASSERT_ERR);       //断言

  }

  

  if(BUTTON_ALL_RIGGER == btn_event) //所有都触发一遍

  {

    for(uint8_t i = 0 ; i < number_of_event-1 ; i++)

      btn->CallBack_Function[i] = btn_callback; //按键事件触发的回调函数,用于处理按键事件

  }

  Else //只触发对应回调函数

  {

    btn->CallBack_Function[btn_event] = btn_callback; //按键事件触发的回调函数,用于处理按键事件

  }

}

btn_event : 按键触发事件

typedef enum {

  BUTTON_DOWM = 0, //按下

  BUTTON_UP, //弹起

  BUTTON_DOUBLE,

  BUTTON_LONG,

  BUTTON_LONG_FREE,

  BUTTON_CONTINUOS, //连按

  BUTTON_CONTINUOS_FREE,

  BUTTON_ALL_RIGGER,

  number_of_event, /* 触发回调的事件 */

  NONE_TRIGGER

}Button_Event;

按键触发之后的回调处理函数

//定义一个函数指针输入(void*),返回void。        void*是任意类型

typedef void (*Button_CallBack)(void*);   /* 按键触发回调函数,需要用户实现 */

按键周期处理函数

/************************************************************

  * @brief   按键周期处理函数

  * @param   btn:处理的按键

  * @return  NULL

  * @note    必须以一定周期调用此函数,建议周期为20~50ms

  ***********************************************************/

void Button_Cycle_Process(Button_t *btn)

{

  uint8_t current_level = (uint8_t)btn->Read_Button_Level(); //获取当前按键电平

  

  if((current_level != btn->Button_Last_Level)&&(++(btn->Debounce_Time) >= BUTTON_DEBOUNCE_TIME)) //按键电平发生变化,消抖

  {

      btn->Button_Last_Level = current_level; //更新当前按键电平

      btn->Debounce_Time = 0;                  //确定了是按下

      

      //如果按键是没被按下的,改变按键状态为按下(首次按下/双击按下)

      if((btn->Button_State == NONE_TRIGGER)||(btn->Button_State == BUTTON_DOUBLE))

      {

        btn->Button_State = BUTTON_DOWM;

      }

      //释放按键

      else if(btn->Button_State == BUTTON_DOWM)

      {

        btn->Button_State = BUTTON_UP;

        PRINT_DEBUG("释放了按键");

      }

  }

 

 

  switch(btn->Button_State)

  {

    case BUTTON_DOWM :            // 按下状态

    {

      if(btn->Button_Last_Level == btn->Button_Trigger_Level) //按键按下

      {

        #if CONTINUOS_TRIGGER     //支持连续触发

 

        if(++(btn->Button_Cycle) >= BUTTON_CONTINUOS_CYCLE)

        {

          btn->Button_Cycle = 0;

          btn->Button_Trigger_Event = BUTTON_CONTINUOS;

          TRIGGER_CB(BUTTON_CONTINUOS);    //连按

          PRINT_DEBUG("连按");

        }

        

        #else

        if(btn->Button_Trigger_Event != BUTTON_LONG)

          btn->Button_Trigger_Event = BUTTON_DOWM;

      

        if(++(btn->Long_Time) >= BUTTON_LONG_TIME)  //释放按键前更新触发事件为长按

        {

          #if LONG_FREE_TRIGGER

          

          btn->Button_Trigger_Event = BUTTON_LONG;

          

          #else

          

          if(++(btn->Button_Cycle) >= BUTTON_LONG_CYCLE)    //连续触发长按的周期

          {

            btn->Button_Cycle = 0;

            btn->Button_Trigger_Event = BUTTON_LONG;

            TRIGGER_CB(BUTTON_LONG);    //长按

          }

          #endif

          

          if(btn->Long_Time == 0xFF)  //更新时间溢出

          {

            btn->Long_Time = BUTTON_LONG_TIME;

          }

          PRINT_DEBUG("长按");

        }

          

        #endif

      }

 

      break;

    }

    

    case BUTTON_UP :        // 弹起状态

    {

      if(btn->Button_Trigger_Event == BUTTON_DOWM)  //触发单击

      {

        if((btn->Timer_Count <= BUTTON_DOUBLE_TIME)&&(btn->Button_Last_State == BUTTON_DOUBLE)) // 双击

        {

          btn->Button_Trigger_Event = BUTTON_DOUBLE;

          TRIGGER_CB(BUTTON_DOUBLE);    

          PRINT_DEBUG("双击");

          btn->Button_State = NONE_TRIGGER;

          btn->Button_Last_State = NONE_TRIGGER;

        }

        else

        {

            btn->Timer_Count=0;

            btn->Long_Time = 0;   //检测长按失败,清0

          

          #if (SINGLE_AND_DOUBLE_TRIGGER == 0)

            TRIGGER_CB(BUTTON_DOWM);    //单击

          #endif

            btn->Button_State = BUTTON_DOUBLE;

            btn->Button_Last_State = BUTTON_DOUBLE;

          

        }

      }

      

      else if(btn->Button_Trigger_Event == BUTTON_LONG)

      {

        #if LONG_FREE_TRIGGER

          TRIGGER_CB(BUTTON_LONG);    //长按

        #else

          TRIGGER_CB(BUTTON_LONG_FREE);    //长按释放

        #endif

        btn->Long_Time = 0;

        btn->Button_State = NONE_TRIGGER;

        btn->Button_Last_State = BUTTON_LONG;

btn->Button_Trigger_Event = BUTTON_DOWM;

      }

      

      #if CONTINUOS_TRIGGER

        else if(btn->Button_Trigger_Event == BUTTON_CONTINUOS)  //连按

        {

          btn->Long_Time = 0;

          TRIGGER_CB(BUTTON_CONTINUOS_FREE);    //连发释放

          btn->Button_State = NONE_TRIGGER;

          btn->Button_Last_State = BUTTON_CONTINUOS;

        }

      #endif

      

      break;

    }

    

    case BUTTON_DOUBLE :

    {

      btn->Timer_Count++;     //时间记录

      if(btn->Timer_Count>=BUTTON_DOUBLE_TIME)

      {

        btn->Button_State = NONE_TRIGGER;

        btn->Button_Last_State = NONE_TRIGGER;

      }

      #if SINGLE_AND_DOUBLE_TRIGGER

      

        if((btn->Timer_Count>=BUTTON_DOUBLE_TIME)&&(btn->Button_Last_State != BUTTON_DOWM))

        {

          btn->Timer_Count=0;

          TRIGGER_CB(BUTTON_DOWM);    //单击

          btn->Button_State = NONE_TRIGGER;

          btn->Button_Last_State = BUTTON_DOWM;

        }

        

      #endif

 

      break;

    }

 

    default :

      break;

  }

  

}

 

整个逻辑是这样的按键“按下”状态触发状态为连按,长按,单击3种状态的分支。连按是及时触发。长按分两种,一种是及时触发,一种的弹起触发。

接下来如果“弹起”,触发双击,单击,长按释放。触发状态调整为初始或者等待双击。

最后就是“双击”状态,双击状态就是计时,记住双击的时间,判断触发状态是双击还是单击。

 

 

 

单链表插入元素

//数据结构中使用的头插法

/*  随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */

void CreateListHead(LinkList *L, int n)

{

LinkList p;

int i;  

srand(time(0));                         /* 初始化随机数种子 */

*L = (LinkList)malloc(sizeof(Node));

(*L)->next = NULL;                      /*  先建立一个带头结点的单链表 */

for (i=0; i<n; i++)

{

p = (LinkList)malloc(sizeof(Node)); /*  生成新结点 */

p->data = rand()%100+1;             /*  随机生成100以内的数字 */

p->next = (*L)->next;    

(*L)->next = p; /*  插入到表头 */

}

}

 

 

//下面这个是自己打造的头插法

/************************************************************

  * @brief   使用单链表将按键连接起来  --- 头插法

  ***********************************************************/

static struct button* Head_Button = NULL;

static void Add_Button(Button_t* btn)

{

  struct button *pass_btn = Head_Button;

  

  while(pass_btn)

  {

    pass_btn = pass_btn->Next;

  }

  

  btn->Next = Head_Button;

  Head_Button = btn;

}

遍历单链表

//书上

/* 初始条件:顺序线性表L已存在 */

/* 操作结果:依次对L的每个数据元素输出 */

Status ListTraverse(LinkList L)

{

    LinkList p=L->next;

    while(p)

    {

        visit(p->data);

        p=p->next;

    }

    printf("\n");

    return OK;

}

//下面是自己构建

void Search_Button(void)

{

  struct button* pass_btn;

  for(pass_btn = Head_Button; pass_btn != NULL; pass_btn = pass_btn->Next)

  {

    PRINT_INFO("button node have %s",pass_btn->Name);

  }

}

清空结构体内存

//下面这个是自己构造——0

 

 memset(btn, 0, sizeof(struct button));  //在string.h中定义

 

//下面这个是自己构造——1,这个是错误的不知道为什么会把指针也清零?

#define DDL_ZERO_STRUCT(x)          ddl_memclr((uint8_t*)&(x), (uint32_t)(sizeof(x)))

void ddl_memclr(void *pu8Address, uint32_t u32Count)

{

    uint8_t *pu8Addr = (uint8_t *)pu8Address;

    

    if(NULL == pu8Addr)

    {

        return;

    }

    

    while (u32Count--)

    {

        *pu8Addr++ = 0;

    }

}

错误

防止初始化两遍按键程序,这样他就首位相接循环出不来了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值