马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
步调架构等同于头脑体系
建一栋楼房,地基要先计划好,而不是立刻砌砖,地基和布局都公道,房子就住得惬意,也不会闹心。
写一段步调也一样,步调构架要想好,而不是一边写一边调解构架,想到这个功能就先写这个功能,多个功能放在一起后无法调和,又要改构架,服从非常低。
把构架用图形的情势计划好,再像添砖加瓦一样去编码,实现功能。
一、前背景序次法
公司都有负责接待来宾的前台,当有人来访或有电话来,前台就会关照其他负责人,正在有序工作的负责人必须立刻克制手中工作,接待或接听竣过后再回到原来的事变上继续。有人来访或有电话比做前台,正在有序工作比做背景。
前背景序次实行是入门开辟者常用的步调架构,逻辑简朴,复杂度低,代码量少,最直观了,从上往下实行,全部使命都在一个循环里实行,不思量每个函数实行所须要的时间。但遇到延时就要等候,因此会造成其他函数隔断实行时间的差别,只管可以大概通过定时器停止的方式,但条件是停止服务函数实行的时间必须短。
序次实行就是在你前面的实行完了,才到你实行,好比上体育课列队报数一样,必须按序次来,你前面相邻的同砚报完数,你才气报数。
另有就是有停止发生,就优先实行停止要实行的事变,好比体育课,点到谁的名字就优先得去跑两圈。
未到场停止的轮询体系
- int main(void)
- {
- uint8 keyValue;
-
- InitSys(); // 系统初始化
- while (1)
- {
- TaskDisplayClock();//显示系统时钟
- keyValue = TaskKeySan();//扫描键盘
- switch (keyValue)
- {
- case x: TaskDispStatus(); break;
- ...
- default: break;
- }
- }
- }
复制代码 在while(1)死循环中从第一行代码往下实行到末了一行,然后返回到第一行,云云反复实行。
在轮询体系根本上到场停止处理处罚
- int flag1 = 0,flag2= 0,flag3 = 0;
- int main(){
- hardwareInit();//硬件初始化
- while(1){
- if(flag1){
- doSomething1();//处理事件1
- flag1=0;//清除标志位
- }
- if(flag2){
- doSomething2();//处理事件2
- flag2=0;//清除标志位
- }
- if(flag3){
- doSomething3();//处理事件3
- flag3=0;//清除标志位
- }
- }
- }
- //中断处理程序1
- void ISR1(void ){
- flag1=1//置位标志位
- }
- //中断处理程序2
- void ISR2(void ){
- flag2=1//置位标志位
- }
- //中断处理程序3
- void ISR3(void ){
- flag3=1//置位标志位
- }
复制代码 外部变乱的相应(改变一些标记位,确保所用时间非常短)在停止内里完成,变乱的处理处罚照旧要回到轮询体系中完成。
1、如果停止服务步调实行时间太长,大概导致停止嵌套非常深,轻易导致栈溢出,也轻易出现非常,好比死机。
2、停止只处理处罚告急变乱!告急变乱要用停止处理处罚!好比串口数据的吸取和发送,速率太快,放在循环里处理处罚太慢了。然而对于数据的处理处罚,就不消发急了,可以在停止里吸取完成后设置标记位,然后在主循环里根据标记位完成处理处罚,之后手动开启相应停止去发送。
停止在这里称为前台,main()函数中的主循环称为背景。
二、时间片轮询法
敢言:时间片轮询法是万能的裸机步调构架
时间片法是一种多使命实行法,它通过为每个使命分配肯定的实行时间片,使得全部使命都可以大概按照肯定的时间隔断瓜代实行。使命实行的时间片是固定的,当一个使命的时间片用完后,体系切换到下一个使命实行。这种方式使得多个使命可以大概并行实行,进步了体系的资源使用率和服从。
时间片法实用于中等复杂度的嵌入式体系,可以满意对实时性要求较高的场景。它须要公道设置使命的优先级和时间片巨细,以确保紧张使命优先实行,而且每个使命都能在得当的时间内完成。
使用定时器设定差别的时间片,定时到了某个时间节点,就去实行对应时间片里的代码,代码实行时间不能凌驾期间片时间,时间片时间应该根据所实行使命的耗时来设定。好比,按键扫描,通常都须要软件防抖,序次法是延时10ms左右再去判断,但10ms极大浪费了CPU的资源,在这段时间内CPU完全可以处理处罚很多其他事变,时间片轮询法就能很好使用CPU资源。
天下万事万物都是基于时间这条线而存在而运作的,谁都摆脱不了与时间的关联。
时间片轮询方式也轻易明确,你设定的时间到了,你就实行,没到就继续等候,各个时间片之间没有优先级。
时间片轮询法体系的实现思绪(只须要一个恣意的定时器)
1、在体系初始化里初始化一个定时器,假设定时停止为1ms(也可以改成10ms,根据实际需求,但须要留意,停止过于频仍服从就低,停止太长,实时性差)。
2、界说多个使命变量及标记位
3、界说使命函数
4、把使命调治添加到主循环中
5、把使命切换逻辑控制添加到停止服务中
1、初始化定时器,以51单片机为例
- //定时器初始化,定时1ms中断一次
- void Timer1_Init(){
- AUXR &= 0xBF; //定时器时钟12T模式
- TMOD &= 0x0F; //设置定时器模式
- TMOD |= 0x10; //设置定时器模式
- TL1 = 0x30; //设置定时初值
- TH1 = 0xF8; //设置定时初值
- TF1 = 0; //清除TF1标志
- TR1 = 1; //定时器1开始计时
- ET1 = 1; //使能定时器中断
- EA = 1;
- }
- //定时器中断服务函数
- void TM1_Isr() interrupt 3
- {
- TL1 = 0x30; //设置定时初值
- TH1 = 0xF8; //设置定时初值
- }
复制代码 2、界说使命干系
- //任务结构
- typedef struct _TASK_COMPONENTS
- {
- unsigned int Run; //程序运行标记: 0-不运行,1-运行
- unsigned int Timer; //计时器
- unsigned int ItvTime; //任务运行时间间隔
- void (*TaskHook)(void); //要运行的任务函数
- }TASK_COMPONENTS;//任务定义,TASK_COMPONENTS是_TASK_COMPONENTS的别名
- static TASK_COMPONENTS TaskComps[]= //时间节拍为1ms,由定时器产生
- {
- {0,10,10,TaskRfCheck}, //处理RF数据
- {0,20,20,TaskUart2Check}, //每次只处理串口2的一个字节数据
- {0,8,8,TaskRGBdata},//RGB数据处理 刷新频率不能低于30HZ
- {0,30,30,TaskPWMout}, //刷新PWM数据
- {0,1000,1000,TaskTimers}, //计时器
- };
- //任务清单
- typedef enum _TASK_LIST
- {
- TASK_RF_CHECK,
- TASK_UART2_CHECK,
- TASK_RGB_DATA,
- TASK_UART1_PRINTF,
- TASK_TIMERS,
- TASKS_MAX, //总共可分配的定时任务数目
- }TASK_LIST;
复制代码 3、界说使命函数
- void TaskRfCheck() //识别遥控器按键
- {
- //在此处添加代码
- }
- void TaskUart2Check() //解析串口2数据
- {
- //在此处添加代码
- }
- void TaskRGBdata() //处理RGBW数据
- {
- //在此处添加代码
- }
- void TaskPWMout() //刷新PWM数据
- {
- //在此处添加代码
- }
- void TaskTimers() //灯带独立计时器
- {
- //在此处添加代码
- }
复制代码 4、界说使命调治函数并添加到主循环中
- void TaskProcess()
- {
- static unsigned char i=0; //必须是static类型
- for(i=0;i<TASKS_MAX;i++) //逐个任务时间处理
- {
- if(TaskComps[i].Run) //任务可以运行
- {
- TaskComps[i].TaskHook(); //调用任务函数
- TaskComps[i].Run = 0; //标志清0
- }
- }
- }
- void SysInit_all()
- {
- Timer1_Init(); //定时器1初始化,用于多个任务调度
- WDT_CONTR=0x35;//看门狗设置,超时2秒则复位
- }
- //主函数
- int main()
- {
- SysInit_all();//系统初始化
- while(1)
- {
- TaskProcess(); //任务处理
- WDT_CONTR |= 0x10; //喂看门狗,防止系统复位
- }
- return 1;
- }
复制代码 5、把使命切换逻辑控制添加到停止服务中
- void TaskRemarks(void)
- {
- static unsigned char i=0; //必须是static类型
- for(i=0;i<TASKS_MAX;i++) //逐个任务处理
- {
- if(TaskComps[i].Timer) //时间不为0
- {
- TaskComps[i].Timer--; //减去一个节拍
- if(TaskComps[i].Timer<=0)//时间减完了
- {
- TaskComps[i].Timer = TaskComps[i].ItvTime; //恢复计时器值,重新下一次
- TaskComps[i].Run = 1; //任务可以运行
- }
- }
- }
- }
- //每1ms就执行一次TaskRemarks()
- void TM1_Isr() interrupt 3
- {
- TL1 = 0x30; //设置定时初值
- TH1 = 0xF8; //设置定时初值
- TaskRemarks(); //任务调度
- }
复制代码 时间片轮询法的流程图大抵为:
在步调中,只要把时间节点控制成有规律性的,有周期性的,那么就非常好添加代码或调试信息了。
好比在时间片为1秒的使命里,到场调试信息打印语句,那么在步调运行时每秒就会打印一次打印信息。
好比每秒打印一次字符串信息,task_1s_function函数每秒就被调用一次:
- void task_1s_function()
- {
- printf("1 sec is up\r\n");
- }
复制代码 好比每5秒打印一次字符串信息:
- void task_1s_function()
- {
- static unsigned int count = 0;
- if(++count > 5)
- {
- count = 0;
- printf("5 sec is up\r\n");
- }
- }
复制代码 好比变乱A发生3秒后,实行一次变乱B:
- static unsigned int count_down = 0;
- void A_event_occured()
- {
- printf("Event A occurs\r\n");
- count_down = 3;
- }
- void B_event_occured()
- {
- printf("Event B occurs\r\n");
- }
- void task_1s_function()
- {
- if(count_down)
- {
- count_down--;
- if(count_down == 0)
- {
- B_event_occured();
- }
- }
- }
复制代码 当A变乱被触发时,实行一次B变乱:
- bool A_occured_flag = false;
- A事件被触发,启用标志位 A_occured_flag
- A_occured_flag = true;
- 周期地(比如10ms计时一到,F_Timer_10MS被设为true)循环执行那边判断标志位,如果被启用了,就执行语句体,否则不执行。只要限制条件满足,相关处理逻辑就会执行一次。
- while(1)
- {
- if(F_Timer_10MS)
- {
- F_Timer_10MS = false;
- if(A_occured_flag )
- {
- if(限制条件)
- {
- 相关处理逻辑
- A_occured_flag = false;
- }
- }
- }
- }
复制代码 当一个状态发生反转时,打印一次信息
- if(R_loadON != R_loadON_last){
- R_loadON_last = R_loadON;
- if(R_loadON) LOG_STR("R_loadON == 1\r\n");
- else LOG_STR("R_loadON == 0\r\n");
- }
复制代码 |