本文链接上篇继续叙述,如果没有看到看一篇的,大家可以点击传送门观看。
作者:良知犹存
转载授权以及围观:欢迎添加wx:Allen-Iverson-me-LYN
总述
1.通过运用RTOS,线程里面调用系统提供的延时等函数,实现解决阻塞。
2.全局变量方法,定时器计数条件判断替代延时。
3.使用状态机方式,分解动作,替代延时实现。
4.使用链表式,与第三种相似。
三、 状态机法
状态机执行与全局变量的区别在于,状态机的方法是更进一层的定时操作,可以通过状态机在主循环或者触发函数实现多个动作的延时操作。
状态机中大致会分为两类延时替代操作,一种注重多个动作的延时,一种叫精确时间的延时。第一种情况经常会在一些需要双方有来有往的应答操作出现,而后一种出现在明确延时操作过程中。接下来我们展示一下代码风格吧。
/*堵塞型的代码*/ void clogged(void) { taskfun1(); delay(500); taskfun2(); delay(100); taskfun3(); delay(20); } int main(int argc,char** argv) { SystemInit(); while(1) { clogged(); taskfun();/*此段代码执行的时候会被堵塞*/ } }
1.注重动作的状态机
/*注重分解动作的状态机*/ void TIM_IRQ(void)/*定时器中断服务函数*/ { FSMtimer ++;/*设置合适的中断时间间隔*/ } static u32 FSMtimer = 0,LastFSMtimer = 0;/*用来计时变量*/ static u8 FSMSta = 0;/*设置状态的工作步骤的变量*/ void actionFSM(void) { switch(FSMSta) { case 0: taskfun1(); FSMSta = 1; break; case 1: taskfun2(); FSMSta = 2; break; case 2: taskfun3(); FSMSta = 3; break; case 3: if(FSMtimer - LastFSMtimer >100 )/*定时判断完成任务*/ { taskfun4(); FSMSta = 4; } else { taskfun5(); FSMSta = 1;/*条件判断出现问题,实现动作跳转,进行动作重复*/ } break; case 4: taskidle();/*状态机的状态执行完成可以进行空闲任务*/ break; } int main(int argc,char** argv) { SystemInit(); FSMSta = n;/*初始化设置状态机的起始步*/ while(1) { actionFSM();/*替换效果如下*/ taskfun();/*此段代码执行的时候会被堵塞*/ } }
2.注重精确延时固定动作的状态机
/*延时操作的结构体*/ __packed typedef struct{ bool IsFSM_start;/*延时计数的标志*/ u8 FSM_Sta:7;/*状态机执行的状态*/ u32 FSM_target;/*延时定时的目标值*/ u32 FSM_count;/*状态机用来在计数器中累加计时的变量*/ u8 FSM_flag;/*状态机part的标志*/ }DelayTypeDef; DelayTypeDef DelayFSM; /*定时中断服务函数*/ void TIM_IRQ(void) { if(DelayFSM.FSM_flag==1)/*执行状态机部分标志*/ { if(DelayFSM.IsFSM_start== 1)/*动作执行代码,为零则不进入计时操作*/ { (DelayFSM.FSM_count<DelayFSM.FSM_target)? (DelayFSM.FSM_count++): (DelayFSM.IsFSM_start=0,DelayFSM.FSM_Sta++);/*IsFSM_start==0 进入状态机执行*/ } } } u8 taskfun(void) { if(DelayFSM.FSM_flag==0)/*初始化代码部分*/ { DelayFSM.FSM_flag= 1;/*设置状态机flag为1 ,执行下一部分程序*/ DelayFSM.IsFSM_start= 0;/*开始状态机*/ DelayFSM.FSM_Sta= 1;/*初始化第一步*/ } else if (DelayFSM.FSM_flag==1) { if(DelayFSM.IsFSM_start== 0) { switch(DelayFSM.FSM_Sta) { case 0x01: DelayFSM.IsFSM_start= 1;/*IsFSM_start==1 进入计时器计时*/ DelayFSM.FSM_count= 0; /*计数器清零*/ DelayFSM.FSM_target= 1000;/*设置计时的目标时间为 1000个定时器触发时间间隔*/ taskfun1();/*任意任务函数*/ break; case 0x02: DelayFSM.IsFSM_start= 1;/*注释如上*/ DelayFSM.FSM_count= 0; /*注释如上*/ DelayFSM.FSM_target= 500;/*目标值为500基数*/ taskfun2();/*任意任务函数*/ break; case 0x03: DelayFSM.IsFSM_start= 0;/*状态机完成状态 start清零*/ DelayFSM.FSM_Sta= 0; /*清零代表状态机将进入空闲模式*/ DelayFSM.FSM_target= 0;/*目标值设置位零*/ DelayFSM.FSM_flag= 3;/*随意赋值,这个值与你在主程序的判断, 重启状态机的标记相关,可看main中程序*/ taskfun3();/*任意任务函数*/ break; default: break; } } } else return 0; return 1; } int main(int argc,char** argv) { SystemInit(); while(1) { taskfun();/*状态机执行*/ if (DelayFSM.flag==3)/*重复使用次状态机*/ { DelayFSM.flag = 0; } } }
这就是我分享的通过状态机实现的解决堵塞方法,里面代码是实践过的,如果大家有什么更好的思路,欢迎分享交流哈
四、 链表法
通过上面结构体状态机程序,我们知晓通过一些特定的延时程序等待,配合状态机可以替换很多的队列式延时。可是通过结构体 定义的时候,如果需要很多处的延时阻塞替换,则会产生很多的延时变量,这个时候需要管理很多不同名称的变量,这样容易造成混乱。所以我们可以选择链表使用,链表定义好head之后,只要模式一样数据变量,我们只需要添加一个节点即可。
注:这里使用链表是单链表,双链表暂且不需要用到。链表的使用方法我是参考一位博主的文章而使用的,如果需要大家也可以去欣赏他的文章,这里是传送门。
#define ONCE 0 #define CYCLE 1 typedef struct Delay_TIMER { bool mode;/*设置的模式 0 单次计时 1 循环计时*/ bool isActivate;/*判定是否需要启动定时*/ volatile u32 cnt;/*计数器*/ u32 target;/*目标数值*/ struct Delay_TIMER* next;/*下一个节点*/ }Delay_TIMER_t; static struct Delay_TIMER *head = NULL;/*定义链表的头*/ /* 定时器中断服务程序*/ void TIM_ISR(void) { struct Delay_TIMER *t = head; if (t == NULL) return; while(t != NULL) { if (t->isActivate == true) t->cnt++;/*计数器定时累加*/ t = t->next; } } /*插入一个需要定时的链表节点,并初始化成员变量*/ void Insert_delay_node(Delay_TIMER_t *node,bool mode,uint32_t target) { struct Delay_TIMER *t = head; node->isActivate = true;/*设置为开*/ node->mode = mode;/*模式支持自己设置*/ node->cnt = 0; node->target = target;/*设置目标时长数值*/ node->next = NULL; if (head == NULL) { head = node; return; } while(t->next != NULL ) { if (t == node) return; t = t->next; } t->next = node; } /*删除不需要使用的延时节点*/ void Delete_delay_node(Delay_TIMER_t *node) { struct Delay_TIMER *p = head,*t = head; if (head == node) { head = node->next; return; } while(t->next != NULL )/*当需要删除尾部最后一个节点的时候, tail->next == NULL,这个时候循环跳出,节点无法删除*/ { if (t == node) /*删除节点*/ { p->next = node->next;/*node->next == NULL;*/ node->next = NULL; } else { p = t; t = t->next;/* t = p->next;*/ } } if(t->next == NULL && head != node)/*删除最后一个节点*/ { p->next = NULL;/*尾部最后一个节点直接置NULL*/ } } /*查询软件计时器是否超时*/ bool Is_delay_timer_Out(Delay_TIMER_t *node) { bool res = false; if (node->cnt >= node->target) { res = true; if (node->mode == CYCLE) { node->cnt = 0; node->isActivate = true; }else node->isActivate = false; } return res; } /*主程序*/ int main(int argc,char** argv) { SystemInit(); u8 mode = 0;/*单次模式*/ static u8 STA = 1;/*设置状态机初始step*/ static Delay_TIMER_t task_delay; Insert_delay_node(task_delay,mode,1000);/*初始化节点,设置为单次模式, 计时目标为1000个计数间隔*/ while(1) { switch(STA) { case 1: taskfun1(); STA = 2; break; case 2:/*延时等待状态*/ if (Is_delay_timer_Out(&task_delay) == true) STA = 1; break; } } }