一.实验目的
1.了解并掌握UC/OS系统的移植方法和应用
2.更高层次掌握LPC2000系列的用法
二.实验器材
1.PC机
2.Keil软件或ADS软件
3.LPC2000系列开发板
三.要求
学习并掌握UC/OS移植原理 运用移植的系统 实现任务同步运行
四.实验原理
1.为什么使用UC/OS
·现在ARM系列的处理器其运算能力已经达到了50MHZ以上,但在我们设计大型的应用工程时,往往感觉逻辑不明,各部件之间协调不紧密。UCOS的产生就是为了解决这个问题
·市场上主流的嵌入式实时操作系统有Vxworks、pSos、WinCE、Linux等,但其在运用,学习上和UC/OS比,都不太适合我们学生阶段的学习和运用.
·UC/OS是开放源代码的嵌入式实时操作系统,便于分析和应用。
·UC/OS具有高度可移植性,方便快捷。
2. UC/OS 介绍
UC/OS (Micro Control Operation System)是一个可以基于ROM运行的、可裁减的、抢占式实时多任务内核(见图1)
(见图1)
UC/OS 可以简单的视为一个多任务调度器,在这个任务调度器之上完善并添加了和多任务操作系统相关的系统服务,如信号量、邮箱等。其主要特点有公开源代码,代码结构清晰、明了,注释详尽,组织有条理,可移植性好,可裁剪,可固化。内核属于抢占式,最多可以管理60个任务。从1992年开始,由于高度可靠性和安全性,UC/OS II已经广泛使用在从照相机到航空电子产品的各种应用中。
3.UCOS的移值
·UC/OS中的任务总是处于五种状态之一:睡眠态、就绪态 、 运行态 、等待状态和中断服务态。任何任务必须首先创建且就绪态之后才有可能运行.
·UC/OS的正常运行需要处理器平台满足以下要求:
a)处理器的C编译器能产生可重入代码。
b)用C语言就可以打开和关闭中断。
c)处理器支持中断,并且能产生定时中断(通常在10至100Hz之间)。
d)处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈。
e)处理器有将堆栈指针和其它CPU寄存器读出和存储到堆栈或内存中的指令。
我们的KEIL和ADS均支持UC/OS的要求。因此可以在LPC2000系列上移值。
4. UCOS系统结构
因此,我们所做的任务就是写与处理器相关的代码。主要有OS_CPU.H和OS_CPU_A.ASM和OS_CPU_C.C三个文件
五.移值步骤分析
5.1 设置与处理器及编译器相关的代码[OS_CPU.H]
不同的编译器会使用不同的字节长度来表示同一数据类型,所以要定义一系列数据类型以确保移植的正确性。下面是uC/OS II定义的一部分数据类型。
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;/*无符号8位*/
typedef signed char INT16S;/*带符号8位*/
typedef unsigned int INT16U;/*无符号16位*/
typedef signed int INT16S;/*带符号16位*/
typedef unsigned long INT32U;/*无符号32位数*/
typedef signed long INT32S;/*带符号32位数*/
typedef float FP32;/*单精度浮点数*/
typedef double FP;/*双精度浮点数*/
typedef unsigned int OS_STK;/*堆栈入口宽度*/
typedef unsigned int OS_CPU_SR;/*寄存器宽度*/
uC/OS II需要先关中断再访问临界区的代码,并且在访问完后重新允许中断。uC/OS II定义了两个宏来禁止和允许中断:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(),本移植实现这两个宏的汇编代码。
#define OS_ENTER_CRITICAL()(cpu_sr=OSCPUSaveSR()) /*Disable interrupts*/
#define OS_EXIT_CRITICAL()(OSCPURestoreSR(cpu_sr)) /*Enable interrupts*/
OSCPUSaveSR
MRS R0, CPSR ;关中断
ORR R1, R0, #0xC0
MSR CPSR_c, R1
MOV PC, LR
OSCPURestoreSR
MSR CPSR_c, R0 ;开中断
MOV PC, LR
5.2 用C语言实现与处理器任务相关的函数[OS_CPU_C.C]
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
实际需要修改的只有OSTaskStkInit()函数,其他五个函数需要声明,但不一定有实际内容。这五个函数都是用户定义的,所以OS_CPU_C.C中没有给出代码。如果需要使用这些函数,可以将文件OS_CFG.H中的#define constant OS_CPU_HOOKS_EN设为1,设为0表示不使用这些函数。
OSTaskStkInit()函数由OSTaskCreate()或OSTaskCreateExt()调用,需要传递的参数是任务代码的起始地址、参数指针(pdata)、任务堆栈顶端的地址和任务的优先级,用来初始化任务的堆栈,初始状态的堆栈模拟发生一次中断后的堆栈结构。堆栈初始化工作结束后,OSTaskStkInit()返回新的堆栈栈顶指针,OSTaskCreate()或OSTaskCreateExt()将指针保存在任务的OS_TCB中。调用OSTaskStkInit()给任务做一个初始的任务上下文堆栈,形状如图3。
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
opt = opt; /*防止警告 */
stk = ptos; /*堆栈 栈顶指针 */
*(stk) = (OS_STK)task; /* PC :任务进入 (R15) */
*(--stk) = (INT32U)0; /* LR (R14) */
*(--stk) = (INT32U)0; /* R12 */
*(--stk) = (INT32U)0; /* R11 */
*(--stk) = (INT32U)0; /* R10 */
*(--stk) = (INT32U)0; /* R9 */
*(--stk) = (INT32U)0; /* R8 */
*(--stk) = (INT32U)0; /* R7 */
*(--stk) = (INT32U)0; /* R6 */
*(--stk) = (INT32U)0; /* R5 */
*(--stk) = (INT32U)0; /* R4 */
*(--stk) = (INT32U)0; /* R3 */
*(--stk) = (INT32U)0; /* R2 */
*(--stk) = (INT32U)0; /* R1 */
*(--stk) = (INT32U)p_arg; /* R0 : 参数 */
*(--stk) = (INT32U)0x00000013; /* CPSR 管理模式 开启 总中断 */
return (stk);
}
5.3 处理器相关部分汇编实现
整个uC/OS II移植实现中,只需要提供一个汇编语言文件,提供几个必须由汇编才能实现的函数。
a)OSStartHighRdy()
该函数在OSStart()多任务启动之后,负责从最高优先级任务的TCB控制块中获得该任务的堆栈指针sp,通过sp依次将CPU现场恢复,此时系统就将控制权交给用户创建的该任务的进程,直到该任务被阻塞或者被其他更高优先级的任务抢占了CPU。该函数仅仅在多任务启动时被执行一次,用来启动第一个,也就是最高优先级的任务执行。
OSStartHighRdy
MSR CPSR_cxsf,#SVCMODE|NOINT ;Switch to SVC mode with IRQ&FIQ disable
BL OSTaskSwHook ;Call user define Task switch hook
LDR R0, =OSRunning ; OSRunning =TRUE
MOV R1, #1
STRB R1, [R0] ;告诉uC/OS-II自身已经运行
LDR R0, =OSTCBHighRdy ;OSTCBHighRdy是个指针 获得OSTCBHighRdy地址
LDR R0, [R0] ;获得最高优先级任务的TCB的地址
LDR SP, [R0] ;获得最高优先级任务的SP: TCB的第一个字是SP 栈顶指针
LDMFD SP!, {R0} ;从栈顶取得CPSR (从前备份的当前状态 相当于SPSR的备份)
MSR SPSR_cxsf, R0 ;恢复SPSR
LDMFD SP!, {R0-R12, LR, PC}^ ;任务切换 (同时 把SPSR复制到当前CPSR)
b)OSCtxSw()
该函数是任务级的上下文切换函数,在任务因为被阻塞而主动请求与CPU调度时执行,主要工作是先将当前任务的CPU现场保存到该任务堆栈中,然后获得最高优先级任务的堆栈指针,从该堆栈中恢复此任务的CPU现场,使之继续执行,从而完成一次任务切换。
OSCtxSw
STMFD SP!, {LR} ;保存返回的PC值 因为是从OS_Sched()跳转这里的
STMFD SP!, {R0-R12,LR} ;R0-R12 LR
MRS R0, CPSR ;保存CPSR
STMFD SP!, {R0}
LDR R0, =OSTCBCur ;把当前任务的栈顶指针保存到当前任务TCB控制块的堆栈指针变量中
LDR R0, [R0]
STR SP, [R0]
BL OSTaskSwHook
LDR R0, =OSTCBHighRdy ;设置最高优先级控制块堆栈指针到当前优先级任务控制块堆栈指针变量中
LDR R1, =OSTCBCur
LDR R0, [R0]
STR R0, [R1]
LDR R0, =OSPrioHighRdy ;设置最高优先级到当前优先级变量中
LDR R1, =OSPrioCur
LDRB R0, [R0]
STRB R0, [R1]
LDR R0, =OSTCBHighRdy ;获取最高优先级控制块堆栈指针 此时 堆栈已经切换了
LDR R0, [R0]
LDR SP, [R0]
LDMFD SP!, {R0} ;从栈顶取得CPSR (从前备份的当前状态 相当于SPSR的备份)
MSR SPSR_cxsf, R0 ;恢复SPSR
LDMFD SP!, {R0-R12, LR, PC}^ ;任务切换 (同时 把SPSR复制到当前CPSR)
C)OSIntExit()
该函数是中断级的任务切换函数,在时钟中断ISR中发现有高优先级任务在等待时,需要在中断退出后不返回被中断的任务,而是直接调度就绪的高优先级任务执行。其目的在于能够尽快让高优先级的任务得到响应,保证系统的实时性能。
d)OSTickISR()
该函数是时钟中断处理函数,主要任务是负责处理时钟中断,调用系统实现的OSTimeTick函数,如果有等待时钟信号的高优先级任务,则需要在中断级别上调度其执行。另外两个相关函数是OSIntEnter()和OSIntExit(),都需要在ISR中执行。
六:实验要求
利用void OSTimeDly (INT16U ticks)完成简单的任务切换,如
#include "LPC2220.h"
#include "ucos_ii.h"
#define TaskStkLengh 30
extern void Timer0Init(void);
void Task0(void *pdata);
void Task1(void *pdata);
OS_STK TaskStk [TaskStkLengh];
OS_STK TaskStk1 [TaskStkLengh];
void GPIO_INIT(void)
{
IO0DIR|=((0x07<<17)|(0x07<<4)); //复位后默认为I/O口 P0.17-P0.19置输出
}
void Delay(INT32U x)
{
while(x--);
}
void LEDDATA(INT32U x)
{
IO0SET=0x07<<17; //先将LED端口P0.17-P0.19灯熄灭
IO0CLR=0x01< void LEDDATA1(INT32U x) { IO0CLR=0x07<<4; //先将LED端口P0.4-P0.6 清零 IO0SET=(~x)<<4; } int main(void) { OSInit (); Timer0Init(); GPIO_INIT(); OSTaskCreate (Task0,(void *)0, &TaskStk[TaskStkLengh - 1], 2); OSTaskCreate (Task1,(void *)0, &TaskStk1[TaskStkLengh - 1], 3); OSStart (); } void Task0(void *pdata) { pdata = pdata; while (1) { LEDDATA(17); Delay(0x5fff); IO0SET=0x07<<17; Delay(0x5fff); OSTimeDly (5); } } void Task1(void *pdata) { int i; pdata = pdata; while (1) { for(i=0;i<3;i++) { LEDDATA1(0x01< Delay(0x3fff); } } } 实验效果: LED端口P0.4-P0.6轮流亮的同时,LED端口P0.17灯闪烁 目的 : 实现2个任务的同步运行 提高要求:利用信号量OS_EVENT 实现两个任务的同步运行