uCOS-II移植到LPC17XX方法

发布者:花海鱼最新更新时间:2019-10-14 来源: eefocus关键字:uCOS-II  移植  LPC17XX 手机看文章 扫描二维码
随时随地手机看文章

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

中断类型

表项地址偏移量

中断向量

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

[1] [2]
关键字:uCOS-II  移植  LPC17XX 引用地址:uCOS-II移植到LPC17XX方法

上一篇:lpc1114热敏打印机源程序
下一篇:S32K144_Uart串口源程序

推荐阅读最新更新时间:2024-11-04 13:37

STM32入门学习笔记之uCOS-II系统移植3
⑤这两个函数都用于任务切换,它们的本质都是触发PendSV中断,具体切换过程在PendSV的中断函数中进行,其中OSCtxSw是任务级切换,OSIntCtxSw是中断级切换,是从中断退出时切换到一个任务中,从中断切换到任务的过程中,CPU的寄存器入栈工作已经完成。 OSCtxSw PUSH {R4, R5} LDR R4, =NVIC_INT_CTRL ;触发PendSV异常 LDR R5, =NVIC_PENDSVSET STR R5, ;向NVIC_INT_CTRL写入NVIC_PENDSVSET触发PendSV中断 POP {R4, R5} BX LR OSIntCt
[单片机]
ucos-ii移植到ARM上的一个简单例子mini2440
基于mini2440的一个简单移植程序led.c #include config.h #define LED1 (0 5) //定义LED,GPB5 #define TaskStkLength 64 //定义堆栈长度 OS_STK TaskLEDStk ; //定义任务LED的堆栈 void TaskLED(void *pdata); //声明任务LED int Main(void) //ADS中不能写成main(void),一定大写 { TargetInit(); //目标板初始化 OSInit(); //uC/OS-II初始化 OSTimeSet(
[单片机]
s3c2440 android 移植
ARMv4移植简单教程: 相信国内很多朋友手上都有s3c2410/2440的片子,基于armv4t(arm920t)的指令架构。在之前,因为android的一些底层代码含有armv5t的指令,所以以前无法移植到这样的平台。在这里也放出移植教程和已经编译好的image。让更多的朋友可以在自己的开发板上亲身体验android。教程基于勤研qt2410以及扬创utu2440完成。 注意,移植是到armv4而不是armv4t, 原因应该是不开thumb支持会比较好移植一些(改动未涉及的库依然会用thumb代码,所以kernel依然要开thumb支持)。 kernel移植及nfs配置的详细部分等请自行查阅相关文档,本
[单片机]
使用ADS移植uC/OS-II实例分析
选择开发工具在嵌入式系统设计中,开发工具的选取是一个重要的考虑因素,通常这是与开发项目的需求和应用背景相关。一般嵌入式开发工具包含用于目标系统的交叉编译器、连接器、调试器以及辅助处理用的二进制文件分析工具等。 目前可以用来编译链接产生 ARM 处理器执行代码的开发工具主要有如下几类:1. ARM 公司提供的 ARM Developer Suite 集成开发环境主要工具有 armasm、armcc、armlink、fromelf 等。 2. GNU 组织提供的 tool chain for arm主要工具有 arm-elf-gcc、arm-elf-gdb、arm-elf-objcopy 等3. Microsoft公司提供的 eMbed
[单片机]
ucgui在stm32上的移植
在MDK环境下将3.90版本的UCGUI移植到STM32下了,为了方便大家,特写此移植方法,大家可以借鉴(有错误之处,望大家指点出来共同讨论!) 移植步骤: 第一步:首先,得把你的TFT底层驱动写好,既在裸机下,可以正常显示。 第二步:加入UCGUI程序包。 第三步:配置LCDConf.h GUIConf.h GUITouchConf.h(由于我的液晶不带触摸功能,此配置在此不讲。) 配置LCDConf.h文件如下: #ifndef LCDCONF_H #define LCDCONF_H #define LCD_XSIZE (160) //配置TFT的水平分辨率 #define LCD_Y
[单片机]
移植嵌入式Linux到ARM处理器S3C2410:操作系统
在笔者撰写的《C语言嵌入式系统编程修炼之道》一文中,主要陈诉的软件架构是单任务无操作系统平台的,而本文的侧重点则在于讲述操作系统嵌入的软件架构,二者的区别如下图:   嵌入式操作系统并不总是必须的,因为程序完全可以在裸板上运行。尽管如此,但对于复杂的系统,为使其具有任务管理、定时器管理、存储器管理、资源管理、事件管理、系统管理、消息管理、队列管理和中断处理的能力,提供多任务处理,更好的分配系统资源的功能,很有必要针对特定的硬件平台和实际应用移植操作系统。鉴于Linux的源代码开放性,它成为嵌入式操作系统领域的很好选择。国内外许多知名大学、公司、研究机构都加入了嵌入式Linux的研究行列,推出了一些著名的版本:   ·RT-L
[单片机]
<font color='red'>移植</font>嵌入式Linux到ARM处理器S3C2410:操作系统
基于s3c2440的madplay移植
在mini2440移植声卡后,赶紧找了移植madplay的资料,看上去挺简单的,于是接着移植madplay。 一、 环境和软件介绍 1、主机环境:虚拟机下Fedora9 2、交叉编译器:arm-linux-gcc-4.3.2 3、软件包:zlib-1.2.3、libid3tag-0.15.1b.tar.gz、libmad-0.15.1b.tar.gz、madplay-0.15.2b.tar.gz 4、 把以上软件包解压到/opt/studyarm/madplay-make目录下,分别改名为zlib、libid3tag、libmad、madplay。并在madplay-make目录下新建include和lib目录用于存放编译后
[单片机]
基于μClinux的SoPC应用系统设计详解
嵌入式系统一般由嵌入式微处理器、外围硬件设备、嵌入式操作系统以及用户应用程序四部分组成,其发展主要体现在芯片技术的进步上,以及在芯片技术限制下的算法与软件的进步上。随着芯片制造技术的发展,嵌入式系统的结构也随之发生了重大变革,从基于微处理器的嵌入式系统到基于微控制器的嵌入式系统,继而将可编程逻辑PLD(Programmable Logic Device)技术引入到嵌入式系统设计中,进而又发展到SoC(System on Chip),最终将PLD与嵌入式处理器结合而成为SoPC(System on Programmable Chip),使得SoPC成为嵌入式系统设计的一个发展趋势。    本文采用SoPC内嵌32位的软核处理器Nios
[嵌入式]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved