国内最全IT社区平台 联系我们 | 收藏本站
华晨云阿里云优惠2
您当前位置:首页 > 互联网 > 如何把UCos-ii_在STM32上的移植

如何把UCos-ii_在STM32上的移植

来源:程序员人生   发布时间:2014-09-04 14:44:11 阅读次数:3329次
下载代码
stm32 标准外设库是 stm32 全系列芯片的外设驱动,有了它可以大大加速我们
开发 stm32。
首先从 st 公司的网站下载最新的 stm32 标准外设库,写本文时最新的版本是
V3.5.0。
解压该 zip 文件,得到如下文件夹和文件
STM32F10x_StdPeriph_Lib_V3.5.0 
_htmresc 
Libraries 
Project 
Utilities 
Release_Notes.html 
stm32f10x_stdperiph_lib_um.chm
其中 Libraries 包含库的源代码,Project 包含 stm32 各个外设的使用范例和一
个工程模板,Utilities是使用st公司评估板的例子,stm32f10x_stdperiph_lib_um.chm
教我们怎么用标准外设库。
工程目录结构
既然准备使用 32 位单片机,应该是个不小项目,因此工程目录也应该做个规划。
这里我推荐一下我所使用的目录结构。假设工程名字叫 template,建一个名为
template 的文件夹,该目录下有个 3 个固定文件夹 doc,src,include,doc 用来存
放工程相关的资料文件,src 放源代码,在 src 下每个功能模块一个文件夹,include
放各个模块都要使用的公共头文件。output 放编译输出文件,内含两个子文件夹 obj
和 list。
template 
 doc 
 src 
 include 
 outputobj 
 list 整理库代码
由于 Libraries 下的 CMSIS 文件夹中很多代码是和编译器及芯片相关的,导致
文件夹多且深度大,不利于工程维护,实际上一个项目往往是用固定的编译器和芯
片,因此有必要对库进行整理。
在 src 下建立 libstm32 目录
1. 把 LibrariesSTM32F10x_StdPeriph_Driver下的内容拷贝到 libstm32 目录

2. 在 libstm32 目录下建立 cmsis 文件夹,把
LibrariesCMSISCM3CoreSupport下的 core_cm3.c,core_cm3.h;
LibrariesCMSISCM3DeviceSupportSTSTM32F10x下的 stm32f10x.h,
system_stm32f10x.c,system_stm32f10x.h 拷贝到 cmsis 文件夹中。
3. 根据你所选的芯片类型,将
LibrariesCMSISCM3DeviceSupportSTSTM32F10xstartuparm下对应的启动
文件拷贝到 cmsis 文件夹中。这里我拷贝的是 startup_stm32f10x_hd.s(大容量型
stm32 芯片的启动文件)。
下面对该库文件做个简单介绍:
LibrariesSTM32F10x_StdPeriph_Driver下的内容很好理解就是 stm32 的各个
外设模块驱动代码。
 misc.h 和 misc.c 是和 CM3 内核有关的 NVIC 和 SysTick 的驱动代码。 
 LibrariesCMSIS 下是什么呢?cmsis 英文全称:Cortex Microcontroller 
Software Interface Standard,是 Cortex 系列处理器硬件抽象层,可以理解为 cortex
内核的软件接口。
 core_cm3.c, core_cm3.h 
