转载,原作者:合嵌电子
移植准备
1. 建立工程所需的文件夹 l
建立文件夹uCOS-II-Port |
:工程根目录 |
l 建立文件夹uCOS-II-Port/App |
:存放用户应用程序相关 |
l 建立文件夹uCOS-II-Port/Bsp l 建立文件夹uCOS-II-Port/Library |
:存放开发板初始化驱动文件 |
l 建立文件夹uCOS-II-Port/Library/CM3 l 建立文件夹uCOS-II-Port/Library/CM3/startup |
:存放启动文件及内核支撑文件 |
l 建立文件夹uCOS-II-Port/Library/STM32_Lib l 建立文件夹uCOS-II-Port/OS-uCOSII |
:存放标准外设函数库文件 |
l 建立文件夹uCOS-II-Port/OS-uCOSII/core |
:存放uCOS-II源代码,无需修改 |
l 建立文件夹uCOS-II-Port/OS-uCOSII/port |
:存放移植相关文件,需修改 |
l 建立文件夹uCOS-II-Port/Project l 建立文件夹uCOS-II-Port/Project/List l 建立文件夹uCOS-II-Port/Project/Obj |
:存放工程相关文件 |
此步骤完成以后,目录结构如下所示:
2. 移植源码包(光盘中附带):
l STM32标准外设驱动库v3.5
此源代码的文件结构不再说明
l uCOS-II系统源代码v2.86
解压后文件结构如下:
具体文件结构说明如下图所示:
文件名 |
说明 |
||||||
AppNote |
uCOS-II说明,其中AppNotes\AN1xxx-RTOS\AN1018-uCOS-II-Cortex-M3.pdf文件介绍了 移植过程的详细说明。 |
||||||
Licensing |
uCOS-II的应用许可。 |
||||||
Software |
此文件夹在移植过程中需要修改,具体为uCOS-II文件夹下的ports和source。 |
||||||
uCOS-II |
|
Doc |
官方自带说明文档和教程 |
||||
|
Ports |
官方移植M3的文件(RealView) |
|||||
Cpu.h |
数据类型、处理器相关代码、函数原型 |
||||||
Cpu_c.c |
定义用户钩子函数,提供扩充软件功能的入口 点(所谓钩子函数,就是指那些插入到某函数 中拓展这些函数功能的函数) |
||||||
Cpu_a.asm |
与 |
处理器相关汇编函数,主要是任务切换函数 |
|||||
Os_dbg.c |
内核调试数据和函数 |
||||||
|
Source |
uCOS-Ii的 源代码文件 |
|||||
ucos_ii.h |
内核函数参数设置 |
||||||
os_core.c |
内核结构管理,uC/OS 的核心,包含了内核初 始化,任务切换,事件块管理、事件标志组管 理等功能。 |
||||||
os_time.c |
延时处理 |
||||||
os_tmr.c |
定时器管理,设置定时时间,时间到了就进行 |
||||||
|
一次回调函数处理。 |
||||||
os_task.c |
任务管理 |
||||||
os_mem.c |
内存管理 |
||||||
os_mutex.c |
|
信号量管理 |
|||||
os_mbox.c |
邮箱消息 |
||||||
os_q.c |
|
队列 |
|||||
os_flag.c |
事件标志组 |
||||||
CPU |
S |
TM32标准外设固件库 |
|||||
EvalBoards |
m |
icrium 官方评估板的代码 |
|||||
uC-CPU |
|
基于 micrium 官方评估板的 CPU 移植代码 |
|||||
uC-LIB |
m |
icrium 官方的一个库代码 |
|||||
uC-Probe |
|
一个通用工具,能让嵌入式开发人员在实时环境中监测嵌入式系统。 |
|||||
3. 文件对号入座
通过之前的准备工作,我们需要把官方源码包中相应的文件,拷贝到我们建立的工程文件夹中,
首先进行库函数源代码搬移工作:
STM32F10x_StdPeriph_Lib_V3.5.0 |
\ |
Libraries |
l 打开\STM32F10x_StdPeriph_Driver 将其下的inc和src拷贝至uCOS-II-Port\Library\STM32_Lib
CM3 |
STM32F10x_StdPeriph_Lib_V3.5.0 |
\ |
Libraries |
l 打开\CMSIS\
其下有CoreSupport和DeviceSupport两个文件夹
CoreSupport |
Ø 分别将下的core_cm3.c和core_cm3.h
DeviceSupport |
\ |
ST |
和\STM32F10x下的stm32f10x.h、system_stm32f10x.c和
system_stm32f10x.h拷贝至uCOS-II-Port\Library\CM3,并去掉只读属性
DeviceSupport |
STM32F10x |
\ |
startup |
Ø 再将\arm下的startup_stm32f10x_hd.s
拷贝至uCOS-II-Port\Library\CM3\startup
注:盘古UE-STM32F103的主芯片的内部flash为512K
STM32F10x_StdPeriph_Lib_V3.5.0 |
\ |
Project |
l 打开\STM32F10x_StdPeriph_Template
将其下的stm32f10x_conf.h、stm32f10x_it.c和stm32f10x_it.h拷贝至uCOS-II-Port\App
至此,库函数的源代码搬移工作已经完成,现在进行uCOS-II的源代码搬移工作:
Micrium |
\ |
Software |
\ |
uCOS-II |
\ |
Source |
l 打开
将其下的所有文件拷贝至uCOS-II-Port\OS-uCOSII\core
Micrium |
\ |
Software |
\ |
uCOS-II |
\ |
Ports |
\ |
ARM-Cortex-M3 |
\ |
Generic |
\ |
RealView |
l 打开
将其下的所有文件拷贝至ucos\uCOS-II-Port\OS-uCOSII\port
Micrium |
\ |
Software |
ST |
\ |
STM3210B-EVAL |
\ |
RVMDK |
\ |
OS-Probe |
l 打开\EvalBoards\
将其下的os_cfg.h拷贝至ucos\uCOS-II-Port\App
至此,所有的可利用的文件已经搬移结束,不过仍然需要建立一些文件,这个工程的文件结构才算完
整,具体如下:
l 打开
新建app.c、app_cfg.h 和includes.h三个空文件
l 打开
ucos |
\ |
uCOS-II-Port |
ucos |
\ |
uCOS-II-Port |
新建bsp.c和bsp.h两个空文件
到目前为止,我们所有的文件准备工作已经完成,我们可以了解一下uCOS-II的体系结构,如下所示:
4. 建立Keil工程
l 打开Keil_v4.20,新建工程UE-uCOS-II-Port工程,并将其保存至uCOS-II-Port\Project
在随后跳出的窗口中,选择芯片型号,盘古UE-STM32F103开发板的芯片为:STM32F103VET6
否 |
点击OK,跳出对话框,是否自动添加启动文件,注意此处选择,因为我们会自己添加。
l 右击项目窗口中Target1,选择Manage Components,在窗口中创建文件组,并在相应的组添加
文件,具体如下:
UE-uCOS-II-Port |
Ø 将Project Targets中的Target1重命名为
STM32F10x_StdPeriph_Driver |
Ø 新建组,并将uCOS-II-Port\Library\STM32_Lib\src下的所
有文件添加到此组下
STM32F10x_CM3 |
Ø 新建组,并将uCOS-II-Port\Library\CM3下所有文件添加到此组中(包括C
文件、H文件和startup下的文件)
APP |
Ø 新建组,并将uCOS-II-Port\App下所有文件添加到此组中
BSP |
Ø 新建组,并将uCOS-II-Port\Bsp下所有文件添加到此组中
uCOSII_port |
uCOSII_core |
Ø 新建组,并将uCOS-II-Port\OS-uCOSII\core下所有C文件添加到此组中Ø 新建组,并将uCOS-II-Port\OS-uCOSII\port下所有文件添加到此组中
具体操作结果,及各文件说明如下图所示:
STM32F10x_StdPeriph_Driver: STM32固件库函数v3.5,包含了各个外设驱动代码; STM32F10x_CM3: core_cm3.h和core_cm3.c文件为内核支撑文件,其他CM3核 的芯片也能使用; stm32f10x.h为标准函数库的入口文件,包含了一些寄存器 的定义; system_stm32f1ox.h、system_stm32f10x.c提供了初始化 stm32芯片的库函数,以及配置PLL、系统时钟和内置flash的接 口函数; startup_stm32f10x_hd.s为stm32的启动文件,hd表示大容 量的芯片。 APP: stm32f10x_conf.h为外设配置文件,此文件可以使能/禁用 外设驱动; stm32f10x_it.c和stm32f10x_it.h为中断服务程序文件; includes.h为全部头文件的头文件,对头文件进行统一管 理; app.c为应用程序文件,包含Main函数; app_cfg.h用来配置应用软件,主要是任务的优先级和堆栈 大小及中断优先级; os_cfg.h为内核配置头文件,移植时需要修改; BSP: Bsp.c存放了开发板初始化启动函数,包含设置系统时钟, 初始化硬件; Bsp.h包含有与开发板初始化相关函数的头文件; uCOSII_core: 此文件组包含了uCOS-II的源代码文件,在移植的过程中, 不需要修改。 uCOSII_port: 此文件组包含了移植的相关文件 os_cpu.h进行数据类型定义,处理器相关代码和几个函数 型; os_cpu_c.c定义一些用户 hook 函数; os_cpu_a.asm为移植需要用汇编代码完成的函数,主要就是 任务切换函数; os_dbg.c为内核调试相关数据和函数,可以不改。 |
5. 设置Option选项
l Device选项卡
stm32f103vet6 |
此步骤前面已经操作,即选择主芯片:
l Output选项卡
uCOS-II-Port\Project\Obj |
设置工程输出文件至:
l Listing选项卡
uCOS-II-Port\Project\List |
设置工程Listing路径值
l C/C++选项卡
设置H文件的路径
l Debug选项卡
在此选项卡中选择你所连接的JLINK,并作相应配置
l Utilities选项卡
作出如下选择操作
至此为止,工程已经建立完毕,接下来需要对相关文件进行修改移植。
6. 移植修改
以下移植步骤来自Micrium\AppNotes\AN1xxx-RTOS\AN1018-uCOS-II-Cortex-M3\AN-1018.pdf l os_cpu.h此文件定义数据类型、处理器相关代码、声明函数原型,下面为部分代码的解释说明。
/*全局变量*/
#ifdef OS_CPU_GLOBALS
#defineOS_CPU_EXT
#else
#defineOS_CPU_EXT extern
#endif
/*数据类型*/
typedef unsigned charBOOLEAN;
typedef unsigned charINT8U;
typedef signed charINT8S;
typedef unsigned shortINT16U;
typedef signed shortINT16S;
typedef unsigned intINT32U;
typedef signed intINT32S;
typedef float FP32;
typedef double FP64;
typedef unsigned intOS_STK;
typedef unsigned intOS_CPU_SR;
/*临界段*/
#define OS_CRITICAL_METHOD 3 //进入临界段的三种模式,一般选择第3种
#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}
#define OS_EXIT_CRITICAL(){OS_CPU_SR_Restore(cpu_sr);}
为了实现资源共享,一个操作系统必须提供临界段擦作的功能。
uCOS-II为了处理临界段代码需要关中断,处理完毕后再开中断。这使得uCOS-II能够避免同时
有其它任务或中断服务进入临界段代码。
微处理器一般都有关中断/开中断指令,用户使用的C语言编译器必须有某种机制能够在C中直
接实现关中断/开中断地操作。某些C编译器允许在用户的C源代码中插入汇编语言的语句。这使得
插入微处理器指令来关中断/开中断很容易实现。而有的编译器把从C语言中关中断/开中断放在语言
的扩展部分。uCOS-II定义两个宏(macros)来关中断和开中断,以便避开不同C编译器厂商选择不同
的方法来处理关中断和开中断。uCOS-II中的这两个宏调用分别是:OS_ENTER_CRITICAL()和
OS_EXIT_CRITICAL()。
/*栈方向*/
#define OS_STK_GROWTH 1
Cotex-M3的栈生长方向是由高地址向低地址增长的,因此OS_STK_GROWTH定义为1
/*任务切换宏*/
#define OS_TASK_SW() OSCtxSw()
/*开中断 关中断*/
#ifOS_CRITICAL_METHOD == 3
OS_CPU_SR OS_CPU_SR_Save(void);
void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
#endif
其 中OS_CPU_SR_Save() 和OS_CPU_SR_Restore()是用汇编代码写的,代码在 os_cpu_a.asm
/*任务切换的函数*/
voidOSCtxSw(void); //用户任务切换
voidOSIntCtxSw(void); //中断任务切换函数
voidOSStartHighRdy(void); //在操作系统第一次启动的时候调用的任务切换
voidOS_CPU_PendSVHandler(void); //用户中断处理函数
voidOS_CPU_SysTickHandler(void); //系统定时中断处理函数,时钟节拍函数
voidOS_CPU_SysTickInit(void); //系统 SysTick定时器初始化 INT32U OS_CPU_SysTickClkFreq(void);//返回 SysTick 定时器的时钟频率
关于任务切换,会涉及到异常处理,具体为SVC(系统服务调用,亦简称系统调用)和PendSV
(可悬起系统调用),它们常用于在操作系统之上的软件开发中。
SVC用于产生系统函数的调用请求。例如,操作系统不让用户程序直接访问硬件,而是通过提供
一些系统服务函数,用户程序使用SVC发出对系统服务函数的呼叫请求,以这种方法调用它们来间接
访问硬件。因此,当用户程序想要控制特定的硬件时,它就会产生一个SVC异常,然后操作系统提供
的SVC异常服务例程得到执行,它再调用相关的操作系统函数,后者完成用户程序请求的服务。SVC
异常通过执行”SVC”指令来产生,该指令需要一个立即数,充当系统调用代号。SVC异常服务例程
稍后会提取出此代号,从而解释本次调用的具体要求,再调用相应的服务函数。
另一个相关的异常是PendSV(可悬起的系统调用),它和SVC协同使用。一方面,SVC异常是必
须立即得到响应的(若因优先级不比当前正处理的高,或是其它原因使之无法立即响应,将上访成硬
fault),应用程序执行SVC时都是希望所需的请求立即得到响应。另一方面,PendSV则不同它是可
以像普通的中断一样被悬起的(不像SVC那样会上访)。OS可以利用它“缓期执行”一个异常,直到
其它重要的任务完成后才执行动作。悬起PendSV的方法是:手工往NVIC的PendSV悬起寄存器中写
1。悬起后,如果优先级不够高,则将缓期等待执行。
具体异常处理相关知识,若想知其原理,请详细阅读《Cotex-M3权威指南》。
在此处,我们需要对此文件进行修改:
void OS_CPU_PendSVHandler(void) 需替换成void PendSV_Handler(void) |
1)
一般我们自己开发基于 stm32 芯片的软件,都会使用标准外设库 CMSIS 中提供的启动文
件,比如startup_stm32f10x_hd.s,而Micrium官方没有用ST的标准启动文件,而且分开写成
了两个.s 文件,即
init.s和vectors.s(Micrium\Software\EvalBoards\ST\STM3210B-EVAL\RVMDK)
init.s 负责进入main(),vectors.s设置中断向量
由于OS_CPU_PendSVHandler这个中断向量就是在vectors.s中被设置的,且我们使用的是
startup_stm32f10x_hd.s作为启动文件的,而在startup_stm32f10x_hd.s文件中,PendSV的中
断向量名为PendSV_Handler,所以只需用PendSV_Handler替换掉相应文件的
OS_CPU_PendSVHandler,其中函数声明在OS_CPU_C.h中,具体的中断服务函数原型在 OS_CPU_A.ASM中,后面也将对其进行修改。
这样子,替换后的PendSV_Handler函数在OS_CPU_C.h中有声明,在OS_CPU_A.ASM中有具
体的中断服务函数代码,与startup_stm32f10x_hd.s 中的向量地址就对应上了。
注释掉最后三个关于SysTick服务函数 |
2)
void OS_CPU_SysTickHandler(void);
void OS_CPU_SysTickInit(void); INT32U OS_CPU_SysTickClkFreq(void);
其中,OS_CPU_SysTickHandler函数在ST标准库stm32f10x_it.c中已定义,此处不需要;
其中,OS_CPU_SysTickInit定义在os_cpu_c.c中,依赖于OS_CPU_SysTickClkFreq,用于初始
化SysTick定时器,需注释掉;其中,OS_CPU_SysTickClkFreq定义在官方EvalBoards的BSP.c
中,需解除依赖,若需要,我们可以在bsp.c中实现。
修改后如下所示:
SysTick 作为 OS 的“心跳”,可称为滴答时钟,本质上来说就是一个定时器,和PendSV中
断一样,在startup_stm32f10x_hd.s中SysTick的中断向量名为SysTick_Handler,且因为ST
标准库已经有相关库函数,所以我们只需作如下修改:
打开os_cpu_c.c文件,找到void OS_CPU_SysTickHandler(void)的内容代码 |
|
||||||||
|
|
|
OS_CPU_SR cpu_sr; |
|
|||||
|
|
|
OS_ENTER_CRITICAL(); |
|
|||||
|
|
|
OSIntNesting++; |
|
|||||
|
|
|
OS_EXIT_CRITICAL(); |
|
|||||
|
|
|
OSTimeTick(); |
|
|||||
|
|
|
OSIntExit(); |
|
|||||
复制到 stm32f10x_it.c文件中的 SysTick_Handler (void)函数内; |
|
||||||||
|
|
|
void SysTick_Handler(void) |
||||||
|
|
|
{ |
|
|||||
|
|
|
|
OS_CPU_SR cpu_sr; |
|||||
|
|
|
|
OS_ENTER_CRITICAL(); |
|||||
|
|
|
|
OSIntNesting++; |
|||||
|
|
|
|
OS_EXIT_CRITICAL(); |
|||||
|
|
|
|
OSTimeTick(); |
|||||
|
|
|
|
OSIntExit(); |
|||||
|
|
|
} |
|
|||||
|
并且在文件头部添加:#include |
|
|||||||
注释掉EXPORT OS_CPU_PendSVHandler,并修改成EXPORT PendSV_Handler,如下 |
l os_cup_a.asm 根据前面的描述,OS_CPU_PendSVHandler中断服务函数的原型在此文件中,我们需要用PendSV_Handler将其替换,以实现在startup_stm32f10x_hd.s中的中断向量的匹配。
1)
所示 |
:
找到OS_CPU_PendSVHandler程序原型,并重命名为PendSV_Handler |
2)
l os_cpu_c.c
此文件需要由我们来写10个相当简单的C函数
OSInitHookBegin()
OSInitHookEnd()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskIdleHook()
OSTaskStatHook()
OSTaskStkInit()
OSTaskSwHook()
OSTCBInitHook()
OSTimeTickHook()
主要包括9个钩子函数和1个负责建立任务堆栈的函数OSTaskStkInit()。
所谓钩子函数,指那些插入到某些函数中为扩展这些函数功能而存在的函数。一般来说,钩
子函数是进行软件功能扩充的入口点。不仅如此,uCOS-II中还提供有大量的钩子函数,用户不
需要修改uCOS-II的内核代码程序,而只需要向钩子函数添加代码即可拓展uCOS-II的功能,如
果要用到这些钩子函数,需要在
OS_CFG.H中使能OS_CPU_HOOKS_EN为1,即:#defineOS_CPU_HOOKS_EN 1
同时关于OSTaskStkInit,OSTaskCreate和OSTaskCreateExt通过调用OSTaskStkInt来初
始化任务的堆栈结构,因此,堆栈看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情
形一样。一旦用户初始化了堆栈,OSTaskStkInit就需要返回堆栈指针所指的地址,OSTaskCreate
和 OSTaskCreateExt会获得该地址并将它保存到任务控制块(OS_TCB)中,处理器的文档会告诉
用户堆栈指针会指向下一个堆栈空闲位置,还是会指向最后存入数据的堆栈单元位置。
#define OS_CPU_CM3_NVIC_ST_CTRL (*((volatile INT32U *)0xE000E010))
#define OS_CPU_CM3_NVIC_ST_RELOAD (*((volatile INT32U *)0xE000E014))
#define OS_CPU_CM3_NVIC_ST_CURRENT (*((volatileINT32U *)0xE000E018)) #define OS_CPU_CM3_NVIC_ST_CAL (*((volatile INT32U *)0xE000E01C))
#define OS_CPU_CM3_NVIC_ST_CTRL_COUNT 0x00010000
#define OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC 0x00000004
#define OS_CPU_CM3_NVIC_ST_CTRL_INTEN 0x00000002 #define OS_CPU_CM3_NVIC_ST_CTRL_ENABLE 0x00000001
|
#define OS_FLAG_EN |
0 //禁用信号量集 |
|||
|
#define OS_MBOX_EN |
0 //禁用邮箱 |
|||
|
#define OS_MEM_EN |
0 //禁用内存管理 |
|||
|
#define OS_MUTEX_EN |
0 //禁用互斥信号量 |
|||
|
#define OS_Q_EN |
0 //禁用队列 |
|||
|
#define OS_SEM_EN |
0 //禁用信号量 |
|||
|
#define OS_TMR_EN |
0 //禁用定时器 |
|||
|
#define OS_DEBUG_EN |
0 //禁用调试 |
|||
也可以禁用应用软件的钩子函数和多重事件控制 |
|
||||
|
#define OS_APP_HOOKS_EN |
0 |
|
||
|
#define OS_EVENT_MULTI_EN |
0 |
|
||
这些所做的修改主要是把一些功能给去掉,减少内核大小,也利于编译调试。等用到的时候,再开启
相应的功能。
7 应用实例
至此,所有的移植已经完成,如需更详细的说明整个移植过程,请参考AN-1018.pdf,接下来我
们将编写应用相关的代码,其中有一些入门知识需要说明,uCOS-II可以管理多达64个任务,但保
留了优先级为0、1、2、3、OS_LOWEST_PRIO-3、OS_LOWEST_PRI0-2,OS_LOWEST_PRI0-1以及
OS_LOWEST_PRI0这8个任务以被将来使用,用户可以有多达56个应用任务,必须给每个任务赋以不
同的优先级,优先级号越低,任务的优先级越高。
uCOS-II的初始化流程为:在调用uCOS-II的任何其它任务之前,uCOS-II要求用户首先调用系
统初始化函数OSInit(),且多任务的启动是用户通过调用OSStart()实现的。然而,启动uCOS-II之
前,用户至少要建立一个应用任务, 用户可以通过传递任务地址和其它参数到以下两个函数之一来建
立任务:OSTaskCreate()或者OSTaskCreateExt(),如以下所示:
main()
{
…..
OSInit(); /* 初始化uC/OS-II*/
……
OSTaskCreate()或OSTaskCreateExt();
……
OSStart(); /*开始多任务调度!永不返回 */
}
在UE-STM32F103开发板上有3个LED,我们将创建两个任务,分别控制这3个LED,具体代码如下所
示:
l app.c
#include
static OS_STK led1_task_stk[LED1_TASK_STK_SIZE]; //开辟任务堆栈
static OS_STK led2_task_stk[LED1_TASK_STK_SIZE ]; //开辟任务堆栈
static void systick_init(void); static void systick_init(void) { RCC_ClocksTypeDef rcc_clocks; |
|
|
|
|
|
//函数声明 |
RCC_GetClocksFreq(&rcc_clocks); |
|
|
|
//调用标准库函数,获取系统时钟。 |
SysTick_Config(rcc_clocks.HCLK_Frequency/ OS_TICKS_PER_SEC); //初始化并使能
SysTick }
static void led1_task(void *para)
{
para = para;
while(1)
{
GPIO_SetBits(GPIOD,GPIO_Pin_7);
OSTimeDlyHMSM(0,0,1,0); //1s延时,释放CPU控制权
GPIO_ResetBits(GPIOD,GPIO_Pin_7);
OSTimeDlyHMSM(0,0,1,0); //1s延时,释放CPU控制权
} }
static void led2_task(void *para)
{
para = para;
while(1)
{
GPIO_SetBits(GPIOD,GPIO_Pin_5);
GPIO_SetBits(GPIOD,GPIO_Pin_6);
OSTimeDlyHMSM(0,0,0,500); //500ms延时,释放CPU控制权
GPIO_ResetBits(GPIOD,GPIO_Pin_5);
GPIO_ResetBits(GPIOD,GPIO_Pin_6);
OSTimeDlyHMSM(0,0,0,500); //500ms延时,释放CPU控制权
}
}
intmain(void) {
BSP_Init(); OSInit();
systick_init();
OSTaskCreate(led1_task, 0, &led1_task_stk[LED1_TASK_STK_SIZE -1], LED1_TASK_PRIO);
OSTaskCreate(led2_task,0, &led2_task_stk[LED2_TASK_STK_SIZE - 1],
LED2_TASK_PRIO); OSStart();
return 0;
}
l app_cfg.h
/* task priority */
#defineLED1_TASK_PRIO 4 #define LED2_TASK_PRIO 6
/* task stack size */
#define LED1_TASK_STK_SIZE 80
#define LED2_TASK_STK_SIZE 80
l Bsp.c
#include
static void BSP_LED_Init(void);
void BSP_Init (void)
{
SystemInit();
BSP_LED_Init(); /* Initialize the LED */
}
static void BSP_LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE); //使能时钟
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; //LED_pin
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_2MHz;
GPIO_Init(GPIOD,&GPIO_InitStructure);
}
l Bsp.h
#ifndef __BSP_H
#define __BSP_H
void BSP_Init(void);
#endif