基于ARM11的μC/OS-II操作系统内核移植实验的设计
2016-09-06滕艳平贾思禹李丽丽
滕艳平, 贾思禹, 金 梅, 李丽丽
(齐齐哈尔大学 计算机与控制工程学院, 黑龙江 齐齐哈尔 161006)
基于ARM11的μC/OS-II操作系统内核移植实验的设计
滕艳平, 贾思禹, 金梅, 李丽丽
(齐齐哈尔大学 计算机与控制工程学院, 黑龙江 齐齐哈尔161006)
在分析嵌入式实时操作系统μC/OS-II内核结构的基础上,针对ARM11微处理器,提出了μC/OS-II移植的方案,并在所移植的操作系统上进行了多任务同步设计。通过RVDS集成开发环境的测试结果表明,移植后的操作系统运行正常,实现了多个任务之间的切换,并满足系统对实时性、稳定性的需求。
操作系统; 内核移植; ARM11; μC/OS-II; RVDS
ARM11系列微处理器是ARM公司近年推出的新一代RISC处理器,它具有ARM新指令架构,提供高性能处理能力,使其在嵌入式系统开发中得到广泛的应用。嵌入式实时操作系统μC/OS-II以其实时性强、内核公开、易于学习和开发等特点受到广大技术人员和嵌入式爱好者的青睐[1-3]。本文以μC/OS-II为系统设计平台,给出其内核代码在ARM11系列微处理器上移植的实验步骤,并在所移植的操作系统上进行多任务的同步设计,完成多任务之间的切换。
1 μC/OS-II内核移植框架
在μC/OS-II内核的移植时,需要考虑编译器的选取和ARM11工作模式的选取、移植中需要修改的文件以及利用ARM11软中断实现系统调用等相关问题。移植框架见图1所示。
图1 μC/OS-II操作系统移植框架
2 移植实验步骤
在移植工作中,需要对μC/OS-II内核中的OS_CPU.H、OS_CPU_A.S和OS_CPU_C.C等3个文件进行重新编写,以下面给出具体的实现步骤。
2.1文件OS_CPU.H的实验设计
在编写文件OS_CPU.H时,主要包括声明10个数据类型、声明3个宏以及定义堆栈的增长方向。
2.1.1数据类型的定义
在定义数据类型时使用了typedef关键字,这样做的目的,一方面是为了创建与平台无关的数据类型,另一方面也为移植提供了便利。由于在移植代码中大量使用隐含着不可移植性的数据类型,例如short、int、long等,当移植失败后需要修改数据类型时,却需要多处修改,浪费时间。而使用typedef关键字创建与平台无关的数据类型后,当移植失败后需要修改数据类型时,只需要修改typedef定义即可实现批量修改。在文件OS_CPU.H中,这些数据类型的定义如下:
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned int INT32U;
typedef signed int INT32S;
typedef float FP32;
typedef double FP64;
typedef INT32U OS_STK;
2.1.2宏定义
在OS_CPU.H文件中,还需定义OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()两个宏。OS_ENTER_CRITICAL()用于关中断;而OS_EXIT_CRITICAL()用于开中断。这两个宏直接调用OS_CPU_A.ASM文件中的汇编语言函数OSCPUSaveSR()、OSCPURestoreSR()来实现关中断和开中断操作。之所以采用汇编语言来实现关中断和开中断操作,主要是从实时性的角度来考虑的。
C语言与汇编语言的混合编程为嵌入式操作系统的开发提供了极大的便利,例如:对ARM11的CPSR寄存器无法使用C语言直接访问,但是只要嵌入一小段简单的汇编代码就可以实现这一功能。需要注意的是,在C语言与汇编语言混合编程的程序中,参数是通过通用寄存器进行传递的,在OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()的宏定义中,cpu_sr的值正是通过通用寄存器R0进行传递的。
在OS_CPU.H文件中定义OS_TASK_SW()宏,用于实现任务的切换,这个宏定义同样是直接调用OS_CPU_A.ASM文件中的汇编语言函数OSCtxSw()来实现任务的切换。
2.1.3堆栈的生长方向的定义
存储器堆栈可分为两种:升栈,是向高地址方向生长,全称为递增堆栈;降栈,是向低地址方向生长,全称为递减堆栈。堆栈指针指向最后压入的堆栈的有效数据项,称为满堆栈;堆栈指针指向下一个要放入的空位置,称为空堆栈[4]。这样就有4种类型的堆栈表示递增和递减的满堆栈和空堆栈的各种组合:
(1) 满升栈:堆栈通过增大存储器的地址向上增长,堆栈指针指向内含有效数据项的最高地址;
(2) 空升栈:堆栈通过增大存储器地址向上增长,堆栈指针指向堆栈上的第;
(3) 满降栈:堆栈通过减小存储器的地址向下增长,堆栈指针指向内含有效数据项的最低地址;
(4) 空降栈:堆栈通过减小存储器地址向下增长,堆栈指针指向堆栈下的第一个空位置。
ARM11处理器对于4种类型的堆栈均给予支持,但考虑到本文所用的RVDS编译器仅支持一种增长方式,即从上往下增长,并且必须是满递减堆栈。因此将OS_STK_GROWTH的值设为1,即使用满降栈。其定义如下:
#define OS_STK_GROWTH1
2.2文件OS_CPU_A.S的实验设计
在OS_CPU_A.S文件的修改中,涉及汇编函数OSStartHighRdy()、OSCtxSw()、OSTickISR()、OSIntCtxSw()、OSCPUSaveSR()以及OSCPURestoreSR()等的重新编写。
2.2.1编写OSStartHighRdy()函数
使就绪状态的任务开始运行的函数是OSStart(),也就是说当程序运行到OSStart()之后就进入μC/OS-II多任务环境了;而高优先级就绪任务启动函数OSStartHighRdy()用于运行多任务启动前优先级最高的任务,该函数仅仅在多任务启动时被执行一次,用来启动第一个——也就是最高优先级的任务执行。其实现代码如下:
OSStartHighRdy
MSRCPSR_cxsf,#SVCMODE|NOINT
BLOSTaskSwHook
LDRR0, =OSRunning
MOVR1, #1
STRBR1, [R0]
LDRR0, =OSTCBHighRdy
LDRR0, [R0]
LDRSP, [R0]
LDMFDSP!, {R0}
MSRSPSR_cxsf, R0
LDMFDSP!, {R0-R12, LR, PC}^
分析:在上述代码的第一部分,首先对ARM11处理器的程序状态字寄存器CPSR进行赋值,SVCMODE、NOINT是使用ARM汇编指令EQU将它们分别赋值为0x13、0xc0,对这两个值进行按位或操作后得出的值,可以将ARM11处理器运行在SVC模式,并且屏蔽普通中断及快速中断。
SVC模式是ARM11处理器一种保护模式,可以将其理解为一种特权模式,在此模式下具有更高的执行权限。此后将OSRunning的值置为1,此时的LDR指令是伪指令,并不是普通的寄存器加载指令。然后将最高优先级任务的堆栈指针赋值给SP,并且将最高优先级任务的堆栈中保存的数据恢复到寄存器中,从而实现了最高优先级就绪任务的启动。ARM11的CPSR寄存器如图2所示。
图2 ARM11的CPSR寄存器
2.2.2编写OSIntCtxSw()函数
μC/OS-II在每个时间片都要进行任务的调度。调度的结果或者是返回原来的任务继续执行,或者是因为找到了就绪的更高优先级的任务而让该任务运行。用户时钟中断服务程序OSTickISR,也就是时钟节拍服务程序,是操作系统的核心内容。OSIntCtxSw()是一个中断级的任务切换函数,它是在中断程序中调用的。在μC/OS-II中,由于中断的产生可能会引起任务切换,在中断服务程序的最后会调用OSIntExit()函数检查任务就绪状态,如果需要进行任务切换,将调用OSIntCtxSw()。其中OSIntCtxSw()的实现代码如下:
OSIntCtxSw:
OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
SP =OSTCBHighRdy->OSTCBStkPtr;
RestoreNew task context
分析:OSCtxSw()和OSIntCtxSw()都是用于任务切换的函数,其区别在于:在OSIntCtxSw()中无需再保存处理器寄存器,因为在OSIntCtxSw()之前已发生中断,所以可以保证所有的处理器寄存器都被正确地保存到了被中断的任务的堆栈之中。
2.3文件OS_CPU_C.C的实验设计
在编写文件OS_CPU_C.C时,用C语言编写get_x()、get_int_vect()、OSTaskStkInit()等函数[5-6]。以下重点给出了中断检测函数get_x()的代码设计过程。
ARM1176JZF-S处理器支持2组共64种中断源,中断处理对于μC/OS-II的移植是必不可少的。这里2组中断源VICO和VIC1的寄存器基地址分别为0x71200000和0x71300000,状态寄存器的偏移地址为0x000,通过访问这一只读寄存器即可知道当前中断源的屏蔽状态。其实现关键代码如下:
unsigned int get_x(unsigned long x)
{int i;
for(i=0;i<32;i++)//该循环语句用于测试x的每一位是否为1
{if(x&(1<
return i;
}
return 0;
}
unsigned int get_int_vect()
{unsigned int ret;
unsigned long status;
status = * ((unsigned long*)0x71200000); //将VIC0的地址保存到STATUS变量if(status)
{ret = get_x(status);
return ret; }//在STATUS不为0的情况下检测其中为1的位的位号
else
{status = * ((unsigned long*)0x71300000); //将VIC1的地址保存到STATUS变量if(status)
{ret = get_x(status)+32;//一个寄存器占32位
return ret;
}
}
}
分析:根据S3C6410处理器的结构和特点来确定任务的堆栈结构,特别在OSTaskStkInit()函数代码的编写中,其初始化参数由R0来传递,而数值0x00000013L将会使处理器设置为SVC模式,即特权模式,拥有更高的权限。
3 多任务同步的设计及实现
3.1多任务同步的设计
在多任务同步的设计中,任务创建与任务切换的流程图如图3所示。
图3 任务创建和切换流程图
任务的设计选择了RVDS集成开发环境,创建一个新的工程,并向工程添加一个新的文件,可为所添加文件进行分组,其实现的关键代码如下:
void Task0(void *pdata)
{#if OS_CRITICAL_METHOD == 3
OS_CPU_SRcpu_sr;
#endif
OS_ENTER_CRITICAL();
ISRInit();
OS_EXIT_CRITICAL();
OSPrintfInit();
OSStatInit();
OSTaskCreate (Task1,(void *)0, &Task1Stk[Task1StkLengh -1], Task1Prio);
while(1)
{OSPrintf(″this is from task0 ″);
OSTimeDly(OS_TICKS_PER_SEC*3); } }
分析:在上述代码中,实现了Task0、Task1、Task2等3个任务的创建,并完成任务之间的切换。首先由Task0创建了任务Task1、在窗口控制台上打印了语句this is from task0,并延时了3个时间片;随后Task1创建了任务Task2、在窗口控制台上打印了语句this is from task1,并延时了2个时间片;最后Task2在窗口控制台上打印了语句this is from task2,并延时了1个时间片。
另外,本实验中,为了实现3个任务Task0、Task1、Task2的切换,并在串口控制台输出结果,还需移植printf()函数,该函数的主要功能是将要显示的字符串信息发送到串口消息队列中。OSPrintf函数实现代码如下:
void OSPrintf(const char *fmt,...)//控制台打印函数
{INT8U *pUartBuf; // 定义一个串口消息缓冲区
INT8U err; //定义错误信息变量
va_list ap;//定义缓冲区链表
pUartBuf=OSMemGet(pUartMem,&err);//将要发送的内容传送到串口
va_start(ap,fmt);//开始遍历链表
vsprintf(pUartBuf,fmt,ap);//打印控制台内容
va_end(ap);//结束遍历链表
OSQPost(pUart_Q,pUartBuf);//发送消息
}
3.2移植系统的测试
在CodeWarrior for RVDS的Edit选单中的Debug Settings项中修改测试选项,将Language Settings项中的RealView Assembler、RealView Compiler中的架构均修改为ARM1176JZF-S[7-9],还可以将Linker项中的RealView FromELF项修改生成的BIN文件名称。代码测试界面如图4所示。
图4 代码测试界面
在CodeWarrior for RVDS的Project选项下选择Debug,将自动启动RVDS集成开发环境中的AXD Debugger v1.3.1,烧写二进制文件到ARM11开发板中并运行BOOTLOADER引导程序,对部分硬件进行初始化工作。之后,内核正常启动,并对中断模块进行了初始化。移植后的内核运行情况如图5所示。3个任务同步的测试可利用JLink V8仿真器[10-12]来实现,将编写的代码下载到内存即可进行仿真调试。测试结果见串口控制台的输出,3个任务同步实现如图6所示。
图5 移植内核的初始化
图6 3个任务同步实现
图6表明3个任务在新移植的操作系统中完成了任务的切换,实现任务的调度,并达到了预期的目的。
4 结语
本文在充分了解ARM11指令系统的基础上,分析了μC/OS-II内核结构,提出了μC/OS-II在ARM11上的移植思想和方法,并实现了多个任务的同步设计,选择了RVDS集成开发环境进行了系统测试。结果表明,新移植的操作系统运行稳定,实现了多任务的切换,完成了多任务的调度,满足系统对实时性的要求。下一步工作将对所移植的操作系统内核进一步优化,并实现更多的应用。
References)
[1] 徐海龙,邱建,王晓娜,等.μC/OS-Ⅱ的优化移植和设备驱动框架设计[J].计算机测量与控制,2012,20(9):2501-2506.
[2] 马涛,白瑞林,石坚.Cortex-A8平台的μC/OS-Ⅱ及LwIP协议栈的移植与实现[J].计算机应用与软件,2014,31(1):242-245.
[3] 孙彦景,王梦龙,王迎.基于μC/OS-Ⅱ智能公交系统终端设计与实现[J].计算机工程与设计,2012,33(12):4509-4513.
[4] 郭德源,何虎,杨旭.面向嵌入式实时操作系统的MPI实现[J].微电子学与计算机,2011,28(3):35-42.
[5] 李祁,王凤芹,张燕红.嵌入式实时操作系统μC/OS-Ⅱ在STM32开发板上的应用[J].计算机与数字工程,2014,42(1):164-168.
[6] 邓昀,程小辉,王新政.微内核结构嵌入式实时操作系统的研究与设计[J].微电子学与计算机,2012,29(10):133-139.
[7] 葛强,王宜怀,曹振华.一种基于ARM核的嵌入式操作系统的设计实现[J].计算机应用与软件,2010,27(3):268-271.
[8] 蒋建春,汪同庆.一种异构多核处理器嵌入式实时操作系统构架设计[J].计算机科学,2011,38(6):298-303.
[9] 李昌刚,黄敏江,张昕,等.μC/OS-Ⅱ在XC164CS上的移植方法[J].计算机工程,2010,36(12):242-244.
[10] 李山山,李耀锵,刘敬晗,等.μC/OS-Ⅱ内核在基于FPGA的CPU上的移植[J].实验技术与管理,2010,27(4):87-90.
[11] 杨云,张勇.基于ARM7的μC/OS-Ⅱ移植分析与实现[J].计算机工程与设计,2009,30(3):539-541.
[12] 孙顺远,秦会斌,崔佳冬,等.μC/OS-Ⅱ在Cortex-M3内核上的移植及优化[J].计算机系统应用,2010,19(4):208-211.
Design of kernel transplantation experiment μC/OS-II operating system based on ARM11
Teng Yanping, Jia Siyu, Jin Mei, Li Lili
(College of Computer and Control Engineering,Qiqihar University,Qiqihar 161006, China)
Based on the analysis of the kernel of embedded real-time μC/OS-II operating system and aiming at the ARM11 Micro-Processor, a migration scheme of μC/OS-II is put forward, and its multi-tasking synchronous demo is performed. The testing results by the RealView Development Suite(RVDS)Integrated Development Environment show that the migrated-operating system can run steadily and the goals of the switch between many tasks are realized, which meets the demands for real-time quality and stability.
operating system; kernet transplantation; ARM11; μC/OS-II; RVDS
10.16791/j.cnki.sjg.2016.03.036
2015- 08- 05
黑龙江省自然科学基金项目(F201218);黑龙江省教育厅科学技术研究项目(12541880)
滕艳平(1965—),女,黑龙江齐齐哈尔,硕士,副教授,主要研究方向为嵌入式操作系统.
E-mail:typ2732996@163.com
TP316.2
A
1002-4956(2016)3- 0142- 05