嵌入式常用算法:时间触发下的嵌入式软件设计模式

在嵌入式软件开发当中,常常第一步就是设计整个系统的架构。有基于OS的,也有裸机的。我们先拿裸机说事儿,我想很多人在做单片机的裸机开发(甚至基于OS)时脑海里根本没有设计架构的想法,从来都是while(1)循环到底,能写点状态机已经算是基础扎实的人物了。那么问题来了,while(1)是什么?在嵌入式设计模式里是怎么给while(1)定义的。《C嵌入式编程设计模式》一书中将while(1)称之为“超循环模式”,也就是嵌入式的入门傻瓜模式。在此模式下即使有前台监听也势必会导致系统实时性傻逼,从硬件上分析:while(1)的高CPU占用率也使得功耗大大提高。当然存在即合理,超循环模式在安全性和可靠性上是非常占优势的。结构简单也易于理解,这对于安全性能要求较高的系统上还是非常具有吸引力的。

说了这么多不上代码真没意思,具体的看书吧,强烈推荐《C嵌入式编程设计模式》,在此也做一个小广告:朋友埋头2年编写的关于嵌入式设计架构书籍(基于飞思卡尔系列MCU)也即将在明年年初由清华大学出版社出版,爱好飞思卡尔的同学可以关注一下哦。

/**
  ******************************************************************************
  * @file    cx_sch.c
  * @author  CX
  * @version V1.0.0.1
  * @date    2016-7-26
  * @brief   1.0.0.1
	     修改任务数据结构,增加一级抢占任务
	     加入RunNum预编译选项,限制是否开启任务执行次数
	     1.0.0.0 
	     完成基础架构搭建
  ******************************************************************************
  * @attention
  *
  * 项目   :None
  * 官网   : None
  * 实验室 :None
  ******************************************************************************
  */


#include "cx_sch.h"



sTask_Typedef SCH_tasks_G[SCH_MAX_TASKS];



/**
  * @brief   增加任务
  * @param   pFunction 任务指针, Delay 延迟时标, Peroid 任务执行周期, RunNum 任务执行次数, ModeEnum 任务模式
  * @retval  任务队列索引
  * @notice  None
*/
uint8_t SCH_Add_Task(void(*pFunction)(), uint32_t Delay, uint32_t Peroid, uint8_t RunNum, TaskMode_Enum ModeEnum)
{
	uint8_t Index = 0;
	while((SCH_tasks_G[Index].pTask != 0) && (Index < SCH_MAX_TASKS))	
	{
		Index++;                                                             
	}
	if(Index == SCH_MAX_TASKS)
	{
		return SCH_MAX_TASKS;
	}
	SCH_tasks_G[Index].pTask = pFunction;
	SCH_tasks_G[Index].Delay = Delay;
	SCH_tasks_G[Index].Peroid = Peroid;
	SCH_tasks_G[Index].RunMe = 0;
#if RunNum_ON
	SCH_tasks_G[Index].RunNum = RunNum;
#endif
	SCH_tasks_G[Index].ModeEnum = ModeEnum;
	return Index;
}



/**
  * @brief   删除任务
  * @param   Index, 任务队列索引
  * @retval  None
  * @notice  None
*/
void SCH_Delete_Tasks(uint8_t Index)
{
	SCH_tasks_G[Index].pTask = 0;
	SCH_tasks_G[Index].Delay = 0;
	SCH_tasks_G[Index].Peroid = 0;
	SCH_tasks_G[Index].RunMe = 0;
#if RunNum_ON
	SCH_tasks_G[Index].RunNum = 0;
#endif
}



/**
  * @brief   更新任务
  * @param   None
  * @retval  None
  * @notice  需要心跳支持, 抢占任务执行时长必须小于任务调度器的时标
*/
void SCH_Update_Tasks(void)
{
	u8 Index;
	for(Index = 0;Index < SCH_MAX_TASKS;Index++)
	{
		if(SCH_tasks_G[Index].pTask)
		{
			if(SCH_tasks_G[Index].Delay == 0)   
			{
				switch(SCH_tasks_G[Index].ModeEnum)
				{
					case SEIZ_Enum:                         //抢占任务立即运行
						SCH_tasks_G[Index].pTask();
						if(SCH_tasks_G[Index].RunMe > 0)
						{
							SCH_tasks_G[Index].RunMe--;
#if RunNum_ON
							if(SCH_tasks_G[Index].RunNum > 0)
							{
								SCH_tasks_G[Index].RunNum--;
								if(SCH_tasks_G[Index].RunNum == 0)
								{
									SCH_Delete_Tasks(Index);
								}	
							}
#endif
						}
						break;
					case COOP_Enum:
						SCH_tasks_G[Index].RunMe++;     
						break;
					default:break;
				}				
				if(SCH_tasks_G[Index].Peroid)
				{
					SCH_tasks_G[Index].Delay = SCH_tasks_G[Index].Peroid;
				}
			}
			else
			{
				SCH_tasks_G[Index].Delay--;     
			}
		}
	}
}



