建立一个AVR单片机RTOS(7)—占先式内核(只带延时服务)

发布者:MagicGarden最新更新时间:2016-09-30 来源: eefocus关键字:AVR单片机  RTOS  占先式内核  延时服务 手机看文章 扫描二维码
随时随地手机看文章
第七篇:占先式内核(只带延时服务)

Preemptive Multitasking

当大家理解时间片轮番调度法的任务调度方式后,占先式的内核的原理,已经伸手可及了。

先想想,占先式内核是在什么地方实现任务调度的呢?对了,它在可以在任务中进行调度,这个在协作式的内核中已经做到了;同时,它也可以在中断结束后进行调度,这个问题,已经在时间片轮番调度法中已经做到了。

由于中断是可以嵌套的,只有当各层嵌套中要求调度,并且中断嵌套返回到最初进入的中断的那一层时,才能进行任务调度。

#include

#include

#include

 

unsigned char Stack[400];

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

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

register unsigned char IntNum asm("r4"); //中断嵌套计数器

 

//只有当中断嵌套数为0,并且有中断要求时,才能在退出中断时,进行任务调度

register unsigned char OSCoreState asm("r16"); // 系统核心标志位 ,R16 编译器没有使用

//只有大于R15的寄存器才能直接赋值 例LDI R16,0x01

//0x01 正在任务 切换 0x02 有中断要求切换

 

#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__("LDI R16,0x01 \n\t");

//清除中断要求任务切换的标志位,设置正在任务切换标志位

__asm__ __volatile__("SEI \n\t");

//开中断,因为如果因中断在任务调度中进行,要重新进行调度时,已经关中断

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

__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__("Int_OSSched: \n\t"); //当中断要求调度,直接进入这里

__asm__ __volatile__("SEI \n\t");

//开中断,因为如果因中断在任务调度中进行,已经关中断

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

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

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

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

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

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

OSNextTaskPrio++);

 

OSTaskRunningPrio = OSNextTaskPrio ;

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 出栈

//中断时出栈完成

__asm__ __volatile__("CLI \n\t"); //关中断

__asm__ __volatile__("SBRC R16,1 \n\t"); //SBRC当寄存器位为0刚跳过下一条指令

//检查是在调度时,是否有中断要求任务调度 0x02是中断要求调度的标志位

__asm__ __volatile__("RJMP OSSched \n\t"); //重新调度

__asm__ __volatile__("LDI R16,0x00 \n\t");

//清除中断要求任务切换的标志位,清除正在任务切换标志位

__asm__ __volatile__("RETI \n\t"); //返回并开中断

}

 

//从中断退出并进行调度

void IntSwitch(void)

{

//当中断无嵌套,并且没有在切换任务的过程中,直接进行任务切换

if(OSCoreState == 0x02 && IntNum==0)

{

//进入中断时,已经保存了SREG和R0,R1,R18~R27,R30,R31

__asm__ __volatile__("POP R31 \n\t"); //去除因调用子程序而入栈的PC

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

__asm__ __volatile__("LDI R16,0x01 \n\t");

//清除中断要求任务切换的标志位,设置正在任务切换标志位

__asm__ __volatile__("RJMP Int_OSSched \n\t"); //重新调度

}

}

 

// 任务延时

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)

