找回密码
 立即注册
首页 业界区 安全 基于定时器中断的多任务轮询架构

基于定时器中断的多任务轮询架构

嫁蝇 昨天 12:25
概述

嵌入式系统通常需要进行多个任务的处理,不同任务对于时间响应的要求不同。因此需要调度系统确保任务被及时处理。本调度系统以定时器中断驱动不同频率的任务执行。
此任务调度系统的实现基于任意的一个定时器,通过定时器中断的溢出时间来确定任务执行中,时间片的最小单位。在创建任务时,根据时间片单位配置任务执行间隔时间。在定时器中断中轮询任务列表,若任务运行标位置为零,则任务待执行时间减一,当任务待执行之间为零,置任务运行标志位为一。主循环中执行任务处理,即轮询任务列表,执行运行标志位为一的任务。
架构及实现

调度器的主要构成
Architecture:调度架构
        task.h:总任务队初始化 开启,各项任务的初始化,注销,执行函数定义;任务号定义;
        task.c:总任务对初始化 开启(调用调度器),各项任务的初始化,注销,执行函数(调用相应组件)实现
        scheduler.h:任务调度器的相关操作定义:任务注册,任务卸载,调度器初始化,调度器开关,调度器回调,调度器任务;任务结构体;
        scheduler.c:任务调度器的相关操作实现,注册和卸载即对任务列表中的任务结构体赋值(调用定时器驱动);
BSP:硬件外设
        BSP_INIT
                bsp_init.h:硬件外设初始化定义
                bsp_init.c:硬件外设初始化实现
        BSP_TIMER
                bsp_timer.h:定时器驱动器定义
                bsp_timer.c:定时器驱动器实现
本架构的核心是基于定时器中断驱动任务执行,因此首先需要初始化一个定时器,并配置中断溢出时间,即最小时间单位。在定时器中断中执行调度器函数,即调度器函数中检查任务执行时间、标志位等参数并进行变更。
任务结构体如下

用于配置任务参数,包括任务号、任务函数、任务参数、任务执行标志和任务时间相关参数。
1.gif
  1. typedef struct TASK_COMPONENTS
  2. {
  3.     volatile unsigned int iTaskTimerCnt;  // 任务执行倒计时
  4.     volatile unsigned int iTaskTimerInval;  // 任务执行间隔
  5.     volatile unsigned char ucIndex;  // 任务号
  6.     volatile unsigned char isRunFlag;  // 任务执行标志
  7.     void (*TaskHook)(void);  // 任务函数
  8.     void *arg;  // 任务函数传参
  9. } TASK_COMPONENTS_t;
复制代码
调度器函数如下

在每次定时器中断中轮询任务列表,依次判断任务是否存在,若任务执行倒计时不为0,则接着检查任务运行标志,若任务执行标志为FLASE,即任务倒计时没有到并且任务未标记执行未FLASE的情况下,则对任务的倒计时进行减减。直到再次轮询到这个任务的执行倒计时为0的时候,将任务执行标志位置为1,并重装任务倒计时,任务标志位用于之后的任务调度函数执行各任务。
  1. void Scheduler_Timer_Hook(void)
  2. {
  3.     int i = 0;
  4.     for(i = 0; i < MAX_TASK_NUMS; i++)
  5.     {
  6.         if(g_TASK_COMPONENTS[i].ucIndex != 0)
  7.         {
  8.             if(g_TASK_COMPONENTS[i].iTaskTimerCnt != 0)  // 检查任务执行倒计时
  9.             {
  10.                 if(g_TASK_COMPONENTS[i].isRunFlag == FALSE)  // 检查任务执行标志位
  11.                 {
  12.                     g_TASK_COMPONENTS[i].iTaskTimerCnt--;  // 任务执行倒计时减减
  13.                     if(g_TASK_COMPONENTS[i].iTaskTimerCnt == 0)
  14.                     {
  15.                         g_TASK_COMPONENTS[i].isRunFlag = TRUE;  // 任务执行标志位置位
  16.                         g_TASK_COMPONENTS[i].iTaskTimerCnt = g_TASK_COMPONENTS[i].iTaskTimerInval;  // 任务执行倒计时重装
  17.                     }
  18.                 }
  19.             }
  20.         }
  21.     }
  22. }
