基于单片机的多任务轮询系统

@

目录

时间片轮询系统

通过时间片的划分,可以利用一个定时器或者系统滴答定时器,通过多任务轮询方法,实现一个多任务的基于时间轮询调度的系统——Schedule.

版权声明

本文展示的源码为网络上所获取的资源,如有侵权,请告知删除。
此处仅为交流学习使用。

文件结构

有三个文件构成,两个h文件一个c文件。
sch_chg.h 类型定义文件
schedule.c 源码实现
shcedule.h 宏定义和配置文件

源码

sch_chg.h

#ifndef __SCH_CFG_H_
#define __SCH_CFG_H_


//定义可裁剪部分
#define SCH_CFG_Q_EN            1u            /*       任务内建消息使能        */
#define SCH_MAX_TASKS           4             /*  最大任务数量可以增加最大255个*/


#define SCH_MBOX_EN             1    	     /* Enable (1) or Disable (0) 邮箱*/
#define SCH_TICKS_PER_SEC       1000         /* 1秒钟多少个系统时钟数         */
/*------------------------------------------------------------------------------
*                       与编译器相关数据类型定义 
*-----------------------------------------------------------------------------*/
typedef unsigned char   BOOLEAN;
typedef unsigned char   INT8U;                 /* Unsigned  8 bit quantity */
typedef signed   char   INT8S;                 /* Signed    8 bit quantity */
typedef unsigned short  INT16U;                /* Unsigned 16 bit quantity */
typedef signed   short  INT16S;                /* Signed   16 bit quantity */
typedef unsigned int    INT32U;                /* Unsigned 32 bit quantity */
typedef signed   int    INT32S;                /* Signed   32 bit quantity */
typedef float            FP32;           /* Single precision floating point */
typedef double           FP64;           /* Double precision floating point */
typedef unsigned int    SCH_TICKS;             //定义延时时钟数据类型

//定义数据类型
typedef unsigned char  SCH_UINT8;

typedef unsigned int   SCH_UINT16;
#endif      

schedule.h

#ifndef __SCHEDULE_H
#define __SCHEDULE_H

