使用单片机的滴答定时器作为核心时间,实现多任务定时周期执行从而完成任务切换。
1.滴答定时器的配置
bsp_systicker.c:
/*
========================================================================================================================
* 【文件包含】 【文件包含】 【文件包含】
========================================================================================================================
*/
#include "bsp_systicker.h"
/*
========================================================================================================================
*【本地宏定义】 【本地宏定义】 【本地宏定义】
========================================================================================================================
*/
/*
========================================================================================================================
*【本地数据类型定义】 【本地数据类型定义】 【本地数据类型定义】
========================================================================================================================
*/
/*
************************************************************************************************************************
* 类型定义 : 系统滴答时钟 相关操作数据结构
************************************************************************************************************************
*/
typedef struct
{
Ticker_t TickCount; /*滴答时钟计数*/
}SysTicker_t;
/*
========================================================================================================================
*【变量定义 & 各种声明】 【变量定义 & 各种声明】 【变量定义 & 各种声明】
========================================================================================================================
*/
static SysTicker_t SysTicker;
/*
========================================================================================================================
*【函数定义】 【函数定义】 【函数定义】
========================================================================================================================
*/
/*
************************************************************************************************************************
* 函数名称 : BSP_GPIO_Init
* 功能描述 : 系统定时器初始化。
* 输入参数 : 无
* 返回参数 : 无
* 补充说明 : 无
************************************************************************************************************************
*/
void BSP_SysTicker_Init(void)
{
SysTick_Config(SYSTICK_CLK_HZ / SYS_TICKER_PER_SEC); /* 默认使用内核时钟 core clock*/
SysTicker.TickCount = 0;
}
/*
************************************************************************************************************************
* 函数名称 : BSP_SysTicker_GetCurTick
* 功能描述 : 获取当前系统的tick计数。
* 输入参数 : 无
* 返回参数 : 当前系统的tick计数
* 补充说明 : 无
************************************************************************************************************************
*/
Ticker_t BSP_SysTicker_GetCurTick(void)
{
return SysTicker.TickCount;
}
/*
************************************************************************************************************************
* 函数名称 : BSP_SysTicker_CalLag
* 功能描述 : 计算时间差。
* 输入参数 : tick -- 之前的tick计数
* 返回参数 : tick计数 与 系统当前tick 计数 的差值。
* 补充说明 : 无
************************************************************************************************************************
*/
Ticker_t BSP_SysTicker_CalLag(Ticker_t tick)
{
if(SysTicker.TickCount >= tick)
{
return (SysTicker.TickCount - tick);
}
else /*系统时间计数值已经溢出*/
{
return (SysTicker.TickCount + (TICKER_MAX-tick) + 1);
}
}
/*
************************************************************************************************************************
* 函数名称 : SysTick_Handler
* 功能描述 : 系统定时器中断函数
* 输入参数 : 无
* 返回参数 : 无
* 补充说明 : 无
************************************************************************************************************************
*/
void SysTick_Handler()
{
SysTicker.TickCount++;
}
bsp_systicker.h:
#ifndef _BSP_SYSTICKER_H_
#define _BSP_SYSTICKER_H_
/*
========================================================================================================================
* 【文件包含】 【文件包含】 【文件包含】
========================================================================================================================
*/
#include "bsp.h"
/*
========================================================================================================================
*【全局宏定义】 【全局宏定义】 【全局宏定义】
========================================================================================================================
*/
#ifndef BSP_CFG_SYS_TICKER_PERIOD
#define BSP_CFG_SYS_TICKER_PERIOD 1 /*系统ticker的周期时间,单位ms*/
#endif
#ifndef BSP_CFG_SYS_TICKER_MAX
#define BSP_CFG_SYS_TICKER_MAX 0xFFFF /*系统ticker的的最大计数值,两字节最大0xFFFF*/
#endif
#define SYS_TICKER_PERIOD BSP_CFG_SYS_TICKER_PERIOD /*系统ticker的周期时间,单位ms*/
#define SYS_TICKER_PER_SEC (1000/SYS_TICKER_PERIOD) /*每秒ticker的次数*/
#define TICKER_MAX BSP_CFG_SYS_TICKER_MAX
/*
========================================================================================================================
*【全局数据类型定义】 【全局数据类型定义】 【全局数据类型定义】
========================================================================================================================
*/
/*
************************************************************************************************************************
* 类型定义 :
************************************************************************************************************************
*/
#if (0xFFFF == TICKER_MAX)
typedef uint16_t Ticker_t;
#elif (0xFF == TICKER_MAX)
typedef uint8_t Ticker_t;
#else
typedef uint32_t Ticker_t;
#endif
/*
========================================================================================================================
*【全局声明】 【全局声明】 【全局声明】
========================================================================================================================
*/
void BSP_SysTicker_Init(void);
Ticker_t BSP_SysTicker_GetCurTick(void);
Ticker_t BSP_SysTicker_CalLag(Ticker_t tick);
#endif /* End of module include. */
其中SYSTICK_CLK_HZ是MCU主频值,需要根据具体的MCU进行修改。SysTick_Config()函数是设置MCU的滴答定时器函数,ARM芯片CM0、CM3的官方库里面基本都有。
2.任务调度函数
ESF_Tmr.c:
/*
========================================================================================================================
* 【文件包含】 【文件包含】 【文件包含】
========================================================================================================================
*/
#include "ESF_Tmr.h"
/*
========================================================================================================================
*【本地宏定义】 【本地宏定义】 【本地宏定义】
========================================================================================================================
*/
/*
========================================================================================================================
*【本地数据类型定义】 【本地数据类型定义】 【本地数据类型定义】
========================================================================================================================
*/
/*
========================================================================================================================
*【变量定义 & 各种声明】 【变量定义 & 各种声明】 【变量定义 & 各种声明】
========================================================================================================================
*/
/*
========================================================================================================================
*【函数定义】 【函数定义】 【函数定义】
========================================================================================================================
*/
/*
************************************************************************************************************************
* 函数名称 : ESF_TmrExeFuncScheduling
* 功能描述 : 定时执行功能函数的调度
* 输入参数 : pTmrExeFuncTab -- 定时执行的功能函数表数组
* FuncTabSum -- 功能函数表数组的大小
* 返回参数 : 无
* 补充说明 : 1、定时执行实现原理:
* 每个定时执行的函数(pTmrExeFunc()),都有其对应的执行周期(ExePeriod)与 时间片计数值(pTickCount)。
* 该函数会将时间片计数值(pTickCount)与系统的时间计数值做比较,
* 当【系统时间计数值 - 时间片计数值(pTickCount) >= 执行周期(ExePeriod)】 时,
* 则执行相应的定时函数,并更新 时间片计数值(pTickCount)。
*
* 2、执行周期(ExePeriod)的建议:
* 由于每个功能都有单独的时间计数值(pTickCount)。因此可以将各个功能的执行周期设置成不同的值。
* 尽量将执行周期错开,避免在同一个执行周期同时执行太多的功能。
************************************************************************************************************************
*/
void ESF_TmrExeFuncScheduling(const ESF_TmrExeFunc_t *pTmrExeFuncTab, uint8_t FuncTabSum)
{
uint8_t i;
Ticker_t lag;
for(i=0; i<FuncTabSum; i++)
{
lag = BSP_SysTicker_CalLag(*(pTmrExeFuncTab[i].pTickCount)); /*计算时间差*/
if(lag >= pTmrExeFuncTab[i].ExePeriod) /*时间差值 大于等于 执行周期*/
{
if(pTmrExeFuncTab[i].pTmrExeFunc != NULL) /*功能要存在*/
{
pTmrExeFuncTab[i].pTmrExeFunc(pTmrExeFuncTab[i].ExePeriod); /*时间到,则执行功能*/
}
(*(pTmrExeFuncTab[i].pTickCount)) += lag; /*更新时间计数*/
}
}
}
ESF_Tmr.h:
#ifndef ESF_TMR_H
#define ESF_TMR_H
/*
========================================================================================================================
* 【文件包含】 【文件包含】 【文件包含】
========================================================================================================================
*/
#include "bsp_systicker.h"
/*
========================================================================================================================
*【全局宏定义】 【全局宏定义】 【全局宏定义】
========================================================================================================================
*/
/*
========================================================================================================================
*【全局数据类型定义】 【全局数据类型定义】 【全局数据类型定义】
========================================================================================================================
*/
/*
************************************************************************************************************************
* 类型定义 : 定时执行的功能函数,处理结构体。
* 将需要定时执行的函数以数组表的方式组织,定义在Flash(ROM)中,
* 而Flash中的数据是不可改变的,因此用*pTickCount指针指向RAM中可操作的数据。
************************************************************************************************************************
*/
typedef struct
{
void (*pTmrExeFunc)(Ticker_t ExePeriod); /*要执行的函数*/
Ticker_t ExePeriod; /*执行周期*/
Ticker_t *pTickCount; /*指向时间计数变量*/
}ESF_TmrExeFunc_t;
/*
========================================================================================================================
*【对外声明】 【对外声明】 【对外声明】
========================================================================================================================
*/
void ESF_TmrExeFuncScheduling(const ESF_TmrExeFunc_t *pTmrExeFuncTab, uint8_t ExeSum);
#endif
3.使用方法举例
/*
************************************************************************************************************************
* 函数名称 : Task1
* 功能描述 : 100ms执行一次
* 输入参数 : ExePeriod 函数执行周期
* 返回参数 : 无
* 补充说明 : 无
************************************************************************************************************************
*/
void Task1(Ticker_t ExePeriod)
{
printf("Task1\r\n");
}
/*
************************************************************************************************************************
* 函数名称 : Task2
* 功能描述 : 200ms执行一次
* 输入参数 : ExePeriod 函数执行周期
* 返回参数 : 无
* 补充说明 : 无
************************************************************************************************************************
*/
void Task2(Ticker_t ExePeriod)
{
printf("Task2\r\n");
}
/*
************************************************************************************************************************
* 函数名称 : Task3
* 功能描述 : 150ms执行一次
* 输入参数 : ExePeriod 函数执行周期
* 返回参数 : 无
* 补充说明 : 无
************************************************************************************************************************
*/
void Task3(Ticker_t ExePeriod)
{
printf("Task3\r\n");
}
#define TASK_TICK_COUNT_BUF_NUM 3
static Ticker_t SaveData_TickCount[TASK_TICK_COUNT_BUF_NUM ];
static const ESF_TmrExeFunc_t Task_TmrExeFuncTab[] =
{
/* 执行函数 执行周期(ms) 时间计数变量*/
{ Task1, 100, &SaveData_TickCount[0]},
{ Task2, 200, &SaveData_TickCount[1]},
{ Task3, 150, &SaveData_TickCount[2]}
};
#define TASK_TMR_EXE_FUNC_TAB_NUM TAB_NUM(Task_TmrExeFuncTab)
void main(void)
{
while(1)
{
/*调度功能函数表------------------------------------------------*/
ESF_TmrExeFuncScheduling(SaveData_TmrExeFuncTab, SAVEDATA_TMR_EXE_FUNC_TAB_NUM);
}
}
上面main函数省略了底层IO初始化的相关内容,最后打印出来的数据应该如下:
Task1
Task3
Task1
Task2
Task1
Task3
Task1
Task2
......
PS:整个系统架构要是非阻塞式的
对于有需要延时的可以使用以下方案,这样如果Task的执行周期变了,原本的代码也不会收到太影响
void Task(Ticker_t ExePeriod)
{
Static Ticker_t Ticker=0;
if(Ticker >= 2000)
{
Ticker = 0;
//执行相应内容
}
else
{
Ticker += ExePeriod;
}
}