前后台程序的嵌入式时间转轮算法
用过stm32的一些芯片,写过一些代码,在学习的过程中对自己的程序框架做一个理解。
在Keil里面,我们利用主函数和中断来运行代码。
main函数理解
我们知道main函数里面是顺序执行代码,while(1)是一个死循环。
而main函数就像一个大中断(例如:70ms运行一次main函数)
看门狗
个人理解看门狗的作用:
定义:看门狗(watchdog timer)为定时器中断。
作用:防止程序跑飞、程序死循环。
应用:初始化中断和定时器T0,开始计时。
主控程序需要70ms运行完成,看门狗定时器设置90ms。
定时器T0每90ms 开始T0=0ms;
如果90ms后定时器还没有重置,则证明程序跑飞或者进入死循环。
这时开启中断服务发送命令,重置程序。
中断
中断的作用是什么,发出响应任务,让CPU处理响应任务。
正常main函数里面的程序为实时任务,当接受到中断的响应任务时,CPU优先处理响应任务,待响应任务结束后,CPU正常运行实时任务。
(一般中断为2.5ms)
前后台系统
前台系统:理解为大中断(主函数的逻辑输入)
后台系统:理解为底层硬件的信号输出。
操作系统时间转轮任务调度:
若有任务A 任务B 任务C
时间片:10ms ,
则运行任务A 15ms,在运行任务B 10ms,在运行任务C 5ms。
优先从队首运行任务,
若仍没在时间片内未运行完:则把改任务放到队尾,继续执行下一个任务。
若在时间片内提前完成任务:则进行下一个任务。
A(10ms)→B(10ms)→C(5ms)→A(5ms)
前后台程序的顺序执行:
这是一种大家新手编程最常用的一种方法,不用思考程序的具体架构,直接按照程序的执行顺序编写代码。
优点:比较容易上手,程序设计简单,思路清晰。
缺点:当程序复杂时,很难看懂程序的运行状态,没有流程图思路混乱,不利于升级以及维护。
前后台的时间转轮算法:
这有什么用?
我们的main函数大中断70ms重置一次,小中断2.5ms重置一次。
那如果有任务在2.5ms——70ms之间呢? 例如20ms 重置一次
利用2.5ms小中端,一直刷新逻辑获取处理,会不会太浪费了?
利用70ms 大中断,会不会使程序在一定时间内得不到响应?
本质其实时定时器的多处复用
首先定义一个结构体
typedef struct TASK_COMPONENTS
{
uint8 Run; // 程序运行标记: 0-不运行, 1 运行
uint8 Timer; // 计时器
uint8 ItvTime; // 任务运行间隔时间
void (*TaskHook)(void); // 要运行的任务函数
}TASK_COMPONENTS;
TASK_COMPONENTS TaskComps[TASK_NUM] //定义结构体数组
#define TASK_NUM 3 //代表3个task任务
函数理解:逐个任务处理,当任务计时时间为0时,设置标志位Run=1,可以进行处理任务,当任务计时时间不为0时,--处理直到为0,重置计时,进行处理。
void TaskRemarks(void)
{
uint8 i;
for (i=0; i<TASK_NUM; i++) // 逐个任务时间处理
{
if (TaskComps[i].Timer) // 时间不为 0
{
TaskComps[i].Timer--; // 减去一个节拍
if (TaskComps[i].Timer == 0) // 时间减完了
{
TaskComps[i].Timer = TaskComps[i].ItvTime; // 恢复计时器值,从新开始
TaskComps[i].Run = 1; // 任务可以运行
}
}
}
}
函数理解:逐个任务处理,当运行状态为1时,进行处理任务,清除标志位。
void TaskProcess(void)
{
uint8 i;
for (i=0; i<TASKS_NUM; i++) // 逐个任务时间处理
{
if (TaskComps[i].Run) // 时间不为 0
{
TaskComps[i].T askHook(); // 运行任务
TaskComps[i].Run = 0; // 标志清 0
}
}
}