{

IntNum++; //中断嵌套+1

sei(); //在中断中,重开中断

 

unsigned char i,j=0;

for(i=0;i

{

if(TCB[i].OSWaitTick)

{

TCB[i].OSWaitTick--;

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

{

OSRdyTbl |= (0x01<

OSCoreState|=0x02; //要求任务切换的标志位

}

}

}

 

TCNT0=100;

cli();

IntNum--; //中断嵌套-1

IntSwitch(); //进行任务调度

}

 

void Task0()

{

unsigned int j=0;

while(1)

{

PORTB=j++;

OSTimeDly(50);

}

}

 

void Task1()

{

unsigned int j=0;

while(1)

{

PORTC=j++;

OSTimeDly(20);

}

}

 

void Task2()

{

unsigned int j=0;

while(1)

{

PORTD=j++;

OSTimeDly(5);

}

}

 

void TaskScheduler()

{

OSSched();

while(1)

{

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

}

}

 

int main(void)

{

TCN0Init();

OSRdyTbl=0;

IntNum=0;

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

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

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

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

OSStartTask();

}

关键字:AVR单片机  RTOS  占先式内核  延时服务 引用地址:建立一个AVR单片机RTOS(7)—占先式内核(只带延时服务)

上一篇:建立一个AVR单片机RTOS(8)—占先式内核(完善的服务)
下一篇:建立一个AVR的RTOS(6)时间片轮番调度法的内核

推荐阅读最新更新时间:2024-03-16 15:12

Small RTOS的键盘扫描程序
Small RTOS的键盘扫描驱动程序及其在DP-51下载仿真实验仪上使用的例子。本驱动程序支持最大254个按键,最多3个按键同时按下。 驱动程序的使用 a) 在用户程序中添加一个任务KeyInput,优先级要比较高。 b) 设置几个常量的值。 KEY_READ_BUF_LEGTH:键盘消息队列缓冲区的大小。 KEY_START:发送KEY_DOWN消息与第一次发送KEY_ALWAYS消息的间隔时间,以系统节拍为单位。 KEY_DELAY:两次发送KEY_ALWAYS消息的间隔时间,以系统节拍为单位。 c) 编写一个与系统相关的函数KeyScan()。 KeyScan()进行一
[单片机]
基于AVR单片机与FPGA的低频数字相位测量仪
在工业领域中经常要用到低频数字式相位仪来精确测量两信号之问的相位差,比如在电力系统、频率特性的研究、激光测距等领域均有广泛的应用,相位检测的精度直接决定系统的整体性能。这就要求测量仪逐渐向智能化和测试自动化方向发展,本设计采用MCU和FPGA相结合的系统方案,以AVR单片机ATmega128和Altera公司的Cyclone系列EP1C3T100为核心,充分发挥各自的优势,如AVR单片机先进的RISC结构和强劲的运算、控制功能,Altera公司的FPGA运算速度快、资源丰富以及易编程的特点,合理设计,此方案的相位仪具备速度快、稳定可靠、精度高等优点,而且容易实现“智能化”和“自动化”。 1 系统方案设计 1.1 测量方法的比较与
[单片机]
基于<font color='red'>AVR单片机</font>与FPGA的低频数字<font color='red'>式</font>相位测量仪
AVR单片机程序设计架构
昨天在回家的火车上,带了一本《匠人手记》,看了几篇,受益匪浅。其中一篇讲到编程思路,也就是如何入手构建一个程序。我用C语言在原文的基础上稍微展开一下,以备日后查看。 简单的程序只要用简单的方式就可以了,我在此之前也只会这一种方式。也就是: void main() { init();//初始化 while (1) { module01();//模块1,或若干语句 } } 匠人在书中讲到事件驱动机制,例如: void main() { init(); while (1) { if (事件1条件) shijian1();//执行事件1 if (事件2条件)
[单片机]
基于AVR单片机的某车型CAN总线系统设计
引言 现场总线技术 是目前自动化控制领域发展比较迅速的一门技术,其中CAN(Controller Area Network)总线 是在国际上应用最广泛的现场总线之一。近几年随着汽车电子行业的发展,现代汽车中CAN总线已经成为必须采用的装置之一。本文以ATMEL公司新近生产的一款内置CAN控制器的单片机——AT90CAN128为基础,设计了一款车载CAN总线系统,通过CAN智能测控仪表对车中部分单元参数量进行实时监控,使各单元之间协调运转。另外,传输线束大大简化,可靠性得到了极大的提高,有效节约了线束安装空间和系统成本。 1 AT90CAN128特性概述 1.1 基本特性 AT90CAN128 是基于AVR
[单片机]
基于<font color='red'>AVR单片机</font>的某车型CAN总线系统设计
AVR单片机的智能家居监控系统提高生活水平
随着科技的发展和人民生活水平的提高,智能家居成为了一个热门的研究领域。智能家居问题中面临的主要问题就是安全防盗、电器控制等问题。 智能 家居不仅能够完成出现问题对主人进行的报警功能,还应提供智能家居设备执行主人的命令操作,比如通过手机或是平板电脑等终端控制电器进行做饭或是空气清洁等工作。 因此, 智能 家居中用到的传感器主要有红外传感器、门磁传感器、实时时钟芯片等。 1 家居监控系统工作原理 系统主要由门磁报警、红外传感器报警、煤气传感器报警、正常工作、用户命令执行动作构成。 1.1 门磁报警 门磁系统完成对门出入安全的监测,主要有磁铁和干簧管组成。当主人离开房子的时候可以启动防盗功能,如果两者保持正对,那么在磁铁的作用下
[电源管理]
<font color='red'>AVR单片机</font>的智能家居监控系统提高生活水平
Mentor Graphics推出适用于微控制器和多核应用的Nucleus RTOS
俄勒冈州威尔逊维尔,2014 年 8 月 18 日—Mentor Graphics公司(NASDAQ:MENT)今天推出新版本的Mentor® Embedded Nucleus® 实时操作系统 (RTOS),这是一款针对嵌入式设备互连的新一代高性能应用程序。Nucleus RTOS 进程模式已扩展了对于基于ARM® Cortex® M内核的支持。软件开发人员首次可以使用单一的嵌入式操作系统,通过整个ARM内核的内存分区来提高系统可靠性,促进整个产品系列(包括高低端设备)的代码复用。新版本增加了一个多核框架,可管理进程间通信(IPC)和复杂异构片上系统(SoC)的处理器生命周期,并增加了利用Imagination和Vivante
[嵌入式]
uc/osii在AVR单片机上的移植小结
最近在学习uc/osii在AVR单片机的移植问题,对UC/OS进行了进一步的研读,可是始终有些问题模棱两可,不得要领。从网上也下载了许多UC/OS在AVR上移植的例子程序,始终无法调试成功。因为不用调试和仿真,对内核的运行过程就不能有直观深入的认识。 总算,功夫不负有心人,昨天下载的实例在ICCAVR7上顺利通过了编译,而且在AVRstudio4上也能够顺利的进行调试,在proteus6.9上也能作仿真,通过AVR studio4上的单步运行了断点全速运行,对uc/osII的运行有了深刻的认识。 首先,理清了任务调度的机理。当应用程序建立了3个任务和一个空闲任务,在任务初始化的时候建立所有任务,在OSStart()中通过
[单片机]
AVR单片机-- I/O口的使用
简介:本文章主要通过程序控制实现按键控制LED 灯的亮灭,按一下亮,再按一下灭,如此循环往复,并学习AVR 单片机的I/O口如何配置为输入和输出。 //文件名:delay_rest.c //文件描述:实现按键控制LED 灯的亮灭,按一下亮,再按一 //下灭,如此循环往复。 //实验目的:学习AVR 单片机的I/O口如何配置为输入和输出 //当前版本号:V1.0 //--------------------------------------------------------------------- //----------------------------start------------------
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved