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 <LPC17xx.h>
#include <ucos_ii.h>

#define TASK_STK_SIZE 512

OS_STK TaskStartStk[TASK_STK_SIZE];

void TaskStart(void *data);

int main(void)
{
    OSInit();

    OSTaskCreate(TaskStart, (, &TaskStartStk[TASK_STK_SIZE - ], );

    OSStart();

    ;
}

void  TaskStart(void *data)
{
    data=data;

    OS_CPU_SysTickInit(SystemFrequency/);

    for(;;)
    {
        OSCtxSwCtr = ;
        OSTimeDlyHMSM(,,,);
    }
}

(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处必须包含一张向量表,用于初始时的中断分配。

中断类型

表项地址偏移量

中断向量

0

0x00

MSP的初始值

1

0x04

复位

2

0x08

NMI

其中,向量表中的第一个元素并非中断向量,而是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个步骤:
    ①NVIC复位,控制内核;
    ②NVIC从复位中释放内核;
    ③内核配置堆栈;
    ④从地址0x00000000处取出MSP的初始值,从地址0x00000004处取出PC的初始值——这个值是复位向量;
    ⑤运行复位中断服务子程序;

其中,复位中断服务子程序的代码如下:

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)

位段

名称

类型

复位值

描述

16

COUNTFLAG

R

0

如果在上次读取本寄存器后,SysTick已经计到了0,则该位为1;如果读取该位,该位自动清0

2

CLKSOURCE

R/W

0

0=外部时钟源(STCLK)

1=内核时钟源(FCLK)

1

TICKINT

R/W

0

1=SysTick倒数计数到0时产生SysTick异常请求

0=倒数到0时无动作

0

ENABLE

R/W

0

SysTick定时器的使能位

②SysTick重装载数值寄存器(地址:0xE000E014)

位段

名称

类型

复位值

描述

23:0

RELOAD

R/W

0

当倒数计数到0时,将被重装载的值

③SysTick当前数值寄存器(地址:0xE000E018)

位段

名称

类型

复位值

描述

23:0

CURRENT

R/Wc

0

读取时返回当前倒数计数的值,写它则使之清零,同时还会清除在SysTick控制及状态寄存器中的COUNTFLAG标志

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, # 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]

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
    LDM     R0, {R4-R11}                                        ; Restore r4- from new process stack
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                                             ; Load PSP with new process SP
    ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR                                                  ; Exception return will restore remaining context

    END

ii)OSCtxSw

ucos-ii中,OSCtxSw的功能是上下文(任务)切换。而由上面的介绍可知,真正的切换工作是在PendSV中断中完成的,那么可想而知,OSCtxSw只需触发一个PendSV中断即可完成上下文(任务)切换的工作。OSCtxSw位于os_cpu_a.asm文件中,源码如下:

OSCtxSw
    LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR

7. 工程源码

工程源码链接:USCO_II_V289.rar

8. 参考资料

[1] 《ARM Cortex-M3权威指南》(宋岩译)

[2] 《嵌入式实时操作系统uC/OS-II(第2版)》(邵贝贝译)

[3] ucosii在stm32上的移植详解系列

