优秀的编程知识分享平台

网站首页 > 技术文章 正文

基于时间触发任务调度软件架构(基于时间触发任务调度软件架构的设计)

nanyue 2025-06-23 21:02:41 技术文章 1 ℃

在单片机开发过程,对于简单功能的开发模式,一般使用前后台开发模式,比如主函数实现复杂逻辑函数,外加中断处理一些紧急的事件,此法称之为: 前后台轮询软件架构。

对于需要紧急处理的事件,则通过后台中断的方式进行处理或标记,主要的“繁重任务”依靠前台轮询来完成,这种方式对于初学者来说,简单,可以快速入门。当应用系统需要处理的事件较多时,如用上述架构,显然对于系统维护、开发都不太友好。今天介绍一种单片机裸机架构,基于时间触发的调度机制。

/* main.c */
#include <stdio.h>
int main(void)
{
    while (1)
    {
        func1();
        func2();
        func3();
        ...
    }
    return 0;
}

/* isr.c */
void ISR_Handler(void)
{
  ...
}

基于时间触发任务调度器,由一个定时器或者任意能产生固定时间戳的中断、以及相应数据结构即可构成,下面将详细介绍。


  • 调度器用到如下几个函数
/* 任务调度器数据结构 */
typedef struct
{
  void (*pTask)(void);			/* 任务函数指针  */
  uint16_t Delay;						  /* 任务延迟启动时间 */
  uint16_t Period;					  /* 任务运行周期 */
  uint8_t RunMe;             /* 任务调度标志 */
  uint8_t Co_op;              /* 任务调用方式: 1:合作式 0:抢占式 */
} sTaskH;


/* 自定义系统最大使用的任务个数 */
#define tSCH_MAX_TASKS  (16)


/* tsch.h 函数指针声明 */
typedef void (*tsch_init_tmr)(void);
typedef void (*tsch_set_handle)(void (*tsch_update)(void));
typedef void (*tsch_open)(void);
typedef void (*tsch_close)(void);
typedef void (*tsch_sleep)(void);
typedef void (*tsch_clr_wdg)(void);
typedef void (*tsch_update)(void);


/* tsch.h 调度器函数声明 */
void tSCH_Init(tsch_init_tmr t_tmr_init, tsch_set_handle t_handle);
void tSCH_Dispatch_Tasks(tsch_clr_wdg t_wdg_clr, tsch_sleep t_sleep);
void tSCH_Start(tsch_open t_open);
void tSCH_Close(tsch_close t_close);
uint8_t tSCH_Add_Task(void (*Fn_p)(), const uint16_t Del, const uint16_t Per, uint8_t Co_op);
uint8_t tSCH_Delete_Task(uint8_t Task_index);
void tSCH_Update(void);
  • 调度器函数实现
/* 定义任务管理结构 */
static sTaskH tSCH_Task_Group[tSCH_MAX_TASKS];

/**
 * tSCH_Init 初始化调度器
 *
 * @param
 *        t_tmr_init:定时器初始化函数指针
 *        t_handle:设置任务调度函数句柄
 * @return 0:SUCC
 *        !0:FAIL
 * @author 嵌入式1号农民工
 * @date 2021-10-10
 */
void tSCH_Init(tsch_init_tmr t_tmr_init, tsch_set_handle t_handle)
{
  if(t_tmr_init != NULL)
  {
    t_tmr_init();
  }
  if(t_handle != NULL)
  {
    t_handle(tSCH_Update);
  }
}

/**
 * tSCH_Add_Task 任务添加
 *
 * @param
 *        Fn_p:任务函数指针
 *        Del: 任务延迟运行时间
 *        Per:任务调度周期
 *        Co_op: 1:合作式 0:抢占式
 * @return 0:SUCC
 *        !0:FAIL
 * @author 嵌入式1号农民工
 * @date 2021-10-10
 */
uint8_t tSCH_Add_Task(void (*Fn_p)(), const uint16_t Del,
    const uint16_t Per, uint8_t Co_op)
{
  uint8_t Index = 0;
  while ((tSCH_Task_Group[Index].pTask != 0) && (Index < tSCH_MAX_TASKS))
  {
    Index++;
  }
  if (Index == tSCH_MAX_TASKS)
  {
    return ERROR_SCH_TOO_MANY_TASKS;
  }
  tSCH_Task_Group[Index].pTask = Fn_p;
  tSCH_Task_Group[Index].Delay = Del;
  tSCH_Task_Group[Index].Period = Per;
  tSCH_Task_Group[Index].Co_op = Co_op;
  tSCH_Task_Group[Index].RunMe = 0;
  return Index;
}

/**
 * tSCH_Delete_Task 任务删除
 *
 * @param
 *        Task_index:任务号索引
 * @return 0:SUCC
 *        !0:FAIL
 * @author 嵌入式1号农民工
 * @date 2021-10-10
 */
