基于C语言的机械按键模块化软件开发(2)

(模块全代码获取方式,关注微信公号,发送switch)

关注微信公众号 (airX嵌入式),获取更多项目经验!

四、软件设计

本软件模块实现按键的单次按下、单次释放、按下状态、释放状态、故障(卡滞)状态的检测。

单次按下:按键从无效电平到有效电平跳变

单次释放:按键从有效电平到无效电平跳变

按下状态:按键处于按下状态

释放状态:按键处于释放状态

故障状态:这里主要指按键在按下状态维持的时间过久,就认定按键卡住了,判为按键故障状态,以指示相关功能模块忽略此按键的操作,达到避免误响应的目的!

软件模块设计先从整体来看,先从配置项开始!!!为何配置?因为外围硬件不同或者需求的不同导致相关参数会不同,按键主要提现在硬件方面上不同的有有效电平设计不同、按键个数不同、芯片不同(导致读取IO口函数不一样);需求方面:滤波时间不同、故障功能是否需要等!因此我们需要在开发前确定这些参数,并在软件层面分配变量或者宏定义。下面我们开始开出配置变量!!

A、需确定硬件上按键的有效电平是高还是低,因此需要给个配置项;

在h文件里:

typedef enum en_switchport_active

{

SWITCHPORT_INACTIVE = 1,  

SWITCHPORT_ACTIVE = 0    

}en_switchport_active_t;   

注意:电平不是1就是0,而且2个枚举必须互斥。

 

B、滤波时间当然要配置了 所以需要开出滤波时间的配置

一个是按键的有效触发滤波,一个是故障的滤波,这个时间取决你主函数的调用周期T

滤波时间=配置参数*T  根据自己产品实际的来 ,一般50ms

#define SWITCHSTUCKFilter       30000  

#define CaSwitchCfg_u_BtnFilter    25

 

  1. 按键个数要让人家配置的吧 于是搞个按键个数的配置

typedef enum en_switch_name

{

SwitchName_1 = 0,

SwitchName_2,

SwitchName_3,

SwitchName_4,

SwitchName_5,

SwitchName_6,

SwitchName_7,

SWITCHNAME_MAX /*按键数量*/

}en_switch_name_t;

这里我用了个小技巧,多定义了一个SWITCHNAME_MAX的枚举用来当做按键的个数,按键的名字各位自己修改,但SWITCHNAME_MAX不能动,其他多少个按键,在上面加就行了!

这名字会在调用函数传参时,让你不会把按键弄乱掉!

 

D、因为有些项目可能对故障没什么要求,所以我做了个故障监测的开关

#define SWITCHCFG_BTNLOCK       ON/*设置为OFF 故障代码不编译*/

 

  1. 获取IO口电平的函数,因为芯片不同,自然获取的函数寄存器等都不同,所以我这里开了个函数bool GetSwitch_u_PortChannel(en_switch_name_t name)

这个函数传入的参数是按键的名称(名称看上面的C配置),返回的是相应按键的IO口电平值!

这个函数需要自己编写,后续在移植说明中会有实例说明!

需要配置的就这么多,其实结束了 哈哈!

 

2、软件的配置东西搞定了,那软件开始了!构建一个模块的代码,当然先构建模块固定的相关参数的结构体,这里固定的意思就是只读的意思;这样做其实就是打包的好处好拿好用就是好移植好调用!

按键的相关固定参数:

1)按键滤波定时器     

2)按键当前稳定状态

3)按键按下单次触发

  1. 按键释放单次触发
  2. 按键有效       
  3. 按键故障状态
  4. 按键故障触发
  5. 按键故障滤波定时器

typedef struct stc_swtich_cfg

{

uint16_t e_u_BtnFilter;      /* 按键滤波定时器     */

uint8_t  e_u_BtnState;      /* 按键当前稳定状态   */

uint8_t  e_u_BtnSingleAvail;      /* 按键按下单次触发   */

uint8_t  e_u_BtnInavail;  /* 按键松开单次触发   */

uint8_t  e_u_BtnAvail;      /* 按键触发有效标志   */

#if(SWITCHCFG_BTNLOCK == ON)

uint8_t  e_u_LockSta;      /* 故障状态 */

uint8_t  e_u_LockAvail;      /* 故障触发 */

uint16_t e_u_LockFilter;     /* 故障滤波定时器  */   

#endif

}stc_swtich_cfg_t;

  1. 函数的构造,从总的来说,就是初始化函数、主滤波运行函数、单次释放获取函数、单次按下获取函数、当前状态获取函数、获取故障状态函数。;

初始化函数:就是把结构体初始化

void Switch_ParameterInit(void)