复制代码
3.gif
定时器中断配置如下

每次定时器中断中调用执行调度器函数。
  1. void TIMx_IRQHandler(void)      
  2. {   
  3.     if(TIM_GetITStatus(TIMx, TIM_IT_Update) == SET)
  4.     {
  5.         Scheduler_Timer_Hook();  // 调度器函数
  6.     }
  7.     TIM_ClearITPendingBit(TIMx, TIM_IT_Update);
  8. }
复制代码
4.gif
定义完定时器中断驱动的调度之后,接着完善整个任务架构。实现任务调度函数不同的任务、任务注册、任务卸载以及调度器开关。
任务调度函数

任务调度函数中对任务列表进行轮询,查找任务标志位为1的函数并执行其任务,同时将此任务的运行标志位置为FLASE,等待下一个任务周期。
  1. void TaskPorcess(void)
  2. {
  3.     int i = 0;
  4.     for(i = 0; i < MAX_TASK_NUMS; i++)
  5.     {
  6.         if(g_TASK_COMPONENTS[i].ucIndex != 0)
  7.         {
  8.             if(g_TASK_COMPONENTS[i].isRunFlag != FALSE)
  9.             {
  10.                 g_TASK_COMPONENTS[i].TaskHook();
  11.                 g_TASK_COMPONENTS[i].isRunFlag = FALSE;
  12.             }
  13.         }
  14.     }
  15. }
复制代码
任务注册函数

调用任务函数,实现任务到任务列表中。即配置任务的任务号、任务运行标志位、任务时间和任务执行函数。
  1. int Register_Task(unsigned char ucIndex, unsigned char isRunFlag, unsigned int iTaskTimerCnt,\
  2.                   unsigned int iTaskTimerInval, void (*TaskHook)(void), void *arg)
  3. {
  4.     unsigned char ucIndexTemp         = ucIndex;
  5.     unsigned char isRunFlagTemp       = isRunFlag;
  6.     unsigned int  iTaskTimerInvalTemp = iTaskTimerInval;
  7.     void *argTemp                     = arg;
  8.     int iIndTemp = 0;
  9.     if(ucIndex >= MAX_TASK_NUMS)
  10.     {
  11.         return -1;
  12.     }
  13.     // 任务号从1开始,任务列表从0开始
  14.     iIndTemp = ucIndex -1;
  15.     if(g_TASK_COMPONENTS[iIndTemp].ucIndex != 0)
  16.     {
  17.         return -2;
  18.     }
  19.    
  20.     g_TASK_COMPONENTS[iIndTemp].ucIndex         = ucIndexTemp;
  21.     g_TASK_COMPONENTS[iIndTemp].isRunFlag       = isRunFlagTemp;
  22.     g_TASK_COMPONENTS[iIndTemp].iTaskTimerCnt   = iTaskTimerInvalTemp;
  23.     g_TASK_COMPONENTS[iIndTemp].iTaskTimerInval = iTaskTimerInvalTemp;
  24.     g_TASK_COMPONENTS[iIndTemp].TaskHook        = TaskHook;
  25.     g_TASK_COMPONENTS[iIndTemp].arg             = argTemp;
  26.     return 1;
  27. }
复制代码
任务卸载函数