它们的目录名为 CoreSupport,说明这两个文件是 CM3 内核支撑文件,其他使
用 CM3 内核的芯片也可以用,不一定是 stm32。这两个文件用来获取设置 CM3 内
核,配置一些内核寄存器。
stm32f10x.h, system_stm32f10x.c, system_stm32f10x.h 和
startup_stm32f10x_hd.s 在 DeviceSupport 目录下,说明这几个文件是和具体的芯
片有关的,也就是 stm32 芯片的支撑文件。其中 stm32f10x.h 是标准外设库的入口,
使用标准外设库的代码中必须包含该头文件。system_stm32f10x.c, 
system_stm32f10x.h 这两个文件提供函数用来初始化 stm32 芯片,配置 PLL、系
统时钟和内置 flash 接口。startup_stm32f10x_hd.s 是大容量型 stm32 芯片的启动
文件。建立工程 
使用 keil MDK(我使用 4.12 版)在 template 目录下建立工程,工程名为
template。选一个 stm32 系列的芯片,哪一个都无所谓(我选的是 STM32F101RC,
因为我的板子就是用这个芯片),接下来要注意的是当弹出是否拷贝启动代码到工
程文件夹时要选 No,因为标准外设库里已经有启动代码了。
将 UV4 中 project window 里的顶层目录名改为 template,并将第一个 group
名改为 libstm32。把 libstm32 目录下所有.c 和.s 文件加载到工程里的 libstm32。
在 src 下建立一个 init 目录用来放置系统初始化代码。把
ProjectSTM32F10x_StdPeriph_Template下的 stm32f10x_it.c 拷贝到 init 文件夹
中,stm32f10x_it.h,stm32f10x_conf.h 拷贝到 include 文件夹中。
stm32f10x_it.c,stm32f10x_it.h 是中断服务程序文件。stm32f10x_conf.h 是标
准外设库的配置文件,对于工程中不需要的外设,可以注释掉里面的包含的头文件。
这里我建议先仅留下 stm32f10x_gpio.h,stm32f10x_rcc.h,misc.h,用到什么再打
开什么,这样编译起来快一点,当然也可都留着。
使用stm32 标准外设库
事实上,stm32 标准外设库的使用在 stm32f10x_stdperiph_lib_um.chm 中的
How to use the Library 一节中已有说明,下面我把其中的步骤罗列一下:
1. 根据所选芯片,把
LibrariesCMSISCM3DeviceSupportSTSTM32F10xstartuparm 中的启动代码
加到工程中,这一步在上面已经做过了。
2. 在 stm32f10x.h 的 66-73 行,根据所选芯片类型,去掉相应注释,这里我去掉
STM32F10X_HD 行的注释(大容量型 stm32 芯片)。
3. 去掉 105 行的 USE_STDPERIPH_DRIVER 注释,启用 stm32 标准外设库。
4. 在 system_stm32f10x.c 的 110-115 行,根据所选芯片主频,去掉相应注释,默
认 SYSCLK_FREQ_72MHz 注释已去掉,如果你的芯片主频是 72MHz,就不用做
修改了,这里我的芯片是 36MHz,注释 SYSCLK_FREQ_72MHz,去掉
SYSCLK_FREQ_36MHz 注释。跑马灯程序
现在可以使用 stm32 标准外设库了,下面以一个简单的跑马灯程序说明。
在 init 目录下建立 main.c 作为系统入口。
在 src 下建立一个 bsp 目录用来放置板级支持代码,建立 led.c,led.h。
代码如下:
led.h
#ifndef _LED_H_ 
#define _LED_H_ 
#include <stdint.h> 
#define LED_0 0 
#define LED_1 1 
#define LED_2 2 
void led_init(void); 
void led_on(uint32_t n); 
void led_off(uint32_t n); 
#endif 
led.c
#include "stm32f10x.h" 
#include "led.h" 
void led_init(void) { 
GPIO_InitTypeDef GPIO_InitStructure; 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8; 
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; 
 GPIO_Init(GPIOC, &GPIO_InitStructure); 
} void led_on(uint32_t n) { 
switch (n) { 
case LED_0: 
 GPIO_SetBits(GPIOC, GPIO_Pin_6); 
 break; 
 case LED_1: 
 GPIO_SetBits(GPIOC, GPIO_Pin_7); 
 break; 
 case LED_2: 
 GPIO_SetBits(GPIOC, GPIO_Pin_8); 
 break; 
 default: 
 break; 
 } 

void led_off(uint32_t n){ 
 switch (n) { 
 case LED_0: 
 GPIO_ResetBits(GPIOC, GPIO_Pin_6); 
 break; 
 case LED_1: 
 GPIO_ResetBits(GPIOC, GPIO_Pin_7); 
 break; 
 case LED_2: 
 GPIO_ResetBits(GPIOC, GPIO_Pin_8); 
 break; 
 default: 
 break; 


main.c
#include "led.h" 
static void delay(uint32_t ms){ 
uint32_t count = 8000; 
 while (ms--) { 
 while (count--); 
 count = 8000; 
 } 
} int main(void){ 
 led_init(); 
 for (;;) { 
 led_on(LED_0); 
 led_off(LED_1); 
 led_off(LED_2); 
 delay(1000); 
 led_off(LED_0); 
 led_on(LED_1); 
 led_off(LED_2); 
 delay(1000); 
 led_off(LED_0); 
 led_off(LED_1); 
 led_on(LED_2); 
 delay(1000); 


在 project 中建立 init,bsp 组,并将各种代码加入。在工程的 Options 中,c/c++
选项卡的 Include Paths 中添加.include; .srclibstm32cmsis; .srclibstm32inc; 
.srcsp;。
Output 选项卡 Select Folder for Objects 中选.outputobj。
Listing 选项卡 Select Folder for Listings 中选.outputlist。
Debug 选项卡选 use ULINK Cortex Debugger, Run to main()打钩,这一步大家
可以根据自己手上的仿真器做不同选择。编译运行。ucosii在stm32 上的移植详解
虽然目前网上已经有不少关于 ucosii 在 stm32 上的移植版本,包括 micrium 也
有官方移植版本。但这些版本具体是怎么移植出来的,又该怎么基于移植好的 ucosii
开发应用软件,网上介绍的并不多。这里介绍一下我的移植经历,希望对大家有所
帮助。
我的移植基本上是从零开始的。首先想要做好移植,有两方面的内容是必须要
了解。1.目标芯片;2.ucosii 内核原理。
虽然我们移植的目标芯片是 stm32,但操作系统的移植基本是针对 Cortex-M3
内核(以下简称 CM3)而言的,所以我们只需了解 CM3 内核就好了。stm32 芯片
就是 CM3 内核加上各种各样的外设。
怎么才能了解 CM3 呢?看一本书<<ARM Cortex-M3 权威指南>>(宋岩译,网
上多的很)就好了,很多同学可能想,看完这本书移植的新鲜劲都没了,因此我把
该书和移植有关的章节都列了出来,并对其中的重点内容进行介绍,我数了数相关
章节还不到 100 页,就这点内容,总要看了吧。
相关章节如下:
chapter2 Cortex-M3 概览
2.1 - 2.9 
主要了解 Cortex-M3 的概貌。刚开始看时不用追求全部理解,后面会有详细介
绍,很多内容多看几遍就明白。其中 2.8 指令集,只要了解,CM3 只使用 thumb2
就 ok 了。
chapter3 Cortex-M3 基础
3.1 寄存器组
 R0-R12: 通用寄存器
R13: 堆栈寄存器
有两个,MSP 和 PSP,同时只能看见一个
引用 R13 时,引用的是正在使用的那个
MSP:可用于异常服务和应用程序
PSP:只能用于应用程序
系统复位后,用的堆栈指针是 MSP。
 
R14: 连接寄存器,又名 LR,存储返回地址
R15: 程序计数寄存器,又名 PC
3.2 特殊功能寄存器
程序状态字寄存器组(PSRs)
中断屏蔽寄存器组(PRIMASK, FAULTMASK, BASEPRI)
控制寄存器(CONTROL)
程序状态字寄存器组(PSRs)分为
应用程序 PSR(APSR)
中断号 PSR(IPSR)
执行 PSR(EPSR) 每个都是 32 位,由于这 3 个寄存器有效位是错开的,因此可以组合访问。
中断屏蔽寄存器组(PRIMASK, FAULTMASK, BASEPRI),这三个寄存器用于
控制异常的使能和除能。
控制寄存器(CONTROL)它有两个作用:
1.定义特权级别
2.选择当前使用哪个堆栈指针
3.3 操作模式和特权极别
操作模式: 处理者模式和线程模式
异常处理:处理者模式
主程序:线程模式
ucosii 不区分特权级和用户级,程序始终工作在特权级
这两个堆栈指针的切换是全自动的,就在出入异常服务例程时由硬件处理。
3.4 - 3.7 
没什么好讲的,需要看。
3.8 复位序列
0x00000000 MSP 初值
0x00000004 PC 初值 复位向量
chapter7 异常
7.1 异常类型
分为系统异常(编号 1-15)和外部中断(大于 15)
7.2 优先级
CM3 支持 3 个固定的高优先级和多达 256 级的可编程优先级。
在 NVIC 中,每个中断都有一个优先级配置寄存器(1 个 byte),用来配置该
中断的优先级。但该寄存器并不是每个位都被使用,不同制造商生产的芯片不相同,
譬如 stm32 使用 4 位,也就是说 stm32 支持 16 个可编程优先级(参考:chapter9)。
注意该寄存器是以 MSB 对齐的,因此 stm32 每个中断的优先级配置寄存器 7:4
位有效,3:0 位无效。
对于优先级,CM3 又分为抢占优先级和亚优先级,
NVIC 中的应用程序中断及复位控制寄存器(AIRCR)的优先级分组(10:8)描述了
如何划分抢占优先级和亚优先级。
什么意思?以 stm32 为例,优先级配置寄存器不是 7:4 位有效吗,如果 AIRCR
中的优先级分组值为 4,则优先级配置寄存器的 7:5 位确定抢占优先级,位 4 确定
亚优先级。此时所有中断有 8 个抢占优先级,每个抢占优先级有 2 个亚优先级。
抢占优先级高的中断可以抢占抢占优先级低的中断,即抢占优先级决定了中断是
否可以嵌套。
相同抢占优先级的中断不能嵌套,但当抢占优先级相同的异常有不止一个到来
时,就优先响应亚优先级最高的异常。
参考附录 D 
表 D.9 中断优先级寄存器阵列 0xE000_E400 - 0xE000_E4EF 共 240 个。
表 D.16 系统异常优先级寄存器 0xE000_ED18 - 0xE000_ED23 共 12 个。
优先级相同,看中断号,中断号小的优先。7.3 向量表 
初始在 0x00000000 处,可以通过向量表偏移量寄存器(VTOR)(地址:
0xE000_ED08)更改,一般无需更改。
7.4 中断输入及挂起行为
需要看。
7.5 Fault 异常
可不看。
7.6 SVC 和 PendSV 
 SVC 
SVC 主要用在分特权级和用户级的操作系统,ucosii 不区分特权级和用户级,
可以不管这个东西。
这里说点题外话,一开始我很奇怪为什么会提供这种中断,因为这种中断一般
都是用在大型的操作系统上,如 linux 系统上,可 CM3 又不提供 MMU,应该是无
法移植 linux 系统。后来我才知道 uclinux 是针对没有 MMU 的嵌入式系统而设计的,
不过还是很怀疑有人会在像 stm32 这种芯片上用 uclinux。
PendSV 
PendSV 中断主要做上下文切换,也就是任务切换,是 ucosii 移植过程中最重要
的中断。
主要有两点:
1.PendSV 中断是手工往 NVIC 的 PendSV 悬起寄存器中写 1 产生的(由 OS
写)。
2.PendSV 中断优先级必须设为最低。
在讲移植代码时会介绍具体是如何做的。
对于 7.6 的 PendSV 部分应认真研读一下。
chapter8 NVIC 与中断控制
NVIC 负责芯片的中断管理,它和 CM3 内核紧密相关。
如果对于 CM3 中断配置不是很了解,可以看看 8.1, 8.2, 8.3, 8.4 节。
8.7 节讲述了 SysTick 定时器,需要看。
chapter9 中断的具体行为
9.1 中断/异常的响应序列
当 CM3 开始响应一个中断时
1.xPSR, PC, LR, R12 以及 R3
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生