{

uint8_t LeSwitch_u_Index;

for (LeSwitch_u_Index = 0U; LeSwitch_u_Index<SWITCHNAME_MAX; LeSwitch_u_Index++)

{

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnFilter      = 0U;                  /*按键滤波清零*/

//这里配置为无效的电平就是按键释放时的电平 默认上电按键处于释放

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnState       = SWITCHPORT_INACTIVE;  /*按键稳定状态*/

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnSingleAvail = INACTIVE;  /*按键单次触发*/

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnInavail     = INACTIVE;  /*按键松开单次触发*/

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnAvail       = INACTIVE;  /*按键触发*/

#if (SWITCHCFG_BTNLOCK == ON)

stcSwtichCfg[LeSwitch_u_Index].e_u_LockSta        = INACTIVE;  /*故障判断标识*/

stcSwtichCfg[LeSwitch_u_Index].e_u_LockAvail      = INACTIVE;  /*故障发生标志*/

stcSwtichCfg[LeSwitch_u_Index].e_u_LockFilter     = 0U;        /*故障滤波*/

#endif

}

Switch_u_ResetFlag = TRUE;

}

主函数 当然这个是最牛的:

滤波的思路就是读取当前的IO口状态,跟上一次稳定的状态对比,如果电平不同,则开始滤波,滤波到达时间后,改变状态,如果电平一样,则不检测或者开启故障监测!

我们来看主函数的设计:

void Switch_MainFunctionMng(void)

{

uint8_t LeSwitch_u_PortState = SWITCHPORT_INACTIVE;

uint8_t LeSwitch_u_Index;

//上电标志的配置 1、为了快速获取按键状态 即把第一次读取的IO口当做按键当前稳定的状态

//2、配合故障代码的话,可以防止按键按下状态下上电带来的影响

if (TRUE == Switch_u_ResetFlag)

{

Switch_u_ResetFlag = FALSE;

for (LeSwitch_u_Index = 0U; LeSwitch_u_Index<SWITCHNAME_MAX; LeSwitch_u_Index++)

{

/*记录端口状态*/

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnState = 1;//GetSwitch_u_PortChannel(LeSwitch_u_Index);

//若按键按下状态的时候上电 故障代码判定为故障 直到释放按键才恢复

#if (SWITCHCFG_BTNLOCK == ON)

/********************************* Power on deal ****************************/          

if(stcSwtichCfg[LeSwitch_u_Index].e_u_BtnState == SWITCHPORT_ACTIVE)

{

stcSwtichCfg[LeSwitch_u_Index].e_u_LockAvail  = ACTIVE;    /*故障发生*/

stcSwtichCfg[LeSwitch_u_Index].e_u_LockSta    = INACTIVE;  /*清掉判断故障标识*/

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnAvail   = INACTIVE;  /*故障时,按键触发无效*/

}

else

{}

#endif

/********************************* Power on deal ****************************/

}  

}

else

{

for (LeSwitch_u_Index = 0U; LeSwitch_u_Index<SWITCHNAME_MAX; LeSwitch_u_Index++)

{

/*读取按键端口状态*/

LeSwitch_u_PortState = 1;//GetSwitch_u_PortChannel(LeSwitch_u_Index);

//读取的当前IO状态与当前稳定状态电平不一样 其实就是电平发生了跳变 /*边沿触发*/

if (stcSwtichCfg[LeSwitch_u_Index].e_u_BtnState != LeSwitch_u_PortState)

{

/*滤波计数*/

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnFilter++;

//达到滤波时间

if (CaSwitchCfg_u_BtnFilter <= stcSwtichCfg[LeSwitch_u_Index].e_u_BtnFilter)

{

/*将当前稳定状态更新为当前IO端口状态*/

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnState = LeSwitch_u_PortState;

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnFilter = 0U;//滤波清零

//判断新的稳定状态的电平 来确定是按下还是释放

if (stcSwtichCfg[LeSwitch_u_Index].e_u_BtnState == SWITCHPORT_ACTIVE)  //按下

{

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnSingleAvail = ACTIVE;    /*按键单次触发有效*/

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnAvail       = ACTIVE;    /*按键触发有效*/

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnInavail     = INACTIVE;  /*按键释放无效*/

#if (SWITCHCFG_BTNLOCK == ON)

stcSwtichCfg[LeSwitch_u_Index].e_u_LockSta        = ACTIVE;    /*判断按键故障标识置起*/

stcSwtichCfg[LeSwitch_u_Index].e_u_LockFilter     = 0U;

#endif

}

else    /*按键无效值*/

{

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnSingleAvail = INACTIVE;

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnAvail       = INACTIVE;

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnInavail     = ACTIVE;

#if (SWITCHCFG_BTNLOCK == ON)

stcSwtichCfg[LeSwitch_u_Index].e_u_LockSta        = INACTIVE;   

stcSwtichCfg[LeSwitch_u_Index].e_u_LockAvail      = INACTIVE;  /*按键释放时,清故障标识*/

stcSwtichCfg[LeSwitch_u_Index].e_u_LockFilter     = 0U;

#endif

}

}

else

{}

}

else

{

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnFilter = 0U; //滤波清零  

}

/*进入判断故障 && 故障标定时间大于0*///这里建议大于触发的滤波时间

#if (SWITCHCFG_BTNLOCK == ON)

if ((ACTIVE == stcSwtichCfg[LeSwitch_u_Index].e_u_LockSta) && (SWITCHSTUCKFilter > 0U))

{

stcSwtichCfg[LeSwitch_u_Index].e_u_LockFilter++;

if (stcSwtichCfg[LeSwitch_u_Index].e_u_LockFilter >= SWITCHSTUCKFilter) //故障条件成立

{

stcSwtichCfg[LeSwitch_u_Index].e_u_LockFilter = 0U;

stcSwtichCfg[LeSwitch_u_Index].e_u_LockAvail  = ACTIVE;    /*故障发生*/

stcSwtichCfg[LeSwitch_u_Index].e_u_LockSta    = INACTIVE;  /*清掉判断故障标识*/

stcSwtichCfg[LeSwitch_u_Index].e_u_BtnAvail   = INACTIVE;  /*故障时,按键触发无效*/

}

else

{}

}

else

{}

#endif

}

}

}

  • 有些项目可能需要上电快速获取一个状态,所以我留了个Switch_u_ResetFlag来控制是否上电快速获取,如果没这个必要就FALSE掉就行了!

