单片机
返回首页

建立一个 AVR的RTOS(4)—只有延时服务的协作式的内核

2016-09-30 来源:eefocus

第四篇:只有延时服务的协作式的内核

Cooperative Multitasking

前后台系统,协作式内核系统,与占先式内核系统,有什么不同呢?

记得在21IC上看过这样的比喻,“你(小工)在用厕所,经理在外面排第一,老板在外面排第二。如果是前后台,不管是谁,都必须按排队的次序使用厕所;如果是协作式,那么可以等你用完厕所,老板就要比经理先进入;如果是占先式,只要有更高级的人在外面等,那么厕所里无论是谁,都要第一时间让出来,让最高级别的人先用。”

#include

#include

#include

 

unsigned char Stack[200];

register unsigned char OSRdyTbl asm('r2'); //任务运行就绪表

register unsigned char OSTaskRunningPrio asm('r3'); //正在运行的任务

 

#define OS_TASKS 3 //设定运行任务的数量

 

struct TaskCtrBlock //任务控制块

{

unsigned int OSTaskStackTop; //保存任务的堆栈顶

unsigned int OSWaitTick; //任务延时时钟

} TCB[OS_TASKS+1];

 

//防止被编译器占用

register unsigned char tempR4 asm('r4');

register unsigned char tempR5 asm('r5');

register unsigned char tempR6 asm('r6');

register unsigned char tempR7 asm('r7');

register unsigned char tempR8 asm('r8');

register unsigned char tempR9 asm('r9');

register unsigned char tempR10 asm('r10');

register unsigned char tempR11 asm('r11');

register unsigned char tempR12 asm('r12');

register unsigned char tempR13 asm('r13');

register unsigned char tempR14 asm('r14');

register unsigned char tempR15 asm('r15');

register unsigned char tempR16 asm('r16');

register unsigned char tempR16 asm('r17');

 

//建立任务

void OSTaskCreate(void (*Task)(void),unsigned char *Stack,unsigned char TaskID)

{

unsigned char i;

*Stack--=(unsigned int)Task>>8; //将任务的地址高位压入堆栈,

*Stack--=(unsigned int)Task; //将任务的地址低位压入堆栈,

*Stack--=0x00; //R1 __zero_reg__

*Stack--=0x00; //R0 __tmp_reg__

*Stack--=0x80; //SREG在任务中,开启全局中断

for(i=0;i<14;i++) //在 avr-libc 中的 FAQ中的 What registers are used by the C compiler?

*Stack--=i; //描述了寄存器的作用

TCB[TaskID].OSTaskStackTop=(unsigned int)Stack; //将人工堆栈的栈顶,保存到堆栈的数组中

OSRdyTbl|=0x01<

}

 

//开始任务调度,从最低优先级的任务的开始

void OSStartTask()

{

OSTaskRunningPrio=OS_TASKS;

SP=TCB[OS_TASKS].OSTaskStackTop+17;

__asm__ __volatile__( 'reti' '\n\t' );

}

 

//进行任务调度

void OSSched(void)