KEIL MDK环境下uCOS-II在LPC17xx上的移植实例的更多相关文章

  1. FormData使用方法详解,以及在IE9环境下,前端的文件上传问题

    FormData的主要用途有两个: 1.将form表单元素的name与value进行组合,实现表单数据的序列化,从而减少表单元素的拼接,提高工作效率. 2.异步上传文件 一.创建formData对象 ...

  2. Tomcat:云环境下的Tomcat设计思路——Tomcat的多实例安装

    Cloud现在是一个热门的技术,Tomcat是学习Java的人一般都会接触的Web服务器,如果在Cloud环境下使用Tomcat,又当如何呢?不可避免的,要安装多个Tomcat了,这里称之为Tomca ...

  3. 【转载】MDK环境下让STM32用上FreeRTOS v8.1.2和FreeRTOS+Trace v2.6.0全过程

    [转载]https://www.amobbs.com/thread-5601460-1-2.html?_dsign=6a59067b   本人选择使用FreeRTOS的最大原因就是想使用FreeRTO ...

  4. 关于Win10 环境下Quartus II 15.0器件列表无法下拉的解决方法

    不知大家在Windows 10 64位系统环境下使用Quartus II 15.0在新建工程时遇到过这种问题没,在新建工程的过程是选择器件的列表无法下拉,只能看到一个器件型号,如图1所示. 图1 开始 ...

  5. STM32 ~ MDK环境下调试程序 HardFault_Handler 相关

    STM32出现HardFault_Handler故障的原因主要有两个方面: 1.内存溢出或者访问越界.这个需要自己写程序的时候规范代码,遇到了需要慢慢排查. 2.堆栈溢出.增加堆栈的大小. 出现问题时 ...

  6. 新唐N32905U1DN开发教程:MDK环境下工程搭建(官方提供的固件库)(转)

    前言:新唐N32905U1DN单片机不支持用户将程序烧录到内部FLASH,必须外挂FLASH存储用户程序,可以是SPI FLASH.NAND FLASH.SD卡.用户采用裸机开发的方式可使用官方提供的 ...

  7. spring+jpg环境下,spring实现文件上传

    jsp: <form method="post" action="excel.do?method=inputExcel" enctype="mu ...

  8. 痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU硬件那些事(2.4)- 串行NOR Flash下载算法(Keil MDK工具篇)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是Keil MDK工具下i.MXRT的串行NOR Flash下载算法设计. 在i.MXRT硬件那些事系列之<在串行NOR Flash ...

  9. windows环境下 生成git公钥和私钥

    windows环境下 生成公钥和私钥 上传代码到远程仓库的时候需要秘钥进行验证是否本人上传的.打开Git目录下的Git Bash 输入ssh-keygen,回车 可直接不输入路径,使用默认路径(c/U ...

随机推荐

  1. python-整理--使用IDE

    如何使用python的IDE 安装好python3.4之后,默认有一个叫IDLE,就是目录lib/idlelib之下,是一个简单实用的工具. 在VS2013上安装一个插件就可以使用VS当IDE了.插件 ...

  2. RemoteViews的内部机制

    1.RemoteViews的构造方法public RemoteViews(String packageName,int layoutId) 第一个表示当前应用的包名(反射机制需要),第二个表示加载的布 ...

  3. 使用ThinkPHP开发中MySQL性能优化的最佳21条经验

    使用ThinkPHP开发中MySQL性能优化的最佳21条经验讲解,目前,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更 ...

  4. Leetcode算法刷题:217和219题 Contains Duplicate

    从题目名字就可以看出这两道题是相似的,219是217的加强版 217:Contains Duplicate 题目 给予一个数组,判断是否有重复的元素.如果有就返回True,没有就返回False.以下是 ...

  5. 补丁惹的祸-ContractName Microsoft.VisualStudio.Text.ITextDocumentFactoryService

    未找到与约束ContractName Microsoft.VisualStudio.Text.ITextDocumentFactoryService...匹配的导出 问题: 重新安装了VS2012,结 ...

  6. GDAL的RASTERIO功能

             为了能快速的显示大影像,最近一直在学习GDAL,GDAL确实是一个功能强大的开源库,其核心部分数据集和波段,下面这个图很详细的描述了它们之间的关系,还有其中的细节:     GDAL ...

  7. Linux文件系统学习笔记-1

       在Linux中, 一切皆文件,不论是目录,设备,套接字等都可以看成文件,而且每一个文件对应一个inode号,这是一一对应的关系. [root@oracle ~]# ls -il 总用量 2624 ...

  8. fragment低版本

    http://bbs.csdn.net/topics/390271980 Fragment框架开发东西确实很方便,但是恼人的是从4.0才开始支持.以前的版本必须用兼容模式开发,本人在网上找了大量资料, ...

  9. 1.elk 入门示例

    zjtest7-frontend:/usr/local/logstash-2.3.4/bin# ./logstash -e 'input{stdin{}} output{stdout{codec=&g ...

  10. 浅谈JNDI的使用

    原文:http://www.weicoop.com/web/article/52.html 关于什么是JNDI的概念这里不做解释,本文作为初学者根据所了解到内容做些总结,主要内容如下: 1.JNDI使 ...