#ifdef __cplusplus
extern "C" {
#endif

/*------------------------------------------------------------------------------
                             schedule 版本号
------------------------------------------------------------------------------*/

#define  SCH_VERSION                 02200u              

/*------------------------------------------------------------------------------
*                            包含头文件
------------------------------------------------------------------------------*/

#include "sch_cfg.h"
    
#ifndef  SCH_GLOBALS
#define  SCH_EXT  extern
#else
#define  SCH_EXT
#endif
/*------------------------------------------------------------------------------
*                         系统状态定义
------------------------------------------------------------------------------*/
#define SCH_DLY_TYPE 	            SCH_UINT16
#define SCH_ERR_NONE                     0u
#define SCH_ERR_TIMEOUT                 10u
#define SCH_TASK_RUN		             0
#define SCH_TASK_PEND		       (SCH_DLY_TYPE)0xffff

#if	SCH_MAX_TASKS <= 255
  #define SCH_MAX_TASK_TYPE	  SCH_UINT8	      //最大任务数<=255时定义为u8
#else
  #define SCH_MAX_TASK_TYPE	  SCH_UINT16	  //最大任务为>255则定义为u16
#endif
/*------------------------------------------------------------------------------
*                           数据结构定义
------------------------------------------------------------------------------*/
typedef struct 
{					
	SCH_TICKS		TaskDly;
    /*延时数据*/
	INT8U			SubExitFlag;
    /*这个是什么?*/
#if SCH_CFG_Q_EN > 0u
    void          *pData;      
    /*队列指针*/
    SCH_UINT8 	    Size;      
    /*消息的长度*/
#endif

}SCH_TCB;

/*----------------------------------------------------------------------------*/
/*----------------------------邮箱数据类型定义--------------------------------*/
typedef struct 
{					
  
	SCH_TICKS Dly;
    /*延时量*/
    
	void *pMbox;
    /*邮箱的指针*/
    
} MBOX;
typedef SCH_TICKS	SEM;			//信号量数据类型定义

/*------------------------------------------------------------------------------
*                                全局变量定义
------------------------------------------------------------------------------*/

SCH_EXT SCH_TCB 	TaskTcb[SCH_MAX_TASKS];			
//任务状态数据结构

SCH_EXT INT8U		CurRunTaskId;					
    //当前运行任务的ID号
/*----------------------------------------------------------------------------*/

/*------------------------------------------------------------------------------
                                 任务相关宏
【1】任务创建按实例创建:
void Task_xxx(void)
{
	SCH_TaskBegin();
	while(1) {
		//用户代码
		SCH_TimeDly(SCH_TICKS_PER_SEC/100);		//根据任务实际周期进行延时
	}
	SCH_TaskEnd();
}
【注意】
	1,任务必须是无限循环,不能返回;
	2,任务中必须有延时函数或等待信号量,便于任务出让CPU使用权;
	3,任务函数中使用的非静态局部变量会在任务出让CPU后,
       变量消失,因此要想某个变量不消失,则要使用静态变量

【2】任务调度,按以下实例
SCH_TaskRun(Task_xxx,0);
【注意】
	1,各任务的ID不能相同
------------------------------------------------------------------------------*/

//@任务开始宏在任务函数的变量声明后引用
#define SCH_TaskBegin()	static INT16U _lc=0u; switch(_lc){case 0: 
						
//@任务结束宏在任务尾引用
#define SCH_TaskEnd() }				

//@任务调用宏在主循环中调用
//@ TaskName:任务名
//@ TaskID:任务ID,最大值不能超过"SCH_MAX_TASKS"值,且每个任务的ID不能相同
#define SCH_TaskRun(TaskName,TaskID) \
    do { \
		CurRunTaskId = TaskID; \
        if(TaskTcb[CurRunTaskId].TaskDly==0u) { \
            TaskName(); \
        } \
    } while(0); 
/*----------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
                                  子任务相关宏
--------------------------------------------------------------------------------    
【1】子任务创建按实例创建:
void SubTask_xxx(void)
{
	SCH_SubTaskBegin();
	//用户代码
	SCH_TimeDly(SCH_TICKS_PER_SEC/100);		//根据任务实际周期进行延时
	SCH_SubTaskEnd();
}
【注意】
	1,子任务不可以是无限循环,否则子任务后面程序无法执行;
	2,子任务可以带参数,但是不能返回;
	3,子任务中必须有延时函数或等待信号量,便于任务出让CPU使用权;
	4,子任务函数中使用的非静态局部变量会在任务出让CPU后,变量消失,
       因此要想某个变量不消失,则要使用静态变量

【2】子任务调度,按以下实例
	SCH_CallSubTask(SubTask_xxx());
------------------------------------------------------------------------------*/

//@子任务开始宏,在子任务函数的变量声明后引用
#define SCH_SubTaskBegin() static INT16U _lc=0u;switch(_lc){case 0: 
	
//@子任务结束宏,在子任务函数尾引用
#define SCH_SubTaskEnd() }_lc=0u;TaskTcb[CurRunTaskId].SubExitFlag=0u;return

//@子任务调用宏,在任务中调用,允许多层嵌套
//@ SubTaskName:子任务名
//@ p_arg:子线程函数输入参数
#define SCH_CallSubTask(SubTaskFun)	\
	TaskTcb[CurRunTaskId].SubExitFlag=1u; \
	while(1) { \
		_lc=(__LINE__%65535);return;case(__LINE__%65535): { \
			SubTaskFun; \
		} \
		if(TaskTcb[CurRunTaskId].SubExitFlag==0u) { \
			break; \
		} \
	} 
	
/*------------------------------------------------------------------------------
*                                延时宏定义
------------------------------------------------------------------------------*/

//@延时宏,在任务开始宏和结束宏之间的任意位置引用;
//@【注】由于此调度器运用了C语言的行号的宏代码,
//@    因此此宏定义最好放在一行代码中,不能拆分此宏定义,防止行号返回出错
//@ ticks:延时时间
#define SCH_TimeDly(ticks) 	do{_lc=(__LINE__%65535);TaskTcb[CurRunTaskId].TaskDly=ticks;TaskTcb[CurRunTaskId].SubExitFlag=1u;return;}while(0);case (__LINE__%65535):

/*------------------------------------------------------------------------------
                             信号量相关调用宏定义
------------------------------------------------------------------------------*/
//@信号量创建
#define SCH_SemCreate(Sem) Sem = 1u

//@w信号量等待
//@	sem--等待信号量;
//@	timeout--等待超时时间,0--直到成功等待信号量;
//@	err--等待信号状态,SCH_ERR_NONE-等待成功,SCH_ERR_TIMEOUT-等待超时
#define SCH_SemPend(sem,timeout,err) \
    do { \
		if(sem==0u) { \
			sem = 1u; \
			err = SCH_ERR_NONE; \
		} else { \
			sem = timeout + 1u; \
			SCH_TimeDly(0u); \
			if(timeout==0u) { \
				if(sem>0u) { \
					TaskTcb[CurRunTaskId].TaskDly = 0u; \
					return; \
				} \
				sem = 1u; \
				err = SCH_ERR_NONE; \
			} else { \
				if(sem>1u) { \
					sem --; \
					TaskTcb[CurRunTaskId].TaskDly = 1u; \
					return; \
				} else if(sem==1u) { \
					err = SCH_ERR_TIMEOUT; \
				} else { \
					sem = 1u; \
					err = SCH_ERR_NONE; \
				} \
			} \
		} \
    } while(0) 

//@ 发出一个信号量
#define SCH_SemPost(Sem) \
    do { \
        Sem = 0u; \
    } while(0)
     
/*------------------------------------------------------------------------------
                              邮箱相关调用宏定义
------------------------------------------------------------------------------*/
#if SCH_MBOX_EN > 0u
	
//@创建邮箱
#define SCH_MboxCreate(mbox) \
	do { \
		mbox.Dly = 1u; \
		mbox.pMbox = (void*)0; \
	} while(0)

//@ 邮箱等待
//@	mbox--等待邮箱;
//@ pmsg--等待成功后接收邮箱数据指针;
//@	timeout--等待邮箱超时时间,0--直到成功等待邮箱;
//@	err--等待邮箱状态,SCH_ERR_NONE-等待成功,SCH_ERR_TIMEOUT-等待超时
#define SCH_MboxPend(mbox,pmsg,timeout,err) \
    do { \
		if(mbox.pMbox!=(void*)0) { \
			pmsg = mbox.pMbox; \
			mbox.pMbox = (void*)0; \
			err = SCH_ERR_NONE; \
		} else { \
			mbox.Dly = timeout + 1u; \
			SCH_TimeDly(0); \
			if(timeout==0u) { \
				if(mbox.pMbox==(void*)0) { \
					TaskTcb[CurRunTaskId].TaskDly = 0u; \
					return; \
				} \
				pmsg = mbox.pMbox; \
				mbox.pMbox = (void*)0; \
				err = SCH_ERR_NONE; \
			} else { \
				if(mbox.pMbox!=(void*)0) { \
					pmsg = mbox.pMbox; \
					mbox.pMbox = (void*)0; \
					err = SCH_ERR_NONE; \
				} else { \
					if(mbox.Dly>1u) { \
						mbox.Dly --; \
						TaskTcb[CurRunTaskId].TaskDly = 1u; \
						return; \
					} else if(mbox.Dly==1u) { \
						pmsg = (void*)0; \
						mbox.pMbox = (void*)0; \
						err = SCH_ERR_TIMEOUT; \
					} \
				} \
			} \
		} \
    } while(0) 

//@ 发出一个邮箱
#define SCH_MboxPost(mbox,pmsg) \
    do { \
        mbox.Dly = 0u; \
		mbox.pMbox = (void*)pmsg; \
    } while(0)
	
#endif 
/*------------------------------------------------------------------------------
                             操作指定任务,不常用      
------------------------------------------------------------------------------*/
//挂起(暂停)指定任务
#define SCHTaskPend(TaskPendTCB)      TaskPendTCB.TimeCounter = SCH_TASK_PEND

//恢复指定任务(运行)
#define SCHTaskResume(TaskResumeTCB)  TaskResumeTCB.TimeCounter = SCH_TASK_RUN

//指定任务延时X个时间节拍后恢复
#define SCHTaskDly(TaskDlyTCB, Ticks) TaskDlyTCB.TimeCounter = Ticks

/*------------------------------------------------------------------------------
                             消息队列功能实现 
------------------------------------------------------------------------------*/      
#if SCH_CFG_Q_EN > 0u

#define SCH_Q_FREE  1
#define SCH_Q_BUSY  0

//等待消息
#define SCHTaskQpend() {_lc=(__LINE__%65535);pCurTCB->TaskDly = SCH_TASK_PEND;pCurTCB->pData=(void *)0;pCurTCB->Size=0;}return;case (__LINE__%65535):

//释放消息
#define SCHTaskQpost(PostTCB, pDat, Len)  PostTCB.pData = pDat; PostTCB.Size = Len; PostTCB.TaskDly = SCH_TASK_RUN
                        
//查询消息列队状态,是否是*(可用)或忙(不可用),
//调用SCHTaskQpend()时会将其设置为*状态
#define SCHTaskGetQFree(TaskTCB, RetStatus)  RetStatus = SCH_Q_BUSY; if (TaskTCB.TaskDly == SCH_TASK_PEND){RetStatus = SCH_Q_FREE;}
#endif
//------------------------------------------------------------------------------


/*------------------------------------------------------------------------------
                                外部变量声明
------------------------------------------------------------------------------*/
extern SCH_TCB   *pCurTCB;	
/*------------------------------------------------------------------------------
                                  函数申明
------------------------------------------------------------------------------*/
void SCH_Init(void);
void SCH_TimeTick(void);

#ifdef __cplusplus
}
#endif

#endif	

shcedule.c

#define  SCH_GLOBALS

#include  "schedule.h"

SCH_TCB  *pCurTCB;

/*------------------------------------------------------------------------------
** 函数名:	SCH_Init
** 输  入:	无
** 输  出:	无
** 功能说明:调度器初始化函数,任务运行前调用
**----------------------------------------------------------------------------*/
void SCH_Init(void)	
{ 
	INT8U i; 
	
	CurRunTaskId = 0;
    
	for(i=SCH_MAX_TASKS;i>0;i--) 
    { 
		TaskTcb[i-1].TaskDly = 0; 
		
        TaskTcb[i-1].SubExitFlag = 0;
	} 
}
/*------------------------------------------------------------------------------
** 函数名:	SCH_TimeTick
** 输  入:	无
** 输  出:	无
** 功能说明:调度器时钟节拍函数,在定时中断中调用
**----------------------------------------------------------------------------*/
void SCH_TimeTick(void)	
{ 
	INT8U i; 
	for(i=SCH_MAX_TASKS;i>0;i--) 
    {       
        if(TaskTcb[i-1].TaskDly>0) 
        {     
            //任务延时时间处理
            TaskTcb[i-1].TaskDly --; 
        } 
	} 
}
/*-----------------------------End Of File------------------------------------*/

