浅谈架构方法之时间片轮询

PS:最近在逛CSDN的时候偶然发现了一篇文章讲到了这个架构,发现之前做过一个项目就用了这个东西,于是我搜了一下,感觉挺多文章都不好理解,由于我也是最近才接触到这个东西,所以我决定自己也写一篇,加深印象。

架构的类型

首先,在嵌入式中有三种架构,除了我们熟知的裸机系统和操作系统,第三种就是这个时间片轮询。我认为这种架构是介于二者之间的过渡。

裸机系统能够处理一些相对简单的任务,但是功能单一,只会一直按顺序执行死循环里的任务,CPU只会全力做完一件事才会进行下一项任务,延时的时候也只会等待,浪费资源。

操作系统的实时性和可靠性高还能同时执行多个任务,但是操作系统较为复杂,需要额外的学习成本,而且其对内存空间的要求需要我们注意内存的大小。

而时间片轮询不仅能够实现多任务,保证实时性,而且结构简单,占用内存小,在实际开发中也节省时间和精力。

对于简单且实时性强的简单任务可以使用时间片轮询,但是当任务数较多的时候还是老实用操作系统更好。

代码实现

这里我只叙述原理,不设定具体的任务,将整个代码拆分来看,防止看的太乱,整体代码放在最后。

我设置了三个任务,分别是5ms任务、10ms任务和20ms任务。

在开始之前,我们需要在主函数里进行一个延时。这个延时一般是最大的时间片时间,像这个例子里,我可以延时20ms。

首先,我们定义一个最大任务数为3的宏:

复制代码
#define TASKS_MAX   (3)

接下来定义一个结构体,其内成员含义如下:

程序运行标志位。

倒计时时间。

重装载倒计时。

任务指针。

它们的含义会在后面讲。

复制代码
typedef struct _TASK_COMPONENTS
{
    uint8_t Run;            
    uint16_t Timer;            
    uint16_t ItvTime;              
    void (*TaskHook)(void);   
} TASK_COMPONENTS; 

接下来我们创建一个结构体数组用来存放我们定义的结构体成员变量。

数组中每个元素都对应着我们定义的结构体成员。

复制代码
/* 任务数组 */
static TASK_COMPONENTS TaskComps[] = 
{
    {0, 5, 5, task_5ms},            // 5ms任务
    {0, 10, 10, task_10ms},         // 10ms任务
    {0, 20, 20, task_20ms},         // 20ms任务
};

接下来是重点了。

这个函数需要放到系统定时器中断中执行,同时对所有任务进行计时。

当有任务的值为0时,将其代入for()循环执行。

当每个任务的Timer不为0时,进行减减操作,即倒计时。当其减到0的时候,将ItvTime的值赋给Timer,以方便下次计时。同时将程序运行标志位Run置1.

复制代码
/* 任务启动倒计时 */
void TaskRemarks(void)
{
    uint8_t i;

    for (i=0; i<TASKS_MAX; i++)          
    {
         if (TaskComps[i].Timer)        
        {
           TaskComps[i].Timer--;         
           if (TaskComps[i].Timer == 0)       
           {
             TaskComps[i].Timer = TaskComps[i].ItvTime; 
             TaskComps[i].Run = 1;           
           }
        }
   }
}

下面这个函数依然是从0开始遍历每个任务,判断它们的Run是否为1,如果是1则执行对应的任务,然后将标志位置0。

复制代码
void TaskProcess(void)
{
    uint8_t i;

    for (i=0; i<TASKS_MAX; i++)           
    {
         if (TaskComps[i].Run)           
        {
             TaskComps[i].TaskHook();         
             TaskComps[i].Run = 0;          
        }
    }   
}

至于任务里面实现什么功能,就任由我们按实际需求了。

当20ms任务执行过一次之后,一个周期就结束了。如果我们把TaskRemarks()放到SysTick的中断服务函数中,那么当这个中断执行的时候,滴答计时器的计数就会重置,同时TaskRemarks()函数也会重新执行,从而达到时间片轮询的效果。

完整代码

复制代码
#define TASKS_MAX   (3)

typedef struct _TASK_COMPONENTS
{
    uint8_t Run;            
    uint16_t Timer;            
    uint16_t ItvTime;              
    void (*TaskHook)(void);   
} TASK_COMPONENTS; 

/* 任务数组 */
static TASK_COMPONENTS TaskComps[] = 
{
    {0, 5, 5, task_5ms},            // 5ms任务
    {0, 10, 10, task_10ms},         // 10ms任务
    {0, 20, 20, task_20ms},         // 20ms任务
};

/* 任务启动倒计时 */
void TaskRemarks(void)
{
    uint8_t i;

    for (i=0; i<TASKS_MAX; i++)          
    {
         if (TaskComps[i].Timer)        
        {
           TaskComps[i].Timer--;         
           if (TaskComps[i].Timer == 0)       
           {
             TaskComps[i].Timer = TaskComps[i].ItvTime; 
             TaskComps[i].Run = 1;           
           }
        }
   }
}

void TaskProcess(void)
{
    uint8_t i;

    for (i=0; i<TASKS_MAX; i++)           
    {
         if (TaskComps[i].Run)           
        {
             TaskComps[i].TaskHook();         
             TaskComps[i].Run = 0;          
        }
    }   
}

void task_5ms(void)
{ }
void task_10ms(void)
{ }
void task_20ms(void)
{ }
相关推荐
我想进大厂33 分钟前
图论---朴素Prim(稠密图)
数据结构·c++·算法·图论
我想进大厂38 分钟前
图论---Bellman-Ford算法
数据结构·c++·算法·图论
AIGC大时代40 分钟前
高效使用DeepSeek对“情境+ 对象 +问题“型课题进行开题!
数据库·人工智能·算法·aigc·智能写作·deepseek
CODE_RabbitV1 小时前
【深度强化学习 DRL 快速实践】近端策略优化 (PPO)
算法
Wendy_robot2 小时前
【滑动窗口+哈希表/数组记录】Leetcode 438. 找到字符串中所有字母异位词
c++·算法·leetcode
程序员-King.2 小时前
day49—双指针+贪心—验证回文串(LeetCode-680)
算法·leetcode·贪心算法·双指针
cooldream20092 小时前
深入解析大数据的Lambda架构:设计、特点与应用场景
大数据·架构·系统架构师
转基因3 小时前
Codeforces Round 1020 (Div. 3)(题解ABCDEF)
数据结构·c++·算法
我想进大厂4 小时前
图论---Kruskal(稀疏图)
数据结构·c++·算法·图论
@Aurora.4 小时前
数据结构手撕--【二叉树】
数据结构·算法