搜索
bottom↓
回复: 77

OS学习初探 --- 时间片轮转调度 (GCC)

[复制链接]

出0入0汤圆

发表于 2006-8-31 11:11:52 | 显示全部楼层 |阅读模式
OS学习初探-时间片轮转调度 (GCC)

作者:zhb2000   时间:2006.08.31



时间片轮转:各任务执行相同的时间片。从宏观上看感觉象是各任务在同时运行。



前言:

    以前就听说有运行在单片机上的操作系统(以下称OS)。由于最开始用的是C51,片内可用资源很少。再加上总算不要用操作系统了 :) 。全部的资源交给了自己,心中觉得这是一片自由的净土,不由的更是热爱。后来又用了AVR,可用资源是比原来大多了。但自己直接用的还算好,就没有考虑过用OS。

    但对OS还是有一种神秘的向往,总想来学习学习,自己写一个简单点的,哪怕只有一两个功能的OS。看看以前别人写的OS,好长一串,由于一时用不到,就没有去深究了。不过今年年初看过ARMOK站长转载的一遍说RTOS的好文,受益非浅。(它的链接是:http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=574348&bbs_page_no=1&bbs_id=1000)自己动手练了练但是还是没有细细的研究。只到今年夏天,朋友帮我买来了ARM7的开发板,我开始写了一点点初级的ARM程序。看到好多搞ARM的在研究各种OS的移植,才有了想写一下OS的冲动。由于用AVR多一点就从AVR的OS入手。由于刚学习OS不久,还是有很多不足的地方,希望能和大家一起进步。

    在网上看了好多OS的文章,下载了好多OS的源码。对它有了初步的了解:主要是利用人工堆栈,结合SP指针对不同的任务进行调度。难点是入栈、出栈及对SP指针的控制。
-----此内容被zhb2000于2006-08-31,12:01:09编辑过

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

 楼主| 发表于 2006-8-31 11:12:43 | 显示全部楼层
学习1:

运行原理:task0任务和task1任务分别通过createTask() 创建任务函数将task0任务和task1任务的地址压入人工堆栈的不同位置。开启定时器0,将SP指针设为task0人工堆栈栈底的位置,弹出运行task0任务。当定时器0溢出发生中断后,先保存当前task0的下一条指令执行地址和相关的寄存器(这一部中断会自己保存),再将SP的指针设为task1人工堆栈栈底的位置,弹出运行task1任务。就这样循环运行。从而能够使两个任务在中断后能够接着原来的指令再次运行。



源程序如下:

/*UT03.C*/

/*

        操作系统学习------堆栈的使用 真正的时间片轮转

        ATMega16L        1MH WinAVR

        作者:zhb2000

        2006.08.30 写到2006.08.31 0:15 才写成

*/

#include "ut03.h"

uchar Stack[STACK_LENGHT];

uchar TASKNUMB=0;                //当前要create的任务

struct _ZS_TASK

{

        uchar ready;        //0 not ready, 1 ready 已执行过一次,2 ready未执行

        uint  taskBottomPoint;        //任务栈底指针

}zsTask[MAXTASK];



SIGNAL(SIG_OVERFLOW0)

{        //计时器0溢出中断

        static uchar n=0;

        zsTask[n].taskBottomPoint=SP;

        if(zsTask[n+1].ready!=0)

        {        //下一个任务

                n++;

                SP=zsTask[n].taskBottomPoint;

                if(zsTask[n].ready==2)

                {

                        zsTask[n].ready=1;

                        popAll();

                        _asm("reti");

                }

        }

        else

        { //第一个任务或只有一个任务

                n=0;

                SP=zsTask[0].taskBottomPoint;

        }

        //_asm("reti");                //现在此句不能要了

}



void init(void);

void task0(void);

void task1(void);

void createTask(void (*proc)(void));

void startTask(void);

int main(void)

{

        init();

        createTask(task0);

        createTask(task1);

        startTask();

        return 0;

}



void init()

{

        PORTA=0xff;

        DDRA=0xff;

        PORTB=0xff;

        DDRB=0xff;

        PORTC=0xff;

        DDRC=0xff;

        PORTD=0xff;

        DDRD=0xff;

        TASKNUMB=0;

}



void createTask(void (*proc)(void))

{        //创建任务

        uchar *_sp;

        uint  old_sp;

        _sp=(uchar*)(&Stack[STACK_LENGHT-1-TASKNUMB*30]);

        *_sp--=(uint)proc;

        *_sp--=(uint)proc>>8;

        old_sp=SP;

        SP=(uint)_sp;

        pushAll();

        zsTask[TASKNUMB].taskBottomPoint=SP;

        zsTask[TASKNUMB].ready=2;

        TASKNUMB++;        //当前任务数加1

        SP=old_sp;

}