uint8_t tSCH_Delete_Task(uint8_t Task_index)
{
  uint8_t Return_code;
  if (tSCH_Task_Group[Task_index].pTask == 0)
  {
    Return_code = RETURN_ERROR;
  }
  else
  {
    Return_code = RETURN_NORMAL;
  }
  tSCH_Task_Group[Task_index].pTask = 0;
  tSCH_Task_Group[Task_index].Delay = 0;
  tSCH_Task_Group[Task_index].Period = 0;
  tSCH_Task_Group[Task_index].RunMe = 0;
  return Return_code;
}

/**
 * tSCH_Dispatch_Tasks 任务调度运行
 *
 * @param
 *        t_wdg_clr:喂狗函数指针
 *        t_sleep:休眠函数指针
 * @return 0:SUCC
 *        !0:FAIL
 * @author 嵌入式1号农民工
 * @date 2021-10-10
 */
void tSCH_Dispatch_Tasks(tsch_clr_wdg t_wdg_clr, tsch_sleep t_sleep)
{
  uint8_t Index;
  for (Index = 0; Index < tSCH_MAX_TASKS; Index++)
  {
    if ((tSCH_Task_Group[Index].Co_op) && (tSCH_Task_Group[Index].RunMe > 0))
    {
      if (tSCH_Task_Group[Index].pTask)
      {
        (*tSCH_Task_Group[Index].pTask)();
      }
      tSCH_Task_Group[Index].RunMe -= 1;
      if (tSCH_Task_Group[Index].Period == 0)
      {
        tSCH_Task_Group[Index].pTask = 0;
      }
    }
  }
  //!< clear watchdog
  if (t_wdg_clr != NULL)
  {
    t_wdg_clr();
  }
  if (t_sleep != NULL)
  {
    t_sleep();
  }
}

/**
 * tSCH_Update 任务调度时基管理,此函数被定时器中断调用
 *
 * @param  None
 * 
 * @return 0:SUCC
 *        !0:FAIL
 * @author 嵌入式1号农民工
 * @date 2021-10-10
 */
void tSCH_Update(void)
{
  uint8_t Index;
  for (Index = 0; Index < tSCH_MAX_TASKS; Index++)
  {
    if (tSCH_Task_Group[Index].pTask)
    {
      if (tSCH_Task_Group[Index].Delay == 0)
      {
        if (tSCH_Task_Group[Index].Co_op)
        {
          tSCH_Task_Group[Index].RunMe += 1;
        }
        else
        {
          (*tSCH_Task_Group[Index].pTask)();
          tSCH_Task_Group[Index].RunMe -= 1;
          if (tSCH_Task_Group[Index].Period == 0)
          {
            tSCH_Task_Group[Index].pTask = 0;
          }
        }
        if (tSCH_Task_Group[Index].Period)
        {
          tSCH_Task_Group[Index].Delay = tSCH_Task_Group[Index].Period - 1;
        }
      }
      else
      {
        tSCH_Task_Group[Index].Delay -= 1;
      }
    }
  }
}
  • 应用案例
#include <stdio.h>
#include "tsch.h"
#include "..."


void task1(void) 
{
    /* 任务实体 */
}


void task2(void)
{
    /* 任务实体 */
}


void task3(void)
{
    /* 任务实体 */
}


/* 喂狗函数实现 */
void wdt_refresh(void)
{
}


/* 低功耗休眠操作函数 */
void sys_sleep(void)
{
}

int main(void)
{
    tSCH_Add_Task(task1,0,100,1);
    tSCH_Add_Task(task2,100,500,1);
    tSCH_Add_Task(task3,0,10,0);
    tSCH_Init(sys_tick_init, set_task_cb);
    while (1)
    {
        tSCH_Dispatch_Tasks(wdt_refresh, sys_sleep);
    }
    return 0;
}

set_task_cb函数实现为一个设置tSCH_Update函数被时基中断回调的一个接口,可自行实现,如:

typedef void (*SYS_tick_cb_t)(void);
SYS_tick_cb_t SYSTICK_isr_cb = NULL;
void set_task_cb(SYS_tick_cb_t task)
{
  SYSTICK_isr_cb = task;
}
/* SysTick中断 */
void SysTick_Handler(void)
{
  if (SYSTICK_isr_cb != NULL)
  {
    SYSTICK_isr_cb();
  }
  HAL_IncTick();
}

如上应用案例:

任务1将以100个时基的周期调度,

任务2将以500个时基的周期调度,

任务3将以10个时基的周期调度,

其中任务3优先级最高,其属于中断式任务,需要快速执行的任务才可以设置为中断式任务。每个任务即为一个功能模块,在实际开发过程,可以分为不同文件管理,结构清晰,维护简单。

此架构实现ROM、RAM消耗几乎可以忽略不计,已在小容量8位机、16位机、32位机均有移植并大量使用过。

最近发表
标签列表