/**
  * @brief   任务调度器
  * @param   None
  * @retval  None
  * @notice  None
*/
void SCH_Dispatch_Tasks(void)
{
	u8 Index;
	while(1)
	{
		for(Index = 0;Index < SCH_MAX_TASKS;Index++)
		{
			if(SCH_tasks_G[Index].RunMe > 0)
			{
				SCH_tasks_G[Index].pTask();
				SCH_tasks_G[Index].RunMe--;
#if RunNum_ON
				if(SCH_tasks_G[Index].RunNum > 0)
				{
					SCH_tasks_G[Index].RunNum--;
					if(SCH_tasks_G[Index].RunNum == 0)
					{
						SCH_Delete_Tasks(Index);
					}	
				}
#endif
				if(SCH_tasks_G[Index].Peroid == 0)
				{
					SCH_Delete_Tasks(Index);
				}
			}
		}
	}
}



/**
  * @brief   任务初始化
  * @param   None
  * @retval  None
  * @notice  None
*/
void SCH_Init(void)
{
	TIM2Base_Config(10);
}


/**
  * @brief   启动任务调度
  * @param   None
  * @retval  None
  * @notice  None
*/
void SCH_Start(void)
{
	TIM_Cmd(TIM2, ENABLE);	
}
#ifndef __CX_SCH_H
#define __CX_SCH_H


#include "stm32f10x.h"
#include "cx_timbase.h"


#define      RunNum_ON          0


typedef enum
{
	COOP_Enum = 0x0,
	SEIZ_Enum = 0x1
}TaskMode_Enum;




typedef struct
{
	void (*pTask)(void); 
	uint32_t Delay;			       
	uint32_t Peroid;		     
	uint8_t RunMe;		               //标记任务就绪
#if RunNum_ON
	uint8_t RunNum;                        //指定任务执行次数,执行完自动销毁
#endif
	TaskMode_Enum ModeEnum;
}sTask_Typedef;




#define      SCH_MAX_TASKS         10


void SCH_Init(void);
void SCH_Start(void);
uint8_t SCH_Add_Task(void(*pFunction)(), uint32_t Delay, uint32_t Peroid, uint8_t RunNum, TaskMode_Enum ModeEnum);
void SCH_Update_Tasks(void);
void SCH_Dispatch_Tasks(void);
void SCH_Delete_Tasks(uint8_t Index);



#endif

老规矩,不分析代码了,代码加起来也没几行,数据结构也很简单,静下心来一定看得懂的。这段代码在51单片机(12MHZ,12T)上实际测试在1ms的时标下,满载12个任务下的CPU使用率仅为89%,依然有11%的空闲。如果使用国产STC15系列的1T单片机情况估计刚好相反(没实际测试过,仅仅猜测)。

在看懂代码后,也许有人要问我循环调度下响应事件就迟滞了,实时性就降低了呀!那么针对这样的疑问我想说以下几点:

1:迟滞是相对的,比起超循环系统的while(1)+中断标志位+阻塞延时,轮询调度的响应速度足够快了。

2:循环调度一般情况下还是添加短任务的比较好,长任务忽略的好,至于为什么,自己去琢磨吧。

3:这段代码不是在写操作系统,只能作为嵌入式系统代码中的一部分,不要吹毛求疵,当你能1s读完红楼梦时再来喷吧。

4:任务抖动是无法避免的,除非你是抢占式核,可以抢占CPU使用权。本人汇编菜鸟,写不出来。

5:说的很明确,本代码仅限于裸机开发。

感恩,感谢...   ...

使用过程中出现任何BUG请联系我本人QQ:951253606,说明bug现象以及重现过程。

寻求MCU产品开发请联系我本人QQ:951253606。

你可能感兴趣的:(嵌入式常用算法)