1. 知识准备
要想对ucos-ii的移植有较深的理解,需要两方面知识:
(1)目标芯片,这里是lpc17xx系列芯片,它们都是基于ARMv7 Cortex-M3内核,所以这一类芯片的ucos-ii移植几乎都是一样的,要想了解Cortex-M3内核,推荐《ARM Cortex-M3权威指南》(宋岩译);
(2)ucos-ii内核原理,推荐《嵌入式实时操作系统uC/OS-II(第2版)》(邵贝贝译)。
2. 下载文件
ucos-ii移植过程主要涉及三个文件:os_cpu.h, os_cpu_a.asm和os_cpu_c.c
实际上,一般情况下,我们想要移植的目标芯片前辈们都已经移植成功过了,我们需要做的就是下载就可以了。
需要下载两类文件:
(1)lpc17xx芯片启动/初始化代码:LPC17xx.h, system_LPC17xx.h, core_cm3.h, core_cm3.c, startup_LPC17xx.s和system_LPC17xx.c,这几个文件都可以从lpc官方网站lpc17xx系列芯片的任何一个项目中找到;
(2)ucos-ii移植代码:可以在Micrium官方网站中找到uCOS-II在LPC17xx上的移植代码(IAR平台)。
3. 创建工程
(1)创建文件夹UCOS_II_V289,在该目录下创建子目录APP, lpc17xx, Output, uC-CPU, UCOS-II,在Output下创建obj和list子目录,然后将第2步下载的文件添加进相应的文件夹中,文件拓扑图如下:
UCOS_II_V289
├─APP
│ hello.c
│
├─lpc17xx
│ core_cm3.c
│ core_cm3.h
│ LPC17xx.h
│ startup_LPC17xx.s
│ system_LPC17xx.c
│ system_LPC17xx.h
│ type.h
│
├─Output
│ ├─list
│ └─obj
├─uC-CPU
│ os_cpu.h
│ os_cpu_a.asm
│ os_cpu_c.c
│ os_dbg.c
│
└─uCOS-II
app_cfg.h
os_cfg.h
os_core.c
os_flag.c
os_mbox.c
os_mem.c
os_mutex.c
os_q.c
os_sem.c
os_task.c
os_time.c
os_tmr.c
ucos_ii.h
其中,hello.c中的文件代码如下:
#include #include #define TASK_STK_SIZE 512 OS_STK TaskStartStk[TASK_STK_SIZE]; void TaskStart(void *data); int main(void) { OSInit(); OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 0); OSStart(); return 0; } void TaskStart(void *data) { data=data; OS_CPU_SysTickInit(SystemFrequency/100); for(;;) { OSCtxSwCtr = 0; OSTimeDlyHMSM(0,0,0,10); } } (2)Keil uVision4创建新工程,选择UCOS_II_V289作为工程目录,选择芯片型号,需要注意的是当提示“Copy NXP LPC17xx Startup Code to Project Folder and Add File to Project?”时,选择“否”,因为我们已经有这个文件了。创建组,添加相应文件到组,如下所示: 右击“UCOS_II_V289”,更改工程设置: 如果勾选“Run to main()”,那么在仿真的时候,就会跳过启动代码,直接到main函数。 4. 编译 编译,会报很多错误,下面一个一个改: (1)将os_cpu_a.asm中的“public”改为“EXPORT”; (2)将os_cpu_a.asm中的 RSEG CODE:CODE:NOROOT(2) THUMB 改为 AREA OSKernelschedular,code,READONLY THUMB (3)将os_cfg.h中“OS_APP_HOOKS_EN”、“OS_DEBUG_EN”和“OS_TASK_STAT_EN”设置为0; (4)将startup_LPC17xx.s中的所有的“PendSVHandler”改为“OS_CPU_PendSVHandler”,所有的“SysTickHandler”改为“OS_CPU_SysTickHandler”。 编译通过。 5. 软件仿真调试 在步骤4中已经设置为软件仿真调试,编译成功后,即可添加断点进行软件仿真调试,查看代码运行是否符合预期。至此,移植结束。 6. 相关说明 (1)启动文件与启动流程 i)启动文件 启动文件为以下几个文件core_cm3.c, core_cm3.h, LPC17xx.h, startup_LPC17xx.s, system_LPC17xx.c 和 system_LPC17xx.h,下面分别说明它们的功能。 startup_LPC17xx.s:该文件是Cortex-M3的启动汇编代码,阅读源代码不难发现,它的作用是:堆和栈的初始化以及向量表的定义。Cortex-M3的向量表其实就是一个32位整数数组,每个下标对应一个向量,该下标元素的值则是该中断服务子程序的入口地址。向量表在地址空间中的位置是可以设置的,通过NVIC(向量中断控制器)中的一个重定位七寸器来指出向量表的地址。复位后,该寄存器的值为0,因此,在地址0处必须包含一张向量表,用于初始时的中断分配。 其中,向量表中的第一个元素并非中断向量,而是MSP(主堆栈寄存器)的初始值。 LPC17xx.h:该文件是CM3(Cortex-M3,下同)内核芯片的头文件,它定义了芯片寄存器的结构体。 core_cm3.h和core_cm3.c:这两个文件分别是CM3内核芯片的外围驱动头文件和源代码。 system_LPC17xx.h和system_LPC17xx.c:这两个文件为我们提供了一个系统初始化函数SystemInit(),CM3的初始化包括时钟配置、电源管理、功耗管理等。其中时钟配置比较复杂,因为他包括两个PLL倍频电路,一个是主PLL0,主要为系统和USB提供时钟,另一个是PLL1,专门为USB提供48M时钟。默认情况下,系统使用12M外部晶振,通过PLL0倍频到一个较高的频率,之后可以通过分频为CPU、外设以及可选的USB子系统提供精确的时钟。 ii)启动流程 由Cortex-M3的启动步骤可知,系统上电后,首先执行复位的5个步骤: 其中,复位中断服务子程序的代码如下: Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT SystemInit IMPORT __main LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP 可知,通过复位中断服务子程序,首先引导程序进入SystemInit()函数,然后进入__main(此__main是C_Library中的函数,非main())。 (2)SysTick定时器、SysTickInit与SysTickHandler i)SysTick定时器 Cortex-M3内核内部包含了一个简单的定时器——SysTick定时器。SysTick定时器被捆绑在NVIC中,用于产生SysTick中断。一般情况下,操作系统以及所有使用了时基的系统,都必须由硬件定时器来产生需要的“滴答”中断,作为整个系统的时基。SysTick定时器就是用来产生周期性的中断,以维持操作系统“心跳”节律的。SysTick定时器的时钟源可以是内部时钟(FCLK,CM3上的自由运行时钟),或者是外部时钟(CM3上的STCLK信号)。 SysTick定时器能产生中断,CM3为它专门开出一个中断类型,并且在向量表中有它的一席之地——SysTickHandler,它使得操作系统和其它软件系统在CM3内核的移植变得更加简单,因为在所有的CM3微处理器上,SysTick的处理方式都是相同的。 有4个寄存器控制SysTick定时器,下面只介绍其中经常用到的三个: ①SysTick控制及状态寄存器(地址:0xE000E010) ②SysTick重装载数值寄存器(地址:0xE000E014) ③SysTick当前数值寄存器(地址:0xE000E018) ii) SysTickInit()函数 该函数用于初始化SysTick定时器,在本移植实例中,它位于os_cpu_c.c文件中,其函数名被更改为“OS_CPU_SysTickInit”,源代码如下: void OS_CPU_SysTickInit (INT32U cnts) { OS_CPU_CM3_NVIC_ST_RELOAD = cnts - 1u; /* 设置SysTickHandler中断优先级为最低优先级 */ OS_CPU_CM3_NVIC_PRIO_ST = OS_CPU_CM3_NVIC_PRIO_MIN; /* 使能定时器 */ OS_CPU_CM3_NVIC_ST_CTRL |= OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC | OS_CPU_CM3_NVIC_ST_CTRL_ENABLE; /* 使能SysTickHandler中断 */ OS_CPU_CM3_NVIC_ST_CTRL |= OS_CPU_CM3_NVIC_ST_CTRL_INTEN; } 第一行用于装载SysTick重装载数值寄存器,其他几行都有注释,在此不再解释。 iii)SysTickHandler 当SysTick定时器倒数计数到0时,将产生SysTick中断,该中断位于向量表15号位置(中断向量号:15,下同)。在本移植实例中,该中断服务子程序的名称被更改为“OS_CPU_SysTickHandler”,其位于os_cpu_c.c文件中,源码如下: void OS_CPU_SysTickHandler (void) { OS_CPU_SR cpu_sr; OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */ OSIntNesting++; OS_EXIT_CRITICAL(); OSTimeTick(); /* Call uC/OS-II's OSTimeTick() */ OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR */ } (3) OSCtxSw与PendSVHandler i)PendSV中断 Cortex-M3内核内置了一个重要的中断——PendSV中断(可挂起的系统调用)。与SVC中断(系统服务调用,简称系统调用)不同的是,PendSV可以像普通的中断一样被挂起,OS可以利用它“缓期执行”一个中断——直到其他重要的任务完成后才执行动作。挂起PendSV的方法是:手动往NVIC的PendSV挂起寄存器中写1,挂起后,如果该中断优先级不够高,则将缓期等待执行。 PendSV的典型功能是上下文(任务)切换。若在即将做上下文(任务)切换时发现CPU正在响应一个中断,这时,OS是不能执行上下文(任务)切换的,否则将使中断请求被延迟,而这在实时系统中是绝不能容忍的。PendSV可以完美地解决这个问题,PendSV中断会自动延迟上下文(任务)切换的请求,直到其他的中断请求都完成后才响应。为实现这个机制,需要把PendSV的优先级设置为最低,当OS需要做上下文(任务)切换时挂起一个PendSV中断即可。 PendSV中断位于向量表14号位置,在本移植实例中,该中断服务子程序的名称被更改为“OS_CPU_PendSVHandler”,其位于os_cpu_a.asm文件中,源码如下: OS_CPU_PendSVHandler CPSID I ; Prevent interruption during context switch MRS R0, PSP ; PSP is process stack pointer CBZ R0, OS_CPU_PendSVHandler_nosave ; Skip register save the first time SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack STM R0, {R4-R11} LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP; LDR R1, [R1] STR R0, [R1] ; R0 is SP of process being switched out ; At this point, entire context of process has been saved OS_CPU_PendSVHandler_nosave PUSH {R14} ; Save LR exc_return value LDR R0, =OSTaskSwHook; OSTaskSwHook(); BLX R0 POP {R14} LDR R0, =OSPrioCur; OSPrioCur = OSPrioHighRdy; LDR R1, =OSPrioHighRdy LDRB R2, [R1] STRB R2, [R0] LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy; LDR R1, =OSTCBHighRdy LDR R2, [R1] STR R2, [R0] SP = OSTCBHighRdy->OSTCBStkPtr; LDR R0, [R2] ; R0 is new process SP; LDM R0, {R4-R11} ; Restore r4-11 from new process stack
①NVIC复位,控制内核;
②NVIC从复位中释放内核;
③内核配置堆栈;
④从地址0x00000000处取出MSP的初始值,从地址0x00000004处取出PC的初始值——这个值是复位向量;
⑤运行复位中断服务子程序;
上一篇:lpc1114热敏打印机源程序
下一篇:S32K144_Uart串口源程序
推荐阅读最新更新时间:2024-11-04 13:37
设计资源 培训 开发板 精华推荐
- 【训练营】物联网时钟+1491630A
- 9 - RTL8153气氛下的USB3.0千兆网卡
- 适用于高温环境的 16 位、600 kSPS、低功耗数据采集系统
- NCV33074ADR2G 低压快速数模转换器的典型应用
- 当 LTC2380HMS-16 中启用数字增益压缩时,使用 LT6350 的典型应用被配置为接受 ±10V 输入信号,同时运行一个 5.5V 单电源
- 使用 Microchip Technology 的 MIC94300YCS 的参考设计
- 使用 Analog Devices 的 LTC4076 的参考设计
- ADA4062-4ARUZ-RLOp-Amp 用作反相解决方案电路的典型应用电路
- NCP5603GEVB:高效电荷泵转换器/白光 LED 驱动器评估板
- 用于 1500W/3000W 功率因数校正器的半导体套件