首页 uCOS II 移植与深入实战指南

uCOS II 移植与深入实战指南

举报
开通vip

uCOS II 移植与深入实战指南教程简介名称《uCOS-II移植与深入实战指南》作者WildFireTeam@野火科技版本V1.0硬件平台野火STM32ISO/ISO-MINI开发板淘宝店http://firestm32.taobao.com论坛http://www.chuxue123.com野火系列教程简介,可到论坛下载。STM32篇《零死角玩转STM32》系统篇《uCOS-II移植与深入实战指南》GUI篇《emWin实战指南》GSM篇《野火WF-SIM900A数据手册》《野火WF-SIM900A用户手册》GPS篇《野火WF-NEO-6M模块数...

uCOS II 移植与深入实战指南
教程 人力资源管理pdf成真迷上我教程下载西门子数控教程protel99se入门教程fi6130z安装使用教程 简介名称《uCOS-II移植与深入实战指南》作者WildFireTeam@野火科技版本V1.0硬件平台野火STM32ISO/ISO-MINI开发板淘宝店http://firestm32.taobao.com论坛http://www.chuxue123.com野火系列教程简介,可到论坛下载。STM32篇《零死角玩转STM32》系统篇《uCOS-II移植与深入实战指南》GUI篇《emWin实战指南》GSM篇《野火WF-SIM900A数据手册》《野火WF-SIM900A用户手册》GPS篇《野火WF-NEO-6M模块数据手册》《野火WF-NEO-6M模块用户手册》开源共享共同进步!从0开始移植UCOS-II到野火STM32开发板前言uC/OS是一个微型的实时操作系统,包括了一个操作系统最基本的一些特性,如任务调度、任务通信、内存管理、中断管理、定时管理等。而且这是一个代码完全开放的实时操作系统,简单明了的结构和严谨的代码风格,非常适合初涉嵌入式操作系统的人士学习。很多人在学习STM32中,都想亲自移植一下uC/OS,而不是总是用别人已经移植好的。在我学习uC/OS的过程中,查找了很多资料,也看过很多关于如何移植uC/OS到STM32处理器上的教程,但都不尽人意,主要是写得太随意了,思路很乱,读者看到最后还是不确定该怎样移植。为此,我决定写这个教程,让广大读者真正了解怎样移植。学前建议:C语言+数据结构WildfireTeam2011年11月3日-第2页-1、官方源代码介绍首先我们下载源代码,官方下载地址:http://micrium.com/page/downloads/ports/st/stm32(下载资料需要注册帐号)或者网盘下载:http://dl.dbank.com/c0jnhmfxcp我们需要下载的就是下面这个,因为我用到的开发板芯片是STM32F103VET6注意:下载的源代码开发环境是IAR编译器的。我们使用的uCOS是2.86版本。下载解压后可以看到Micrium含有三个文件夹:-第3页-文件名说明包含uCOS-II的说明文件,其中文件Micrium\AppNotes\AN1xxx-AppNoteRTOS\sAN1018-uCOS-II-Cortex-M3\AN-1018.pdf是很重要的。这个文件对uC/OS在M3内核移植过程中需要修改的代码做了详细的说明。Licensin包含了uCOS-II使用许可证g应用软件,我们这里用到的就是uCOS-II文件夹。在整个移植过程中我们只需用到uCOS-II下的两个文件,分别是Ports和Source.DocuC/OS官方自带说明文档和教程官方移植到M3的移植文件(IAR工程)定义数据类型、处理器相关cpu.h代码、声明函数原型定义用户钩子函数,提供扩充软件功能的入口点。(所Portscpu_c.c谓钩子函数,就是指那些插入到某函数中拓展这些函数Softwar功能的函数)euCOS-II与处理器相关汇编函数,主cpu_a.asm要是任务切换函数os_dbg.c内核调试数据和函数uC/OS的源代码文件ucos_ii.h内部函数参数设置Sourc内核结构管理,uC/OS的核e心,包含了内核初始化,任os_core.c务切换,事件块管理、事件标志组管理等功能。-第4页-os_time.c时间管理,主要是延时定时器管理,设置定时时os_tmr.c间,时间到了就进行一次回调函数处理。os_task.c任务管理os_mem.c内存管理os_sem.c信号量os_mutex.c互斥信号量os_mbox.c消息邮箱os_q.c队列os_flag.c事件标志组CPUSTM32 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 外设库micrium官方评估板的代码EvalBoardOS-sProbe-os_cfg.h内核配置LCDuC-CPU基于micrium官方评估板的CPU移植代码uC-LIBmicrium官方的一个库代码uC-Probe有关的代码,是一个通用工具,能让嵌入式开uC-Probe发人员在实时环境中监测嵌入式系统。以上这些都是下载下来的官方资源。有没有发现,uC/OS的代码文件都被分开放到不同的文件夹里了?呵呵,这个是官方移植好到STM32的uC/OS系统,他已经帮我们对uC/OS的文件进行分类存放。如果你不想要移植好的,也可以下载没有移植的,那样就所以文件都放在一个文件夹里。下载地址:http://micrium.com/download/Micrium-uCOS-II-V290.ZIP-第5页-提示一下,如果是没移植好的,是找不到main函数的哦!初学者,相信很多都下载没移植好的,然后直接看它的源代码,然后看到头晕也找不到工程的入口。其实,uC/OS就是一个库而已,熟悉它的运行 流程 快递问题件怎么处理流程河南自建厂房流程下载关于规范招聘需求审批流程制作流程表下载邮件下载流程设计 和函数接口,就可以基本跑起来。在自己亲自移植之前,总是看到移植好的例程包含有CPU、uC-CPU、uC-LIB、uCOS-II四个文件夹下的代码。uCOS-II文件夹下的是源代码,这个好理解;但是前面三个有什么用啊?通常看其他移植教程时,一般都说只需改os_cpu.h,os_cpu_a.asm和os_cpu_c.c就可以了,就没听说过有CPU、uC-CPU、uC-LIB这些的。心中一直很纳闷,难道后三个都要自己编写的吗?后来在上面网址把源代码下载后,才知道CPU、uC-CPU、uC-LIB这三个文件是官方自己写的移植文件,而我们使用了标准外设库CMSIS中提供的启动文件及固件库了,因此可以不用这三个文件,哈哈,心中的疑团解决了!先看一下开发板与uC/OS-II的框架图(注意APP.C就是main文件,我们下面移植的文件并没有APP_VECT.C这个文件,应用文件可以灵活处理的)-第6页--第7页-2、重要文件代码详解移植前,我们需要先了解一下uC/OS的重要文件代码。对于从没接触过uC/OS或者其他嵌入式系统的朋友们,你们需要先了解uC/OS的工作原理和各模块功能,不然就不知道为啥这样移植。推荐教程作者书名推荐理由清晰简单地讲解了uC/OS的运野火团队初探uCOS-II行流程,方便初学者学习。通俗易懂的一本uC/OS教程,嵌入式实时操作系统非常适合初学者学习。uC/OS-II原理及应用任哲不过教程没得到更新,不能适(北京航空航天出版应uC/OS的发展,但还是值得社)推荐。呵呵,不用说吧?移植uC/OSJosephYiu著Cortex-M3权威指南到M3内核中,怎么能不了解宋岩译内核呢?下面的内容主要来自于刚才下载的文件里面的Micrium\AppNotes\AN1xxx-RTOS\AN1018-uCOS-II-Cortex-M3\AN-1018.pdf文件来讲的,因为这文件是uC/OS作者移植uC/OS到STM32的移植手册,里面谈到很多移植说需要注意的事项和相关知识。我在这里添加也按照作者的思路来讲解,并加入个人理解,如果有误,欢迎指出错误。-第8页-2.1os_cpu.h定义数据类型、处理器相关代码、声明函数原型全局变量OS_CPU_GLOBALS和OS_CPU_EXT允许我们是否使用全局变量。1.#ifdefOS_CPU_GLOBALS2.#defineOS_CPU_EXT3.#else//如果没有定义OS_CPU_GLOBALS4.#defineOS_CPU_EXTextern//则用OS_CPU_EXT声明变量已经外部定义了。5.#endif数据类型6.typedefunsignedcharBOOLEAN;7.typedefunsignedcharINT8U;8.typedefsignedcharINT8S;9.typedefunsignedshortINT16U;//大多数Cortex-M3编译器,short是16位,int是32位10.typedefsignedshortINT16S;11.typedefunsignedintINT32U;12.typedefsignedintINT32S;13.typedeffloatFP32;//尽管包含了浮点数,但uC/OS-II中并没用到14.typedefdoubleFP64;15.16.typedefunsignedintOS_STK;//M3是32位,所以堆栈的数据类型OS_STK设置32位17.typedefunsignedintOS_CPU_SR;//M3的状态寄存器(xPSR)是32位临界段临界段,就是不可被中断的代码段,例如常见的入栈出栈等操作就不可被中断。uC/OS-II是一个实时内核,需要关闭中断进入和开中断退出临界段。为此,uC/OS-II定义了两个宏定义来关中断OS_ENTER_CRITICAL()和开中断OS_EXIT_CRITICAL()。18.#defineOS_CRITICAL_METHOD3//进入临界段的三种模式,一般选择第3种,即这里设置为319.20.21.#defineOS_ENTER_CRITICAL(){cpu_sr=OS_CPU_SR_Save();}//进入临界段22.#defineOS_EXIT_CRITICAL(){OS_CPU_SR_Restore(cpu_sr);}//退出临界段-第9页-事实上,有3种开关中断的方法,根据不同的处理器选用不同的方法。大部分情况下,选用第3种方法。另外,关于汇编函数OS_CPU_SR_Save()和OS_CPU_SR_Restore(),在后面谈到os_cpu_a.asm文件时会再说。栈生长方向M3的栈生长方向是由高地址向低地址增长的,因此OS_STK_GROWTH定义为1。23.#defineOS_STK_GROWTH1任务切换宏定义任务切换宏,关于汇编函数OSCtxSw(),在后面谈到os_cpu_a.asm文件时会再说。24.#defineOS_TASK_SW()OSCtxSw()函数原型开中断和关中断如果定义了进入临界段的模式为3,就声明开中断和关中断函数25.#ifOS_CRITICAL_METHOD==326.OS_CPU_SROS_CPU_SR_Save(void);27.voidOS_CPU_SR_Restore(OS_CPU_SRcpu_sr);28.#endif任务管理函数-第10页-29./******************任务切换的函数******************/30.voidOSCtxSw(void);//用户任务切换31.voidOSIntCtxSw(void);//中断任务切换函数32.voidOSStartHighRdy(void);//在操作系统第一次启动的时候调用的任务切换33.34.voidOS_CPU_PendSVHandler(void);//用户中断处理函数,旧版本为OSPendSV35.36.voidOS_CPU_SysTickHandler(void);//系统定时中断处理函数,时钟节拍函数37.voidOS_CPU_SysTickInit(void);//系统SysTick定时器初始化38.39.INT32UOS_CPU_SysTickClkFreq(void);//返回SysTick定时器的时钟频率这三个函数是为SysTick定时器服务的关于任务切换,利用到异常处理知识,可以看《Cortex-M3权威指南》(JosephYiu著宋岩译)中第3.4小节。关于PendSV,有不懂的朋友,可以看《Cortex-M3权威指南》中第7.6小节SVC和PendSV:SVC(系统服务调用,亦简称系统调用)和PendSV(可悬起系统调用),它们多用在上了操作系统的软件开发中。SVC用于产生系统函数的调用请求,SVC异常是必须在执行SVC指令后立即得到响应的。PendSV(可悬起的系统调用)则不同,它是可以像普通的中断一样被悬起的(不像SVC那样会上访)。OS可以利用它“缓期执行”一个异常——直到其它重要的任务完成后才执行动作。悬起PendSV的方法是:手工往NVIC的PendSV悬起寄存器中写1。悬起后,如果优先级不够高,则将缓期等待执行。PendSV的典型使用场合是在上下文切换时(在不同任务之间切换)。例如,一个系统中有两个就绪的任务,上下文切换被触发的场合可以是:执行一个系统调用系统滴答定时器(SysTick)中断。(轮转调度中需要)注:此部分内容出自《Cortex-M3权威指南》-第11页-关于SysTick定时器的三个函数,为了便于理解,我们把它注释掉,不采用官方的,自己编写:需要注释的函数在os_cpu_c.c中定义,是SysTick中断的中断处OS_CPU_SysTickHandler()理函数,而在stm32f10x_it.c中已经有该中断函数的定义SysTick_Handler(),这里也就不需要了。定义在BSP.C中,此函数我们自己会编写,把它注OS_CPU_SysTickClkFreq()释掉。定义在os_cpu_c.c中,用于初始化SysTick定时OS_CPU_SysTickInit()器,它依赖于OS_CPU_SysTickClkFreq(),也要注释掉。2.2os_cpu_c.c移植uC/OS时,我们需要写10个相当简单的C函数:9个钩子函数和1个任务堆栈结构初始化函数。钩子函数所谓钩子函数,指那些插入到某些函数中为扩展这些函数功能的函数。一般地,钩子函数为第三方软件开发人员提供扩充软件功能的入口点。为了拓展系统功能,uC/OS-II中提供有大量的钩子函数,用户不需要修改uC/OS-II内核代码程序,而只需要向钩子函数添加代码就可以扩充uC/OS-II的功能。注:此部分内容出自张勇的《嵌入式操作系统原理与面向任务程序设计——基于uC/OS-IIv2.86和ARM920T》尽管uC/OS-II中提供了大量的钩子函数,但实际上,移植时我们需要编写的也就9个钩子函数:40.OSInitHookBegin()//OSIinit()系统初始化函数开头的钩子函数41.OSInitHookEnd()//OSIinit()系统初始化函数结尾的钩子函数42.OSTaskCreateHook()//OSTaskCreate()或OSTaskCreateExt()创建任务钩子函数43.OSTaskDelHook()//OSTaskDel()删除任务钩子函数-第12页-44.OSTaskIdleHook()//OS_TaskIdle()空闲任务钩子函数45.OSTaskStatHook()//OSTaskStat()统计任务钩子函数46.OSTaskSwHook()//OSTaskSW()任务切换钩子函数47.OSTCBInitHook()//OS_TCBInit()任务控制块初始化钩子函数48.OSTimeTickHook()//OSTaskTick()时钟节拍钩子函数这些函数都是一些钩子函数,一般由用户拓展。如果要用到这些钩子函数,需要在OS_CFG.H中定义OS_CPU_HOOKS_EN为1,即:49.#defineOS_CPU_HOOKS_EN1//在OS_CFG.H中定义钩子函数的编写,例如:50./***系统初始化函数OSInit()开头调用***/51.voidOSInitHookBegin(void)52.{53.#ifOS_TMR_EN>0//当使用OS_TMR.C定时器管理模块54.OSTmrCtr=0;//初始化系统节拍计数变量OSTmrCtr为055.//每个时钟节拍OSTmrCtr(全局变量,初始值为0)增156.#endif57.}58./***创建任务OSTaskCreate()或OSTaskCreateExt()中调用***/59.voidOSTaskCreateHook(OS_TCB*ptcb)60.{61.#ifOS_APP_HOOKS_EN>0//如果有定义应用任务62.App_TaskCreateHook(ptcb);//调用应用任务创建钩子函数63.#else//否则64.(void)ptcb;//告诉编译器ptcb没用到65.#endif66.}67./***切换任务时被调用***/68.voidOSTaskSwHook(void)69.{70.#ifOS_APP_HOOKS_EN>071.App_TaskSwHook();//应用任务切换时调用的钩子函数72.#endif73.}74./***每个系统节拍到了***/75.voidOSTimeTickHook(void)76.{77.#ifOS_APP_HOOKS_EN>078.App_TimeTickHook();//应用软件的时钟节拍钩子79.#endif80.81.#ifOS_TMR_EN>0//如果有启动定时器管理82.OSTmrCtr++;//计时变量OSTmrCtr加183.if(OSTmrCtr>=(OS_TICKS_PER_SEC/OS_TMR_CFG_TICKS_PER_SEC)){//如果时间到了84.OSTmrCtr=0;//计时清085.OSTmrSignal();//发送信号量OSTmrSemSignal(初始值为0)86.//以便软件定时器扫描任务OSTmr_Task能请求到信号量而继续运行下去87.}88.#endif89.}-第13页-这些钩子函数是必须声明的,但不是必须定义的,只是为了拓展你的系统功能而已。任务堆栈结构初始化函数90.OSTaskStkInit()//任务堆栈结构初始化函数通常,我们的任务定义都是这样的:91.voidMyTask(void*p_arg)92.{93./*可选,例如处理‘p_arg’变量*/94.while(1){95./*任务主体*/96.}97.}典型的ARM编译器(Cortex-M3也是这样)都会把这个函数的第一个参量传递到R0寄存器中。对于像ARM内核一般都比较多寄存器的单片机,我们可以把函数中断的局部变量保存在寄存器中,以加快速度。98.OS_STK*OSTaskStkInit(void(*task)(void*pd),void*p_arg,99.OS_STK*ptos,INT16Uopt)100.{101.OS_STK*stk;102.103.104.(void)opt;//'opt'并没有用到,防止编译器提示警告105.stk=ptos;//加载栈指针106.107./*中断后xPSR,PC,LR,R12,R3-R0被自动保存到栈中*/108.*(stk)=(INT32U)0x01000000L;//xPSR109.*(--stk)=(INT32U)task;//任务入口(PC)110.*(--stk)=(INT32U)0xFFFFFFFEL;//R14(LR)111.*(--stk)=(INT32U)0x12121212L;//R12112.*(--stk)=(INT32U)0x03030303L;//R3113.*(--stk)=(INT32U)0x02020202L;//R2114.*(--stk)=(INT32U)0x01010101L;//R1115.*(--stk)=(INT32U)p_arg;//R0:变量116.117./*剩下的寄存器需要手动保存在堆栈*/118.*(--stk)=(INT32U)0x11111111L;//R11119.*(--stk)=(INT32U)0x10101010L;//R10120.*(--stk)=(INT32U)0x09090909L;//R9121.*(--stk)=(INT32U)0x08080808L;//R8122.*(--stk)=(INT32U)0x07070707L;//R7123.*(--stk)=(INT32U)0x06060606L;//R6124.*(--stk)=(INT32U)0x05050505L;//R5125.*(--stk)=(INT32U)0x04040404L;//R4126.127.return(stk);128.}-第14页-这是初始化任务堆栈函数。OSTaskStkInit()被任务创建函数调用,所以要在开始时,在栈中作出该任务好像刚被中断一样的假象。在ARM内核中,函数中断后,xPSR,PC,LR,R12,R3-R0被自动保存到栈中的,R11-R4如果需要保存,只能手工保存。为了模拟被中断后的假象,OSTaskStkInit()的工作就是在任务自己的栈中保存cpu的所有寄存器。这些值里R1-R12都没什么意义,这里用相应的数字代号(如R10x01010101)主要是方便调试。问大家两个问题,以便大家知道是否掌握了这个知识点:为什么程序是*(--stk)=(INT32U)******;而不是保存寄存器的值:*(--stk)=*(INT32U)*******呢? 答案 八年级地理上册填图题岩土工程勘察试题省略号的作用及举例应急救援安全知识车间5s试题及答案 很简单,就是上面说的,任务还没开始运行,栈里保存的R1-R12值都没什么意义的,这里仅仅是模拟中断那样的假象,R1-R12可以是其他任意义的值。为什么程序是*(--stk)=(INT32U)******;而不是*(++stk)=(INT32U)******前面已经讲过,M3的栈生长方向是由高地址向低地址增长的。-第15页-栈初始化后,各寄存器的初始值如下:xPSR=0x01000000L,xPSRT位(第24位)置1,否则第一次执行任务时Fault,PC必须指向任务入口,R14=0xFFFFFFFEL,最低4位为E,是一个非法值,主要目的是不让使用R14,即任务是不能返回的。R0用于传递任务函数的参数,因此等于p_arg。。SysTick时钟初始化OS_CPU_SysTickInit()会被第一个任务调用,以便初始化SysTick定时器。OS_CPU_SysTickInit()将会调用OS_CPU_SysTickClkFreq()获取系统时钟频率,用户需要为自己的开发板编写此函数获取时钟频率。129.voidOS_CPU_SysTickInit(void)130.{131.INT32Ucnts;-第16页-132.133.134.cnts=OS_CPU_SysTickClkFreq()/OS_TICKS_PER_SEC;135.//OS_CPU_SysTickClkFreq()获取时钟频率136.//OS_TICKS_PER_SEC定义每秒时钟节拍中断的次数,即时钟节拍时间为1/OS_TICKS_PER_SEC137.138./*使能SysTick定时器*/139.OS_CPU_CM3_NVIC_ST_RELOAD=(cnts-1);140.141./*使能SysTick定时器中断*/142.OS_CPU_CM3_NVIC_ST_CTRL|=OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC143.|OS_CPU_CM3_NVIC_ST_CTRL_ENABLE;144.145.OS_CPU_CM3_NVIC_ST_CTRL|=OS_CPU_CM3_NVIC_ST_CTRL_INTEN;146.}但在这里,为了便于理解,我们需要手动修改成自己的,不用这些函数(看上面任务管理函数中需要注释掉的函数)。除了注释刚才上面说的三个函数外,我们还要注释掉这些宏定义:147./*******************************************************************************148.*SYSTICKDEFINES149.********************************************************************************/150.#defineOS_CPU_CM3_NVIC_ST_CTRL(*((volatileINT32U*)0xE000E010))151./*SysTickCtrl&StatusReg.*/152.#defineOS_CPU_CM3_NVIC_ST_RELOAD(*((volatileINT32U*)0xE000E014))153./*SysTickReloadValueReg.*/154.#defineOS_CPU_CM3_NVIC_ST_CURRENT(*((volatileINT32U*)0xE000E018))155./*SysTickCurrentValueReg.*/156.#defineOS_CPU_CM3_NVIC_ST_CAL(*((volatileINT32U*)0xE000E01C))157./*SysTickCalValueReg.*/158.#defineOS_CPU_CM3_NVIC_PRIO_ST(*((volatileINT8U*)0xE000ED23))159./*SysTickHandlerPrioReg.*/160.161.#defineOS_CPU_CM3_NVIC_ST_CTRL_COUNT0x00010000162./*Countflag.*/163.#defineOS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC0x00000004164./*ClockSource.*/165.#defineOS_CPU_CM3_NVIC_ST_CTRL_INTEN0x00000002166./*Interruptenable.*/167.#defineOS_CPU_CM3_NVIC_ST_CTRL_ENABLE0x00000001168./*Countermode.*/169.#defineOS_CPU_CM3_NVIC_PRIO_MIN0xFF170./*Minhandlerprio.*/因为它们为SysTick定时器服务的,即需要把所有与SysTick有关的代码都要去掉。-第17页-2.3os_cpu_a.asm这个文件包含了需要用汇编编写的代码。声明外部定义171.EXTERNOSRunning;声明外部定义,相当于C语言的extern172.EXTERNOSPrioCur173.EXTERNOSPrioHighRdy174.EXTERNOSTCBCur175.EXTERNOSTCBHighRdy176.EXTERNOSIntNesting177.EXTERNOSIntExit178.EXTERNOSTaskSwHook申明这些变量是在其他文件定义的。声明全局变量由于编译器的原因,我们需要将下面的PUBIC改为EXPORT。(如果下载的源代码是用RealView编译的,则此处就不用改了,因为代码本来就是用EXPORT)179.PUBLICOS_CPU_SR_Save;声明函数在此文件定义180.PUBLICOS_CPU_SR_Restore181.PUBLICOSStartHighRdy182.PUBLICOSCtxSw183.PUBLICOSIntCtxSw184.PUBLICOS_CPU_PendSVHandler修改后185.EXPORTOS_CPU_SR_Save;声明函数在此文件定义186.EXPORTOS_CPU_SR_Restore187.EXPORTOSStartHighRdy188.EXPORTOSCtxSw189.EXPORTOSIntCtxSw190.EXPORTOS_CPU_PendSVHandler关于EXPORT的用法和意义,可以参考RealView编译工具4.0版《汇编器指南》第7.8.7小节EXPORT或GLOBAL:EXPORT指令声明一个符号,链接器可以使用该符号解析不同对象和库文件中的符号引用。GLOBAL是EXPORT的同义词。使用EXPORT可使其他文件中的代码能够访问当前文件中的符号。与EXPORT相对应的是IMPORT,可以参考RealView编译工具4.0版《汇编器指南》第7.8.10小节IMPORT和EXTERN:-第18页-这些指令为汇编器提供一个未在当前汇编中定义的名称。在链接时,名称被解析为在其他对象文件中定义的符号。该符号被当作程序地址。如果未指定[WEAK]且在链接时没有找到相应的符号,则链接器会产生错误。段由于编译器的原因,也要将下面的内容替换一下:191.RSEGCODE:CODE:NOROOT(2);RSEGCODE:选择段code。第二个CODE表示代码段的意思,只读。192.;NOROOT表示:如果这段中的代码没调用,则允许连接器丢弃这段193.;(2)表示:4字节对齐。假如是(n),则表示2^n对齐替换为:194.AREA|.text|,CODE,READONLY,ALIGN=2;AREA|.text|表示:选择段|.text|。195.;CODE表示代码段,READONLY表示只读(缺省)196.;ALIGN=2表示4字节对齐。若ALIGN=n,这2^n对齐197.THUMB;Thumb代码198.REQUIRE8;指定当前文件要求堆栈八字节对齐199.PRESERVE8;令指定当前文件保持堆栈八字节对齐对于汇编命令,想了解更多,请看RealView编译工具4.0版《汇编器指南》关于段的补充:段可以分为代码段和数据段,其中代码段的内容就是可执行代码。用keil编译时,经常会出现这样的提示:Code是代码占用的空间,RO-data是ReadOnly只读常量的大小,如const型,RW-data是(ReadWrite)初始化了的可读写变量的大小,ZI-data是(ZeroInitialize)没有初始化的可读写变量的大小。ZI-data不会被算做代码里因为不会被初始化。简单的说就是在烧写的时候是FLASH中的被占用的空间为:Code+ROData+RWData程序运行的时候,芯片内部RAM使用的空间为:RWData+ZIData-第19页-向量中断控制器NVIC前面讲过,关于PendSV,可以看《Cortex-M3权威指南》中第7.6小节SVC和PendSV。不知道有多少位朋友看过呢?呵呵,如果看过,那下面的内容,就容易理解很多,不然,像看天书那样。200.NVIC_INT_CTRLEQU0xE000ED04;中断控制及状态寄存器ICSR的地址201.;见《Cortex-M3权威指南》第8.4.5小节表8.5)202.NVIC_SYSPRI14EQU0xE000ED22;系统异常优先级寄存器PRI_14203.;即设置PendSV的优先级204.;见《Cortex-M3权威指南》第8.4.2小节表8.3B205.NVIC_PENDSV_PRIEQU0xFF;定义PendSV的可编程优先级为255,即最低206.;为啥是最低呢?大家思考一下207.NVIC_PENDSVSETEQU0x10000000;中断控制及状态寄存器ICSR的位28208.;写1以悬起PendSV中断。读取它则返回PendSV的状态关于向量中断控制器NVIC,推荐大家看《Cortex-M3权威指南》的第7、第8章,里面有很详细的说明,我这里就不做太多的解释。回答一下刚才提出的问题:为啥要把PendSV的可编程优先级设为最低?与SVC异常必须在执行SVC指令后立即得到响应的不同,它是可以像普通的中断一样被悬起的(不像SVC那样会上访)。OS可以利用它“缓期执行”一个异常——直到其它重要的任务完成后才执行动作。悬起PendSV的方法是:手工往NVIC的PendSV悬起寄存器中写1。悬起后,如果优先级不够高,则将缓期等待执行(这里是为什么需要定义NVIC_PENDSVSET的原因)。PendSV的典型使用是用在任务切换上。假如系统使用SysTick异常进行任务切换,则正常情况下:-第20页-但实际上,有时候单片机会进入中断状态响应其他中断,这时如果再产生滴答定时器中断,进行任务切换,打断了原来的中断服务,则运行流程为:显然,中断服务被打断了,间距的时间比较长,这是实时系统所无法忍受的。为此,引入了PendSV来完美解决这个问题了:-第21页-PendSV异常会自动延迟上下文切换的请求,直到其它的ISR都完成了处理后才放行。为实现这个机制,需要把PendSV编程为最低优先级的异常。注:此部分内容出自《Cortex-M3权威指南》中断与中断方式3相关的有两个汇编函数:209.;OS_ENTER_CRITICAL()里进入临界段调用,保存现场环境210.OS_CPU_SR_Save211.MRSR0,PRIMASK;读取PRIMASK到R0(保存全局中断标记,除了故障中断)212.CPSIDI;PRIMASK=1,关中断213.BXLR;返回,返回值保存在R0214.215.216.;OS_EXIT_CRITICAL()里退出临界段调用,恢复现场环境217.OS_CPU_SR_Restore218.MSRPRIMASK,R0;读取R0到PRIMASK中(恢复全局中断标记),通过R0传递参数219.BXLR功能:关全局中断前,保存全局中断标志,进入临界段。退出临界段后恢复中断标记。-第22页-汇编命令讲解:功能作用会更改CPSR中的一个或多个模式以及A、I和F位,但不更改其他CPSR位。CPSID就是中断禁止,CPSIE中断允许。CPS(更改处理器状态)A表示启用或禁用不精确的中止。I表示启用或禁用IRQ中断。F表示启用或禁用FIQ中断。此处CPSIDI就表示禁止IRQ中断MRS将CPSR或SPSR的内容移到一个通用寄存器中。将立即数或通用寄存器的内容加载到CPSR或MSRSPSR的指定字段中。跳转指令,可将下一个指令的地址复制到LRBL(R14,链接寄存器)中。注:此部分内容出自RealView编译工具4.0版《汇编器指南》启动最高优先级任务OSStartHighRdy()启动最高优先级任务,由OSStart()里调用,调用前必须先调用OSTaskCreate创建至少一个用户任务,否则系统会发生崩毁。220.OSStartHighRdy221.LDRR0,=NVIC_SYSPRI14;装载系统异常优先级寄存器PRI_14222.;即设置PendSV中断优先级的寄存器223.LDRR1,=NVIC_PENDSV_PRI;装载PendSV的可编程优先级(255)224.STRBR1,[R0];无符号字节寄存器存储。R1是要存储的寄存器225.;存储到内存地址所基于的寄存器226.;即设置PendSV中断优先级为255227.228.MOVR0,#0;把数值0复制到R0寄存器-第23页-229.MSRPSP,R0;将R0的内容加载到程序状态寄存器PSR的指定字段中。230.231.LDRR0,__OS_Running;OSRunning=TRUE232.MOVR1,#1233.STRBR1,[R0]234.235.LDRR0,=NVIC_INT_CTRL;装载中断控制及状态寄存器ICSR的地址236.LDRR1,=NVIC_PENDSVSET;中断控制及状态寄存器ICSR的位28237.STRR1,[R0];设置中断控制及状态寄存器ICSR位28为1238.;以悬起(允许)PendSV中断239.240.CPSIEI;开中断(前面已经讲解过)任务切换当任务放弃CPU的使用权时,就会调用OS_TASK_SW()一般情况下,OS_TASK_SW()是做任务切换。但在M3中,任务切换的工作都被放到PendSV的中断处理服务中去做以加快处理速度,因此OS_TASK_SW()只需简单的悬起(允许)PendSV中断即可。当然,这样就只有当再次开中断的时候,PendSV中断处理函数才能执行。OS_TASK_SW()是由OS_Sched()(此函数在OS_CORE.C)调用。241./******************任务级调度器******************/242.voidOS_Sched(void)243.{244.#ifOS_CRITICAL_METHOD==3245.OS_CPU_SRcpu_sr=0;246.#endif247.248.OS_ENTER_CRITICAL();249.if(OSIntNesting==0){//如果没中断服务运行250.if(OSLockNesting==0){//调度器没上锁251.OS_SchedNew();//查找最高优先级就绪任务252.//见os_core.c,会修改OSPrioHighRdy253.if(OSPrioHighRdy!=OSPrioCur){//如果得到的最高优先级就绪任务不等于当前254.//注:当前运行的任务也在就绪表里255.OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];//得到任务控制块指针256.#ifOS_TASK_PROFILE_EN>0257.OSTCBHighRdy->OSTCBCtxSwCtr++;//统计任务切换到次任务的计数器加1258.#endif259.OSCtxSwCtr++;//统计任务切换次数的计数器加1260.OS_TASK_SW();//进行任务切换261.}262.}悬起(允许)PendSV263.}264.OS_EXIT_CRITICAL();//退出临界段,开中断265.}开中断,执行PendSV中断-第24页-OS_TASK_SW()就是用宏定义包装的OSCtxSw()(见OS_CPU.H):266.#defineOS_TASK_SW()OSCtxSw()前面已经说了,OS_TASK_SW()只需简单的悬起(允许)PendSV中断即可。267.OSCtxSw268.;悬起(允许)PendSV中断(看不懂这段代码的,可参考前面见过的OSStartHighRdy)269.LDRR0,=NVIC_INT_CTRL;装载中断控制及状态寄存器ICSR的地址270.LDRR1,=NVIC_PENDSVSET;中断控制及状态寄存器ICSR的位28271.STRR1,[R0];设置中断控制及状态寄存器ICSR位28为1272.;以悬起(允许)PendSV中断273.BXLR;返回中断退出处理当中断处理函数退出时,就会调用OSIntExit()来决定是否有优先级更高的任务需要执行。如果有,OSIntExit()会调用OSIntCtxSw()做任务切换。在M3里,与OSCtxSw一样,任务切换时,OSIntCtxSw都只需简单的悬起(允许)PendSV中断即可,真正的任务切换工作放在PendSV中断服务程序里,等待开中断时才正在执行任务切换。在这里,OSCtxSw的代码是与OSCtxSw完全相同的:274.OSIntCtxSw275.LDRR0,=NVIC_INT_CTRL;triggerthePendSVexception276.LDRR1,=NVIC_PENDSVSET277.STRR1,[R0]278.BXLR尽管这里的SCtxSw()和OSIntCtxSw()代码是完全一样的,但事实上,这两个函数的意义是不一样的。OSCtxSw()做的是任务之间的切换。例如任务因为等待某个资源或做延时,就会调用这个函数来进行任务调度,有任务调度进行任务切换。OSIntCtxSw()则是中断退出时,如果最高优先级就绪任务并不是被中断的任务就会被调用,由中断状态切换到最高优先级就绪任务中,所以OSIntCtxSw()又称中断级的中断任务。-第25页-由于调用OSIntCtxSw()之前肯定发生了中断,所以无需保存CPU寄存器的值了。这里只不过由于CM3的特殊机制导致了在这两个函数中只要做触发PendSV中断即可,具体切换由PendSV中断服务来处理。PendSV中断服务前面已经讲解过很多次PendSV的作用了,这里就不啰嗦了,先来PendSV中断服务的伪代码吧,方便理解:279.//OS_CPU_PendSVHandler伪代码思路280.OS_CPU_PendSVHandler:281.if(PSP!=NULL){//当调用OS_CPU_PendSVHandler()时,282.//CPU就会自动保存xPSR、PC、LR、R12、R0-R3寄存器到堆栈283.//保存后,CUP的栈SP指针会切换到使用主堆栈指针MSP上284.//我们只需检测进入栈指针PSP是否为NULL就知道是否进行任务切换285.//因此当我们第一次启动任务是,OSStartHighRdy()就把PSP设为NULL,286.//避免系统以为已经进行任务切换287.SaveR4-R11ontotaskstack;//手动保存R4-R11288.OSTCBCur->OSTCBStkPtr=SP;//保存进入栈指针PSP到任务控制块289.//以便下次继续任务运行时继续使用原来的栈290.}291.OSTaskSwHook();//此处便于我们使用钩子函数来拓展功能292.OSPrioCur=OSPrioHighRdy;//获取最高优先级就绪任务的优先级293.OSTCBCur=OSTCBHighRdy;//获取最高优先级就绪任务的任务控制块指针294.PSP=OSTCBHighRdy->OSTCBStkPtr;//保存进入栈指针295.RestoreR4-R11fromnewtaskstack;//从新的栈恢复R4-R11寄存器296.Returnfromexception;//返回具体的汇编代码:297.OS_CPU_PendSVHandler;CPU会自动保存xPSR,PC,LR,R12,R0-R3298.CPSIDI;关中断299.MRSR0,PSP;PSP就是栈指针,R0=PSP300.CBZR0,OSPendSV_nosave;当PSP==0,执行OSPendSV_nosave函数301.302.SUBR0,R0,#0x20;装载r4-11到栈,共8个寄存器,32位,4个字节303.;即8*4=32=0x20304.STMR0,{R4-R11};305.306.LDRR1,__OS_TCBCur;R1=&OSTCBCur307.LDRR1,[R1];R1=*R1(R1=OSTCBCur)308.STRR0,[R1];*R1=R0(*OSTCBCur=SP)309.310.OSPendSV_nosave311.PUSH{R14};保存R14-第26页-312.LDRR0,__OS_TaskSwHook;调用钩子函数OSTaskSwHook()313.BLXR0314.POP{R14};恢复R14315.316.LDRR0,__OS_PrioCur;设置当前优先级为最高优先级就绪任务的优先级317.;OSPrioCur=OSPrioHighRdy318.LDRR1,__OS_PrioHighRdy319.LDRBR2,[R1]320.STRBR2,[R0]321.322.LDRR0,__OS_TCBCur;设置当前任务控制块指针323.LDRR1,__OS_TCBHighRdy;OSTCBCur=OSTCBHighRdy324.LDRR2,[R1]325.STRR2,[R0]326.327.LDRR0,[R2];R0是新的SP328.;SP=OSTCBHighRdy->OSTCBStkPtr;329.330.LDMR0,{R4-R11};从新的栈恢复R4-R11331.ADDR0,R0,#0x20332.MSRPSP,R0;PSP=R0,用新的栈SP加载PSP333.ORRLR,LR,#0x04;确保LR位2为1,返回到使用进程堆栈334.CPSIEI;开中断335.BXLR;返回当第一次开始任务切换时时,而任务刚创建时R4-R11已经保存在堆栈中,此时不用再保存,就会跳到OS_CPU_PendSVHandler_nosave执行。前面已经说过真正的任务切换是在PendSV中断处理函数里做的,由于M3在中断时会有一些的寄存器自动保存到任务堆栈里,所以在PendSV中断处理函数中只需保存R4-R11并调节堆栈指针即可。其中xPSR,PC,LR,R12,R0-R3已自动保存,不用我们管了。-第27页-下面是一个任务切换时寄存器的情况:到此,重要的代码知识点就讲解完毕了,呵呵,初学者看起来会有点困难,不过要加油哦!多看几次就弄懂的!限于个人能力,欢迎各位高手指出错误,在此先表达感谢!-第28页-3、uC/OS-II移植到STM32处理器的步骤下面,我们将讲解移植uC/OS-II到野火开发板的示范实验,先来一张野火STM32开发板的图片:我们的uC/OS-II移植实验是在野火STM32开发板附带的LED实验基础上来讲的,所用的工程文件也是野火STM32开发板所带的LED例程。对于没接触过野火STM32开发板实验教程的朋友,建议你们还是看下野火的LED教程。好了,转到正题上。看完前面的内容,不知道各位是否对uC/OS-II的移植有了整体的把握了?对于uC/OS-II的工程文件结构,又是否了解呢?-第29页-我们先来回顾一下一个uC/OS-II的开发板工程的文件结构吧:很明显,为了让开发板硬件驱动程序与uC/OS-II系统的文件系统分开,好让我们开发工程时不必太乱,我们需要按照一定的规则建立分类文件夹。好了,下面开始正式移植uC/OS-II了:在这里,我们直接采用野火STM32的LED工程来作为基础,进行uC/OS-II移植的讲解(如果不知道LED工程如何建立,请看野火STM32的LED教程,这里就不再重复):-第30页-3.1打开LED工程模版首先,我们从野火STM32光盘资料那里提取LED实验:LED工程文件在光盘目录下:\实验代码+PDF教程\野火Stm32-实验代码\2-LED.rar-第31页-解压打开工程后,就会看到LED工程的文件结构为:这个是我们过往开发裸机单片机程序时写的工程文件结构,但对于uC/OS-II,或者其他大型点的软件工程,这样的文件结构就会很乱的。3.2搭建uC/OS-II工程文件结构我们需要建立的文件结构为(其他没显示出来的文件,按照原来位置那样不改变):-第32页-STM32+UCOS+LED│├─USER│main.c│includes.h//新建├─uCOS-II│├─Source//这文件夹来自于下载附件的Micrium\Software\uCOS-II││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│└─Ports//里面文件来自于\Micrium\Software\uCOS-II\Ports\arm-cortex-m3\Generic\IAR│os_cpu.h│os_cpu_a.asm│os_cpu_c.c│os_dbg.c├─BSP//这文件夹新建,里面文件来自USER文件夹│led.c-第33页-│led.h│BSP.c│BSP.h└─APP//这文件及里面的文件(除os_cfg.h)都是新建app.capp.happ_cfg.h//是用来配置应用软件,主要是任务的优先级和堆栈大小,中断优先级等os_cfg.h//拷贝自Micrium\Software\EvalBoards\ST\S.\I.\OS.\os_cfg.h为了方便初学者,下面的为具体的详细步骤,如果会自行搭建文件结构,可跳过这一小节:1把LED工程所在的文件夹先改名为:STM32+UCOS+LED(建议这样做,避免与原来LED工程混乱)2在USER文件夹下新建includes.h头文件。3按照之前给的uC/OS-II文件结构图,我们在工程的根目录下建立BSP文件夹、APP文件夹和uCOS-II文件夹。BSP文件夹存放外设硬件驱动程序。APP文件夹存放应用软件任务uCOS-II文件夹uC/OS-II的相关代码4把USER文件夹下的led.h和led.c文件剪切到BSP文件夹里。在BSP文件夹里新建BSP.c和BSP.h文件。-第34页-5在APP文件夹下建立app.h、app.c和app_cfg.h文件。拷贝uC/OS-II源代码附件那里的Micrium\Software\EvalBoards\ST\STM32F103ZE-SK\IAR\OS-Probe-L
本文档为【uCOS II 移植与深入实战指南】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: ¥14.4 已有0 人下载
最新资料
资料动态
专题动态
个人认证用户
喜子喜子
暂无简介~
格式:pdf
大小:1MB
软件:PDF阅读器
页数:0
分类:工学
上传时间:2020-03-28
浏览量:27