适用于小资源的小小调度器程序(原创)
发布这个小小调度器主要起个抛砖引玉的作用。自认为有如下特点:
1)超级可以移植性,与CPU无关,几乎任何支持C语言编程的CPU都可以用!
2)小之又小, 原理很简单,一看就懂。
3)省之又省, 可以说对RAM和ROM省到极致。
4)取protothread之精华,将定时器与状态机和伪线程语法融合到一个框架,任务函数可以有两种写法。
5)基于定时器触发,调度效率高,最大化减少无效的代码运行时间。
***********************************************************/
#include <stc89c51.h>
#include <stdio.h>
/*****************小小调度器部分开始********************************************/
#define_SS static char lc=0; switch(lc){ case 0: lc=0;
#define_EE }; lc=0;
#defineWaitX(a,b)settimer(&lc,__LINE__,a,b); return ; case __LINE__:
struct TASK {
char td;
void (*fp)();
};
#define MAXTASKS 5
struct TASK tasks;
//设置定时器
void settimer(char *lc,charline,chartmrid,int d){
*lc=line;
tasks.td=d;
}
//逻辑定时器处理,在定时器中断里调用
void dectimers() {
unsigned char i;
for (i=0;i<MAXTASKS;i++){
if (tasks.td>0)tasks.td--;
}
}
//任务调度函数,在main里面运行
void runtasks() {
unsigned char i;
for(i=0;i<MAXTASKS;i++)
{
if (tasks.fp!=0){
if (tasks.td==0){
tasks.td=-1;
tasks.fp();
}
}
}
}
/****************小小调度器部分结束*******************************************************/
sbit KEY = P3^2;
unsigned char code numtab={0x24,0x6F,0xE0,0x62,0x2B,0x32,0x30,0x67,0x20,0x22,0x21,0x38,0xB4,0x68,0xB0,0xB1};
sfr IAP_CONTR = 0xC7;
sfr WDT_CONTR = 0xC1;
//清除看门狗
void clr_wdt()
{
WDT_CONTR =0x3C;
}
//初始化定时器
void InitT0()
{
TMOD = 0x21;
IE |= 0x82;// 12t
TL0=0Xff;
TH0=0Xb7;
TR0 = 1;
}
//定时器中断
void INTT0(void) interrupt 1 using 1
{
TL0=0Xff; //10ms 重装
TH0=0Xb7;
dectimers();
}
sbit LED1= P2^4;
//任务一,状态机写法
void ontimer0(){
LED1=!LED1;// LED1引脚接在发光管负极,LED1=0 为亮,LED1=1为灭。
//重装定时器
if (LED1) tasks.td=45;//450mS 灭
else tasks.td=5;//50ms亮
}
//任务二,状态机写法
char keycount=0;
void task1(){
if(KEY==0) {
keycount++;
if (keycount>20) IAP_CONTR = 0x60;//持续按下键1秒,将重启并进入固件升级
}
else{
keycount=0;
}
//重装定时器
tasks.td=5;
}
//任务三,伪线程写法
voidtask2()
{
static char i;
_SS
while(1){
for(i=0;i<=9;i++){ //从0--9快速显示,间隔200mS
WaitX(2,20); // 等待200mS,实际是设置定时器2为200mS
P1=numtab;
}
for(i=0;i<=9;i++){ //从0--9慢速显示,间隔500mS
WaitX(2,50); // 等待500mS,实际是设置定时器2为500mS
P1=numtab;
}
}
_EE
}
void main()
{
unsigned char KeyNum;
P3M0 = 0x00;
P3M1 =0x00;
//WDT_CONTR= 0x00; //关闭看门狗
P1 = 0xff; //关显示
clr_wdt();
InitT0();
KEY =1; //按键IO口
KeyNum=0; //按下次数
//装载任务:
tasks.fp=ontimer0;
tasks.fp=task1;
tasks.fp=task2;
//循环调度
while(1){
runtasks();
clr_wdt();
}
}
真舍得拿出来呀!楼主牛
好人!!!士大夫十分
话不多说,上工程!请验证哈。 {:sweat:}楼主一帖两发是违规的哦 哦,抱歉,我以为两个单片机都需要呢,呵呵。不好意思啦。请版主高抬贵手。
想法不错,可是不太适合在51下用,ARM平台应该不错。
MARK一下,谢谢啦!~ 替换之后还是不怎么明白,请明白的帮忙解释解释。
//任务三,伪线程写法
voidtask2()
{
static char i;
static char lc=0;
switch(lc){
case 0: lc=0;
while(1){
for(i=0;i<=9;i++){ //从0--9快速显示,间隔200mS
//WaitX(2,20);
settimer(&lc,__LINE__,2,20); return ; case __LINE__:
P1=numtab;
}
for(i=0;i<=9;i++){ //从0--9慢速显示,间隔500mS
//WaitX(2,50);
settimer(&lc,__LINE__,2,50); return ; case __LINE__:
P1=numtab;
}
}
};
lc=0;
} 这个就是一个状态机,只是以行号__LINE__作为状态变量而已。
在切换时,先保存当前行号__LINE__作为状态变量,并设置下次调用本状态机函数的时刻。
下次调用本函数时,直接跳至改行号__LINE__处,再往下运行。 没太看懂,这个和protothread有关系么? aworker 发表于 2012-11-27 11:19 static/image/common/back.gif
没太看懂,这个和protothread有关系么?
取其精髓而已,看来你还没有真正理解protothread 本帖最后由 aworker 于 2012-11-27 13:41 编辑
smset 发表于 2012-11-27 13:21 static/image/common/back.gif
取其精髓而已,看来你还没有真正理解protothread
__LINE__+switch,似乎就是protothread的骨髓。你写的似乎不够“简明”,还不如直接用protothread好些。 {:lol:}你可以再对比下,看看那种用法更简明,不过我一直很崇拜protothread的作者,是他创造了这种写法,我只是应用其思想而已。 mark!学习了!
PT_INIT(pt) 初始化任务变量,只在初始化函数中执行一次就行
PT_BEGIN(pt) 启动任务处理,放在函数开始处
PT_END(pt) 结束任务,放在函数的最后
PT_WAIT_UNTIL(pt, condition) 等待某个条件(条件可以为时钟或其它变量,IO等)成立,否则直接退出本函数,下一次进入本 函数就直接跳到这个地方判断
PT_WAIT_WHILE(pt, cond)和上面一个一样,只是条件取反了
PT_WAIT_THREAD(pt, thread) 等待一个子任务执行完成
PT_SPAWN(pt, child, thread) 新建一个子任务,并等待其执行完退出
PT_RESTART(pt) 重新启动某个任务执行
PT_EXIT(pt) 任务后面的部分不执行,直接退出重新执行
PT_YIELD(pt) 锁死任务
PT_YIELD_UNTIL(pt, cond) 锁死任务并在等待条件成立,恢复执行
protothread有这些东西做基础,似乎更好理解,更易用。 不错,能再讲透彻点更好了 和这个有什么不一样?
《给51 DIY超轻量级多任务操作系统》
http://www.amobbs.com/forum.php?mod=viewthread&tid=1398508
代码如下:
#include <reg51.h>
#define MAX_TASKS 2 //任务槽个数.必须和实际任务数一至
#define MAX_TASK_DEP 12 //最大栈深.最低不得少于2个,保守值为12.
unsigned char idata task_stack;//任务堆栈.
unsigned char task_id; //当前活动任务号
//任务切换函数(任务调度器)
void task_switch(){
task_sp = SP;
if(++task_id == MAX_TASKS)
task_id = 0;
SP = task_sp;
}
//任务装入函数.将指定的函数(参数1)装入指定(参数2)的任务槽中.如果该槽中原来就有任务,则原任务丢失,但系统本身不会发生错误.
void task_load(unsigned int fn, unsigned char tid){
task_sp = task_stack + 1;
task_stack = (unsigned int)fn & 0xff;
task_stack = (unsigned int)fn >> 8;
}
//从指定的任务开始运行任务调度.调用该宏后,将永不返回.
#define os_start(tid) {task_id = tid,SP = task_sp;return;}
/*============================以下为测试代码============================*/
void task1(){
static unsigned char i;
while(1){
i++;
task_switch();//编译后在这里打上断点
}
}
void task2(){
static unsigned char j;
while(1){
j+=2;
task_switch();//编译后在这里打上断点
}
}
void main(){
//这里装载了两个任务,因此在定义MAX_TASKS时也必须定义为2
task_load(task1, 0);//将task1函数装入0号槽
task_load(task2, 1);//将task2函数装入1号槽
os_start(0);
}
gaoyichuan000 发表于 2012-11-27 20:55 static/image/common/back.gif
和这个有什么不一样?
《给51 DIY超轻量级多任务操作系统》
http://www.amobbs.com/forum.php?mod=viewthre ...
表面上很相似哈,实际上有很多不同,
1)本调度系统与单片机无关。并非51专用。
2)本调度系统每个任务占3个字节,后续将有新版本,在lgt那边会放出。新版本是每个任务1-2字节ram.
3)本调度系统不涉及堆栈操作。所有任务公用一个堆栈空间。
好贴,慢慢看! 收藏啦~~ 本帖最后由 wxlcj 于 2012-11-27 22:44 编辑
能不能加上休眠
改到stm32要注意什么? {:smile:}WatiX()就是休眠啊, 改stm32没啥特殊的,只要能产生定时中断就行。 先收藏,学习一下! smset 发表于 2012-11-27 22:50 static/image/common/back.gif
WatiX()就是休眠啊, 改stm32没啥特殊的,只要能产生定时中断就行。
你是说STM32的SysTick定时器吗? 留个足印,以后学习学习。。。{:smile:}{:smile:} MARk 一下 {:smile:}不错哦,呵呵。 标记下!慢慢看! mark!学习了! Mark 调度器
这个是不是发俩贴了啊 强悍,学习了! 本帖最后由 fnems 于 2012-12-6 21:01 编辑
牛!
Wait配合伪线程这个结构LZ怎么想出来的……
能不能再加入事件等待呢?这样就更完美了,因为线程block有时候是等待时间,有时候是等待事件
另外感觉可以在任务中加入this指针全局变量,切换任务的时候顺便切换this,this指向的结构体可以包含任务号的信息,这样wait函数中不用指定任务号了。
还有,以前我用STM8状态机的时候,主循环喜欢加上WFI,类似这样的结构:#define WFI _asm("wfi")
main(void) {
init();
while(1) {
switch(state) {
case STATE_INIT:
case STATE_A:
case STATE_B:
} /* end switch */
WFI;
} /* end while */
} 本帖最后由 smset 于 2012-12-7 09:55 编辑
新版本已无需指定定时器的。
/**********************************************************/
#include "STC89C51.h"
/****小小调度器开始**********************************************/
#define MAXTASKS 3
unsigned char currid;
unsigned char timers;
#define _SS static unsigned char lc=0; switch(lc){ case 0:
#define _EE ;}; lc=0;
#define WaitX(b) {lc=__LINE__; timers=b;} return ; case __LINE__:
#define RunTask(a,b) {currid=b; if (timers==0){timers=255; a();}}
#define CallSub(x) WaitX(0); x(); if (timers!=255) return;
/*****小小调度器结束*******************************************************/
sbit LED1 = P2^1;
sbit LED2 = P2^2;
void InitT0()
{
TMOD = 0x21;
IE |= 0x82;// 12t
TL0=0Xff;
TH0=0XDB;//22M---b7;
TR0 = 1;
}
void INTT0(void) interrupt 1 using 1
{
unsigned char i;
TL0=0Xff; //10ms 重装
TH0=0XDB;//b7;
for (i=0;i<MAXTASKS;i++){
if ((timers!=0)&&(timers!=255)) {
timers--;
}
}
}
voidtask1(){
_SS
while(1){
WaitX(100);
LED1=!LED1;
}
_EE
}
voidtask2(){
_SS
while(1){
WaitX(100);
LED2=!LED2;
}
_EE
}
void main()
{
InitT0();
while(1){
RunTask(task1,1);
RunTask(task2,2);
}
}
好贴,慢慢学习~~ 不错,慢慢学习 本帖最后由 modelsim 于 2012-12-7 14:00 编辑
settimer(&lc,__LINE__,500);return;case __LINE__:
void settimer(char *lc,char line,unsigned char d)
{
*lc=line;
timers=d;
}
这段代码没看懂,请楼主讲解下,那个_LINE_宏有什么用,为什么要加入这个,如何能去掉 modelsim 发表于 2012-12-7 13:51 static/image/common/back.gif
settimer(&lc,__LINE__,500);return;case __LINE__:
void settimer(char *lc,char line,unsigned char d)
请直接看34楼的新版本 后面的怎么看不到了。 谢谢楼主....... 谢谢楼主 好东西,标记下了~ {:sad:}看看先~ 现在还不算很懂慢慢学习!!! kk 打算移植到合泰的芯片上HT45F232KROM 128RAM smset 发表于 2012-11-27 11:13
这个就是一个状态机,只是以行号__LINE__作为状态变量而已。
在切换时,先保存当前行号__LINE__作为状态变 ...
__LINE__这个值会不会超过一个char 即lc 类型值呢??? 请教 学习一下!!! mark mark mark 这种实时性效率咋样? smset 发表于 2012-12-7 00:08
新版本已无需指定定时器的。
楼主,感觉任务展开的逻辑不太对啊
RunTask(task1,1);展开如下:
currid = 1;
if (timers == 0)
{
timers = 255;
static unsigned char lc = 0;
switch (lc)
{
case 0:
while(1){
lc = __LINE__;
timers = 100;
return ;
case __LINE__:
LED1=!LED1;
}
;};
lc=0;
}
应该把task里面的while(1)去掉
void task1(){
_SS
//while(1){
WaitX(100);
LED1=!LED1;
// }
_EE
} 感谢分享。 谢谢,思路不错 我记得有个群的,找不见,怎么能加回来?
页:
[1]