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

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)
{ }
相关推荐
ZGIAI4 分钟前
企业级 Agent 平台开源:ZGI 把模型、知识库、Skills 和 Workflow 放进同一个 Runtime
人工智能·架构
YaraMemo38 分钟前
元启发式算法框架
人工智能·算法·5g·信息与通信·启发式算法·信号处理
weixin_307779131 小时前
一维无粘 Burgers 方程的激波形成问题:MacCormack 格式求解
c++·算法·matlab
HugoStudio_SWAN1 小时前
洛谷 B4500 / B4449 / B3843 凯撒密码、密码强度与密码合规——加密与安全的三道门
c++·学习·程序人生·算法·安全
商业看点解说1 小时前
企业要统一接入OpenAI/Claude/DeepSeek选择哪款云平台更合适?Amazon Bedrock 将三类模型整合进一套企业 AI 架构
架构
枫叶林FYL2 小时前
【群体智能集群控制工程实践】第3章 一致性协同算法
算法
禹凕3 小时前
滑动窗口算法实战指南
python·算法
DisonTangor3 小时前
【腾讯混元雪耻归来】 Hy4 preview:770B 参数 MoE 旗舰模型,1M 上下文全面开源
人工智能·算法·开源·aigc·腾讯云·腾讯云ai代码助手
javaDocker4 小时前
电视台自建AI短视频生产线与算力底座:技术架构、关键实现与优化实践
人工智能·架构·音视频
明月_清风4 小时前
从二叉树到 B+ 树:一文搞懂工程中「树」的演化之道
数据结构·算法·go