打印
[AVR单片机]

请教:AVR的调度程序,任务定义的变量在任务重新执行时都

[复制链接]
2404|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
linnaldf|  楼主 | 2007-9-25 22:03 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    自己写的一个基于AVR单片机(Meag16)、编译器WINAVR20070122的调度程序,基于时间片轮转法,定时器0计时,轮流让8个任务运行,切换任务时保存全部寄存器R0-R31、SREG到当前任务的堆栽,然后恢复下一个任务的现场,用中断返回指令作任务切换。设置让编译器在中断程序不保存任何寄存器,由自己保存寄存器。
    8个任务可以轮流运行,但是任务内部定义的局部在任务每次重新执行时都变为0。请教大家,谢谢了!程序代码如下:

   (1)头文件:任务控制块TCB定义、保存任务环境的入栽宏、恢复任务现场的出栽宏。
typedef struct
{
    unsigned char* task_stk_top;  //保存任务的栽顶
    unsigned char task_stack[100];//任务堆栽
     
}OS_TASK_TCB;

//保存所有寄存器的宏
#define pushall() 
__asm__ __volatile__ ("push r1"   " ");
__asm__ __volatile__ ("push r0"   " ");
__asm__ __volatile__ ("in  r0, 0x3f"   " ");保存CPU状态寄存器
__asm__ __volatile__ ("push r0"   " ");
__asm__ __volatile__ ("eor r1, r1"   " ");WINAVR要求R1等于0
__asm__ __volatile__ ("push r2"   " ");
__asm__ __volatile__ ("push r3"   " ");
__asm__ __volatile__ ("push r4"   " ");
__asm__ __volatile__ ("push r5"   " ");
__asm__ __volatile__ ("push r6"   " ");
__asm__ __volatile__ ("push r7"   " ");
__asm__ __volatile__ ("push r8"   " ");
__asm__ __volatile__ ("push r9"   " ");
__asm__ __volatile__ ("push r10"   " ");
__asm__ __volatile__ ("push r11"   " ");
__asm__ __volatile__ ("push r12"   " ");
__asm__ __volatile__ ("push r13"   " ");
__asm__ __volatile__ ("push r14"   " ");
__asm__ __volatile__ ("push r15"   " ");
__asm__ __volatile__ ("push r16"   " ");
__asm__ __volatile__ ("push r17"   " ");
__asm__ __volatile__ ("push r18"   " ");
__asm__ __volatile__ ("push r19"   " ");
__asm__ __volatile__ ("push r20"   " ");
__asm__ __volatile__ ("push r21"   " ");
__asm__ __volatile__ ("push r22"   " ");
__asm__ __volatile__ ("push r23"   " ");
__asm__ __volatile__ ("push r24"   " ");
__asm__ __volatile__ ("push r25"   " ");
__asm__ __volatile__ ("push r26"   " ");
__asm__ __volatile__ ("push r27"   " ");
__asm__ __volatile__ ("push r28"   " ");
__asm__ __volatile__ ("push r29"   " ");
__asm__ __volatile__ ("push r30"   " ");
__asm__ __volatile__ ("push r31"   " ");

//恢复任务现场时弹出所有寄存器的宏
#define popall() 
__asm__ __volatile__ ("pop r31"   " ");
__asm__ __volatile__ ("pop r30"   " ");
__asm__ __volatile__ ("pop r29"   " ");
__asm__ __volatile__ ("pop r28"   " ");
__asm__ __volatile__ ("pop r27"   " ");
__asm__ __volatile__ ("pop r26"   " ");
__asm__ __volatile__ ("pop r25"   " ");
__asm__ __volatile__ ("pop r24"   " ");
__asm__ __volatile__ ("pop r23"   " ");
__asm__ __volatile__ ("pop r22"   " ");
__asm__ __volatile__ ("pop r21"   " ");
__asm__ __volatile__ ("pop r20"   " ");
__asm__ __volatile__ ("pop r19"   " ");
__asm__ __volatile__ ("pop r18"   " ");
__asm__ __volatile__ ("pop r17"   " ");
__asm__ __volatile__ ("pop r16"   " ");
__asm__ __volatile__ ("pop r15"   " ");
__asm__ __volatile__ ("pop r14"   " ");
__asm__ __volatile__ ("pop r13"   " ");
__asm__ __volatile__ ("pop r12"   " ");
__asm__ __volatile__ ("pop r11"   " ");
__asm__ __volatile__ ("pop r10"   " ");
__asm__ __volatile__ ("pop r9"   " ");
__asm__ __volatile__ ("pop r8"   " ");
__asm__ __volatile__ ("pop r7"   " ");
__asm__ __volatile__ ("pop r6"   " ");
__asm__ __volatile__ ("pop r5"   " ");
__asm__ __volatile__ ("pop r4"   " ");
__asm__ __volatile__ ("pop r3"   " ");
__asm__ __volatile__ ("pop r2"   " ");
__asm__ __volatile__ ("pop r0"   " ");
__asm__ __volatile__ ("out 0x3f, r0"   " ");恢复CPU状态寄存器
__asm__ __volatile__ ("pop r0"   " ");
__asm__ __volatile__ ("pop r1"   " ");
__asm__ __volatile__ ("RETI"   " ");
//******************************************************************

   (2)主函数main()代码,包括8个任务,调度程序,定义8个任务,每个任务对应一个LED灯的闪烁:
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "RTOS.h"

#define LED_PORT PORTA //LED端口定义
#define LED_DDR  DDRA
#define LED_PIN  PINA

//第1个任务定义
void task1( void )
{   
    unsigned char temp ;//任务1的局部变量
        
    TCCR0 = 0X05;//定时器0初始化
    TIMSK = 0X01;         
         
    TCNT0 = 0x00;
    sei();       //打开全局中断
     
    while(1)
    {        
    //任务1每次重新执行temp都会变为0,就是这里请教大家为什么?
         temp++; 

         LED_PORT ^=0X01;
    for( unsigned char i=0; i<250; i++)
    _delay_ms(10);
    }    
}

//第2个任务定义
void task2(void)
{    
    while(1)
    {        
    LED_PORT ^=0X02;
    for( unsigned char i=0; i<250; i++)
    _delay_ms(10);
    }
}

//第3个任务定义
void task3(void)
{
    while(1)
    {       
    LED_PORT ^=0X04;
    for( unsigned char i=0; i<250; i++)
    _delay_ms(10);
    }
}

//第4个任务定义
void task4(void)
{
    while(1)
    {        
    LED_PORT ^=0X08;
    for( unsigned char i=0; i<250; i++)
    _delay_ms(10);
    }
}

//第5个任务定义
void task5(void)
{
    while(1)
    {       
    LED_PORT ^=0X10;
    for( unsigned char i=0; i<250; i++)
    _delay_ms(10);
    }
}

//第6个任务定义
void task6(void)
{
    while(1)
    {        
    LED_PORT ^=0X20;
    for( unsigned char i=0; i<250; i++)
         _delay_ms(10);
    }
}

//第7个任务定义
void task7(void)
{
    while(1)
    {        
    LED_PORT ^=0X40;
    for( unsigned char i=0; i<250; i++)
    _delay_ms(10);
    }
}

//第8个任务定义
void task8(void)
{
    while(1)
    {        
    LED_PORT ^=0X80;
    for( unsigned char i=0; i<250; i++)
         _delay_ms(10);
    }
}
//*******************************************************************

volatile unsigned char next_task = 0;//用来计算下一个运行的任务

//计数变量,在定时器0中断函数里加1,用来控制任务切换频率
volatile unsigned char count = 0;

volatile OS_TASK_TCB task_tcb[ 8 ];//定义8个任务的任务控制块TCB数组

//任务堆栽初始化函数,参数为:任务入口地址、任务序号(1-8)
unsigned char* stackinit( void (*task)( void ), unsigned char task_id )
{
    unsigned char* stk;
    unsigned int temp;     
     
    temp = (unsigned int)task;//任务入口地址    
     
    stk = &( task_tcb[task_id].task_stack[99] );//取得任务栽顶指针
    *stk-- = (unsigned char)(temp&0xff);//将任务入口地址入栽
    *stk-- = (unsigned char)(temp>>8);

    //以下是CPU所有寄存器初始化,为0值,
    *stk-- = (unsigned char)0x00;        /* R0  = 0x00 */
    *stk-- = (unsigned char)0x00;        /* R1  = 0x00 */
    *stk-- = (unsigned char)0x80;        /* R2  = 0x00 */
    *stk-- = (unsigned char)0x00;        /* R3  = 0x00 */
    *stk-- = (unsigned char)0x00;        /* R4  = 0x00 */
    *stk-- = (unsigned char)0x00;        /* R5  = 0x00 */
    *stk-- = (unsigned char)0x00;        /* R6  = 0x00 */
    *stk-- = (unsigned char)0x00;        /* R7  = 0x00 */
    *stk-- = (unsigned char)0x00;        /* R8  = 0x00 */
    *stk-- = (unsigned char)0x00;        /* R9  = 0x00 */
    *stk-- = (unsigned char)0x00;        /* R10 =      */
    *stk-- = (unsigned char)0x00;        /* R11 =      */
    *stk-- = (unsigned char)0x00;        /* R12 =      */
    *stk-- = (unsigned char)0x00;        /* R13 =      */
    *stk-- = (unsigned char)0x00;        /* R14 =      */
    *stk-- = (unsigned char)0x00;        /* R15 =      */
    *stk-- = (unsigned char)0x00;        /* R16 =      */
    *stk-- = (unsigned char)0x00;        /* R17 =      */
    *stk-- = (unsigned char)0x00;        /* R18 =      */
    *stk-- = (unsigned char)0x00;        /* R19 =      */
    *stk-- = (unsigned char)0x00;        /* R20 =      */
    *stk-- = (unsigned char)0x00;        /* R21 =      */
    *stk-- = (unsigned char)0x00;        /* R22 =      */
    *stk-- = (unsigned char)0x00;        /* R23 =      */
    *stk-- = (unsigned char)0x00;        /* R24 = 0x00 */
    *stk-- = (unsigned char)0x00;        /* R25 = 0x00 */
    *stk-- = (unsigned char)0x00;        /* R26 =      */
    *stk-- = (unsigned char)0x00;        /* R27 =      */
    *stk-- = (unsigned char)0x00;        /* R28 =      */
    *stk-- = (unsigned char)0x00;        /* R29 =      */
    *stk-- = (unsigned char)0x00;        /* R30 =      */
    *stk-- = (unsigned char)0x00;        /* R31 =      */
    *stk-- = (unsigned char)0x00;        /* SREG =    */
    return ( (unsigned char*)stk );//返回当前堆栈指针
}