调用任务卸载函数,对已实现的任务列表中的任务。清空任务的配置参数。
  1. int UnRegister_Task(unsigned char ucIndex)
  2. {
  3.     int iIndTemp = 0;
  4.     if(ucIndex >= MAX_TASK_NUMS)
  5.     {
  6.         return -1;
  7.     }
  8.     iIndTemp = ucIndex -1;
  9.     if(g_TASK_COMPONENTS[iIndTemp].ucIndex == 0)
  10.     {
  11.         return -2;
  12.     }
  13.     g_TASK_COMPONENTS[iIndTemp].ucIndex         = 0;
  14.     g_TASK_COMPONENTS[iIndTemp].isRunFlag       = 0;
  15.     g_TASK_COMPONENTS[iIndTemp].iTaskTimerCnt   = 0;
  16.     g_TASK_COMPONENTS[iIndTemp].iTaskTimerInval = 0;
  17.     return 1;
  18. }
复制代码
调度器开关

完成初始化调度器的最小时间和启停调度器
  1. void Task_Scheduler_Timer_Init(void)
  2. {
  3.     // 定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/f.
  4.     TIM3_Int_Init(arr, psc);
  5. }
  6. int Task_Scheduler_Switch(unsigned char isOn)
  7. {
  8.     if(!(isOn == YES || isOn == NO))
  9.     {
  10.         return -1;
  11.     }
  12.     if(isOn == YES)
  13.     {
  14.         TIM_Cmd(TIM3, ENABLE);
  15.     }
  16.     if(isOn == NO)
  17.     {
  18.         TIM_Cmd(TIM3, DISABLE);
  19.     }
  20.     return 0;
  21. }
复制代码
至此基于定时器中断的多任务轮询架构实现完成
举个例子

通过一个例子,使用这个架构完成LED等闪烁任务
开启调度器

首先需要对选中的定时器初始化并开启调度器
  1. void APP_Task_Start(void)
  2. {
  3.     Task_Scheduler_Timer_Init();
  4.     Task_Scheduler_Switch(YES);
  5. }
复制代码
完成任务注册、卸载和执行函数

传入任务号和任务相关配置参数完成任务注册和卸载函数实现,并在组件层完成相关任务实现细节,保持良好的分层架构设计。
  1. // 任务注册
  2. void App_Led_Task_Init(void)
  3. {  
  4.     Register_Task(TASK_NUM_LED_FLASH, 0, 0, 1000, App_Led_Task_Fun, NULL);  // 任务执行时间间隔1000ms
  5. }
  6. // 任务卸载
  7. void App_Led_Task_Exit(void)
  8. {
  9.     UnRegister_Task(TASK_NUM_LED_FLASH);
  10. }
  11. // 任务执行
  12. void App_Led_Task_Fun(void)
  13. {   
  14.     LED1_Turn();
  15. }
复制代码
任务统一管理

建立任务初始化函数,便与后续扩展。
  1. void App_Task_Init(void)
  2. {
  3.     App_Led_Task_Init();
  4.     // 添加后续任务
  5. }
复制代码
主函数

主函数进行任务从初始化,调度器启动并在主函数中执行任务调度函数。编译并烧录程序后,可以看到LED等以注册任务中1000ms参数间隔进行闪烁。
  1. int main(void)
  2. {
  3.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  4.     BSP_Init();
  5.     App_Task_Init();
  6.     APP_Task_Start();
  7.     while(1)
  8.     {
  9.         TaskPorcess();
  10.     }
  11. }
复制代码
 
分层架构设计

项目分层基于江科大的模板;
Architecture:调度架构
BSP:各种硬件外设 硬件配置 (参数初始化 设备自检等 放在这里之后)
Component:组件
        举个例子中实现LED反转
        COM_LED:
                com_led.h:led组件头文件
                com_led.c:led组件源(调用相应驱动)文件
Library、Start和User:为使用江科大模板原本文件夹,其中Library包含 STM32F10x 系列芯片的 标准外设驱动库 源码;Start包含启动文件(startup files)、系统初始化代码、CMSIS 核心文件;User为用户自定义源码,启用哪些库,中断服务例程;
缺陷:

任务越多,管理难度越大;任务间隔时间需要调整,防止相互干涉;不同任务对相同变量的操作,BUG难排查;一个任务阻塞,影响所有任务;

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册