建立一个AVR单片机RTOS(8)—占先式内核(完善的服务)

发布者:乡村乐园最新更新时间:2016-09-30 来源: eefocus关键字:AVR单片机  RTOS  占先式内核 手机看文章 扫描二维码
随时随地手机看文章
第八篇:占先式内核(完善的服务)

如果将前面所提到的占先式内核和协作式内核组合在一起,很容易就可以得到一个功能较为完善的占先式内核,它的功能有:

1,挂起和恢复任务

2,任务延时

3,信号量(包括共享型和独占型)

另外,在本例中,在各个任务中加入了从串口发送任务状态的功能。

#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 OSTaskSuspend(unsigned char prio)

{

TCB[prio].OSWaitTick=0;

OSRdyTbl &= ~(0x01<

if(OSTaskRunningPrio==prio) //当要挂起的任务为当前任务

OSSched(); //从新调度

}

 

//恢复任务 可以让被OSTaskSuspend或 OSTimeDly暂停的任务恢复

void OSTaskResume(unsigned char prio)

{

OSRdyTbl |= 0x01<

TCB[prio].OSWaitTick=0; //将时间计时设为0,到时

if(OSTaskRunningPrio>prio) //当要当前任务的优先级低于重置位的任务的优先级

OSSched(); //从新调度 //从新调度

}

 

// 任务延时

void OSTimeDly(unsigned int ticks)

{

if(ticks) //当延时有效

{

OSRdyTbl &= ~(0x01<

TCB[OSTaskRunningPrio].OSWaitTick=ticks;

OSSched(); //从新调度

}

}

 

//信号量

struct SemBlk

{

unsigned char OSEventType; //型号 0,信号量独占型;1信号量共享型

unsigned char OSEventState; //状态 0,不可用;1,可用

unsigned char OSTaskPendTbl; //等待信号量的任务列表

} Sem[10];

 

//初始化信号量

void OSSemCreat(unsigned char Index,unsigned char Type)

{

Sem[Index].OSEventType=Type; //型号 0,信号量独占型;1信号量共享型

Sem[Index].OSTaskPendTbl=0;

Sem[Index].OSEventState=0;

}

 

//任务等待信号量,挂起

//当Timeout==0xffff时,为无限延时

unsigned char OSTaskSemPend(unsigned char Index,unsigned int Timeout)

{

//unsigned char i=0;

if(Sem[Index].OSEventState) //信号量有效

{

if(Sem[Index].OSEventType==0) //如果为独占型

Sem[Index].OSEventState = 0x00; //信号量被独占,不可用

}

else

{ //加入信号的任务等待表

Sem[Index].OSTaskPendTbl |= 0x01<

TCB[OSTaskRunningPrio].OSWaitTick=Timeout; //如延时为0,刚无限等待

OSRdyTbl &= ~(0x01<

OSSched(); //从新调度

if(TCB[OSTaskRunningPrio].OSWaitTick==0 ) //超时,未能拿到资源

return 0;

}

 

return 1;

}

 

//发送一个信号量,可以从任务或中断发送

void OSSemPost(unsigned char Index)

{

if(Sem[Index].OSEventType) //当要求的信号量是共享型

{

Sem[Index].OSEventState=0x01; //使信号量有效

OSRdyTbl |=Sem [Index].OSTaskPendTbl; //使在等待该信号的所有任务就绪Sem[Index].OSTaskPendTbl=0; //清空所有等待该信号的等待任务

}

else //当要求的信号量为独占型

{

unsigned char i;

for (i = 0; i < OS_TASKS && !(Sem[Index].OSTaskPendTbl & (0x01<

if(i < OS_TASKS) //如果有任务需要

{

Sem[Index].OSTaskPendTbl &= ~(0x01<

OSRdyTbl |= 0x01<

}

else

{

Sem[Index].OSEventState =1; //使信号量有效

}

}

}

 

//从任务发送一个信号量,并进行调度

void OSTaskSemPost(unsigned char Index)

{

OSSemPost(Index);

OSSched();

}

 

//清除一个信号量,只对共享型的有用。

//对于独占型的信号量,在任务占用后,就交得不可以用了。

void OSSemClean(unsigned char Index)

{

Sem[Index].OSEventState =0; //要求的信号量无效

}

 

void TCN0Init(void) // 计时器0

{

TCCR0 = 0;

TCCR0 |= (1<

TIMSK |= (1<

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

}

 

SIGNAL(SIG_OVERFLOW0)

{

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

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

unsigned char i;

for(i=0;i

{

if(TCB[i].OSWaitTick && TCB[i].OSWaitTick!=0xffff)

{

TCB[i].OSWaitTick--;

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

{

OSRdyTbl |= (0x01<

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

}

}

}

 

TCNT0=100;

cli();

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

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

}

 

unsigned char __attribute__ ((progmem)) proStrA[]="Task ";

unsigned char strA[20];

SIGNAL(SIG_UART_RECV) //串口接收中断

{

strA[0]=UDR;

}

 

/////////////////////////////////////串口发送

unsigned char *pstr_UART_Send;

unsigned int nUART_Sending=0;

void UART_Send(unsigned char *Res,unsigned int Len) //发送字符串数组

{

if(Len>0)

{

pstr_UART_Send=Res; //发送字串的指针

nUART_Sending=Len; //发送字串的长度

UCSRB=0xB8; //发送中断使能

}

}

 

//SIGNAL 在中断期间,其它中断禁止

SIGNAL(SIG_UART_DATA) //串口发送数据中断

{

IntNum++; //中断嵌套+1,不充许中断

if(nUART_Sending) //如果未发完

{

UDR=*pstr_UART_Send; //发送字节

pstr_UART_Send++; //发送字串的指针加1

nUART_Sending--; //等待发送的字串数减1

}

 

if(nUART_Sending==0) //当已经发送完

{

OSSemPost(0);

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

UCSRB=0x98;

}

 

cli(); //关发送中断

IntNum--;

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

}

 

void UARTInit() //初始化串口

{

#define fosc 8000000 //晶振8 MHZ UBRRL=(fosc/16/(baud+1))%256;

#define baud 9600 //波特率

OSCCAL=0x97; //串口波特率校正值,从编程器中读出

//UCSRB=(1<

UCSRB=0x98;

//UCSRB=0x08;

UBRRL=(fosc/16/(baud+1))%256;

UBRRH=(fosc/16/(baud+1))/256;

UCSRC=(1<

UCSRB=0xB8;

UDR=0;

}

 

//打印unsigned int 到字符串中 00000

void strPUT_uInt(unsigned char *Des,unsigned int i)

{

unsigned char j;

Des=Des+4;

for(j=0;j<5;j++)

{

*Des=i%10+’0’;

i=i/10;

Des--;

}

}

 

void strPUT_Star(unsigned char *Des,unsigned char i)

{

unsigned char j;

for(j=0;j

{

*Des++=’*’;

}

 

*Des++=13;

}

 

unsigned int strPUT_TaskState(unsigned char *Des,unsigned char TaskID,unsigned char Num)

{

//unsigned int i=0;

*(Des+4)=’0’+TaskID;

strPUT_uInt(Des+6,Num);

strPUT_Star(Des+12,TaskID);

return 12+TaskID+1;

}

 

void Task0()

{

unsigned int j=0;

while(1)

{

PORTB=j++;

if(OSTaskSemPend(0,0xffff))

{

unsigned int m;

m=strPUT_TaskState(strA,OSTaskRunningPrio,j);

UART_Send(strA,m);

}

 

OSTimeDly(200);

}

}

 

void Task1()

{

unsigned int j=0;

while(1)

{

PORTC=j++;

if(OSTaskSemPend(0,0xffff))

{

unsigned int m;

m=strPUT_TaskState(strA,OSTaskRunningPrio,j);

UART_Send(strA,m);

}

 

OSTimeDly(100);

}

}

 

void Task2()

{

unsigned int j=0;

while(1)

{

if(OSTaskSemPend(0,0xffff))

{

unsigned int m;

m=strPUT_TaskState(strA,OSTaskRunningPrio,j);

UART_Send(strA,m);

}

 

PORTD=j++;

OSTimeDly(50);

}

}

 

void TaskScheduler()

{

OSSched();

while(1)

{}

}

 

int main(void)

{

strlcpy_P(strA,proStrA,20);

UARTInit();

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(8)—占先式内核(完善的服务)

上一篇:ATmega128(ICC7.22 "delay.h")
下一篇:建立一个AVR单片机RTOS(7)—占先式内核(只带延时服务)

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

实时操作系统-RTLinux
摘要: 介绍了RTLinux的两个重点特点:硬实时性和完备性,及其在嵌入式系统应用中的一些重要功能,并结合实时处理的具体实例对其编程方法加以说明。 关键词: 操作系统 实时处理 Linux 嵌入式系统 近年来,基于PC的嵌入式系统得到迅速的发展。在各种不同的操作系统中,由于Linux操作系统的廉价、源代码的开放性以及系统的稳定性,使其在基于PC的嵌入式系统中的应用日益广泛。RTLinux(RealTime Linux) 是一种基于Linux的实时操作系统,是由FSML
[嵌入式]
使用AVR单片机做AD转换的几点建议
结合自己的开发经验给出几点建议: AD转换时间应当大于64us(预分频决定),如果采样保持时间短,采集结果可能不准确; 应该让采集电压源尽量保持稳定; (是让采样保持电路稳定。) AVCC和VCC之间的电压差不得超过0.3V,否则采样值波动会很大; 在采样的过程中,不要有数字电路的操作,比如IO口赋值等; 如果有可能,请使用外部参考电压源; 选择合适的参考电压值,比如测1.5V电压,参考电压为2V就比较合适; 如果AD采样要求较高,被测电路与芯片工作电路分开供电。 调试的过程中不要有JTAG的参与,JTAG会影响采样结果,应该将程序直接烧录到单片机中,用串口或者LED等查看结果。 按照以上建议,
[单片机]
AVR单片机中左移位和右移位指令
计算机的指令系统是一套控制计算机操作的代码,称之为机器语言。计算机只能识别和执行机器语言的指令。为了便于人们理解、记忆和使用,通常用汇编语言指令来描述计算机的指令系统。汇编语言指令可通过汇编器翻译成计算机能识别的机器语言。 AVR单片机指令系统是RISC结构的精简指令集,是一种简明易掌握﹑效率高的指令系统。 SL-DIY02-3开发实验器使用AT90S8535单片机,有118条指令, 而我们所做的11个实验程序仅用了34条指令, 我们重点讲这34条指令, 其余指令就可自学了。 AVR器件(指令速查表) 118条指令器件 AT90S2313/2323/2343/2333 ,AT90S4414/4433/4434/85
[单片机]
<font color='red'>AVR单片机</font>中左移位和右移位指令
avr单片机 Mega16定时器实验报告
1、实验目的 通过实验,掌握定时器的使用方法。着重掌握定时中断的使用! 2、实验内容 1 使用Mega16单片机的timer0产生1ms间隔的定时中断(建议采用application builder,既可以用Normal工作模式也可以使用CTC模式),(1)在定时中断中实现4位数码管的动态刷新。(2)在1ms的定时中断中设计100mS的时间标志位,在主程序中查询时间标志位,实现LED闪烁。 2 重新配置TImer0工作在PWM模式,用按键改变PWM的占空比,调节电机转速; 3、实验预习要求 仔细阅读教材中定时中断的范例; 4、实验步骤 1 启动ICCAVR,创建一个工程文件,配置timer0,实现10ms
[单片机]
<font color='red'>avr单片机</font> Mega16定时器实验报告
使用AVR单片机驱动舵机
1.舵机驱动的基本原理   (可以参考http://blog.sina.com.cn/s/blog_8240cbef01018hu1.html)    控制信号由接收机的通道进入信号调制芯片,获得直流偏置电压。它内部有一个基准电路,产生周期为20ms,宽度为1.5ms的基准信号,将获得的直流偏置电压与电位器的电压比较,获得电压差输出。最后,电压差的正负输出到电机驱动芯片决定电机的正反转。当电机转速一定时,通过级联减速齿轮带动电位器旋转,使得电压差为0,电机停止转动。 简单的来讲,就是输出一个周期为20Ms,不同的占空比对应舵机转过不同的角度。 难点主要在于 舵机控制信号需要保持,这样就比用脉冲控制步进电机要复杂一些。 你需要
[单片机]
使用<font color='red'>AVR单片机</font>驱动舵机
AVR单片机中断实现 ATmega16 INT ISR(INT0_vect)
2.21实例功能 前面例子中分别介绍了按键控制发光二极管的亮灭,但是我们注意到,在程序中需要一直检测按键的状态,这样明显的浪费了单片机的资源,降低了单片机的工作效率,。那么有没有一种方法可以让单片机不用一直检测按键的状态,而只在有按键动作时才去响应呢? 当然有!单片机中除了具有基本输入输出功能的作用外,还有专门检测外界信号并作出响应的中断系统。在本例中,通过利用外部中断实现单片机对按键事件的响应和处理。 本例中三个功能模块描述如下: ● 单片机系统:对按键事件产生的中断时间作出响应,并在数码管上显示按键按下的次数。 ● 外围电路:通过将按键连接到单片机的外部中断检测端口,实现中断产生电路,数码管显示电路用于指示
[单片机]
重温经典——AVR单片机串口ISP下载器
  在 A VR单片机的学习过程中,ISP编辑下载器是一个必不可少的调试工具。在Atmel官方网站也分布并开源了简易ISP下载器的原理图与BOM。如下图所示:   这个版本使用了一个数字逻辑芯片74HC244,逻辑芯片主要启到数据缓冲的作用。当然,还有一个更加简易的设计,如下图所示:   这两个版本小编在实际应用中都有所实践,配合双龙的下载软件,非常好用。只是速度较慢。某个项目中,小编的atmega16的编写,约15K的flash,完成一次擦除,烧写,校验三个步骤,一般需要20+分钟。每次小编烧写程序时都是选择这个时候泡杯咖啡,吃点茶点   上面二个版的ISP下载器虽然制作简易,但是一个灰常关键问题就是,偶的
[单片机]
重温经典——<font color='red'>AVR单片机</font>串口ISP下载器
基于AVR单片机的三相晶闸管触发电路的研制
0 引 言 用于晶闸管三相全控桥整流装置的触发电路,若仅从触发信号的相位控制方式来看,只有多通道相位控制和单通道相位控制两种。前者的典型电路为锯齿波移相触发电路,它用于三相全控桥式主电路时,移相通道多达6个,由于各个通道中同步电路本身特性的差异,发出的触发脉冲相位对称度很差。另外,传统的晶闸管整流或逆变系统需要3个同步变压器来得到触发脉冲的同步信号,不仅增加了系统的成本,同时给安装调试带来不便。随着新型器件的发展,数字移相技术逐渐开始取代传统的模拟移相技术。AVR单片机具有强大的逻辑分析和计算能力,而且可以在系统编程,可靠性很高。 本文详细介绍了一种以AVR单片机为核心,并且不需同步变压器、具有相序自适应功能的双脉冲序列数字移
[单片机]
基于<font color='red'>AVR单片机</font>的三相晶闸管触发电路的研制
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

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