//任务创建函数:
void taskcreat( void (*task)( void ), unsigned char task_id )
{
    unsigned char *temp;

    temp = stackinit( task, task_id );//堆栽初始化,并返回栽顶指针
    
    //初始化之后将堆栽栽顶指针保存在任务的TCB里,这个变量一直指向栽顶
    task_tcb[task_id].task_stk_top = temp;
}

void OS_Start( void )
{    
    SP = task_tcb[0].task_stk_top + 33;//堆栽指针指向第一个任务

    //用中断返回指令将入口地址弹出到PC指针,运行第一个任务
    asm("RETI");
}

//定时器0的中断函数:加上下面这句,WINAVR将不保存任何寄存器
void SIG_OVERFLOW0( void ) __attribute__ ( ( signal, naked ) );
SIGNAL( SIG_OVERFLOW0 ) 
{    
    pushall( );//自己人工保存所有寄存器

    count++;   //每次中断加1
    if(count>100)//100时任务切换,count值可以调整任务切换频率
    {
              count = 0;//重新清0

         next_task++;指向下一个任务
              
              //堆栽指针指向下一个任务栽顶
              SP = task_tcb[ next_task % 8 ].task_stk_top;
    }

    popall( );//人工恢复所有寄存器
}

//main()函数
int main( void )
{  
    //LED灯控制端口设置
    LED_PORT=0Xff;
    LED_DDR =0Xff;    
     
    //创建并初始化8个任务
    taskcreat( task1, 0 );
    taskcreat( task2, 1 );
    taskcreat( task3, 2 );
    taskcreat( task4, 3 );
    taskcreat( task5, 4 );
    taskcreat( task6, 5 );
    taskcreat( task7, 6 );
    taskcreat( task8, 7 );
     
    OS_Start( );//开始运行任务
     
    while( 1 );   
}//****************************************************************

    我的问题就是任务1内部定义的局部变量temp为什么每次在任务1重新执行都为从0开始。这个时自己学习RTOS写的,请大家多指教!我想不断的进步,谢谢了!
    祝大家中秋快乐,工作顺心,呵呵!

相关帖子

沙发
hudaidai| | 2007-9-29 16:12 | 只看该作者

粗看了一下

似乎是没有保存任务切换时的SP值,即,除了初始化之外没有对task_stk_top的赋值操作。

对楼主的钻研精神鼓励一下,虽然从单纯开发的角度做这些事现在已经没有什么价值了(因为已有太多的RTOS可下载),但对个人成长是非常有好处的。

顺便说一下,嵌入式RTOS一般不应该使用时间片轮转方式,因为它兼有协作式和抢占式的缺点:既不能保证优先级实时性,又因为切换点不确定而使任务间间的数据保护复杂化。

好的强实时系统中一般要控制任务的数量,尤其要控制抢占式任务的优先级总数。抢占式任务应该是个别的(通常不超过3个),其它大部分任务应该在同一优先级上协作式运行,有助于减少系统的内务开销。

使用特权

评论回复
板凳
linnaldf|  楼主 | 2007-10-7 20:16 | 只看该作者

谢谢

这是自己初步写的,以后逐步改成占先式的。确实是忘记保存堆栽指针造成的,改了之后就好了,呵呵

使用特权

评论回复
地板
wandersky| | 2008-12-1 14:52 | 只看该作者

这么多高手

这么多高手

使用特权

评论回复
5
xtldw| | 2014-9-12 17:20 | 只看该作者
楼主,能联系我一下吗,我现在正为这事情苦恼呢,我的qq 314187849  谢谢[em:2:][em:2:][em:2:][em:2:][em:2:]

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

27

主题

70

帖子

1

粉丝