ucos_ii的移植
- 格式:pdf
- 大小:1.10 MB
- 文档页数:36
uC/OS-II是源码开放、可固化、可移植、可裁剪、可剥夺的实时多任务OS 内核,适用于任务多、对实时性要求较高的场合。
uC/OS-II适合小型系统,具有执行效率高、占用空间小、实时性优良和可扩展性等特点,最小内核可编译至2K。
uC/OS-II内核提供任务调度与管理、时间管理、任务间同步与通信、内存管理和中断服务等功能。
所谓RTOS移植,就是使一个实时内核能在某个微处理器或微控制器上运行。
大部分的uC/OS-II代码试用C写的,但仍需要用C和ASM写一些与处理器相关的代码,这是因为uC/OS-II在读写处理器寄存器时只能通过ASM实现。
要是uC/OS-II正常运行,处理器必须满足一定的条件:处理器的C编译器能产生可重入代码;用C语言就可以打开和关闭中断;处理器支持中断,并能产生定时中断;处理器支持能够容纳一定量数据的硬件堆栈;处理器有将SP和其他CPU reg读出和存储到堆栈或内存中的指令;uC/OS-II移植工作主要包括以下三个方面的内容:(1)修改与处理器核编译器相关的代码:主要在includes.h中,修改数据类型定义说明,OS_ENTER_CRITICAL()、OS_EXIT_CRITICAL()和堆栈增长方向定义OS_STK_GROWTH。
(2)用C语言编写10个移植相关的函数:主要在OS_CPU_C.C中,包括堆栈初始化OSTaskStkInit()和各种回调函数。
(3)编写4个汇编语言函数:主要在OS_CPU_A.ASM中,包括:_OSTickISR //时钟中断处理函数_OSIntCtxSW //从ISR中调用的任务切换函数_OSCtxSW //从任务中调用的任务切换函数_OSStartHighRdy //启动最高优先级的任务uC/OS-II移植的关键问题:(1)临界区访问:uC/OS-II需要先禁止中断再访问代码临界段,并且在访问完毕后重新允许中断,这就使得uC/OS-II能够保护临界段代码免受多任务或ISR的破坏。
ucOS-II移植到S3C2410笔记之一1: 问题:移植汇编函数:OSStartHighRdy, 如下,但发现只有OS_TaskIdle 一个任务时,都不能正确运行。
.globl OSStartHighRdy .type OSStartHighRdy, %functionOSStartHighRdy: /* C variable OSRunning = TRUE */ mov r0, #1 ldr r1, =OSRunning str r0, [r1] /* get the stack pointer of task that will be running. */ ldr r5, =OSTCBHighRdy /* R0= the address of pointer variable OSTCBHighRdy */ ldr r6, [r5] /* R1= the the value of pointer variable OSTCBHighRdy= OS_TCB(TaskIdle) */ ldr r7, [r6] /* R2= the value of pointer variable OS_TCB->OSTCBStkPtr, the value is address*/ /*ldr r8, [r7]*/ /* R3= the value of pointer*/ mov sp, r7 /* restore SP*/ ldr r1, [sp, #0] msr cpsr, r1 /* restore CPSR */ add sp, sp, #4 /* pointer to R0 location */ ldmia sp!,{r0-r12, lr, pc} /* restore R0-R12, LR, PC */2:实验,删掉如下代码(OSRunning=1,即上面红色部分代码)后,系统正常。
Ucos_II移植总结:之前已经基本算是成功的移植过ucos-II(内存管理部分没有处理),但是由于可恶的硬盘故障,让我的劳动成果付诸东流。
其间的一些移植经验没有及时总结,现在想来颇有点从头再来的悲壮!鉴于之前的教训,这次,边移植边总结,以防重蹈覆辙。
还好之前的移植过程已经解决了部分棘手的难题,现在复现一下权当是复习一下arm和ucos_II了。
这次的移植还是基于SEP4020芯片,其中的一些引导代码和中断处理代码还是照搬已经写好的代码吧,现在已经没有自己动手写的激情了!下面按照自己的移植步骤一步步总结吧:第一步:创建工程,将基本的启动代码照搬过来,建立一个最小系统,能够在开发板上运行成功。
第二步:将ucos-II源代码copy过来。
第三步:对基本的语法错误进行改正。
对工程进行编译,根据提示进行基本语法的改正。
主要包括:INCLUDES.h中头文件的调用第四步:对需要自己手动编写的函数首先要清空,防止编译报错,然后一步步手动编写代码。
1、临界段代码:os_cpu.h中OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()两个宏重新定义为我们自己写的开关中断函数。
Os_cpu_a.s文件添加如下代码:AREA MCUINIT , CODE, READONLYENTRY;/* 开启IRQ中断*/;voidEnableInterrupt(void);{EXPORT EnableInterruptEnableInterruptmrs r0,CPSRbic r0, r0, #0x80 ;set bit7 to 0msr CPSR_cxsf,r0movpc,lr ;Return to caller;};/* 关闭IRQ中断*/;voidDisableInterrupt(void);{EXPORT DisableInterruptDisableInterruptmrs r0,CPSRorr r0, r0, #0x80 ;set bit7 to 1msr CPSR_cxsf,r0movpc,lr ;Return to caller;}END2、OS_CPU_A.S文件代码编写AREA MCUINIT , CODE, READONLYENTRY;/* 开启IRQ中断*/;voidEnableInterrupt(void);{EXPORT EnableInterruptEnableInterruptmrs r0,CPSRbic r0, r0, #0x80 ;set bit7 to 0msr CPSR_cxsf,r0movpc,lr ;Return to caller;};/* 关闭IRQ中断*/;voidDisableInterrupt(void);{EXPORT DisableInterruptDisableInterruptmrs r0,CPSRorr r0, r0, #0x80 ;set bit7 to 1msr CPSR_cxsf,r0movpc,lr ;Return to caller;};任务切换代码OSCTXSWEXPORT OS_TASK_SW_ARMOS_TASK_SW_ARMSTMFD sp!, {lr} ; save pcSTMFD sp!, {lr} ; save lrMRS r14, SPSRSTMFD sp!, {r14} ; save current PSRSTMFD sp!, {r0-r12} ; save register file and ret address ;; OSPrioCur = OSPrioHighRdyIMPORT OSPrioCurIMPORT OSPrioHighRdyLDR r4, =OSPrioCurLDR r5, =OSPrioHighRdyLDRB r6, [r5]STRB r6, [r4]; Get current task TCB addressIMPORT OSTCBCurLDR r4, =OSTCBCurLDR r5, [r4]STR sp, [r5] ; store sp in preempted taskss TCB; Get highest priority task TCB addressIMPORT OSTCBHighRdyLDR r6, =OSTCBHighRdyLDR r6, [r6]LDR sp, [r6] ; get new tasks stack pointer; OSTCBCur = OSTCBHighRdySTR r6, [r4] ; set new current task TCB address;LDMFD sp!, {r0-r12} ; YYY+LDMFD sp!, {r14} ; YYY+; LDR r14, =0x000000D3MSR CPSR_cxsf, r14 ; YYY+;调试时屏掉此句才会跑的通,待解决LDMFD sp!, {lr,pc} ; YYY+;OS启动时开始运行创建的最高优先级任务; void OSStartHighRdy(void); ; Start the task with the highest priority;;EXPORT OSStartHighRdyOSStartHighRdyIMPORT OSTCBCurIMPORT OSTCBHighRdyIMPORT OSRunningLDR r4, =OSTCBCur ; Get current task TCB addressLDR r5, =OSTCBHighRdy ; Get highest priority task TCB addressLDR r5, [r5] ; get stack pointerLDR sp, [r5] ; switch to the new stackSTR r5, [r4] ; set new current task TCB address;OSRunning = 1 'TURE'LDR r4, =0x01 ; Get current task TCB addressLDR r5, =OSRunning ; Get highest priority task TCB addressSTRB r4, [r5];LDMFD sp!, {r0-r12} ; start the new taskLDMFD sp!, {r14} ; get new state from top of the stackMSR CPSR_cxsf, r14 ; CPSR should be SVC32ModeLDMFD sp!, {lr,pc};中断级任务切换EXPORT OSIntCtxSwOSIntCtxSwIMPORT OSTCBCurIMPORT OSPrioCurIMPORT OSTCBHighRdyIMPORT OSPrioHighRdyIMPORT OSTaskSwHookBL OSTaskSwHook;OSTCBCur = OSTCBHighRdyLDR r4, =OSTCBCurLDR r5, =OSTCBHighRdyLDR r6, [r5]STR r6, [r4];OSPrioCur = OSPrioHighRdyLDR r4, =OSPrioCurLDR r5, =OSPrioHighRdyLDRB r6, [r5]STRB r6, [r4];sp = OSTCBHighRdy->OSTCBStkPtrLDR r6, =OSTCBHighRdyLDR r6, [r6]LDR sp, [r6] ; get new tasks stack pointerLDMFD sp!,{r0, r1};在timedly中断服务程序中,函数开始压栈两个寄存器,为保证堆栈中数据一致,需出栈对齐;resume registersLDMFD sp!, {r0-r12} ; start the new taskLDMFD sp!, {r14} ; get new state from top of the stack; LDR r14, =0x000000D3MSR CPSR_cxsf, r14 ; CPSR SVC32Mode调试时屏掉此句才会跑的通,待解决LDMFD sp!, {lr,pc}END中断服务程序代码IRQ_DOstmfd sp!, {r0,r1}ldr r0, =IRQ_R1str r1, [r0]ldmfd sp!, {r0}ldr r1, =IRQ_R0str r0, [r1] ;保存R0和R1寄存器(因为这两个寄存器再后面要用到)add r13, r13, #4 ;restore the sp_irq top to original irq topsub r14, r14, #4mov r0, r14 ;LR_irq(R14)减4并保存在R0mrs r1, spsrorr r1, r1, #0x80 ;将SPSR_irq的中断屏蔽位置‘1’(屏蔽中断),并保存再R1 中msr cpsr_cxsf, r1 ;将模式切换到中断前的模式;---------------------------------------------------------------------------------------------bic r1, r1, #0x80 ;将原先保存的SPSR_irq的R1的中断屏蔽位清零(允许中断)stmfd sp!, {r0}stmfd sp!, {r14}stmfd sp!, {r1} ;依次将R0,R14,R1的值压入中断前模式下的堆栈(当前R0,R14,R1中存放的分别是LR_irq-4,中断前模式下的LR,SPSR_irq)ldr r0, =IRQ_R1ldr r1, [r0]stmfd sp!, {r1}ldr r1, =IRQ_R0ldr r0, [r1]stmfd sp!, {r0}ldmfd sp!, {r0,r1} ;恢复原先保存的R0和R1stmfd sp!, {r0-r12} ;将r0--r12全部压入中断以前模式下的堆栈;; Get current task TCB addressIMPORT OSTCBCurLDR r4, =OSTCBCur;及时保存当前任务中断,因为可能会进行任务切换LDR r5, [r4]STR sp, [r5] ; store sp in preempted taskss TCB;-----------------------------IMPORT int_vector_handlerbl int_vector_handler ;跳转到中断源判断和中断处理程序;----------------------------- ;restore the registerldmfd sp!, {r0-r12} ;恢复原先保存的R0-R12ldmfd sp!, {r14}msr cpsr_cxsf, r14ldmfd sp!, {r14} ;将原先保存的SPSR_irq恢复到CPSR中ldmfd sp!, {pc}3、堆栈初始化函数OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) {unsignedint *stk;opt = opt; /* 'opt' is not used, prevent warning */stk = (unsigned int *)ptos; /* Load stack pointer *//* build a context for the new task */*--stk = (unsigned int) task; /* pc */*--stk = (unsigned int) task; /* lr */*--stk = (0x60000053); /* cpsr IRQ, FIQ disable*/*--stk = 0; /* r12 */*--stk = 0; /* r11 */*--stk = 0; /* r10 */*--stk = 0; /* r9 */*--stk = 0; /* r8 */*--stk = 0; /* r7 */*--stk = 0; /* r6 */*--stk = 0; /* r5 */*--stk = 0; /* r4 */*--stk = 0; /* r3 */*--stk = 0; /* r2 */*--stk = 0; /* r1 */*--stk = (unsigned int) pdata; /* r0 */// *--stk = (0x0); /* spsr IRQ, FIQ disable */return ((void *)stk);}4、timertick函数void Timer_IRQ_Service1(void){U32 dummyread;U8 y;dummyread = *(RP)TIMER_T1ISCR;/* timerflag = 1;*///OSIntNesting = OSIntNesting + 1;clear_reg( TIMER_T1CR, 0);//关闭通道1中断OSTimeTick ();set_reg( TIMER_T1CR, 0);//使能通道1中断OS_ENTER_CRITICAL();if ((OSIntNesting == 0) && (OSLockNesting == 0)) { /* Sched. only if all ISRs done & not locked */y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);if (OSPrioHighRdy != OSPrioCur) { /* No CtxSw if current task is highest rdy */OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];OSCtxSwCtr++; /* Increment context switch counter *///OS_TASK_SW(); /* Perform a context switch */OSIntCtxSw();}}OS_EXIT_CRITICAL();}第五步:对移植好的代码进行调试。
UCOS II在PC上的移植网上移植教程有不少,不过对于初学者还是容易出问题,在这里将移植的详细过程记录如下,建议有兴趣的同学,找台电脑,从头试一遍,这样就算是入门了.一、准备工作在PC上移植ucos系统,因为ucos系统的源代码是c语言写的,因此编译C的软件必不可少。
在pc机上运行,还需要对pc的设备进行一空的控制,会用到汇编语言,因此汇编语言的编译软件也必不可少。
再有就是操作系统的源码,这些都准备好了,就可以进行移植了。
一些教材在移植是c编译环境选BORLAND C++ 4.5,汇编编译用TASM5.0,网络上能找到的移植方法基本都是基于这2个软件的。
这2个编译软件和操作系统源码可以通过网络下载。
图1 ucos移植的必备文件下载解压后,如图1所示。
下边开始安装,编译软件。
BORLAND C++和TASM5.0安装顺序不会影响到使用,在安装之前先来看下c盘的文件结构。
在图2中,c盘根目录下只有3个文件夹,当我们配置完成后,会多出4个文件夹。
图2 编译环境安装前c盘文件结构二、开始安装1.安装编译软件BORLAND C++ 4.5。
在BORLAND C++ 4.5安装文件包里找到找到install.exe文件并双击,默认的安装路径就C:\BC45。
因此安装时,可以用默认设置一直继续,安装过程如图3所示(注意安装包里还有一个setup文件,请不要用它来安装)。
图3 bc4.5安装界面2.安装汇编编译软件TASM5.0(1)这一步如果不小心,很容易安装不正确。
先在C盘建立一个名为TASM的文件夹,然后把TASM5.0安装文件里的所有文件都复制进去。
如图4所示,双击图4中的install 文件开始安装。
图4 TASM5.0安装(2)在弹出的界面按回车键继续,出现安装选择文件界面,将默认的A改为C如图5所示。
图5 修改盘符(3)按回车键继续,出现一个路径设置的界面,继续按回车键,出现安装配置界面如图6所示。
一步步移植uCOS-IIandLwIP(二)2、ethernetif.c函数接口编写ethernetif.c文件主要涉及5个接口函数的编写:static void low_level_init(struct netif *netif);static err_t low_level_output(struct netif *netif,struct pbuf *p);static struct pbuf * low_level_input(struct netif *netif);err_t ethernetif_input(struct netif *netif);err_t ethernetif_init(struct netif *netif);(1)low_level_init(struct netif *netif)从函数的声明可以看出,我们的主要工作是对struct netif定义的netif初始化,查阅LwIP源文件找到struct netif结构体的定义(删除条件编译后的基本定义):struct netif {/** pointer to next in linked list */struct netif *next;/** IP address configuration in network byte order */ip_addr_t ip_addr;ip_addr_t netmask;ip_addr_t gw;/** This function is called by the network device driver* to pass a packet up the TCP/IP stack. */netif_input_fn input;/** This function is called by the IP module when it wants* to send a packet on the interface. This function typically * first resolves the hardware address, then sends the packet. */netif_output_fn output;/** This function is called by the ARP module when it wants* to send a packet on the interface. This function outputs* the pbuf as-is on the link medium. */netif_linkoutput_fn linkoutput;/** This field can be set by the device driver and could point* to state information for the device. */void *state;/** maximum transfer unit (in bytes) */u16_t mtu;/** number of bytes used in hwaddr */u8_t hwaddr_len;/** link level hardware address of this interface */u8_t hwaddr[NETIF_MAX_HWADDR_LEN];/** flags (see NETIF_FLAG_ above) */u8_t flags;/** descriptive abbreviation */char name[2];/** number of this interface */u8_t num;};其中typedef err_t (*netif_input_fn)(struct pbuf *p,struct netif *inp);typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr);typedef err_t (*netif_linkoutput_fn)(struct netif *netif,struct pbuf *p);在这里我们将DM9000底层的MAC地址及初始化写入该函数内,其代码如下:static void low_level_init(struct netif *netif){/* set MAC hardware address length */netif->hwaddr_len = ETHARP_HWADDR_LEN;/* set MAC hardware address */netif->hwaddr[0] = MAC_ADDR0;netif->hwaddr[1] = MAC_ADDR1;netif->hwaddr[2] = MAC_ADDR2;netif->hwaddr[3] = MAC_ADDR3;netif->hwaddr[4] = MAC_ADDR4;netif->hwaddr[5] = MAC_ADDR5;/* maximum transfer unit */netif->mtu = 1500;/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;/* Do whatever else is needed to initialize interface. */DM9000_Init();}(2) struct pbuf * low_level_input(struct netif netif)该函数主要是用来实现将网卡中接收到的数据转换为LwIP定义的struct pbuf类型的数据包,便于协议栈数据处理。
第7章移植7.1 移植说明μC/OS-II作为嵌入式实时操作系统,最终要应用在嵌入式系统上,如单片机、ARM、FPGA、DSP等。
为方便读者学习,本书前面6章的例子都是在Windows下的虚拟平台上运行的,也就是将μC/OS-II移植到这个平台来加以分析和实践。
本章首先给出的就是说明如何移植到这个平台的。
接下来就是在一个实际的嵌入式系统,基于软核的FPGA系统下的移植。
在掌握了这两个平台的移植后,移植到其他的平台都是不难实现的。
学习了前面章节的内容,从原理到代码都有了足够的了解,因此进行移植将不会有太大困难。
但在前面章节中,第1章是原理的介绍,其余几章各有各的核心内容,并未将μC/OS-II 的代码结构给出,读者不太容易掌握μC/OS-II的全貌,因此这里给出μC/OS-II的代码结构。
7.1.1 μC/OS-II的代码结构μC/OS-II的代码结构如图7.1所示。
嵌入式实时操作系统μC/OS原理与实践246内核代码信号量管理互斥信号量管理消息邮箱管理消息队列管理事件标志组管理内存管理定时器管理任务管理操作系统头文件时间管理处理器相关头文件处理器相关C代码处理器相关汇编代码与CPU无关的代码与CPU相关的代码操作系统配置文件图7.1 μC/OS-II的代码结构可见,操作系统的代码分为与CPU无关的代码和与CPU相关的代码两部分。
这样,在进行移植的时候只需修改与CPU相关的代码部分。
尽管如此,为详细了解μC/OS-II代码结构,需要对该结构中各个部件进行说明。
1.操作系统配置文件os_config.hμC/OS-II是可裁剪的操作系统,最终的代码可以包含或不包含诸如各种时间管理、内存管理的代码,甚至可以选择不包含任务删除代码等。
为实现这一目的,在代码中采用了很多条件编译语句。
另外,条件编译语句使用的各种宏就在os_config.h文件中定义。
os_config.h是操作系统的配置文件,其中的所有代码全部是宏定义。
读者可以将该文件理解为操作系统的部件选择器。
如果某个部件设置未打开,那么相关的内容就会被编译出现在最终的操作系统可执行代码中,否则不会包含这部分的代码。
例如,如果将os_config.h文件中的宏OS_ARG_CHK_EN设置为0:#define OS_ARG_CHK_EN 0u操作系统内核中所有代码中都不会进行参数检查。
如果读者的代码不需要使用事件标志组管理,那么可以将将os_config.h文件中的:#define OS_FLAG_EN 1u修改为:#define OS_FLAG_EN 0u即可在最后的可执行代码中不包含所有的和事件标志组有关的代码,这样可以减小内核的内存占用量。
2.操作系统头文件ucos_ii.h操作系统头文件ucos_ii.h中包含大量的内容,不仅包含一些有用的宏定义,还包含诸如第7章 移 植247任务控制块、事件控制块、内存控制块、就绪表等数据类型的定义,以及操作系统函数的声明。
或者说,它包含了除配置信息和与CPU 有关的宏和定义之外的所有需要在头文件中包含的内容。
下面分类加以说明。
(1)ucos_ii.h 中的第一个主要部分是一些宏定义。
简单说明如下:操作系统头文件ucos_ii.h 中首先包含一些杂项的宏定义,如函数调用表示任务本身的优先级的OS_PRIO_SELF 、系统中系统任务数OS_N_SYS_TASKS 、统计任务的优先级OS_TASK_STAT_PRIO 、事件等待表的大小OS_EVENT_TBL_SIZE 等。
然后是任务状态的宏定义,包括OS_STA T_RDY 、OS_STA T_SEM 、OS_STA T_MBOX 等。
任务等待状态的宏定义,包括OS_STA T_PEND_OK 、OS_STA T_PEND_TO 、OS_STA T_PEND_ABORT 。
这些宏用来对任务控制块中的任务状态和任务等待状态赋值及进行状态查询。
接着是事件控制块类型的6个宏定义、事件标志请求类型的宏定义、事件删除类型的宏定义、事件等待及提交类型的宏定义等和事件管理有关的一些宏定义。
然后是使用OSTaskCreateExt 进行任务创建的选项,包括OS_TASK_OPT_NONE 、OS_TASK_OPT_STK_ CHK 、OS_TASK_OPT_STK_CLR 、OS_TASK_OPT_SA VE_FP 。
我们在前面各章节经常看到参数为无符号整型的指针用来获得函数执行过程中返回执行过程中出错的信息,这些信息码的宏定义在ucos_ii.h 占了很大一块,如OS_ERR_NON 、OS_ERR_EVENT_TYPE 、OS_ERR_PEND_ISR 、OS_ERR_Q_FULL 、OS_ERR_PIP_LOWER 等。
(2)第二个主要部分是一些常用的类型定义,如任务优先级类型OS_PRIO 、事件控制块OS_EVENT 、事件标志组OS_FLAG_GRP 、事件标志节点OS_FLAG_NODE 、消息邮箱数据OS_MBOX_DA TA 、内存控制块OS_MEM 、消息队列OS_Q 等。
然后是事件控制块OS_TCB 的定义。
(3)第三个主要部分是全局变量的定义。
这些全局变量是多任务共享的,访问时必须在临界区访问。
因为这些全局变量非常重要,其中就包括了就绪表和就绪组。
将其中最重要的部分用表7.1列出。
表7.1 操作系统头文件ucos_ii.h 中包含的重要全局变量嵌入式实时操作系统μC/OS原理与实践248 续表(4)第四部分是操作系统内核函数的定义。
因为内核函数分布于不同的文件,因此各函数的声明都在操作系统头文件ucos_ii.h中,各文件又都包含此头文件,这样就可以互相调用了。
3.操作系统内核C文件如图7.1所示左半部分的C文件都是操作系统内核的C文件。
其中,os_core.c是内核中最核心的部分,包含了优先级查找表OSUnMapTbl这一数组,然后是各种核心的函数。
首先是各种初始化函数,包含了操作系统初始化函数OSInit、杂项全局变量初始化函数OS_InitMisc、就绪表初始化函数OS_InitRdyList、空闲任务初始化函数OS_InitTaskIdle、初始化空闲事件列表OS_InitEventList、统计任务初始化函数OS_InitTaskStat、任务控制块初始化函数OS_InitTCBList。
然后是中断进入函数OSIntEnter、中断离开函数OSIntExit、调度器上锁函数OSSchedLock、调度器解锁函数OSSchedUnlock、操作系统启动多任务函数OSStart、操作系统调度函数OS_Sched中与CPU无关的部分、时钟中断服务程序中与CPU无关部分OSTimeTick等核心系统调用。
os_core.c中还包含事件管理中的公用函数,如取消事件等待任务OS_EventTaskRemove、第7章 移 植249设置任务的时间等待OS_EventTaskWait 、等待事件的任务就绪OS_EventTaskRdy 等。
最后,os_core.c 还包含了堆栈检查函数OS_TaskStatStkChk 和两个系统任务空闲任务和统计任务的任务代码。
任务管理C 文件os_task.c 包含了任务管理中的创建、删除、请求删除等文件。
os_time.c 中包含了时间管理中的所有文件,如事件延时、按分秒延时、取消延时、设置和获取系统时间等。
4.与CPU 相关代码部分操作系统中与CPU 相关部分的代码包含了头文件os_cpu.h 和C 文件os_cpu.c ,并根据开发的类型,可能包含汇编文件os_cpu_a.asm 。
这些代码包含了和CPU 相关的数据类型定义,如INT8U 、INS8S 、WORD 、LONG 等。
和CPU 密切相关的任务切换中的堆栈操作等也在这部分代码中。
这部分代码将在7.1.2节详细论述。
7.1.2 操作系统中与CPU 相关的代码解析操作系统中与CPU 相关部分的代码包含了头文件os_cpu.h 和C 文件os_cpu.c ,并根据开发的类型,可能包含汇编文件os_cpu_a.asm 。
如果用户使用的编译器支持嵌入式汇编,那么可以不写os_cpu_a.asm ,而将这部分代码写入os_cpu.c ,否则就必须写os_cpu_a.asm 。
本书假设编译器支持C 代码中包含汇编文件,实际上很多编译器都支持该种方法,如果确实需要写os_cpu_a.asm ,读者可以将其中部分代码分割出来写在os_cpu_a.asm 。
1.头文件os_cpu.hos_cpu.h 包含和CPU 相关的不能通用的数据类型的定义、变量声明、函数代码的声明部分。
因为os_cpu.h 是移植部分需要修改的重要内容,所以需要详细说明。
因为对于不同的硬件,使用不同的开发工具、编译器进行开发,因此同样结构的数据类型,在不同的开发环境下定义可能是不同的。
因此,这些数据类型的定义在os_cpu.h 中给出。
os_cpu.h 中定义的数据类型在表7.2中说明。
表7.2 os_cpu.h 定义的数据类型嵌入式实时操作系统μC/OS 原理与实践250举一个例子说明数据类型的定义:typedef unsigned char INT8U;即在VC 环境下对INT8U 的定义为unsigned char 。
对于不同的系统,进入临界区的方法可以不同。
假设对80x86计算机,os_cpu.h 中关于进入临界区的方法示例代码及其他代码如程序7.1所示。
程序7.1 os_cpu.h 除类型定义外的其他代码示例extern BOOLEAN FlagEn; /*FlagEn 应在用户编写的主程序main.c 中声明*/ #define OS_CRITICAL_METHOD 1 /*访问临界区采用方法1*/#if OS_CRITICAL_METHOD == 1/*如果采用方法1访问临界区*/#define OS_ENTER_CRITICAL() FlagEn=0 /*设置全局变量FlagEn 为0,禁止定时器调度*/ #define OS_EXIT_CRITICAL() FlagEn=1 /*设置全局变量FlagEn 为1,允许定时器调度* /#endif#if OS_CRITICAL_METHOD == 2/*如果采用方法2访问临界区*/#define OS_ENTER_CRITICAL() _asm {PUSHF; CLI} /*PSW 入栈,关中断,定时器中断被禁止*/ #define OS_EXIT_CRITICAL() _asm POPF /*PSW 出栈,开中断,定时器中断被允许 */ #endif#if OS_CRITICAL_METHOD == 3/*如果采用方法3访问临界区*//*保存CPU 状态寄存器的值到局部变量,关中断,定时器中断被禁止*/ #define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR()) /*从局部变量恢复CPU 状态寄存器,开中断*/#define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr))第7章 移 植251#endif#define OS_STK_GROWTH 1 /*堆栈从高地址向低地址增长*/#define OS_TASK_SW() OSCtxSw()/*宏定义,OS_TASK_SW()等同于函数OSCtxSw()*//*如果采用方法3保存和恢复CPU 中的状态寄存器PWS ,声明两个函数用于保存和恢复 */ #if OS_CRITICAL_METHOD == 3 OS_CPU_SR OSCPUSaveSR(void);void OSCPURestoreSR(OS_CPU_SR cpu_sr); #endif程序7.1中除了宏OS_STK_GROWTH 为堆栈增长方向的宏及OS_TASK_SW()的宏定义,其他都和临界区的访问方法相关。