基于μC/OS的嵌入式系统应用开发研究
摘要:本文介绍了嵌入式系统的概念,分析了μC/OS的内核结构,并详细介绍了在具有ARM体系结构的S3C44B0微处理器上进行μC/OS操作系统的移植和应用程序及驱动程序的开发。
关键词:嵌入式系统 μc/os 微处理器
Abstract: The paper bring forward the conception of Embedded System ,Analyse the core kere of μc/os ,moreover detailedly introduce grafting the μC/OS's operating system on the ARM's architectural structure of S3C44B0's microprocessor and empolder on application and driver program 。http://www.16sheji8.cn/
Key words: Embedded System μc/os MicroProcessor
一、嵌入式系统概述
嵌入式系统是将先进的计算机技术、半导体技术和电子技术与各个行业的具体应用相结合后的产物,目前嵌入式系统已经渗透到日常生活的各个方面,其在工业、服务业、消费电子等领域的应用范围都不断扩大,嵌入式计算机系统的正式定义为:以应用为中心,以计算机技术为基础,软件硬件可裁减,符合应用系统对功能、可靠性、成本、体积、功耗的严格要求的专用计算机系统。嵌入式系统的主要特征有:系统内核小;专用性强;系统精简;嵌入式软件要求高实时性的操作系统软件;软件要求高质量和高可靠性;嵌入式系统开发需要专门的开发工具和环境。
嵌入式系统由硬件和软件两大部分组成,在本开发应用中,选择ARM7TDMI内核结构的samsung公司的s3c44b0作为微处理器芯片,该芯片具有主频高、运算速度快,超低功耗、价格低廉、结构简单等特点,在该内核基础上扩展了一系列完整的通用外围器件,主要有:片内8KB高速缓存、带有1个专用DMA通道的LCD控制器、2个通用DMA通道、1个多主机I2C总线控制器、5个PWM定时器及1个内部定时器、71个通用I/O口、8个外部中断源、8个10位ADC等资源,主频为66MHZ,系统支持大小端模式,共256MB的地址空间,支持8/16/32位数据总线编程。
开发平台外配与用户交互接口有RS-232串口电路、外扩flash、sdram,USB控制电路、以太网电路、键盘,JTAG接口电路部分。
实时嵌入式操作系统的种类繁多,大体上可以分为两种:商用型和免费型,前者系统功能稳定、可靠,并有完善的技术支持和售后服务,建立应用开发较为容易,但价格昂贵,代表性的有美国WindRiver公司的VxWorks操作系统、Microsoft公司的WinCE操作系统;免费型可以节约成本,且源码公开,便于开发,代表性的有嵌入式Linux系统、μC/OS系统。
二、嵌入式μC/OS的体系结构介绍
由于μC/OS结构简单,编程工具绝大部分是C语言编程,可以在大多数界面友好的编译器中编译生成目标代码,如Borland C、Keil等工具,且其内核最小可以到几十K,可以在多种体系结构的微处理器上移植,用户的工作较小,源代码开放,便于学习。μC/OS-II的几大组成部分有:
核心部分(OSCore.c) 是操作系统的处理核心,包括操作系统初始化、操作系统运行、中断进出的前导、时钟节拍、任务调度、事件处理等多部分。
任务处理部分(OSTask.c)完成任务的操作;包括任务的建立、删除、挂起、恢复等等。
时钟部分(OSTime.c)主要完成任务延时等操作。http://www.16sheji8.cn/
任务同步和通信部分 为事件处理部分,包括信号量、邮箱、邮箱队列、事件标志等部分; μC/OS-II的软件体系结构如图1所示。从图1中可以看到,如果要使用μC/OS-II, 必须为其编写OS_CPU.H、OS_CPU_C.C、OS_CPU_A.ASM三个文件。
三、μC/OS在ARM微处理器上的移植
μC/OS-II的全部源代码量大约是6000-7000行,一共有15个文件。将 μC/OS-II 移植到ARM处理器上,需要完成的工作也非常简单,只需要修改三个和ARM体系结构相关的文件,代码量大约是500行。以下分别介绍这三个文件的移植工作:OS_CPU.H 文件 数据类型定义,这部分的修改是与所用的编译器相关的,不同的编译器会使用不同的字节长度来表示同一数据类型,这里采用的编译器为集成可视化开发环境ARM SDT 2.5,相关的数据类型的定义如下:
#define BYTE INT8S /* Define data types for backward compatibility */
#define UBYTE INT8U /* .to uC/OS V1.xx. Not actually needed for . */
#define WORD INT16S /* ... uC/OS-II. */
#define UWORD INT16U
#define LONG INT32S
#define ULONG INT32U
堆栈单位因为处理器现场的寄存器在任务切换时都将会保存在当前运行任务的堆栈中,所以OS_STK 数据类型应该是和处理器的寄存器长度一致的。
typedef unsigned int OS_STK; /* Each stack entry is 16-bit wide */
堆栈增长方向该设置由编译器选项决定,在本开发中设定堆栈由高地址向低地址增长。
#define OS_STK_GROWTH 1 //define the stack to grow from high to low
2、OS_CPU_C.C 文件http://www.16sheji8.cn/
任务堆栈初始化 这里涉及到任务初始化时的一个堆栈设计,也就是在堆栈增长方向上如何定义每个需要保存的寄存器位置,在ARM体系结构下,任务堆栈空间由高至低依次将保存着pc、lr、r12、r11、r10、… r1、r0、CPSR、SPSR。
void *OSTaskStkInit (void (*task)(void *pd), void *pdata, void *ptos, INT16U opt)
{
unsigned int *stk ;
opt = opt; /* 'opt' is not used, prevent warning */
stk = (unsigned int *)ptos; /* Load stack pointer */
*--stk = (unsigned int) task; / * lr */
……; /* r12—r0 */
*--stk = ARM_MODE_SYS; /* system mode */
*--stk = ARM_MODE_SYS; /* system mode */
return ((void *)stk);
}
当前任务堆栈初始化完成后,OSTaskStkInit 返回新的堆栈指针stk,在 OSTaskCreate()执行时将会调用 OSTaskStkInit 的初始化过程,然后通过OSTCBInit()函数调用将返回的sp指针保存到该任务的TCB块中。
OSStartHighRdy() 该函数是在主程序OSStart( )多任务启动后执行,负责从最高优先级任务的TCB控制块中获得该任务的堆栈指针sp,通过sp依次将cpu现场恢复,这时系统就将控制权交给用户创建的该任务进程,仅执行一次,此后多任务优先级调度由下面函数执行。
OSCtxSw() 任务级的上下文切换,它是当任务因为被阻塞而主动请求cpu调度时被执行,由于此时的任务切换都是在非异常模式下进行的,它的工作是先将当前任务的cpu现场保存到该任务堆栈中,然后获得最高优先级任务的堆栈指针,从该堆栈中恢复此任务的cpu现场,使之继续执行。
OSIntCtxSw() 中断级的任务切换,它是在时钟中断ISR(中断服务例程)中发现有高优先级任务等待的时钟信号到来,则在中断退出后直接调度就绪的高优先级任务执行。http://www.16sheji8.cn/
OSTickISR() 时钟中断处理函数,它的主要任务是负责处理时钟中断,调用系统实现的OSTimeTick函数,如果有等待时钟信号的高优先级任务,则需要在中断级别上调度其执行。其他相关的两个函数是OSIntEnter()和OSIntExit(),都需要在ISR中执行。
移植完以上程序后,用户就可以结合自己的项目要求来编写自己的应用程序了,用户可以添加如打印、空等待等任务,以下给出了一个例程,通过调用OSTaskCreate ( )函数注册了三个任务,由系统根据最优调度原理进行调度。
void main (void)
{
Initialize(); /* Processor specific initialization */
OSInit();
bufferSemaphore = OSSemCreate(BUFFER_LENGTH - 1);
terminalSemaphore = OSSemCreate(1);
OSTaskCreate(Task1, (void*)string1, (void*)&stacks[0][TASK_STK_SIZE - 1], 0);
OSTaskCreate(Task2, (void*)string2, (void*)&stacks[1][TASK_STK_SIZE - 1], 1);
OSTaskCreate(Task3, (void*)string3, (void*)&stacks[2][TASK_STK_SIZE - 1], 2);
OSStart(); /* Start..... */
}
四、驱动程序的添加
由于UC/OS提供的仅仅是一个任务调度的内核,通过以上移植,要想得到一个相对完整、实时的嵌入式多任务操作系统,还必须进行相当多的扩展工作。主要有:建立文件系统、通过开发如LCD液晶显示、USB通信、键盘、串口等驱动程序从而提供应用程序调用的API函数,还有创建图形用户接口(GUI)函数等,下面主要介绍一下串口驱动程序的开发。
void Uart_Init(int mclk,int baud) { }
该函数主要是初始化串口,设置波特率,其中mclk是系统主时钟频率,band参数传递串口通信波特率。
void Uart_Select(int ch) { }http://www.16sheji8.cn/
该函数进行串口选择,ch 传递串口号。
char Uart_Getch(void) { }
该函数从串口读取字符,存放在一数组内。
void Uart_GetString(char *string) { }
该函数读取要发送的字符串,并一个一个字符地从串口发送。
void Uart_SendByte(int data) { }
该函数通过串口发送数据,data是需要发送的字符。
void Uart_SendString(char *pt) { }
该函数通过串口发送字符串,pt是字符串首地址的指针。
通过以上接口函数,系统向用户提供了屏蔽底层硬件的API函数,用户可以通过调用以上函数,方便地对串口进行操作。
五、结束语
目前市场上基于μc/os嵌入式操作系统的产品比较多,应用领域包括工业控制、信息家电、网络设备等方面,而且基于μc/os的应用正潮起云涌,蓬勃发展。随着后PC时代的来临,嵌入式系统理论与应用研究日新月异,μc/os正是我们手中开发嵌入式系统的利器,较好的掌握这门技术可以将理论与实际应用相结合,更好地服务于我们的日常生活和生产中。http://www.16sheji8.cn/
参考文献:
1、邵贝贝译.μC/ OS -Ⅱ源码公开的实时嵌入式操作系统[M] . 北京:中国电力出版社, 2001.
2、王田苗 嵌入式系统设计与实例开发 清华大学出版社2003年10月
3、邹思轶 嵌入式Linux设计与应用 清华大学出版社2002年1月