標題: uCOS-II移植到LPC17XX方法 [打印本頁]
作者: liu100m 時間: 2018-10-16 00:30
標題: 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 <LPC17xx.h>
#include <ucos_ii.h>
#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
ADDS R0, R0, #0x20
MSR PSP, R0 ; Load PSP with new process SP
ORR LR, LR, #0x04 ; Ensure exception return uses process stack
CPSIE I
BX LR ; Exception return will restore remaining context
END
ii)OSCtxSw
ucos-ii中,OSCtxSw的功能是上下文(任務)切換。而由上面的介紹可知,真正的切換工作是在PendSV中斷中完成的,那么可想而知,OSCtxSw只需觸發一個PendSV中斷即可完成上下文(任務)切換的工作。OSCtxSw位于os_cpu_a.asm文件中,源碼如下:
OSCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
7. 工程源碼
工程源碼鏈接:
UCOS_II_V289.rar
(606.79 KB, 下載次數: 24)
2018-10-16 02:05 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
作者: 放開那個雞腿 時間: 2018-12-29 17:46
看看在說話吧啊
作者: pm1981 時間: 2019-2-27 10:53
先看看再說
作者: 147258369q 時間: 2019-6-27 13:47
繼續努力學習
作者: hua123456 時間: 2019-9-26 14:06
謝謝分享
| 歡迎光臨 (http://www.raoushi.com/bbs/) |
Powered by Discuz! X3.1 |