接下里就是获取按键的操作了!!!

在结构体的变量里取就是了!

单次按下

en_switch_active_t GetSwitch_u_ButtonTrigger(en_switch_name_t LeSwitch_e_ButtonIndex)

{

en_switch_active_t LeSwitch_e_TempRet;

if (ACTIVE == stcSwtichCfg[LeSwitch_e_ButtonIndex].e_u_BtnSingleAvail)

/*按键为ACTIVE 返回ACTIVE*/

{

LeSwitch_e_TempRet = ACTIVE;

stcSwtichCfg[LeSwitch_e_ButtonIndex].e_u_BtnSingleAvail = INACTIVE; /*单次触发清除*/

}

else

/*按键不为ACTIVE 返回INACTIVE*/

{

LeSwitch_e_TempRet = INACTIVE;

}

return LeSwitch_e_TempRet;

}

单次释放

en_switch_active_t GetSwitch_u_ButtonInavial(en_switch_name_t LeSwitch_e_ButtonIndex)

{

en_switch_active_t LeSwitch_e_TempRet;

if (ACTIVE == stcSwtichCfg[LeSwitch_e_ButtonIndex].e_u_BtnInavail)

/*按键为ACTIVE 返回ACTIVE*/

{

LeSwitch_e_TempRet = ACTIVE;

stcSwtichCfg[LeSwitch_e_ButtonIndex].e_u_BtnInavail = INACTIVE; /*单次触发清除*/

}

else

/*按键不为ACTIVE 返回INACTIVE*/

{

LeSwitch_e_TempRet = INACTIVE;

}

return LeSwitch_e_TempRet;

}

按键稳定状态

en_switch_active_t GetSwitch_u_ButtonActive(en_switch_name_t LeSwitch_e_ButtonIndex)

{

en_switch_active_t LeSwitch_e_TempRet;

if (ACTIVE == stcSwtichCfg[LeSwitch_e_ButtonIndex].e_u_BtnAvail)

/*索引是否为有效按键*/

{

LeSwitch_e_TempRet = ACTIVE;

}

else

{

LeSwitch_e_TempRet = INACTIVE;

}

return LeSwitch_e_TempRet;

}

故障状态读取

#if (SWITCHCFG_BTNLOCK == ON)

en_switch_active_t GetSwitch_u_ButtonLockAvail(en_switch_name_t LeSwitch_e_ButtonIndex)

{

en_switch_active_t LeSwitch_e_TempRet;

if (ACTIVE == stcSwtichCfg[LeSwitch_e_ButtonIndex].e_u_LockAvail)

/*索引是否为有效按键*/

{

LeSwitch_e_TempRet = ACTIVE;

}

else

{

LeSwitch_e_TempRet = INACTIVE;;

}

return LeSwitch_e_TempRet;

}

#endif

(模块全代码获取方式,关注微信公号,发送switch)

关注微信公众号 (airX嵌入式),获取更多项目经验!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值