STM32 裸机程序的任务调度(学习)

文章目录

目录

前言

一、裸机任务调度方案一(杂糅)

二、裸机任务调度方案二(按需求分配)

三、裸机任务调度方案三(按需求分配,实现软件调度框架)


前言

        在开发单片机时如果不使用操作系统,任务调度是有点让人头疼的,尤其是业务功能模块比较多的情况,比如说有些功能需要几毫米就要执行一次,而有些只需要几百毫秒才用执行一次就可以,这种情况如果做不到及时的任务调度,那么使用起来的感受是非常不好的。


一、裸机任务调度方案一(杂糅)

代码如下(示例):

int main()
{
	SysInit();
	while(1)
	{
		Task1();
		Task2();
		Task3();
		Task4();
	}
}

        这种是初学者经常使用的方案,在while循环中直接添加任务模块从上到下依次执行。但是,这种就有很多弊端,比如:Task1如果是采集环境值的就不需要很快的速度去采集,几秒钟或者几分钟采集一次就可以了;Task2是做人机交互的,是需要几个毫秒就去执行一次。遇见这种情况就不适用,如果再在业务中使用delay函数cpu就没办法去执行其他的功能,就是对cpu资源的一种浪费,在模块多的情况下这种是不可取的。

二、裸机任务调度方案二(按需求分配)

代码如下(示例):

int main()
{
	SysInit();
	while(1)
	{
		if(period30sFlag)
		{
			Task1();
			period1sFlag = 0;
		}
		if(period1sFlag)
		{
			Task2();
			period1sFlag = 0;
		}
		if(period5sFlag)
		{
			Task3();
			period5sFlag = 0;
		}
	}
}

        这种方式是按照对应的时间标志再去运行相应的功能模块,时间标志是采用定时器来来实现时间片轮转的。如果功能模块有很多个,就需要很多的if else语句,这种并不美观,而且可读性也不高。

三、裸机任务调度方案三(按需求分配,实现软件调度框架)

        通过调度框架去调度各个功能模块,主程序代码如下:

int main()
{
	SysInit();
	while(1)
	{
		TaskHandler();
	}
}
void TaskHandler(void)
{
	uint8_t i;
	for(i = 0; i < TASKNM; i++)
	{
		if(true == g_TaskComps[i].run)
		{
			g_TaskComps[i].pTaskFunc();
			g_TaskComps[i].run = false;
		}
	}
}

        通过判断结构体成员的运行标志来判断是否达到运行条件,如果是则运行结构体成员的函数指针。

具体实现流程如下:

typedef struct{
	bool run;								//运行标志 1,运行 0,挂起
	uint32_t timeCount;					//递减计算 时间片 毫秒触发
	uint32_t timeRload;					//重装载值
	void (*pTaskFunc)(void);		//函数指针
} TaskComps_stu;

        构建一个结构体,里面有四个成员分别为:运行标志、递减计数、重装载值以及运行任务模块的函数指针。

#define TASKNM (sizeof(g_TaskComps)/sizeof(g_TaskComps[0])) //任务个数
void task1(void)
{
	printf("I am task1, Runtime:%lld\r\n",getSysRunTime());
}

void task2(void)
{
	printf("I am task2, Runtime:%lld\r\n",getSysRunTime());
}

void task3(void)
{
	printf("I am task3, Runtime:%lld\r\n",getSysRunTime());
}

static TaskComps_stu g_TaskComps[] = {
		{false, 1000,	1000, task1},		//任务一
		{false, 3000,	3000, task2},		//任务二
		{false, 500,	500, task3},		//任务三
};

        这里实现了三个业务功能模模块,task1为1000毫秒运行一次;task2为3000毫秒运行一次;task3为500毫秒运行一次。

        宏(TASKNM)为获取任务个数。

//task.c
void TaskTimeSlice(void)
{
	uint8_t i;
	for(i = 0; i < TASKNM; i++)
	{
		g_TaskComps[i].timeCount--;
		if(0 == g_TaskComps[i].timeCount)
		{
			g_TaskComps[i].run = true;
			g_TaskComps[i].timeCount = g_TaskComps[i].timeRload;
		}
	}
}

void TaskInit(void)
{
	TaskCallback(TaskTimeSlice);
}

        这里初始化函数TaskInit()为回调函数,将TaskTimeSlice()函数地址传给systick.c文件的pCallbackFunc函数指针并且一毫秒执行一次。当结构体成员变量timeCount递减为0时就改变为运行态。

//systick.c
static uint64_t sys_runTime = 0;

/**
****************************************************************
* @brief	系统滴答定时器初始化
* @parm		
* @return
****************************************************************
*/
void SysTickInit(void)
{
	if(SysTick_Config(SystemCoreClock / 1000 ))
	{
		while(1);
	}
}

void (*pCallbackFunc)(void);
/**
****************************************************************
* @brief	回调函数
* @parm		
* @return pFunc,需要调用的函数指针
****************************************************************
*/
void TaskCallback(void (*pFunc)(void))
{
	pCallbackFunc = pFunc;
}

/**
****************************************************************
* @brief	滴答定时器任务
* @parm		
* @return 
****************************************************************
*/
void SysTick_Handler(void)
{
	sys_runTime++;
	pCallbackFunc();
}

/**
****************************************************************
* @brief	获取系统运行时间
* @parm		
* @return	sys_runTime,返回运行时间 单位 ms
****************************************************************
*/
uint64_t getSysRunTime(void)
{
	return sys_runTime;
}

        这里初始化了系统滴答定时器每一毫秒触发一次中断,并且获取到了上层传过来的回调函数,这里每触发一次中断就调用一次该函数。

        此为运行结果图,task1为1000毫秒运行一次;task2为3000毫秒运行一次;task3为500毫秒运行一次。Runtime为系统运行时间,毫秒为单位。

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只想做好编程的小王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值