再出个调度器,极小资源单片机值得一用
2019-04-15 18:27发布
生成海报
再出个调度器,极小资源单片机值得一用 [打印本页]
作者: smset 时间: 2012-11-26 11:41标题: 再出个调度器,极小资源单片机值得一用自认为有如下特点:1) 超级可以移植性,与CPU无关,几乎任何支持C语言编程的CPU都可以用!(本文仅仅以51单片机为例而已,但实际上可以任意移植)2) 小之又小, 原理很简单,一看就懂。3) 省之又省, 可以说对RAM和ROM省到极致。4) 取protothread之精华,将定时器与状态机和伪线程语法融合到一个框架,任务函数可以有两种写法。5) 基于定时器触发,调度效率高,最大化减少无效的代码运行时间。***********************************************************/ #include #include /*****************小小调度器部分开始********************************************/ #define _SS static char lc=0; switch(lc){ case 0: lc=0;#define _EE }; lc=0; #define WaitX(a,b) settimer(&lc,__LINE__,a,b); return ; case __LINE__:struct TASK { char td; void (*fp)();};#define MAXTASKS 5struct TASK tasks[MAXTASKS];//设置定时器void settimer(char *lc,char line,char tmrid,int d){ *lc=line; tasks[tmrid].td=d;}//逻辑定时器处理,在定时器中断里调用void dectimers() { unsigned char i; for (i=0;i
if (tasks[i].td>0) tasks[i].td--;
}
}
//任务调度函数,在main里面运行
void runtasks() {
unsigned char i;
for(i=0;i
{
if (tasks[i].fp!=0){
if (tasks[i].td==0){
tasks[i].td=-1;
tasks[i].fp();
}
}
}
}
/****************小小调度器部分结束*******************************************************/
sbit KEY = P3^2;
unsigned char code numtab[16]={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[0].td=45; //450mS 灭
else tasks[0].td=5; //50ms 亮
}
//任务二,状态机写法
char keycount=0;
void task1(){
if(KEY==0) {
keycount++;
if (keycount>20) IAP_CONTR = 0x60; //持续按下键1秒,将重启并进入固件升级
}
else{
keycount=0;
}
//重装定时器
tasks[1].td=5;
}
//任务三,伪线程写法
void task2()
{
static char i;
_SS
while(1){
for(i=0;i<=9;i++){ //从0--9快速显示,间隔200mS
WaitX(2,20); // 等待200mS,实际是设置定时器2为200mS
P1=numtab[i];
}
for(i=0;i<=9;i++){ //从0--9慢速显示,间隔500mS
WaitX(2,50); // 等待500mS,实际是设置定时器2为500mS
P1=numtab[i];
}
}
_EE
}
void main()
{
unsigned char KeyNum;
P3M0 = 0x00;
P3M1 =0x00;
//WDT_CONTR= 0x00; //关闭看门狗
P1 = 0xff; //关显示
clr_wdt();
InitT0();
KEY =1; //按键IO口
KeyNum=0; //按下次数
//装载任务:
tasks[0].fp=ontimer0;
tasks[1].fp=task1;
tasks[2].fp=task2;
//循环调度
while(1){
runtasks();
clr_wdt();
}
}
新的修改与优化及限制
-
/****小小调度器开始**********************************************/
-
#define MAXTASKS 2
-
static unsigned char timers[MAXTASKS];
-
unsigned char currdt;
-
#define _SS static unsigned char _lc; switch(_lc){default:
-
#define _EE ;}; _lc=0; return 255;
-
#define WaitX(tickets) do {_lc=__LINE__+((__LINE__%256)==0); return tickets ;} while(0); case __LINE__+((__LINE__%256)==0):
-
#define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0) timers[TaskID]=TaskName(); } while(0);
-
-
#define CallSub(SubTaskName) do { _lc=__LINE__+((__LINE__%256)==0); return 0; case __LINE__+((__LINE__%256)==0): currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
-
#define UpdateTimers() unsigned char i; for(i=MAXTASKS;i>0 ;i--){if((timers[i-1]!=0)&&(timers[i-1]!=255)) timers[i-1]--;}
-
-
#define SEM unsigned int
-
//初始化信号量
-
#define InitSem(sem) sem=0;
-
//等待信号量
-
#define WaitSem(sem) do{ sem=1; WaitX(0); if (sem>0) return 1;} while(0);
-
//等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
-
#define WaitSemX(sem,tickets) do { sem=tickets+1; WaitX(0); if(sem>1){ sem--; return 1;} } while(0);
-
//发送信号量
-
#define SendSem(sem) do {sem=0;} while(0);
-
-
/*****小小调度器结束*******************************************************/
-
-
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
-
{
-
UpdateTimers();
-
-
TL0=0Xff; //10ms 重装
-
TH0=0XDB;//b7;
-
}
-
-
-
void task1(){
-
_SS
-
while(1){
-
WaitX(50);
-
LED1=!LED1;
-
}
-
_EE
-
}
-
-
void task2(){
-
_SS
-
while(1){
-
WaitX(100);
-
LED2=!LED2;
-
}
-
_EE
-
}
-
-
-
void main()
-
{
-
InitT0();
-
while(1){
-
RunTask(task1,0);
-
RunTask(task2,1);
-
}
-
}
-
-
复制代码
在keil下编译,又减少了18字节的ROM(超过10%了)。应该运行效率会更高。
------------------以下为说明-----------------------------------
小小调度器任务函数的写法主要注意的,主要有三点:
1) 任务函数内部变量,建议都用静态局部变量来定义。
2) 任务函数内不能用switch语句。
3) 任务函数内,不能用return语句。 因为return已经被赋予任务延时的特定意义。(这是返回型任务函数版本的一个强制要求)
这三点,并不会明显造成写程序的不方便。
---------------------------
从裸奔到使用OS操作系统或调度系统的代价主要有:
硬件资源代价(对RAM和ROM的消耗),学习代价(学会其原理,并掌握其用法),移植代价(往不同cpu上移植的工作量),效率代价(使用调度系统后带来的额外cpu负担),商业代价(版权费用),稳定性代价(是否引入潜在不稳定因素,或者增大bug跟踪调试工作量)。
从这几方面来讲,应用小小调度器的代价,都是非常小的。
1) 硬件资源代价: 前面的优化版本已经说明问题。keil下,本例程ram消耗 : 22字节,rom消耗126字节.
2) 学习代价: 小小调度器总共只有十多行代码,如果我们做一个简单的解释说明,理解起来其实是很快的。我相信学习时间比其他调度系统要短。
3) 移植代价: 几乎没有什么移植工作量,对于各种cpu,几乎是通吃。
4) 效率代价: 我们一直在努力优化,相信调度效率已经不低了。比如任务切换时间,应该是可以做到uS级别,甚至亚uS级别。
5) 商业代价: 小小本调度器为免费使用,无需支付任何费用。
6) 稳定性代价:小小调度器本质上仅仅是几个宏而已,未涉及任何对内部寄存器或堆栈的操作,避免了引入不稳定风险因素,所有操作都在可预见,可把控的前提下进行。
--------------------------------------------------------------------------------
作者: smset 时间: 2012-11-26 11:42
本帖最后由 smset 于 2012-11-26 11:44 编辑
自己写的,抛砖引玉,希望大家指正哈。本例用于说明调度器的核心思想,当然也可以用,但实际上还可以做得更完善。
作者: tim 时间: 2012-11-26 11:44
先收藏再学习
作者: linucos 时间: 2012-11-26 11:57
多谢lz分享!
作者: tianxian 时间: 2012-11-26 12:16
收藏先,谢谢分享
作者: zqy517 时间: 2012-11-26 12:28
多谢lz分享,学习下思想!
作者: hameyou 时间: 2012-11-26 12:29
看看,学习一下
作者: zhonggp 时间: 2012-11-26 13:05
裸机中的战斗机
作者: smset 时间: 2012-11-26 13:15
本调度器的宗旨是:以最小的代价,实现基于自然语法的多任务并行处理机制。并具备代码的高度可以移植性。
每个任务占用3个字节RAM。任务数量没有限制。
其工作原理很简单,大家可在此基础上任意DIY自己的调度方法。
作者: powermeter 时间: 2012-11-26 13:16
xiexie mark
作者: soosqt 时间: 2012-11-26 13:22
把工程也上传上来观摩下
作者: smset 时间: 2012-11-26 13:25
本帖最后由 smset 于 2012-11-26 13:30 编辑
soosqt 发表于 2012-11-26 13:22
把工程也上传上来观摩下
整个工程只有一个文件 main.c, 上面的就是main.c的完整代码。
呵呵,也许是调度器太小了,连单独的.h文件和c文件都没有做。
作者: dory_m 时间: 2012-11-26 13:26
学习,谢谢!!!
作者: chhh 时间: 2012-11-26 13:27
记号,学习一下
作者: whatcanitbe 时间: 2012-11-26 13:30
#define WaitX(a,b) settimer(&lc,__LINE__,a,b); return ; case __LINE__:
请问这句话如何理解啊?
看不明白
作者: zhonggp 时间: 2012-11-26 13:34
替代就明白了,经典啊!!!
作者: xad74 时间: 2012-11-26 13:41
好了,等会看看
作者: dreampet 时间: 2012-11-26 14:14
非常不错,WaitX的替换很巧妙
作者: lee345 时间: 2012-11-26 14:32
学习参考下
作者: huangdongle 时间: 2012-11-26 14:34
mark!!!
作者: sdyzxue 时间: 2012-11-27 09:13
虽然不是很懂,也是要谢谢lz的无私分享。
作者: duanll 时间: 2012-11-27 09:18
再改改,去掉函数指针,这样在51下能工作得更好。
作者: yijiangshan 时间: 2012-11-27 09:20
什么牛人都有啊。
作者: wmm20031015 时间: 2012-11-27 09:46
不错
作者: smset 时间: 2012-11-27 10:07
本帖最后由 smset 于 2012-11-27 10:41 编辑
duanll 发表于 2012-11-27 09:18
再改改,去掉函数指针,这样在51下能工作得更好。
嗯,是的,可以自己改,无需函数指针也完全可以,这样每个任务只需1到2个字节。
我已经在此基础上,继续优化下一个版本:
已经实现的新增特点:
1) 整个调度器总共占用1个字节RAM。 (是的!只占1个字节的RAM! 实在想不出还有没有比这个更省资源的调度器),调度器本身占用的ROM在200字节以内。
2) 去掉函数指针,一个任务只占用1到2个字节,支持任务内延时函数。顶层任务数量最大为255个。
3) 支持子任务调用。 一个子任务只占用1个字节RAM,子任务内也支持延时函数,并支持子任务函数传递参数。支持多级嵌套子任务调用,子任务数量没有限制。
4) 任务函数语法更精炼,如WaitX(2,20)简化为WaitX(20),无需指定定时器编号。
我的目标是: 打造一个极小单片机均可用的,与CPU硬件型号无关的调度系统,让256字节RAM都显得宽敞!
调度器代码将全公开,全免费使用,让天下“贫民”的单片机都用起,而且用得好! 比裸奔都更省资源!让“贫民”单片机不再裸奔,也无需裸奔!
(该版本将在第100楼放出,希望大家捧场哈)
作者: llysc 时间: 2012-11-27 10:36
收藏学习~~~~~~~
作者: end2000 时间: 2012-11-27 10:42
学习参考
作者: formatme 时间: 2012-11-27 10:59
试试看,不要汇编的移植起来容易多了
作者: xiaoziwen 时间: 2012-11-27 11:05
基于时间片的 轮训调度器
作者: kuki0702 时间: 2012-11-27 12:10
非常不错,学习学习!
作者: elecfun 时间: 2012-11-27 12:14
真够小的,我看我以后我的程序都可以用上了
顶到100楼哈
作者: shotstar 时间: 2012-11-27 12:20
楼主的好样的,我帮顶一下。
作者: hameyou 时间: 2012-11-27 12:30
继续顶起了
作者: ihuotui 时间: 2012-11-27 12:46
有点意思,时间分片任务调度。晚上回去试试。
作者: guzhongqi 时间: 2012-11-27 12:50
跟我写的差不多,但有个缺陷,如果某一个任务超时或死循环了,其他任务就没机会运行了,这是跟实时内核最本质的区别。
作者: oktek 时间: 2012-11-27 12:52
顶到100楼
作者: up101 时间: 2012-11-27 12:54
捧场了,好牛逼的样子
作者: soosqt 时间: 2012-11-27 12:56
smset 发表于 2012-11-27 10:07
嗯,是的,可以自己改,无需函数指针也完全可以,这样每个任务只需1到2个字节。
我已经在此基础上,继续 ...
顶100楼,最好把工程设成可以软件仿真的,上面那个工程软件仿真不成
作者: ustbzm 时间: 2012-11-27 12:56
学习一下!
作者: shdjdq 时间: 2012-11-27 13:09
系统调度任务时间太长,个头也太大。如果小而精,那太好了。
作者: makesoft 时间: 2012-11-27 13:12
一般来说大一点的系统才需要OS,像我们一般是超过1W行源程序的才会考虑OS,小系统做不好的人即使使用OS一样做不好,所以这种小资源的OS基本也就是写个什么论文用用罢了
作者: aworker 时间: 2012-11-27 13:15
不错的,支持一下!
作者: smset 时间: 2012-11-27 13:27
soosqt 发表于 2012-11-27 12:56
顶100楼,最好把工程设成可以软件仿真的,上面那个工程软件仿真不成 ...
这个光荣的任务就交给你吧。最好是基于AVR防真,我对proteus又有段时间没弄,生疏了。
作者: lihaolongli 时间: 2012-11-27 13:28
前边的宏定义看的不大明白
作者: metalmadman 时间: 2012-11-27 13:30
mark
作者: zhwm3064 时间: 2012-11-27 13:
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