|
哈哈,各位师傅们好,最近看到了论坛里面的rtos,我很是激动,也有编写一个的冲动,不过我没有什么软件基础,而且是用的iccavr编译器,和其他的编译器有些不一样。但是经过不断地摸索,参考了那个教程《 建立一个属于自己的AVR的RTOS》
,终于编出了这个小东西,这也只能说是十分之一个rtos,不过虽然简单,但是对于我来说,却是一大步。
这其实就是两个任务不断地切换.这两个任务都是led灯的不断闪烁,然后通过定时器进行切换。
(编译器是iccavr)
(12M晶振)
(mega16)
(这个工程包括只有三个c文件)
第一个c文件,放着两个任务
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <ioM16v.h>
#include <macros.h>
#define uchar unsigned char
#define uint unsigned int
void delay() //一个延时函数
{
long i=10000;
while(i--);
}
//////////////////////////////////////////
void task1() //任务一:PORTC口连接着一个LED,可以不断的闪烁
{
DDRC=0xff;
while(1)
{
PORTC=~PORTC;
NOP();
NOP();
delay();
delay();
}
}
////////////////////////////////
void task2() //任务二:PORTD口连接着一个LED,可以不断的闪烁
{
DDRD=0xff;
while(1)
{
PORTD=~PORTD;
NOP();
NOP();
delay();
delay();
}
}
第二个c文件,放着定时器的初始化函数,
使用定时器一,并且20ms产生一次中断。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <ioM16v.h>
#include <macros.h>
#define uchar unsigned char
#define uint unsigned int
void timer1_init(void)
{
TCCR1B = 0x00;//停止定时器
TIMSK |= 0x04;//中断允许
TCNT1H = 0xF1;
TCNT1L = 0x5A;//初始值
OCR1AH = 0xF0;
OCR1AL = 0xFF;//匹配A值
OCR1BH = 0xF0;
OCR1BL = 0xFF;//匹配B值
ICR1H = 0xFF;
ICR1L = 0xFF;//输入捕捉匹配值
TCCR1A = 0x00;
TCCR1B = 0x03;//启动定时器
}
void init_devices(void)
{
CLI(); //禁止所有中断
MCUCR = 0x00;
MCUCSR = 0x80;//禁止JTAG
GICR = 0x00;
timer1_init();
SEI();//开全局中断
}
第三个c文件,这里是主文件
/////////////////////////////////////////////////////////////////////////////////
#include <ioM16v.h>
#include <macros.h>
#define uchar unsigned char
#define uint unsigned int
uchar a;
unsigned char Stack[360]; //堆栈
struct TaskCtr //任务控制块
{
uint OSTaskStackTop; //放着硬件堆栈指针
uint OSTaskStackY; //放着软件堆栈指针,也就是Y指针
}TCB[3]; //这里定义了三个,本来是想写三个人物的,可是后来就写了两个,所以实际就用到了两个
/////////////////////////////////////////////////////////////////////////////////////////////////
void OSTaskCreate(uint Task,uchar *Stack,uchar TaskID) //建立任务 ,uint Task传入的是任务函数的入口地址,不过
{ //iccave获得的地址好像不对,于是就用avr studio看任务
uchar i=0; //函数的汇编直接获得了地址,然后再填写进去;uchar *Stack是堆栈的
for(i=0;i<30;i++) //地址;uchar TaskID是任务号。
{
*Stack--=0xaa;
}
*Stack--=Task;
*Stack--=((uint)Task)>>8;
TCB[TaskID].OSTaskStackTop=(uint)Stack; //将人工堆栈的栈顶,保存到堆栈的数组中
TCB[TaskID].OSTaskStackY=(uint)Stack-50;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
void OSStartTask( ) //任务开始调度
{
a=1;
SP=TCB[1].OSTaskStackY; //这里借用SP寄存器,先把任务一的Y指针赋值到SP,然后切入汇编
asm("IN r28,0x3d"); //把SP的值(也就是任务一的Y指针)加载到Y寄存器中
asm("IN r29,0x3e");
SP=TCB[1].OSTaskStackTop; //最后把任务一的Sp指针加载到SP寄存器中
asm("reti"); //这个没有也可以,其实编译器已经悄悄地加过了
}
///////////////////////////////////////////////////////////
#pragma interrupt_handler timer0_comp_isr:9 //中断函数,其实就是任务切换函数,a,b两个变量没什么用,就是用来记住上次运行的
void timer0_comp_isr(void) //任务,这样可以方便的任务切换
{
uchar b;
b=3-a;
TCB[a].OSTaskStackTop=SP; //保存上次任务的信息
asm("out 0x3d,r28");
asm("out 0x3e,r29");
TCB[a].OSTaskStackY=SP;
SP=TCB.OSTaskStackY; //加载这次任务的信息
asm("IN r28,0x3d");
asm("IN r29,0x3e");
SP=TCB.OSTaskStackTop;
a=b;
TCNT1H = 0xF1; //重装值高位 //定时器的计数器赋初值,保证是20ms
TCNT1L = 0x5A; //重装值低位
}
/////////////////////////////////////////////////////////////
void task1(); //函数声明
void task2();
void task3();
void main()
{
OSTaskCreate(0x014f,&Stack[99],1); //建立任务一
OSTaskCreate(0x015b,&Stack[199],2); //建立任务二
init_devices(); //中断初始化
OSStartTask(); //开始调度
}
嘿嘿,就这么结束了。其实也没有什么,但是我真的很高兴的,学到了新东西。 |
阿莫论坛20周年了!感谢大家的支持与爱护!!
知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)
|