具体代码就不再解释,可以结合源码注释自行研究理解。

应用

基于STM32F103系列单片机,展示实际应用。

包含头文件

schedule.h

定时器调用

定时器调用任务轮询函数

/*
定时器可设置为10ms周期,或其他
*/
void TIM2_IRQHandler(void)
{ 		    		  			    
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    SCH_TimeTick(); 
}

创建任务

/*******************************************************************************
 * 函数名称:Thread_InitMain
 * 函数功能:主线程
 * 入口参数:无
 * 返 回 值:无
 * 其他说明:无
*******************************************************************************/
void Thread_InitMain(void)
{
    SCH_TaskBegin(); //注意这里
    while(1) //注意这是死循环
    {
        SCH_TimeDly(150); //这是这个任务的演示函数,单位为定时器定时周期,如果
        					//如果定时器定时周期为10ms,则此处延时为150*10
    }
    SCH_TaskEnd(); //注意这里
}
/*******************************************************************************
 * 函数名称:Thread_Menu
 * 函数功能:菜单线程
 * 入口参数:无
 * 返 回 值:无
 * 其他说明:无
*******************************************************************************/
void Thread_Menu(void)
{
    SCH_TaskBegin();
    while(1)
    {
        KeyValue = Scan_key();
        if(KeyValue.key != 0)
        {
            QueuePushIn(&KeyValue.key, 1);
        } 
        Menu();
        SCH_TimeDly(100);
    }
    SCH_TaskEnd();
}
/*******************************************************************************

任务初始化

	SCH_Init(); //注意这里哦,先初始化
    Time2_Init(); //注意这里哦,再初始化
    
    //SEGGER_RTT_Init();
    
    Init_V9203();                 //初始化V9203
    UserData_Init();
    InitQueue(&stQueue);
    
    ClearScreen();
    DisplayFrequency = DISPLAY_FREQUENCY;
    while (1)
    {	
        SCH_TaskRun(Thread_InitMain, 0);
        SCH_TaskRun(Thread_Menu,    1);     
    }

任务调用

int main(void)
{

    SCH_Init();
    Time2_Init();
    while (1)
    {	
        SCH_TaskRun(Thread_InitMain, 0);  //这里入口参数的第二个参数为任务id,
        //id不能重复
        SCH_TaskRun(Thread_Menu,    1);     
    }
}

Schedule资源下载

上一篇:A1080 Graduate Admission (30 分)


下一篇:PAT甲级1141 PAT Ranking of Institutions :[C++题解]结构体、排序、哈希表、结构体构造函数、结构体内写函数、排名