rtos系统c语言,让我们来学习RTOS,自己写RTOS

本帖最后由 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

4bf43b75943419c055cc812f1f911a84.gifRIMASK=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

4bf43b75943419c055cc812f1f911a84.gifSP = 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

c9a02a253b390e823b3f3387aa73f3d8.gif

zrtos操作系统.zip

(2.56 MB, 下载次数: 749)

2016-8-22 14:06 上传

点击文件名下载附件

源码可以查看zrtos下的操作系统相关的源码文件。

你可能感兴趣的:(rtos系统c语言)