本帖最后由 lotoohe 于 2016-8-24 08:24 编辑
该RTOS系统命名为ZRTOS,是我在阅读了ucos后,并且借鉴了其它操作系统后实现的,完成了一些基础的工作,可以拿过来学习,也可以进行简单的应用。
笔者独自完成了内存分配算法,多任务切换,消息邮箱,信号量,互斥量等,在其中也学到了非常多的东西。
声明:仅供学习!
要自写一个操作系统,我们首先要实现的就是任务切换,再其次是任务调度(高优先级先执行,同优先级时间片分割执行),然后再是消息邮箱,信号量,互斥量等等。
下面我们来看看任务切换的实现,这部分的代码只能由汇编语言来完成,在handler.s中有任务切换的具体代码:
我们的任务板是stm32,stm32是基于cortex-m3内核的,所以在写这个系统的时候很多参照了cortex-m3内核的文档,其中包括了pendsv中断向量,与中断压栈以及堆栈指针等方面。在设计中中断任务我们用的是msp堆栈指针,而我们的任务使用的是psp指针,我们在汇编代码中开启了psp指针的使用以后就,cpu就会自动的进行切换了:
开全局与关全局中断的代码:[mw_shl_code=asm,true]enter_int
export enter_int
CPSID I
RIMASK=1 关中断
BX LR ;返回
exit_int
export exit_int
CPSIE I ;开中断
BX LR ;返回[/mw_shl_code]第一次启动操作系统,我们往往需要初始化psp指针,设置pendsv中断为最低的优先级,然后开始一次调度:
[mw_shl_code=asm,true];开启操作系统
start_os proc
export start_os
CPSID I
;首先设置pendsv为最低优先级
;设置pendsv的中断优先级
ldr r0,=0xE000ED22
;最低优先级
ldr r1,=0xff
;设置
strb r1,[r0]
;设置psp为0,用于判断是否第一次任务调度
MOVS R0, #0 ;R0 = 0
MSR PSP, R0
SP = R0
;开启pendsv中断
LDR R0, =0xE000ED04 ;R0 = 0xE000ED04
LDR R1, =0x10000000 ;R1 = 0x10000000
;设置触发
STR.w R1, [R0] ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
;打开中断
CPSIE I ;
;死循环
os_start_hang
B os_start_hang
endp[/mw_shl_code]
软件开启pendsv中断的代码:[mw_shl_code=asm,true];出发pendsv中断,以便进行中断调度
open_scheduling proc
export open_scheduling
push {r0-r4,lr}
LDR R0, =0xE000ED04
LDR R1, =0x10000000
;进入pendsv中断
STR R1, [R0]
pop {r0-r4,pc}
endp[/mw_shl_code]最后就是pendsv中断中的代码:
[mw_shl_code=asm,true];pendsv中断
PendSV_Handler PROC
EXPORT PendSV_Handler
REQUIRE8 ; 加这两条对齐伪指令防止链接器报错
PRESERVE8 ; 8 字对齐
;中断调度时不能被打断,这里关闭中断
cpsid I
;获得sp指针的值
MRS R0, PSP ;R0 = PSP
;如果第一次执行,则执行一次中断调度
CBZ R0, thread_change
;不是第一次则保护r4-r11
SUBS R0, R0, #0x20 ;R0 -= 0x20
STM R0, {R4-R11} ;
;保存本次的栈顶地址
ldr r1,=task_mem_
ldr r1,[r1]
str R0,[r1]
thread_change ;任务调度
push {lr}
ldr.w r0,=task_sw
blx r0
pop {lr}
LDM R0, {R4-R11} ;恢复新的r4-r11的值
ADDS R0, R0, #0x20 ;R0 += 0x20
MSR PSP, R0
;切换到用户线程模式
;lr 的第2位为1时自动切换
ORR LR, LR, #0x04
;开中断
cpsie I
BX LR
ENDP[/mw_shl_code]
下面是最最重要的,任务调度器,任务调度器主要是找到最高优先级的任务,并且轮询调度,还要保存要运行的任务的控制块,在task.c中:[mw_shl_code=c,true]//任务调度函数
void *task_sw(void){
uint32 i=0;
TASK_TCB *max_TASK_TCB=TASK_TCB_LIST[0];
static TASK_TCB *back_task_tcb=null;
//查找没有通优先级的
if(back_task_tcb!=null){
uint32 spotted=0;
for(i=0;i
if(back_task_tcb==TASK_TCB_LIST){
spotted=1;
continue;
}
//确保是没有被调度过的任务
if(spotted==1){
if(TASK_TCB_LIST!=null&&
back_task_tcb->level==TASK_TCB_LIST->level){
max_TASK_TCB=TASK_TCB_LIST;
goto step;
}
}
}
}
for(i=0;i
if(TASK_TCB_LIST!=null){
if(TASK_TCB_LIST->status==true&&
//任务没有被延时
TASK_TCB_LIST->delay_count==0){
if(max_TASK_TCB==null){
max_TASK_TCB=TASK_TCB_LIST;
continue;
}
//获取优先级最高的
if(max_TASK_TCB->level > TASK_TCB_LIST->level){
max_TASK_TCB=TASK_TCB_LIST;
}
}
}
}
step:
//运行时间+1
max_TASK_TCB->run_count++;
//当前的堆栈
task_mem_=&max_TASK_TCB->mem_task;
//保存当前运行的tcb
TCBIng=max_TASK_TCB;
//保存上次获得的最大优先级
back_task_tcb=max_TASK_TCB;
//返回堆栈的地址
return max_TASK_TCB->mem_task;
}[/mw_shl_code]
这就是zrtos的任务切换的核心部分,看了这些对rtos也算有一定的了解。请期待下一节,zrtos中API函数的使用。
这下来看看zrtos相关的api函数吧,顺便我也将源码传上来:
(任务)task相关的api:
void start_os(void);
start_os:开始操作系统的任务调度
void open_scheduling(void);
open_scheduling:调用该函数将会立刻立刻进行任务调度,任务自己放弃cpu
void enter_int(void);
endter_int:调用该函数后全局中断屏蔽
void exit_int(void);
exit_int:调用该函数后全局中断屏蔽打开
uint32 os_create_n(void(*fun_poi)(void* prg),void *prg,uint32 level,uint32 task_num);
os_create_n:
参数说明:
fun_poi:任务指向的函数
prg:要传递的参数
level:任务的优先级
task_num:任务占用的内存大小(task_num*4 bytes)
说明:创建一个任务
void z_os_start(void);
z_os_start;内部会创建一个空间任务(最低优先级),开启systick,然后调用start_os函数开始调度
void os_task_delay(uint32 count);
os_task_delay:任务延时,将cpu交给其它的任务
void os_task_del(void );
os_task_del:删除正在运行的本任务
(msg)消息邮箱:
TCB_MSG* msg_create(void);
msg_create:创建一个消息邮箱
void msg_close(TCB_MSG* mTCB_MSG);
msg_close:关闭一个消息邮箱
uint32 msg_get(TCB_MSG* mTCB_MSG,void **msg,uint32 msg_get_delay);
msg_get:
参数说明:
mTCB_MSG:消息邮箱的控制块
msg:消息的地址
msg_get_delay:消息的等待时间
说明:获取消息邮箱内的消息
uint32 msg_put(TCB_MSG* mTCB_MSG,uint32 msg,uint32 msg_get_delay);
msg_put:
参数说明:
mTCB_MSG:消息控制块
msg_get_delay:消息获取的超时时间
(SEM)信号量:
TCB_SEM* sem_create(void);
sem_create:创建一个信号量
void sem_free(TCB_SEM *mSEM);
sem_free:信号量释放
uint32 sem_post(TCB_SEM *mSEM);
sem_post:
参数说明:
mSEM:信号量控制块
说明:发送一个信号量
uint32 sem_pend(TCB_SEM *mSEM,uint32 sem_wait_delay);
sem_pend:
参数说明:
mSEM:信号量控制块
sem_wait_delay:信号量等待超时时间
互斥量:不讲了,和信号量的使用大同小异
工程的代码:
工程中创建到了3个任务,两个同优先级的,一个高优先级的。
工程是stm32的,连接上串口1,即可看到串口500ms打印一个a,1000ms打印一个字符b,1500ms打印一个字符c
zrtos操作系统.zip
(2.56 MB, 下载次数: 749)
2016-8-22 14:06 上传
点击文件名下载附件
源码可以查看zrtos下的操作系统相关的源码文件。