void startTask()

{        //开始任务调度

        if(zsTask[0].ready==0) return;        //无任务返回



        TIMSK=(1<<TOIE0);

        TCNT0=0;

        TCCR0=0x2;        //8分频

        sei();



        SP=zsTask[0].taskBottomPoint;

        zsTask[0].ready=1;

        popAll();

        _asm("ret");        //这句不能去掉

}



void task0()

{

        while(1) PORTA++;

}



void task1()

{

        while(1) PORTB++;

}



////////////////////////////////////////////////////

/* UT03.H */

#ifndef _ZS_UT03

#define _ZS_UT03



#include <avr/io.h>

#include <avr/interrupt.h>

#include <avr/signal.h>

#define uchar unsigned char

#define uint  unsigned int

#define _asm  asm volatile

#define _reg  register unsigned char  

//防止被编译器占用

_reg tempR2  asm("r2");

_reg tempR3  asm("r3");

_reg tempR4  asm("r4");

_reg tempR5  asm("r5");

_reg tempR6  asm("r6");

_reg tempR7  asm("r7");

_reg tempR8  asm("r8");

_reg tempR9  asm("r9");

_reg tempR10 asm("r10");

_reg tempR11 asm("r11");

_reg tempR12 asm("r12");

_reg tempR13 asm("r13");

_reg tempR14 asm("r14");

_reg tempR15 asm("r15");

_reg tempR16 asm("r16");

_reg tempR17 asm("r17");



#define STACK_LENGHT 300                //人工堆栈长度

#define MAXTASK      6      //最大任务数



#define pushAll()        \

{        \

        _asm("push r0"        "
\t"        \

                "push r1"        "
\t"        \

                "push r18" "
\t"        \

                "push r19" "
\t"        \

                "push r20" "
\t"        \

                "push r21" "
\t"        \

                "push r22" "
\t"        \

                "push r23" "
\t"        \

                "push r24" "
\t"        \

                "push r25" "
\t"        \

                "push r26" "
\t"        \

                "push r27" "
\t"        \

                "push r28" "
\t"        \

                "push r29" "
\t"        \

                "push r30" "
\t"        \

                "push r31" "
\t"        \

                );        \

}



#define popAll()        \

{        \

        _asm("pop r31" "
\t"        \

                "pop r30" "
\t"        \

                "pop r29" "
\t"        \

                "pop r28" "
\t"        \

                "pop r27" "
\t"        \

                "pop r26" "
\t"        \

                "pop r25" "
\t"        \

                "pop r24" "
\t"        \

                "pop r23" "
\t"        \

                "pop r22" "
\t"        \

                "pop r21" "
\t"        \

                "pop r20" "
\t"        \

                "pop r19" "
\t"        \

                "pop r18" "
\t"        \

                "pop r1" "
\t"        \

                "pop r0" "
\t"        \

                );        \

}



#endif


-----此内容被zhb2000于2006-08-31,12:02:45编辑过

出0入0汤圆

 楼主| 发表于 2006-8-31 11:14:11 | 显示全部楼层
学习2:

通过以上的代码加入两个任务keyboard和display,用时间片轮转来分别控制按键和显示。



运行原理:keyboard有两个按键,分别为”+”和”-“按下后使左边的2个数码管分别加1或减1显示,右边的4个数码管扫描显示”2006”



图片:





GCC源代码及Proteus6.7仿真下载:

点击此处下载armok01127734.rar

出0入0汤圆

发表于 2006-8-31 12:08:39 | 显示全部楼层
不得不佩服你,我正在学UC/OS

出0入0汤圆

发表于 2006-8-31 13:17:17 | 显示全部楼层
多谢~!:)

出0入0汤圆

发表于 2006-8-31 14:51:05 | 显示全部楼层
赞一个

出0入0汤圆

 楼主| 发表于 2006-8-31 16:26:36 | 显示全部楼层
谢谢 hoguowi、jackiezeng、ilymy 。谢谢大家! 我也是初学,希望能一起共同进步。

出0入0汤圆

发表于 2006-9-1 15:40:34 | 显示全部楼层
恩   不错   

学习

出0入0汤圆

 楼主| 发表于 2006-9-2 12:24:21 | 显示全部楼层
学习3: 已发现的一个BUG的改正:

通过实验发现 学习2 中存在一个现象,程序片段如下:

片段1:

int main(void)

{

        init();

        createTask(keyboard);   //注意:这是第一个运行的任务

        createTask(display);     //注意:这是第二个运行的任务

        startTask();

        while(1);

        return 0;

}

程序如 片段1的写法时运行正常。

片段2:

int main(void)

