在原先一篇文章里面我有分享一种裸机时间片轮训的实现方式,现在在上面的基础上增加一个倒计时判定的功能。
举个例子:现在有一个设定,在采集温度小于50度时,想实现先打开继电器1,30s后打开继电器2,当采集温度大于100度时,关闭这两个继电器,并且要求在接下来的5分钟内,禁止打开继电器(即使温度小于50度)。
上面的逻辑很简单,实现的方式也很多,下面我记录分享一种最近接触到的。
考虑不同时间计时的共同元素都有:计时是否开始,计时是否完成,计时多久。所以先定义一个基础的计时单元结构体定义在定时器头文件;
typedef struct{
uint8_t flg;//计时标志
uint8_t sta;//计时状态
uint32_t cnt;//计数值
}Stc_Timer,*Pstc_Timer;//基本单元
好的,到这里就算完成一半了,可以庆祝了。
接下来我们就可以考虑怎么结合上面的共同元素去开始计时,以及如何计时了,以前提过计时原理基本都是利用单片机的定时器,那么可以设计一个开始计时的函数,在需要计时的时候调用一下,把上面的参数传入。告诉单片机定时器,这个东西我要倒计时,你自己去减,减好了和我说一下。
#define START 1
#define NOT_START 0
#define FINISH 1
#define NOT_FINISH 0
extern int16_t Start_Timer(Stc_Timer *ptm,u32 dly)
{
if(ptm == NULL)
{
return -1;
}
ptm->sta = START;
ptm->flg = NOT_FINISH;
ptm->cnt = dly;
return ERROR_OK;
}
设计了开始计时,就要考虑如何去计时了,很简单,我定时的去判定有没有需要计时的,有我就按照上面要求的去不断地减设定的计数值。
Stc_Timer Relay_Timer[Relay_Num];//定义一个数据类型为计时单元的数组
//只要你想让某个流程开始计时,你就可以为其设定一个计时单元类型的变量
int16_t Relay_Timer_Count(void)
{
for(u8 i = 0;i < Relay_Num;i++)//遍历数组里面所有成员
{
if(Relay_Timer[i].sta == START)//如果是计时状态
{
if(Relay_Timer[i].cnt <= 1)//判断计时数值是否减完
{
Relay_Timer[i].flg = FINISH;//计时完成
Relay_Timer[i].sta = NOT_START;//不用计时
Relay_Timer[i].cnt = INIT_TIME;//装载初始值100次
}
else//计时未完成就继续减
{
gen_timer[i].cnt--;
}
}
}
return 0;
}
上面的函数一定要定时的去执行,比如放在10ms执行一次的任务当中。10ms减一次。这样我填写计数值的时候,填写一个3000,就表示3000个10ms,也就是30s。
如何计时设计完了,接下来就是考虑停止计时了,将计时单元恢复到不用计时的初始状态。有的可能会考虑到,它自己计时完毕已经恢复到初始状态了 ,没必要多此一举。
但用到if,就一定要考虑else的必要性!!!
如果存在开始计时,那么一定存在计时未完成被中断的情况,假如继电器2坏掉,就没有必要计时了。
extern int16_t Stop_Timer(Stc_Timer *ptm)
{
if(ptm == NULL)
{
return -1;
}
ptm->sta = NOT_START;//不开始计时
ptm->flg = NOT_FINISH;//计时结束
ptm->cnt = 0;
return 0;
}
//伪代码
#define RELAY1_ON
#define RELAY2_ON
#define RELAY1_OFF
#define RELAY2_OFF
int Relay_Time_Task(void)
{
if(RELAY1_ON == 1)
{
start_timer(&Relay_Timer[0],3000);
}
if(Relay_Timer.flg != FINISH)
{
return 0;
}
RELAY2_ON;
}
还有关闭5分钟禁止再次开启,也是同样的道理就不写了。
编写一个关继电器的函数,把开启倒计时5分钟的函数也放一起,定义一个新的结构体变量。在开启继电器函数最开始就判定这个倒计时标志是否完成,未完成就直接return不继续往下执行。
有大佬愿意指点,可以分享更好的办法。