主函数为一些STM32CubeMX生成工程时写好的初始化函数,及一个while(1)轮询,里面是系统任务调度进程函数。
int main(void)
{
/* 初始化 */
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
while (1)
{
TaskProcess(); //系统任务调度进程函数
}
}
该函数定义:当变量OSTickSave和OSTick不相等时进入if语句,而它们都被初始化为0,先看它们何时不相等。
static void TaskProcess(void)
{
u8 i = 0;
static u32 OSTickSave = 0;
if (OSTickSave != OSTick) //全局变量 vu32 OSTick = 0;
{
SetMacbRemarks((u16)(OSTick - OSTickSave));
OSTickSave = OSTick;
// MCL_MAX为任务总个数,如果定义N个任务,
// 必须对N个任务初始化,否则程序会出错
for (i = 0; i < MCL_MAX; i++)
{
if ( (theComps[i].inrun) && (theComps[i].lpTaskHook != 0) )
{
theComps[i].lpTaskHook();
theComps[i].inrun = 0;
}
}
}
}
通过OSTick找到滴答时钟中断处理函数,该函数在系统滴答时钟句柄函数中调用。在startup文件已写好,每1ms产生一次中断调用此句柄函数。即每1ms OSTick++,所以在TaskProcess()中表现为等待每1ms产生的一次中断使OSTick+1,进入if调用SetMacbRemarks()函数并传入两变量之差,再如上赋值,这使两变量之差一直为1。
/* 滴答时钟中断处理函数 */
void TimingDelay_Decrement(void)
{
OSTick++; //task counter
}
/* stm32g4xx_it.c */
void SysTick_Handler(void)
{
TimingDelay_Decrement();
}
再看SetMacbRemarks()函数定义,for循环对任务数组遍历,每个成员的clock元素+dif(=1),当clock加到>=timer且mode为AUTO时将clock清零inrun置一,当mode为ALWAYS时inrun置一。先研究任务数组如下(直接拷贝了原例程)。
void SetMacbRemarks(u16 dif)
{
u8 i = 0;
for (i = 0; i < MCL_MAX; i++) //MCL_MAX为任务总数
{
theComps[i].clock += dif;
if ( (theComps[i].clock >= theComps[i].timer) && (theComps[i].mode == MCM_AUTO) )
{
theComps[i].clock = 0;
theComps[i].inrun = 1;
}
else if (theComps[i].mode == MCM_ALWAYS)
{
theComps[i].inrun = 1;
}
}
}
typedef struct _MAINAPP_COMPONENTS
{
u8 inrun;
u16 clock;
u16 timer;
u8 mode;
void (*lpTaskHook)(void);
} MAINAPP_COMPONENTS;
#ifdef START_APP_FUNCTION
/* Local array variable in this file.
** ==================================================================
** theComps : application task in array list.
** theCans : can data buffer in array list.
** theUartCommand : uart command in array list.
** ==================================================================
*/
static MAINAPP_COMPONENTS theComps[] =
{
//{ 0, 0, 100, MCM_AUTO, App_Led },
//{ 0, 0, 1000, MCM_AUTO, App_ADC5 },
#if FMSTR_USART_DEBUG
{ 0, 0, 1, MCM_AUTO, App_FMSTR },
#endif
#if SENSOR_FILTER_MS
{ 0, 0, 1, MCM_AUTO, App_SensorFilter_ms},
#endif
{ 0, 0, 5, MCM_AUTO, App_FeedWD },
{ 0, 0, 900, MCM_AUTO, App_GetDate },
#ifdef ERROR_CHECK
{ 0, 0, 5, MCM_AUTO, App_ERROR_T1 },
{ 0, 0, 1, MCM_AUTO, App_ERROR_T2 },
#endif //ERROR_CHECK
{ 0, 0, 100, MCM_AUTO, App_CoolingFan },
// 空进程,若需添加软件定时处理过程,请在此之前添加,并在main.h MAINAPP_COMPONENTS_LIST枚举中按顺序添加新成员
{ 0, 0, 1000, MCM_AUTO, App_EndProcess },
};
#endif
可看出inrun是任务运行标志,clock相当于每1ms+1的计数器,而timer相当于调用周期,mode是运行模式,lpTaskHook是函数指针。
最后回看TaskProcess()函数可知当任务inrun为1且lpTaskHook非0时,执行一次lpTaskHook所指函数且将inrun清零。
综上所述,一个初始化为{ 0, 0, 100, MCM_AUTO, App_Xxx }的任务,每100ms执行一次APP_Xxx函数,可根据需求改变。
优化,删去暂不会用的,增加偏移量,考虑可能有单个轮询周期执行任务超过1ms(堵塞)情况,为保证同周期不同任务偏移生效改了算法:
typedef struct _MAINAPP_COMPONENTS
{
u8 offset;
u16 clock;
u32 period_counter;
u16 period;
void (*p_func)(void);
} MAINAPP_COMPONENTS;
static void task_process_func(MAINAPP_COMPONENTS* p_comps, u32 number_of_tasks)
{
static u32 OSTick_last = 0;
if (OSTick_last != SYS_TICK)
{
OSTick_last = SYS_TICK;
for (u8 i = 0; i < number_of_tasks; i++)
{
if(SYS_TICK - p_comps[i].clock >= p_comps[i].period + p_comps[i].offset
&& p_comps[i].p_func != 0 )
{
p_comps[i].period_counter++;
p_comps[i].clock = p_comps[i].period_counter * p_comps[i].period;
p_comps[i].p_func();
}
else{}
}
}
}