{

        init();

        createTask(display);    //注意:这是第一个运行的任务

        createTask(keyboard);  //注意:这是第二个运行的任务

        startTask();

        while(1);

        return 0;

}



    程序如“片段2”的写法时显示”2006”的数码管只能显示中间的两个 “00” 通过对生成的汇编进行跟踪发现这是由于GCC对代码的优化在计时器中断后部分寄存器的内容没有被系统自动保存造成的(注我用的是-S级优化)。

知道原因后解决的办法就很简单了。

程序片段如下:

(1)        增加两个宏定义:

#define extPush()        \

{        \

        _asm("push r19"        "
\t"        \

                "push r20" "
\t"        \

                "push r21" "
\t"        \

                "push r22" "
\t"        \

                "push r23" "
\t"        \

                "push r26" "
\t"        \

                "push r27" "
\t"        \

                "push r28" "
\t"        \

                "push r29" "
\t"        \

                );        \

}



#define extPop()        \

{        \

        _asm("pop r29" "
\t"        \

                "pop r28" "
\t"        \

                "pop r27" "
\t"        \

                "pop r26" "
\t"        \

                "pop r23" "
\t"        \

                "pop r22" "
\t"        \

                "pop r21" "
\t"        \

                "pop r20" "
\t"        \

                "pop r19" "
\t"        \

                );        \

}



(2)        修改定时器的溢出中断代码如下:

SIGNAL(SIG_OVERFLOW0)

{        //计时器0溢出中断

        static uchar n=0;

        extPush();                        //注意:新加的代码

        zsTask[n].taskBottomPoint=SP;

        if(zsTask[n+1].ready!=0)

        {        //下一个任务

                n++;

                SP=zsTask[n].taskBottomPoint;

                if(zsTask[n].ready==2)

                {

                        zsTask[n].ready=1;

                        popAll();

                        _asm("reti");

                }

        }

        else

        { //第一个任务或只有一个任务

                n=0;

                SP=zsTask[0].taskBottomPoint;

        }

        extPop();                        //注意:新加的代码

}



    小结:以前没有用过时间片轮转调度法写程序。在没有用它的时候写几个要不断执行的模块,有时几个模块又会相互影响,就会要考虑的多一点。现在有了时间片轮转法,宏观上感觉是几个模块在同时运行,不必理会几个模块要相互影响。有兴趣的朋友可以试一下,真是别有一番风味!


-----此内容被zhb2000于2006-09-02,12:31:00编辑过

出0入0汤圆

发表于 2006-12-14 18:09:41 | 显示全部楼层
大哥 ,  怎么没有 后续 了 啊 ?



期待中 ~!

出0入0汤圆

发表于 2007-10-25 09:03:25 | 显示全部楼层
留个记号
头像被屏蔽

出0入0汤圆

发表于 2007-10-25 09:10:41 | 显示全部楼层
由于本文写得太好,Proteus仿真的事就不计较了。

以后我们写一个专题,推荐大家使用其它的仿真方式,就没有 Proteus 侵权的烦恼了。

出0入4汤圆

发表于 2007-10-25 09:43:02 | 显示全部楼层
有本书《时间触发嵌入式系统设计模式,使用8051系列微控制器开发可靠应用》,正在看,主要也是讲时间片轮调度

出0入0汤圆

发表于 2007-10-25 15:33:18 | 显示全部楼层
关于仿真,还真不好找

VMLAB是免费的,可惜支持的器件不多,不然它的IO输出时序仿真是通讯调试法宝啊

出0入0汤圆

