【几种常用的裸机程序任务调度方案】https://www.bilibili.com/video/BV1si421h768?vd_source=02fb5aaf3a4713e1bfbd90f63f8b5ce5
在这里介绍两种根据裸机根据定时器进行系统任务调度的方法,来自于郭天祥老师的课程。
首先这是第一种
在定时器中断里面进行任务时间片的设置,到达时间之后将任务运行的标志位置1.在main函数的死循环中轮询各个任务的标志位从而进行任务调度。
整体思维框架如下
源码如下,有需自取。
通过网盘分享的文件:定时器时间片按需分配任务调度
链接: https://pan.baidu.com/s/1sqzdiyj7KVf64ECRuEUXkw?pwd=3556 提取码: 3556
--来自百度网盘超级会员v3的分享
#include "stm32f4xx.h"
#include "./tim/bsp_general_tim.h"
#include "./led/bsp_led.h"
#include "delay.h"
uint8_t task1_flag=0 , task2_flag=0;
uint16_t task1_num=100 , task2_num=500;
void task1(void);
void task2(void);
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
LED_GPIO_Config();
delay_init();
/* 初始化通用定时器定时,1s产生一次中断 */
TIMx_Configuration();
while(1)
{
if( task1_flag==1 )
{
task1();
task1_flag = 0;
}
if( task2_flag==1 )
{
task2();
task2_flag = 0;
}
}
}
void task1(void)
{
LED1_TOGGLE;
delay_ms(1000);
}
void task2(void)
{
LED2_TOGGLE;
delay_ms(500);
}
void GENERAL_TIM_IRQHandler (void)
{
if ( TIM_GetITStatus( GENERAL_TIM, TIM_IT_Update) != RESET )
{
if(task1_num)
{
task1_num--;
if(task1_num==0)
{
task1_num=100;
task1_flag=1;
}
}
if(task2_num)
{
task2_num--;
if(task2_num==0)
{
task2_num=500;
task2_flag=1;
}
}
TIM_ClearITPendingBit(GENERAL_TIM , TIM_IT_Update);
}
}
我配置定时器为1ms一次的定时器中断 , 在定时器中对任务的等待时间和标志位进行设置 。
在主函数中,检测到标志位就执行任务并清除标志位。
这种任务调度适用于对系统实时性要求不高和任务时间分配的场合,比如任务一10ms执行一次就可以,任务二50ms执行一次就行,就没有必要在while大循环里无脑持续执行浪费系统资源。
任务一和任务二里面都有延时的话可能对延时时间造成重叠什么的,所以并不适用于对实时性要求极高的场合。
实现了按需分配执行频率,提升效率。
接下来看第二种方法
实现调度框架
首先这里是定义一个结构体,也就是任务的结构体,在结构体里面定义任务的各个参数见下图,包含运行标志,时间片周期和重载时间片周期,最后也就是函数指针。
这里面有一个知识点就算函数指针,定义一个函数指针用来接引任务的执行函数 。
函数指针的数据类型可以自己定义,这里我只是定义了一个简单的无返回值无参数的任务函数指针。
最后定义一个结构体数组用于存放各个任务。
此处我写的代码如下
void task1(void);
void task2(void);
typedef struct {
uint16_t run;
uint32_t time_rload;
uint32_t time_out;
void (*task_os)(void);
}task_tcb;
task_tcb task_list[]={
{0,1000,1000,task1 },
{0,100,100,task2 },
};
uint8_t task1_flag=0 , task2_flag=0;
uint16_t task1_num=100 , task2_num=500;
uint32_t task_max = sizeof(task_list)/sizeof(task_tcb);
这里有一个顺序问题,必须在填写函数指针之前定义任务函数,否则找不到任务函数会报错。
在定义任务函数结构体和数组之后 , 定义任务的标志位和时间片周期并赋予初始值 。
后面的 task_max为任务个数。
这里代表的是在主函数中首先进行各种初始化,
在循环里执行任务调度函数,任务调度函数的框架也给出来了,
就是在任务的数组里面轮询每个任务的标志位。如果标志位被置1,就清除标志位之后执行任务函数。至于是先清除标志位还是先执行函数看自己的时序要求。
int main(void)
{
LED_GPIO_Config();
TIMx_Configuration();
delay_init();
while(1)
{
task_handler();
}
}
void task_handler(void)
{
uint16_t i=0;
for(i=0;i<=task_max;i++)
{
if(task_list[i].run == 1 )
task_list[i].task_os();
task_list[i].run = 0;
}
}
接下来就是时间片的函数框架
void GENERAL_TIM_IRQHandler (void)
{
if ( TIM_GetITStatus( GENERAL_TIM, TIM_IT_Update) != RESET )
{
task_schedule();
TIM_ClearITPendingBit(GENERAL_TIM , TIM_IT_Update);
}
}
void SysTick_Handler(void)
{
//task_schedule();
}
void task_schedule(void)
{
uint8_t i;
for( i=0;i<task_max;i++)
{
if(task_list[i].time_out )
{
task_list[i].time_out--;
if( task_list[i].time_out == 0 )
{
task_list[i].time_out = task_list[i].time_rload ;
task_list[i].run = 1;
}
}
}
}
在 定时器中断里面,和第一个方法差不多,对倒计时变量进行--,为0之后对任务运行标志位进行操作,并重新装载倒计时。
这里我用的是普通的定时器中断,没运行在滴答定时器中断里,主要是普通定时器的中断周期我可以自己设置。
这里需要注意一点,如果使能了中断而不书写中断服务函数的话,程序会卡死。
这种调度方法有个缺点就是任务里面的延时函数会失效,也就是说既没有像第一种方法将任务执行完毕再进行任务切换,也没有像RTOS对任务现场进行保护之后再进行任务切换,而是只要任务倒计时归0,任务运行标志位被置1,就从头开始执行任务。
刚刚试过了,对于大一点的循环操作也会有问题,只能用于快速进出的函数操作
适用于没有延时操作的任务函数。
整体代码如下:
#include "stm32f4xx.h"
#include "./tim/bsp_general_tim.h"
#include "./led/bsp_led.h"
#include "delay.h"
void task1(void);
void task2(void);
typedef struct {
uint16_t run;
uint32_t time_rload;
uint32_t time_out;
void (*task_os)(void);
}task_tcb;
task_tcb task_list[]={
{0,500,500,task1 },
{0,100,100,task2 },
};
uint8_t task1_flag=0 , task2_flag=0;
uint16_t task1_num=500 , task2_num=100;
uint32_t task_max = sizeof(task_list)/sizeof(task_tcb);
void task_schedule(void);
void task_handler(void);
int main(void)
{
LED_GPIO_Config();
TIMx_Configuration();
delay_init();
while(1)
{
task_handler();
}
}
void task1(void)
{
LED1_TOGGLE;
}
void task2(void)
{
LED2_TOGGLE;
}
void task_schedule(void)
{
uint8_t i;
for( i=0;i<task_max;i++)
{
if(task_list[i].time_out )
{
task_list[i].time_out--;
if( task_list[i].time_out == 0 )
{
task_list[i].time_out = task_list[i].time_rload ;
task_list[i].run = 1;
}
}
}
}
void task_handler(void)
{
uint16_t i=0;
for(i=0;i<=task_max;i++)
{
if(task_list[i].run == 1 )
task_list[i].task_os();
task_list[i].run = 0;
}
}
void GENERAL_TIM_IRQHandler (void)
{
if ( TIM_GetITStatus( GENERAL_TIM, TIM_IT_Update) != RESET )
{
task_schedule();
TIM_ClearITPendingBit(GENERAL_TIM , TIM_IT_Update);
}
}
void SysTick_Handler(void)
{
//task_schedule();
}
//打开了定时器中断必须要写定时器中断的函数,否则会卡死
//中断函数写main和it哪个文件里都行
/*********************************************END OF FILE**********************/
源码如下,有需自取
通过网盘分享的文件:定时器调用任务调度函数实现任务调度
链接: https://pan.baidu.com/s/1lWcHQdzPLRSTFdtUBKhqZA?pwd=xca8 提取码: xca8
--来自百度网盘超级会员v3的分享