裸机实现任务调度

【几种常用的裸机程序任务调度方案】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的分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值