发表于 2007-10-26 09:44:35 | 显示全部楼层
SIGNAL(SIG_OVERFLOW0)
{        //计时器0溢出中断
        static uchar n=0;
        extPush();                        //注意:新加的代码
        zsTask[n].taskBottomPoint=SP;
       ........
楼主 这句的作用是什么啊:    zsTask[n].taskBottomPoint=SP;

出0入0汤圆

发表于 2007-10-26 10:08:02 | 显示全部楼层
仔细想了下,明白了些

出0入0汤圆

发表于 2007-10-26 11:48:13 | 显示全部楼层
声明那些寄存器变量防止被编译器占有,而且那些寄存器在程序中也没有使用,拜拜浪费了,可用的寄存器减少了,就会导致程序编译质量下降,干嘛不保存所有寄存器呢?AVR单片机在中断时时不保存所有寄存器的。

出0入0汤圆

发表于 2007-10-26 12:18:52 | 显示全部楼层
总感觉阿莫,一见到Proteus 使用就闹心啊

出0入0汤圆

发表于 2007-10-26 13:12:16 | 显示全部楼层
好东西,记之。

出0入22汤圆

发表于 2007-10-26 13:22:27 | 显示全部楼层
谢谢搂住

出0入0汤圆

发表于 2008-6-19 10:05:59 | 显示全部楼层
标记

出0入0汤圆

发表于 2008-6-19 11:33:22 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-11-12 10:32:22 | 显示全部楼层
good!

出0入0汤圆

发表于 2009-11-12 11:32:16 | 显示全部楼层
不错,有机会学习一下,MARK

出0入0汤圆

发表于 2009-12-15 17:45:53 | 显示全部楼层
调试成功了 有个疑问 楼主的SREG 没有保存啊。。。

出0入0汤圆

发表于 2009-12-16 05:41:32 | 显示全部楼层
用中断切换任务的方式对RAM和机器周期的消耗量非常大,ticks无法做得很高.建议ARM级别以下的单片机最好用协同多任务

出0入0汤圆

发表于 2009-12-16 16:14:01 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-17 00:28:33 | 显示全部楼层
呵呵~~~~~好~~~~~

出0入0汤圆

发表于 2009-12-23 15:21:15 | 显示全部楼层
刚知道OS是什么意思

出0入0汤圆

发表于 2009-12-23 22:42:58 | 显示全部楼层
thanks for sharing

出0入0汤圆

发表于 2009-12-23 23:57:33 | 显示全部楼层
学习~

出0入0汤圆

发表于 2009-12-24 12:26:23 | 显示全部楼层
学习。。。。。

出0入0汤圆

发表于 2009-12-24 12:44:40 | 显示全部楼层
学习....

出0入0汤圆

发表于 2010-1-14 23:44:08 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-15 08:51:43 | 显示全部楼层
mark!

出0入0汤圆

发表于 2010-1-15 09:26:42 | 显示全部楼层
谢谢。

出0入0汤圆

发表于 2010-1-15 10:55:38 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-15 11:02:15 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-30 19:16:56 | 显示全部楼层
mark,学单片机到一定阶段,必然要遇到的问题,多谢楼主分享

出0入0汤圆

发表于 2010-2-7 23:08:59 | 显示全部楼层
MARK

出0入0汤圆

发表于 2010-3-31 21:09:26 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-31 21:29:33 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-31 23:14:50 | 显示全部楼层
记号,一直想仔细看看

出0入0汤圆

发表于 2010-7-15 11:00:39 | 显示全部楼层
感谢楼主,学习!

出0入0汤圆

发表于 2010-7-15 12:22:54 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-15 13:00:18 | 显示全部楼层
很好的资料

出0入0汤圆

发表于 2010-7-15 13:14:29 | 显示全部楼层
很好很好

出675入8汤圆

发表于 2010-7-15 21:07:11 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-16 08:46:40 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-16 09:22:49 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-18 08:47:27 | 显示全部楼层

出0入0汤圆

发表于 2010-7-18 09:46:32 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-18 10:33:26 | 显示全部楼层
mark!

出0入0汤圆

发表于 2010-7-18 11:16:06 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-21 15:48:42 | 显示全部楼层
Remark

出0入0汤圆

发表于 2010-7-21 17:30:38 | 显示全部楼层
很好,学习中

出0入0汤圆

发表于 2010-8-11 20:21:18 | 显示全部楼层
Remark

出0入0汤圆

发表于 2010-8-11 20:37:09 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-20 02:45:44 | 显示全部楼层
GOOD

出0入0汤圆

发表于 2010-10-11 08:30:34 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-11 08:42:51 | 显示全部楼层
markkkkkk

出0入0汤圆

发表于 2010-10-12 15:00:28 | 显示全部楼层
怎么没pc和Sreg的处理?

出0入0汤圆

发表于 2010-10-12 16:39:21 | 显示全部楼层
学习

出0入0汤圆

发表于 2010-10-12 16:39:22 | 显示全部楼层
学习

出0入0汤圆

发表于 2010-10-12 17:05:48 | 显示全部楼层
谢谢楼主。

出0入0汤圆

发表于 2010-10-12 17:48:07 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-12 18:58:58 | 显示全部楼层
时间片轮转调度,关注一下

出0入0汤圆

发表于 2011-1-1 11:15:22 | 显示全部楼层
脚印!

出0入0汤圆

发表于 2011-1-1 15:18:39 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-1-1 16:34:23 | 显示全部楼层
mark!依旧经典

出0入0汤圆

发表于 2011-1-26 09:04:27 | 显示全部楼层
脚印

出0入0汤圆

发表于 2011-1-26 13:39:48 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-1-27 17:50:20 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-1-27 20:38:27 | 显示全部楼层
不错

出0入0汤圆

发表于 2011-8-21 17:45:37 | 显示全部楼层
确实得支持

出0入0汤圆

发表于 2011-8-27 13:46:14 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-2-16 10:39:28 | 显示全部楼层
mark,学习

出0入0汤圆

发表于 2012-2-16 10:48:56 | 显示全部楼层
自己搞os,牛的
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-28 21:30

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表