{

// 根据中断时保存寄存器的次序入栈,模拟一次中断后,入栈的情况

__asm__ __volatile__('PUSH __zero_reg__ \n\t'); //R1

__asm__ __volatile__('PUSH __tmp_reg__ \n\t'); //R0

__asm__ __volatile__('IN __tmp_reg__,__SREG__ \n\t'); //保存状态寄存器SREG

__asm__ __volatile__('PUSH __tmp_reg__ \n\t');

__asm__ __volatile__('CLR __zero_reg__ \n\t'); //R0重新清零

__asm__ __volatile__('PUSH R18 \n\t');

__asm__ __volatile__('PUSH R19 \n\t');

__asm__ __volatile__('PUSH R20 \n\t');

__asm__ __volatile__('PUSH R21 \n\t');

__asm__ __volatile__('PUSH R22 \n\t');

__asm__ __volatile__('PUSH R23 \n\t');

__asm__ __volatile__('PUSH R24 \n\t');

__asm__ __volatile__('PUSH R25 \n\t');

__asm__ __volatile__('PUSH R26 \n\t');

__asm__ __volatile__('PUSH R27 \n\t');

__asm__ __volatile__('PUSH R30 \n\t');

__asm__ __volatile__('PUSH R31 \n\t');

__asm__ __volatile__('PUSH R28 \n\t'); //R28与R29用于建立在堆栈上的指针

__asm__ __volatile__('PUSH R29 \n\t'); //入栈完成

 

TCB[OSTaskRunningPrio].OSTaskStackTop=SP; //将正在运行的任务的堆栈底保存

 

unsigned char OSNextTaskID; //在现有堆栈上开设新的空间

for (OSNextTaskID = 0; //进行任务调度

OSNextTaskID < OS_TASKS && !(OSRdyTbl & (0x01<

OSNextTaskID++);

OSTaskRunningPrio = OSNextTaskID ;

 

cli(); //保护堆栈转换

SP=TCB[OSTaskRunningPrio].OSTaskStackTop;

sei();

 

//根据中断时的出栈次序

__asm__ __volatile__('POP R29 \n\t');

__asm__ __volatile__('POP R28 \n\t');

__asm__ __volatile__('POP R31 \n\t');

__asm__ __volatile__('POP R30 \n\t');

__asm__ __volatile__('POP R27 \n\t');

__asm__ __volatile__('POP R26 \n\t');

__asm__ __volatile__('POP R25 \n\t');

__asm__ __volatile__('POP R24 \n\t');

__asm__ __volatile__('POP R23 \n\t');

__asm__ __volatile__('POP R22 \n\t');

__asm__ __volatile__('POP R21 \n\t');

__asm__ __volatile__('POP R20 \n\t');

__asm__ __volatile__('POP R19 \n\t');

__asm__ __volatile__('POP R18 \n\t');

__asm__ __volatile__('POP __tmp_reg__ \n\t'); //SERG 出栈并恢复

__asm__ __volatile__('OUT __SREG__,__tmp_reg__ \n\t'); //

__asm__ __volatile__('POP __tmp_reg__ \n\t'); //R0 出栈

__asm__ __volatile__('POP __zero_reg__ \n\t'); //R1 出栈

//中断时出栈完成

}

 

void OSTimeDly(unsigned int ticks)

{

if(ticks) //当延时有效

{

OSRdyTbl &= ~(0x01<

TCB[OSTaskRunningPrio].OSWaitTick=ticks;

OSSched(); //从新调度

}

}

 

void TCN0Init(void) // 计时器0

{

TCCR0 = 0;

TCCR0 |= (1<

TIMSK |= (1<

TCNT0 = 100; // 置计数起始值

}

 

SIGNAL(SIG_OVERFLOW0)

{

unsigned char i;

 

for(i=0;i

{

if(TCB[i].OSWaitTick)

{

TCB[i].OSWaitTick--;

if(TCB[i].OSWaitTick==0) //当任务时钟到时,必须是由定时器减时的才行

{

OSRdyTbl |= (0x01<

}

}

}

 

TCNT0=100;

}

 

void Task0()

{

unsigned int j=0;

 

while(1)

{

PORTB=j++;

OSTimeDly(2);

}

}

 

void Task1()

{

unsigned int j=0;

 

while(1)

{

PORTC=j++;

OSTimeDly(4);

}

}

 

void Task2()

{

unsigned int j=0;

 

while(1)

{

PORTD=j++;

OSTimeDly(8);

}

}

 

void TaskScheduler()

{

while(1)

{

OSSched(); //反复进行调度

}

}

 

int main(void)

{

TCN0Init();

OSRdyTbl=0;

OSTaskRunningPrio=0;

OSTaskCreate(Task0,&Stack[49],0);

OSTaskCreate(Task1,&Stack[99],1);

OSTaskCreate(Task2,&Stack[149],2);

OSTaskCreate(TaskScheduler,&Stack[199],OS_TASKS);

OSStartTask();

}

在上面的例子中,一切变得很简单,三个正在运行的主任务,都通过延时服务,主动放弃对CPU的控制权。

在时间中断中,对各个任务的的延时进行计时,如果某个任务的延时结束,将任务重新在就绪表中置位。

最低级的系统任务TaskScheduler(),在三个主任务在放弃对CPU的控制权后开始不断地进行调度。如果某个任务在就绪表中置位,通过调度,进入最高级别的任务中继续运行。

进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • 家用电源无载自动断电装置的设计与制作

  • PIC单片机控制的遥控防盗报警器电路

  • 短波AM发射器电路设计图

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 如何构建一个触摸传感器电路

  • 基于TDA2003的简单低功耗汽车立体声放大器电路

    相关电子头条文章