(模块全代码获取方式,关注微信公 ,发送switch)
四、软件设计
本软件模块实现按键的单次按下、单次释放、按下状态、释放状态、故障(卡滞)状态的检测。
单次按下:按键从无效电平到有效电平跳变
单次释放:按键从有效电平到无效电平跳变
按下状态:按键处于按下状态
释放状态:按键处于释放状态
故障状态:这里主要指按键在按下状态维持的时间过久,就认定按键卡住了,判为按键故障状态,以指示相关功能模块忽略此按键的操作,达到避免误响应的目的!
软件模块设计先从整体来看,先从配置项开始!!!为何配置为外围硬件不同或者需求的不同导致相关参数会不同,按键主要提现在硬件方面上不同的有有效电平设计不同、按键个数不同、芯片不同(导致读取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
- 按键个数要让人家配置的吧 于是搞个按键个数的配置
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 故障代码不编译*/
- 获取IO口电平的函数,因为芯片不同,自然获取的函数寄存器等都不同,所以我这里开了个函数bool GetSwitch_u_PortChannel(en_switch_name_t name)
这个函数传入的参数是按键的名称(名称看上面的C配置),返回的是相应按键的IO口电平值!
这个函数需要自己编写,后续在移植说明中会有实例说明!
需要配置的就这么多,其实结束了 哈哈!
2、软件的配置东西搞定了,那软件开始了!构建一个模块的代码,当然先构建模块固定的相关参数的结构体,这里固定的意思就是只读的意思;这样做其实就是打包的好处好拿好用就是好移植好调用!
按键的相关固定参数:
1)按键滤波定时器
2)按键当前稳定状态
3)按键按下单次触发
- 按键释放单次触发
- 按键有效
- 按键故障状态
- 按键故障触发
- 按键故障滤波定时器
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;
- 函数的构造,从总的来说,就是初始化函数、主滤波运行函数、单次释放获取函数、单次按下获取函数、当前状态获取函数、获取故障状态函数。;
初始化函数:就是把结构体初始化
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)
文章知识点与官方知识档案匹配,可进一步学习相关知识C技能树首页概览113206 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!