如何把UCos-ii_在STM32上的移植
來源:程序員人生 發布時間:2014-09-04 14:44:11 閱讀次數:3457次
下載代碼
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
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