(1)自写zrtos,实现多任务,多优先级,支持消息邮箱,信号量等的简易操作系统

4 篇文章 0 订阅
2 篇文章 0 订阅

该系统是我在阅读了ucos后,并且借鉴了其它操作系统后实现的,完成了一些基础的工作,可以拿过来学习,也可以进行简单的应用。

声明:仅供学习!

要自写一个操作系统,我们首先要实现的就是任务切换,再其次是任务调度(高优先级先执行,同优先级时间片分割执行),然后再是消息邮箱,信号量,互斥量等等。

下面我们来看看任务切换的实现,这部分的代码只能由汇编语言来完成,在handler.s中有任务切换的具体代码:

我们的任务板是stm32,stm32是基于cortex-m3内核的,所以在写这个系统的时候很多参照了cortex-m3内核的文档,其中包括了pendsv中断向量,与中断压栈以及堆栈指针等方面。在设计中中断任务我们用的是msp堆栈指针,而我们的任务使用的是psp指针,我们在汇编代码中开启了psp指针的使用以后就,cpu就会自动的进行切换了:

开全局与关全局中断的代码:

enter_int
			export enter_int
			CPSID I ;PRIMASK=1 关中断
			BX LR ;返回
exit_int
			export exit_int
			CPSIE I	;开中断
			BX LR ;返回

第一次启动操作系统,我们往往需要初始化psp指针,设置pendsv中断为最低的优先级,然后开始一次调度:

;开启操作系统
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 ;PSP = 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


软件开启pendsv中断的代码:

;出发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

最后就死pendsv中断中的代码:

;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
					

下面是最最重要的,任务调度器,任务调度器主要是找到最高优先级的任务,并且轮询调度,还要保存要运行的任务的控制块,在task.c中:

//任务调度函数
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<TASK_TCB_NUM;i++){
			if(back_task_tcb==TASK_TCB_LIST[i]){
				spotted=1;
				continue;
			}
			//确保是没有被调度过的任务
			if(spotted==1){
				if(TASK_TCB_LIST[i]!=null&&
					back_task_tcb->level==TASK_TCB_LIST[i]->level){
					max_TASK_TCB=TASK_TCB_LIST[i];
					goto step;
				}
			}
		}
	}
	for(i=0;i<TASK_TCB_NUM;i++){
		if(TASK_TCB_LIST[i]!=null){
			if(TASK_TCB_LIST[i]->status==true&&
				//任务没有被延时
					TASK_TCB_LIST[i]->delay_count==0){
				if(max_TASK_TCB==null){
					max_TASK_TCB=TASK_TCB_LIST[i];
					continue;
				}
				//获取优先级最高的
				if(max_TASK_TCB->level > TASK_TCB_LIST[i]->level){
					max_TASK_TCB=TASK_TCB_LIST[i];
				}
			}
		}
	}
	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;
}
这就是zrtos的任务切换的核心部分,看了这些对rtos也算有一定的了解。请期待下一节,zrtos中API函数的使用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值