开源:类OS的调度器
此调度器,结合本论坛各位高手的代码,及Protothreads,加上自己的理解,及实际在项目中的使用情况不断修改而成,在这里要感谢各位坛友的代码;
本调度器共3个文件
schedule.h
schedule.c
sch_cfg.h
本调度器特点:
1,仿OS编程方式
2,代码优化等级不能过高
3,适用于前后台及OS的系统中
4,占用一个CPU一个定时器
5,支持信号量
6,支持邮箱
代码如下:
schedule.h
#ifndef _SCH_H
#define _SCH_H
#ifdef __cplusplus
extern "C" {
#endif
/**********************************************************************************************************
* schedule 版本号
**********************************************************************************************************/
#defineSCH_VERSION 02100u
/**********************************************************************************************************
* 包含头文件
**********************************************************************************************************/
#include <sch_cfg.h>
#ifndefSCH_GLOBALS
#defineSCH_EXTextern
#else
#defineSCH_EXT
#endif
/**********************************************************************************************************
* 数据结构定义
**********************************************************************************************************/
typedef struct { //任务TCB数据类型定义
SCH_TICKS TaskDly;
INT8U SpawnExitFlag;
} SCH_TCB;
typedef SCH_TICKS SEM; //信号量数据类型定义
typedef struct { //邮箱数据类型定义
SCH_TICKS Dly;
void *pMbox;
} MBOX;
/**********************************************************************************************************
* 全局变量定义
**********************************************************************************************************/
SCH_EXT SCH_TCB TaskTcb; //任务状态数据结构
SCH_EXT INT8U CurRunTaskId; //当前运行任务的ID号
/**********************************************************************************************************
* 系统状态定义
**********************************************************************************************************/
#define SCH_ERR_NONE 0u
#define SCH_ERR_TIMEOUT 10u
/**************************************************************************************************************
************任务相关宏
【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.TaskDly==0) { \
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;TaskTcb.SpawnExitFlag=1u;switch(_lc){case 0u:
//@子任务结束宏,在子任务函数尾引用
#define SCH_SubTaskEnd() }_lc=0;TaskTcb.SpawnExitFlag=0u;return
//@子任务调用宏,在任务中调用,允许多层嵌套
//@ SubTaskName:子任务名
//@ p_arg:子线程函数输入参数
#define SCH_CallSubTask(SubTaskFun) \
while(1) { \
_lc=(__LINE__%65535);return;case(__LINE__%65535): { \
SubTaskFun; \
} \
if(TaskTcb.SpawnExitFlag==0u) { \
break; \
} \
}
/**************************************************************************************************************
************延时宏定义
**************************************************************************************************************/
//@延时宏,在任务开始宏和结束宏之间的任意位置引用;
//@【注】由于此调度器运用了C语言的行号的宏代码,因此此宏定义最好放在一行代码中,不能拆分此宏定义,防止行号返回出错
//@ ticks:延时时间
#define SCH_TimeDly(ticks) do{_lc=(__LINE__%65535);TaskTcb.TaskDly=ticks;return;}while(0);case (__LINE__%65535):
/**************************************************************************************************************
************信号量相关调用宏定义
**************************************************************************************************************/
//@信号量创建
#define SCH_SemCreate(Sem) Sem = 1
//@w信号量等待
//@ sem--等待信号量;
//@ timeout--等待超时时间,0--直到成功等待信号量;
//@ err--等待信号状态,SCH_ERR_NONE-等待成功,SCH_ERR_TIMEOUT-等待超时
#define SCH_SemPend(sem,timeout,err) \
do { \
if(sem==0) { \
sem = 1; \
err = SCH_ERR_NONE; \
} else { \
sem = timeout + 1; \
SCH_TimeDly(0); \
if(timeout==0) { \
if(sem>0) { \
TaskTcb.TaskDly = 0; \
return; \
} \
sem = 1; \
err = SCH_ERR_NONE; \
} else { \
if(sem>1) { \
sem --; \
TaskTcb.TaskDly = 1; \
return; \
} else if(sem==1) { \
err = SCH_ERR_TIMEOUT; \
} else { \
sem = 1; \
err = SCH_ERR_NONE; \
} \
} \
} \
} while(0)
//@ 发出一个信号量
#define SCH_SemPost(Sem) \
do { \
Sem = 0; \
} while(0)
/**************************************************************************************************************
************邮箱相关调用宏定义
**************************************************************************************************************/
#if SCH_MBOX_EN > 0u
//@创建邮箱
#define SCH_MboxCreate(mbox) do{mbox.Dly = 1;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 + 1; \
SCH_TimeDly(0); \
if(timeout==0) { \
if(mbox.pMbox==(void*)0) { \
TaskTcb.TaskDly = 0; \
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>1) { \
mbox.Dly --; \
TaskTcb.TaskDly = 1; \
return; \
} else if(mbox.Dly==1) { \
pmsg = (void*)0; \
mbox.pMbox = (void*)0; \
err = SCH_ERR_TIMEOUT; \
} \
} \
} \
} \
} while(0)
//@ 发出一个邮箱
#define SCH_MobxPost(mbox,pmsg) \
do { \
mbox.Dly = 0; \
mbox.pMbox = (void*)pmsg; \
} while(0)
#endif
/*************************************
函数申明
*************************************/
void SCH_Init(void);
void SCH_TimeTick(void);
#endif
schedule.c
#defineSCH_GLOBALS
#include<schedule.h>
/********************************************************************************************************
** 函数名: SCH_Init
** 输入: 无
** 输出: 无
** 功能说明:调度器初始化函数,任务运行前调用
*********************************************************************************************************/
void SCH_Init(void)
{
INT8U i;
CurRunTaskId = 0;
for(i=SCH_MAX_TASKS;i>0;i--) {
TaskTcb.TaskDly = 0;
TaskTcb.SpawnExitFlag = 0;
}
}
/********************************************************************************************************
** 函数名: SCH_TimeTick
** 输入: 无
** 输出: 无
** 功能说明:调度器时钟节拍函数,在定时中断中调用
*********************************************************************************************************/
void SCH_TimeTick(void)
{
INT8U i;
for(i=SCH_MAX_TASKS;i>0;i--) {
if(TaskTcb.TaskDly>0) { //任务延时时间处理
TaskTcb.TaskDly --;
}
}
}
sch_cfg.h
#ifndef SCH_CFG_H
#define SCH_CFG_H
#define SCH_MAX_TASKS 10 //最大任务数量,可以增加,最大255个
#define SCH_MBOX_EN 1 // Enable (1) or Disable (0) 邮箱
#define SCH_TICKS_PER_SEC 1000 //1秒钟多少个系统时钟数
/**********************************************************************************************************
* 与编译器相关数据类型定义
**********************************************************************************************************/
typedef unsigned charBOOLEAN;
typedef unsigned charINT8U; /* Unsigned8 bit quantity */
typedef signed charINT8S; /* 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; //定义延时时钟数据类型
#endif
测试代码
#include "includes.h"
#include "app_cfg.h"
SEM Sem1,Sem2; //
MBOX mbox1,mbox2;
/********************************************************************************************************
** 函数名: App_FunSemCreate
** 输入:
** 输出:
** 功能说明:信号量创建函数
*********************************************************************************************************/
static void App_FunSemCreate (void)
{
INT8U os_err;
SCH_SemCreate(Sem1);
SCH_SemCreate(Sem2);
SCH_MboxCreate(mbox1);
SCH_MboxCreate(mbox2);
}
void Ledred(void)
{
SCH_TaskBegin();
while(1) {
LEDOn(RED_LED);
SCH_TimeDly(SCH_TICKS_PER_SEC/2);
LEDOff(RED_LED);
SCH_TimeDly(SCH_TICKS_PER_SEC/2);
}
SCH_TaskEnd();
}
void Ledgreen(void)
{
SCH_TaskBegin();
while(1) {
LEDOff(GREEN_LED);
SCH_TimeDly(SCH_TICKS_PER_SEC/2);
LEDOn(GREEN_LED);
SCH_TimeDly(SCH_TICKS_PER_SEC/2);
}
SCH_TaskEnd();
}
void Delay(void)
{
static uint8_t i;
SCH_TaskBegin();
while(1) {
for(i=0;i<5;i++) {
printf("Delay Cnt = %d\r\n",i);
SCH_TimeDly(SCH_TICKS_PER_SEC/2);
}
printf("Delay exit\r\n");
}
SCH_TaskEnd();
}
void Task_Sem1Test(void)
{
uint8_t err;
SCH_TaskBegin();
while(1) {
printf("Sem1 等待置位...\r\n");
SCH_SemPend(Sem1,0,err);
printf("Sem1 等待成功...\r\n");
}
SCH_TaskEnd();
}
void Task_Sem2Test(void)
{
uint8_t err;
SCH_TaskBegin();
while(1) {
printf("Sem2 等待置位,1秒钟超时...\r\n");
SCH_SemPend(Sem2,SCH_TICKS_PER_SEC*10,err);
if(err==SCH_ERR_TIMEOUT) {
printf("【Sem2】 等待超时...\r\n");
} else {
printf("Sem2 等待成功...\r\n");
}
}
SCH_TaskEnd();
}
void Task_Mbox1Test(void)
{
uint8_t err;
char *p;
SCH_TaskBegin();
while(1) {
printf("mbox1 等待置位,1秒超时...\r\n");
SCH_MboxPend(mbox1,p,SCH_TICKS_PER_SEC*5,err);
if(err==SCH_ERR_TIMEOUT) {
printf("【mbox1】 等待超时...\r\n");
} else {
printf("mbox1 等待成功...\r\n");
printf("mbox=[%s]\r\n",p);
}
}
SCH_TaskEnd();
}
void Task_Swap(void *p_arg)
{
static uint8_t i;
SCH_SubTaskBegin();
for(i=0;i<5;i++) {
printf("swap cnt = %d\r\n",i);
SCH_TimeDly(SCH_TICKS_PER_SEC*2);
}
printf("Task_Swap Exit\r\n");
SCH_SubTaskEnd();
}
void Task_Swap1(void *p_arg)
{
static uint8_t i;
uint8_t xx;
SCH_SubTaskBegin();
//SCH_CallSubTask(Task_Swap(NULL));
printf("swap exit \r\n");
for(i=0;i<5;i++) {
printf("swap1 cnt = %d\r\n",i);
SCH_TimeDly(SCH_TICKS_PER_SEC*2);
}
printf("Task_Swap1 Exit\r\n");
SCH_SubTaskEnd();
}
void Task_SwapTest(void)
{
uint8_t err;
void *p;
int test;
SCH_TaskBegin();
while(1) {
printf("子任务调用测试...\r\n");
SCH_CallSubTask(Task_Swap1(NULL));
printf("子任务调用测试完成,延时2秒...\r\n");
SCH_TimeDly(SCH_TICKS_PER_SEC*2);
}
SCH_TaskEnd();
}
/********************************************************************************************************
** 函数名:main
** 输入:
** 输出:
** 功能说明:main函数
*********************************************************************************************************/
int main (void)
{
uint32_t i = 0;
uint32_t ConnectTime;
uint8_t AppDownload = FALSE;
uint8_t buf;
uint32_t Test = 0,Test2 = 0;
uint8_t ConnectStatus = FALSE;
char GetKey;
/*=========初始化核心代码=========*/
SCH_Init(); //调度器初始化
//初始化全局应用变量
GlobalTime = 998; //计数器
App_FunSemCreate();
BSP_GPIOConfiguration(); //GPIO配置
BSP_CpuPeriphInit(); //CPU相关硬件初始化
BSP_WDT_HardInit(); //看门狗初始化
LEDOff(GREEN_LED);
LEDOff(RED_LED);
BSP_WDTHardReset();
while(1) {
SCH_TaskRun(Ledred,0);
//SCH_TaskRun(Delay,1);
//SCH_TaskRun(Task_Sem1Test,2);
//SCH_TaskRun(Task_Sem2Test,3);
//SCH_TaskRun(Task_Mbox1Test,4);
Task_Swap(NULL);
SCH_TaskRun(Task_SwapTest,4);
GetKey = _getkey();
if(GetKey!=-1) {
if(GetKey=='x') {
printf("Test Sem1\r\n");
SCH_SemPost(Sem1);
} else if(GetKey=='y') {
printf("Test Sem2\r\n");
SCH_SemPost(Sem2);
} else if(GetKey=='m') {
printf("Test mobx1\r\n");
sprintf((char*)buf,"Test mobx1\r\n");
SCH_MobxPost(mbox1,buf);
}
}
}
}
/**
* @briefReports the name of the source file and the source line number
* where the assert_param error has occurred.
* @paramfile: pointer to the source file name
* @paramline: assert_param error line source number
* @retval None
*/
#ifdefUSE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
static char str1;
static uint32_t temp;
memcpy((void*)str1,(const void*)file,strlen((const char*)file));
temp = line;
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
//my_printf("Worng parameters value:file%s on line %d\r\n",file,line);
/* Infinite loop */
while(1) {
}
}
#endif
/*************************************************end of file********************************************/
使用说明:
1,定时器中调用函数:SCH_TimeTick()
2,main函数开关调用SCH_Init()
=========================分割线==========================================
错误不合理之处,望大家指正,修改,一起完善。
占楼 顶起,楼主能否解释下思路? 这个宏用的,飞起啊~~~ mark
好好拜读下 也不能使用 switch语句? mark!!!楼主讲一讲思路嘛.... 支持LZ不断改进,造福坛友. 不错
学习学习 支持一下,好像是Protothreads修改的。 解说一下呗, 能否提供完整的工程文件啊,谢谢了啊 太复杂了,看到这么多宏,头大了! mark! 学习了。。 这个OS很适用,顶顶顶!{:smile:} 一个思路,学习了............. 很牛!!!试用一下看看 mark一下 能否提供完整的工程文件啊,谢谢了啊 不知道,1楼的帖子怎么编辑,现将最新版本的程序给出
app.c为测试例子 tabc_123 发表于 2015-5-18 10:23
不知道,1楼的帖子怎么编辑,现将最新版本的程序给出
LZ升级扩展得不错, 感谢分享, 有时间试试 好帖子,赞一个 研究一下调度器 谢谢楼主, 学习了. mark'dddff 请问一下,这个调度器是不是没有优先级了,哪位明白的,给说一下,谢谢。 zhuzi1441 发表于 2015-7-21 15:38
请问一下,这个调度器是不是没有优先级了,哪位明白的,给说一下,谢谢。 ...
还是有优先级的,而且任务的优先级不允许相同 非常感谢楼主的回复,有穵试月子,感觉很不错。 mark,谢了 2015最新版SCH_TimeDly(Ticks) MPLAB X + XC8环境 编译报错:constant expression required zd0305 发表于 2015-7-27 15:39
2015最新版SCH_TimeDly(Ticks) MPLAB X + XC8环境 编译报错:constant expression required
我这没有这种编译器,无法测试,你在仔细研究一下,如果问题解决,希望你说下解决方法 经测试:
#define SCH_TimeDly(ticks) do{_lc=(__LINE__%65535);TaskTcb.TaskDly=ticks;TaskTcb.SubExitFlag=1u;return;}while(0);case (__LINE__%65535):
中的case (__LINE__%65535):这一语句编译不过,错误提示:case 语句需要常量;
临时解决办法:
#define SCH_TimeDly(ticks) do{_lc=(__LINE__);TaskTcb.TaskDly=ticks;TaskTcb.SubExitFlag=1u;return;}while(0);case (__LINE__):
楼主,这个调度器不支持高优先级抢占,这样高优先级的任务实时性不能保证吧~ zd0305 发表于 2015-7-28 10:40
经测试:
#define SCH_TimeDly(ticks) do{_lc=(__LINE__%65535);TaskTcb.TaskDly=ticks;Tas ...
可能是编译器不一样,应该这种表达示是在预编译时处理的,可能你的编译器是将此类表达示放在程序运行中处理的,这是我的理解 qiguibao 发表于 2015-8-2 09:51
楼主,这个调度器不支持高优先级抢占,这样高优先级的任务实时性不能保证吧~ ...
这种调度器与RTOS是有区别的,不能进行抢占,只能说任务编号越小,提前执行而已 这个调度器很好用。{:smile:} eddia2012 发表于 2015-8-13 08:14
这个调度器很好用。
谢谢使用,使用过程中有任何问题,请指出 mark,又是一个关于timer定时器的 有时间了再来验证,谢谢分享~ 已经经过修改用在小产品上了!谢谢楼主的开源! luozhongchao123 发表于 2015-8-16 16:07
已经经过修改用在小产品上了!谢谢楼主的开源!
能否分开修改部分的代码,一起将此代码修改得更加完善。。。。。 楼主,求一个stm8完整工程。 wxlcj 发表于 2015-8-30 22:54
楼主,求一个stm8完整工程。
前面的例子,不用CPU的,直接可以使用 不错,学习 很好,支持 感谢分享,好东西 {:smile:}借用楼主的思想,已用在好多工程中了。 好好学习楼主的思想试试看 tabc_123 发表于 2015-5-18 10:23
不知道,1楼的帖子怎么编辑,现将最新版本的程序给出
能否将getkey()函数发上来,学习你是怎么写的。{:smile:}
我以前都是状态机方式。 本帖最后由 tabc_123 于 2016-9-7 09:14 编辑
rube 发表于 2016-9-6 14:31
能否将getkey()函数发上来,学习你是怎么写的。
我以前都是状态机方式。 ...
我理解错了,getkey()只是一个串口数据接收函数
tabc_123 发表于 2016-9-7 09:03
我理解错了,getkey()只是一个串口数据接收函数
哦,谢谢 学习一个,纯C的调度器太巧妙了,使用调度器管理可以极大地减少阻塞 支持创新 学习一下,谢谢!@! {:smile:},谢谢分享;学习了 该类OS还有维护吗,可否用于产品上? 王二小 发表于 2024-6-28 09:47
该类OS还有维护吗,可否用于产品上?
(引用自56楼)
无维护,喜欢可以用于产品上。 本帖最后由 王二小 于 2024-7-2 20:15 编辑
summarize 发表于 2024-6-28 19:39
无维护,喜欢可以用于产品上。
(引用自57楼)
大神,最近有用你的那个类OS做产品,有遇到问题,可以跟你沟通吗?VX:stm3206 王二小 发表于 2024-7-2 20:13
大神,最近有用你的那个类OS做产品,有遇到问题,可以跟你沟通吗?VX:stm3206 ...
(引用自58楼)
可以把这个头文件做成库吗,我想加密
页:
[1]