當前位置:編程學習大全網 - 編程語言 - 嵌入式高手進 考試題解答

嵌入式高手進 考試題解答

推薦壹:OS_CPU.H

1、定義與編譯器無光的數據類型

只是按照不同的編譯器編寫對應的數據類型的typedef 對應於ARM7的數據類型的編寫如下

typedef unsigned char BOOLEAN;/* 布爾變量*/

typedef unsigned char INT8U; /* 無符號8位整型變量*/

typedef signed char INT8S; /* 有符號8位整型變量*/

typedef unsigned short INT16U; /* 無符號16位整型變量*/

typedef signed short INT16S; /* 有符號16位整型變量*/

typedef unsigned int INT32U; /* 無符號32位整型變量*/

typedef signed int INT32S; /* 有符號32位整型變量*/

typedef float FP32; /*單精度浮點數(32Bit)*/

typedef double FP64; /*雙精度浮點數(64Bit)*/

/*在上面定義的數據類型中按照ARM7的堆棧寬度選擇INT32U*/

typedef INT32U OS_STK; /* 堆棧是32位寬度*/

接下來壹部分是為了兼容低版本UCOS的數據類型所編寫的代碼,在UCOS-II中暫不考慮

2 與處理器相關的代碼

先定義中斷的實現方式,預先設定的中斷方式有三種,在ARM7中設置為方式 2

#define OS_CRITICAL_METHOD 2/*選擇開,關中斷的方式 */

接下來的壹段是我暫時還沒有完全搞懂的壹部分,只知道是設定了12個軟件中斷的函數,當調用這

些函數之前都會執行對應中斷號的事情。具體的看到後面應該能完全搞懂軟件中斷的實現方式,

該段代碼在後面的文件中會有具體的解釋,這裏暫時不看

定義堆棧的生長方式,ARM7內核支持兩種生長方式,但是ADS的C語言編譯器只支持從上往下的生

長方式,因此:

#define OS_STK_GROWTH 1 /* 堆棧是從上往下長的,0-從下往上的生長方式 */

最後幾行分別定義了用戶模式01和系統模式1f以及IRQ中斷禁止的指令80三個立即數,方便調用.

還有兩個預定義往後看應該知道作用,暫不考慮,不是很重要.

軟中斷:

中斷不返回形式:

void _swi(swi_num) swi_name(arguments);

返回壹個結果到R0中

int _swi(swi_num) swi_name(arguments);

最多可以返回四個結果R0-R3到壹個結構struct type{ int a,b,c,d}中

type(返回類型) _value_in_regs(返回多個結果的修飾符) _swi(swi_num) swi_name(arguments);

在ARM中實現軟中斷的方法我在blog裏面搜了很多文章也沒有看到講的通俗壹點的,還是自己看

ARM的移植代碼吧首先定義了壹堆軟中斷的中斷號,其中0和1的中斷服務子程序是用匯編編寫的,

其他的都是在c語言編寫的中斷服務子程序SWI_Exception中。

__swi(0x00) void OS_TASK_SW(void);

/* 任務級任務切換函數 */

__swi(0x01) void _OSStartHighRdy(void);

/* 運行優先級最高的任務 */

__swi(0x02) void OS_ENTER_CRITICAL(void);

/* 關中斷 */

__swi(0x03) void OS_EXIT_CRITICAL(void);

/* 開中斷 */

__swi(0x40) void *GetOSAddr(int Index);

/* 獲取系統服務函數入口 */

__swi(0x41) void *GetUsrAddr(int Index);

/* 獲取自定義服務函數入口 */

__swi(0x42) void OSISRBegin(void);

/* 中斷開始處理 */

__swi(0x43) int OSISRNeedSwap(void);

/* 判斷中斷是否需要切換 */

__swi(0x80) void ChangeToSYSMode(void);

/* 任務切換到系統模式 */

__swi(0x81) void ChangeToUSRMode(void);

/* 任務切換到用戶模式 */

__swi(0x82) void TaskIsARM(INT8U prio);

/* 任務代碼是ARM代碼 */

__swi(0x83) void TaskIsTHUMB(INT8U prio);

/* 任務代碼是THUMB */

比如在程序運行到調用OS_TASK_SW(void)函數時,就產生軟件中斷,然後就進入中斷服務子程序,

按照什麽指令走呢?恩,就按照下面這個代碼,這個代碼是將軟件中斷異常處理程序掛接到內核

的作用的,是在啟動代碼中實現的:

LDR PC,SWI_Addr

SWI_Addr DCD SoftwareInterrupt

因此當產生軟中斷之後PC就跳到了SoftwareInterrupt,這時就算真正進入了軟件異常中斷處理部

分了,然後就是執行下面的匯編代碼SoftwareInterrupt

LDR SP, StackSvc

/*重新設置堆棧指針*/

STMFD SP!, {R0-R3, R12, LR}

/*保存 R0,R1,R2,R3,R12,LR(R14),註意為什麽只保存這幾個

寄存器呢,因為R4-R11存儲局部變量,編譯器自動保護他們*/

MOV R1, SP /* R1指向參數存儲位置 */

MRS R3, SPSR /*保存管理模式的狀態寄存器*/

TST R3, #T_bit /* 中斷前是否是Thumb狀態 */

LDRNEH R0, [LR,#-2] /* 若是,取得Thumb狀態SWI號*/

BICNE R0, R0, #0xff00 /*THUMB指令SWI功能號為8位 */

LDREQ R0, [LR,#-4] /* 為零即ARM指令取得SWI號 */

BICEQ R0, R0, #0xFF000000

/*在ARM指令集中SWI功能號為24位所以高8位清零r0=SWI號*/

CMP R0, #1 /*

LDRLO PC, =OSIntCtxSw /* 疑惑ing */

/* 功能號為0到OSIntCtxSw執行中斷任務切換函數 */

LDREQ PC, =__OSStartHighRdy/*SWI為1第壹次任務切換*/

BL SWI_Exception /*否則進入c編寫的中斷函數 */

LDMFD SP!, {R0-R3, R12, PC}/*R0-R3,R12,LR出棧 */

StackSvc

DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)

怎麽進入c編寫的中斷服務子程序SWI_Exception呢?通過下面的申明

IMPORT SWI_Exception ;軟中斷異常處理程序

表示將c程序中的該函數掛接到此段匯編代碼中,同樣的道理

EXPORT __OSStartHighRdy

EXPORT OSIntCtxSw ;中斷退出時的入口

參見startup.s中的IRQ_Handler

EXPORT SoftwareInterrupt ;軟中斷入口上面的申明是將該段匯編代碼掛接到外面,

因此在外部可以直接調用函數名

繼續看OS_CPU_A.S的其他部分代碼,就是兩個軟件異常中斷處理函數OSIntCtxSw和OSStarHighRdyOSIntCtxSw代碼是中斷服務子程序使得更高優先級的任務進入就緒狀態後,中斷返回後需要切換到該任務時調用的,這是被切換的任務的CPU寄存器的值已經在響應中斷後存入了堆棧中,因此,這裏不需要重復保存了直接切換任務即可,具體過程看代碼OSIntCtxSw

;下面為保存任務環境 ;當響應軟件異常中斷後進入了系統模式,在上面的代碼中我們可以看到,進入系統模式時保存的堆棧結構從頂到底依次是:R0,R1,R2,R3,R12,LR,而在用戶模式中任務的堆棧結構應該是:OsEnterSum,CPSR,RO-12,LR,PC,所以在進行軟件中斷任務切換之前先要保存原來任務的堆棧結構。

LDR R2, [SP, #20] ;獲取PC

LDR R12, [SP, #16] ;獲取R12

MRS R0, CPSR MSR CPSR_c, #(NoInt | SYS32Mode)

MOV R1, LR

STMFD SP!, {R1-R2} ;保存LR,PC

STMFD SP!, {R4-R12} ;保存R4-R12 MSR CPSR_c, R0

LDMFD SP!, {R4-R7} ;獲取R0-R3

ADD SP, SP, #8 ;出棧R12,PC

MSR CPSR_c, #(NoInt | SYS32Mode)

STMFD SP!, {R4-R7} ;保存R0-R3

LDR R1, =OsEnterSum ;獲取OsEnterSum

LDR R2, [R1]

STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum ;保存當前任務堆棧指針到當前任務的TCB

LDR R1, =OSTCBCur

LDR R1, [R1]

STR SP, [R1] BL OSTaskSwHook ;調用鉤子函數

;OSPrioCur <= OSPrioHighRdy

LDR R4, =OSPrioCur

LDR R5, =OSPrioHighRdy

LDRB R6, [R5]

STRB R6, [R4]

;OSTCBCur <= OSTCBHighRdy

LDR R6, =OSTCBHighRdy

LDR R6, [R6]

LDR R4, =OSTCBCur

STR R6, [R4]

OSIntCtxSw_1

;獲取新任務堆棧指針

LDR R4, [R6]

ADD SP, R4, #68 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP

LDR LR, [SP, #-8]

MSR CPSR_c, #(NoInt | SVC32Mode) ;進入管理模式

MOV SP, R4 ;設置堆棧指針 LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum

;恢復新任務的OsEnterSum

LDR R3, =OsEnterSum

STR R4, [R3]

MSR SPSR_cxsf, R5 ;恢復CPSR

LDMFD SP!, {R0-R12, LR, PC }^ ;運行新任務

__OSStartHighRdy

MSR CPSR_c, #(NoInt | SYS32Mode) ;調整到管理模式

;告訴uC/OS-II自身已經運行

LDR R4, =OSRunning

MOV R5, #1

STRB R5, [R4] ;標記多任務運行標記為真 BL OSTaskSwHook ;調用鉤子函數,可以運行用戶自定義的函數 LDR R6, =OSTCBHighRdy ;R6存有最高優先級的就緒任務的控制塊地址

LDR R6, [R6]

B OSIntCtxSw_1 ;轉到前面編寫的中斷返回函數塊的任務跳轉部分的代碼,因為這兩個函數都要用到這部分代碼,進入這段代碼之前高優先級的就緒任務的任務控制快地址存在R6中。 AREA SWIStacks, DATA, NOINIT,ALIGN=2

SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;管理模式堆棧空間 OSIntCtxSw_1的代碼:OSIntCtxSw_1

;獲取新任務堆棧指針

LDR R4, [R6] ;任務控制塊的堆棧指針放在R6中,現在放在R4中

ADD SP, R4, #68 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP

LDR LR, [SP, #-8]

MSR CPSR_c, #(NoInt | SVC32Mode) ;進入管理模式

MOV SP, R4 ;設置堆棧指針,R4存有沒有改動過的堆棧指針 LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum

;恢復新任務的OsEnterSum

LDR R3, =OsEnterSum

STR R4, [R3]

MSR SPSR_cxsf, R5 ;恢復CPSR

LDMFD SP!, {R0-R12, LR, PC }^ ;運行新任務,恢復現場,異常處理返回;中斷返回指令的寄存器列表其中必須包括PC後的^符號,表示這是壹條特殊形式的指令。這條指令在從存儲器中裝載PC的同時,CPSR也得到恢復。這裏使用的堆棧指針SP是屬於異常模式的寄存器,每個異常模式有自己的堆棧指針。SoftwareInterrupt

LDR SP, StackSvc ; 重新設置堆棧指針

STMFD SP!, {R0-R3, R12, LR} ;保存寄存器

MOV R1, SP ; R1指向參數存儲位置 MRS R3, SPSR

TST R3, #T_bit ; 中斷前是否是Thumb狀態

LDRNEH R0, [LR,#-2] ; 是: 取得Thumb狀態SWI號

BICNE R0, R0, #0xff00

LDREQ R0, [LR,#-4] ; 否: 取得arm狀態SWI號

BICEQ R0, R0, #0xFF000000

; r0 = SWI號,R1指向參數存儲位置

CMP R0, #1

LDRLO PC, =OSIntCtxSw

LDREQ PC, =__OSStartHighRdy ; SWI 0x01為第壹次任務切換 BL SWI_Exception

LDMFD SP!, {R0-R3, R12, PC}^

StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)OSIntCtxSw

;下面為保存任務環境

LDR R2, [SP, #20] ;獲取PC(LR)

LDR R12, [SP, #16] ;獲取R12

MRS R0, CPSR MSR CPSR_c, #(NoInt | SYS32Mode)

MOV R1, LR

STMFD SP!, {R1-R2} ;保存LR,PC

STMFD SP!, {R4-R12} ;保存R4-R12 MSR CPSR_c, R0

LDMFD SP!, {R4-R7} ;獲取R0-R3

ADD SP, SP, #8 ;出棧R12,PC

MSR CPSR_c, #(NoInt | SYS32Mode)

STMFD SP!, {R4-R7} ;保存R0-R3

LDR R1, =OsEnterSum ;獲取OsEnterSum

LDR R2, [R1]

STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum ;保存當前任務堆棧指針到當前任務的TCB

LDR R1, =OSTCBCur

LDR R1, [R1]

STR SP, [R1] BL OSTaskSwHook ;調用鉤子函數

;OSPrioCur <= OSPrioHighRdy

LDR R4, =OSPrioCur

LDR R5, =OSPrioHighRdy

LDRB R6, [R5]

STRB R6, [R4] ;把OSPrioHighRdy最高優先級的就緒任務傳給OSPrioCur

;OSTCBCur <= OSTCBHighRdy

LDR R6, =OSTCBHighRdy

LDR R6, [R6]

LDR R4, =OSTCBCur

STR R6, [R4] ;將最高優先級的任務控制塊指針傳給當前任務控制塊指針

關於中斷和時鐘節拍,UCOS-II對於ARM7通用的中斷服務程序的匯編與c函數接口如下:MACRO和MEND偽指令用於宏定義,MACRO標識宏定義的開始,MEND標識宏定義的結束。定義之後在程序中就可以通過宏指令多次調用該段代碼MACRO

$IRQ_Label HANDLER $IRQ_Exception_ EXPORT $IRQ_Label ; 輸出的標號

IMPORT $IRQ_Exception_ ; 引用的外部標號$IRQ_Label

SUB LR, LR, #4 ; 計算返回地址

STMFD SP!, {R0-R3, R12, LR} ; 保存任務環境

MRS R3, SPSR ; 保存狀態

STMFD SP, {R3, SP, LR}^ ; 保存用戶狀態的R3,SP,LR,註意不能回寫

; 如果回寫的是用戶的SP,所以後面要調整SP

LDR R2, =OSIntNesting ; OSIntNesting++

LDRB R1, [R2]

ADD R1, R1, #1

STRB R1, [R2] SUB SP, SP, #4*3

MSR CPSR_c, #(NoInt | SYS32Mode) ; 切換到系統模式

CMP R1, #1

LDREQ SP, =StackUsr

BL $IRQ_Exception_ ; 調用c語言的中斷處理程序 MSR CPSR_c, #(NoInt | SYS32Mode) ; 切換到系統模式

LDR R2, =OsEnterSum ; OsEnterSum,使OSIntExit退出時中斷關閉

MOV R1, #1

STR R1, [R2] BL OSIntExit LDR R2, =OsEnterSum ; 因為中斷服務程序要退出,所以OsEnterSum=0

MOV R1, #0

STR R1, [R2] MSR CPSR_c, #(NoInt | IRQ32Mode) ; 切換回irq模式

LDMFD SP, {R3, SP, LR}^ ; 恢復用戶狀態的R3,SP,LR,註意不能回寫

; 如果回寫的是用戶的SP,所以後面要調整SP

LDR R0, =OSTCBHighRdy

LDR R0, [R0]

LDR R1, =OSTCBCur

LDR R1, [R1]

CMP R0, R1 ADD SP, SP, #4*3 ;

MSR SPSR_cxsf, R3

LDMEQFD SP!, {R0-R3, R12, PC}^ ; 不進行任務切換

LDR PC, =OSIntCtxSw ; 進行任務切換

MEND二:OS_CPU_C.C 個文件中要求用戶編寫10個簡單的C函數,但是只有1個函數是必要的,其余的函數必須聲明,但不壹定要包含任何代碼,大致看了壹下作用好像是用來調試之類的。唯壹要編寫的是OSTaskStkInit() OSTaskStkInit()函數的功能是初始化任務的棧結構,任務的堆棧結構與CPU的體系結構、編譯器有密切的關聯。從ARM的結構可以寫出如下的棧結構:程序計數器PC,程序鏈接器LR,R12-R1,R0用於傳遞第壹個參數pdata,CPSR/SPSR,關中斷計數器(用於計算關中斷的次數,這樣就實現了中斷的嵌套),返回的地址指針是指向的最後壹個存入的數據,而不是壹個空地址。軟件中斷異常SWI服務程序C語言部分 void SWI_Exception(int SWI_Num, int *Regs):參數SWI_Num對應前面文件中定義的功能號,其中0、1號的功能在後面的文件中定義,這裏只定義了其他10個功能。 2、3分別對應關中斷和開中斷 關中斷:MRS R0, SPSR //在軟件中斷的時候直接對程序狀態保存寄存器SPSR操作也就是對CPSR的操作

ORR R0, R0, #NoInt //在匯編語言中對寄存器的對應位置位用ORR,清零用BIC

MSR SPSR_c, R0 //SPSR_c表示的是只改變SPSR的控制段的8位代碼,其他三段_f,_s,_x中標誌位在_f段,其他為保留位 開中斷:MRS R0, SPSR //在開中斷中基本與上面相同,只是ORR改成BIC清零

BIC R0, R0, #NoInt

MSR SPSR_c, R 由於需要實現中斷嵌套,所以只有當關中斷的計數器減為0的時候才能夠開中斷,而且每次關中斷的時候該計數器都應該加1。另外,插入匯編語言時用_asm指令。 80、81、82、83分別對應系統模式、用戶模式、ARM指令集、THUMB指令集 系統模式:MRS R0, SPSR

BIC R0, R0, #0x1f //先將控制模式的低5位清零

ORR R0, R0, #SYS32Mode //設置成系統模式的1F

MSR SPSR_c, R0 用戶模式:MRS R0, SPSR

BIC R0, R0, #0x1f

ORR R0, R0, #USR32Mode //設置成用戶模式的10

MSR SPSR_c, R0 ARM指令集與THUMB指令集的代碼如下: ptcb = OSTCBPrioTbl[Regs[0]];

if (ptcb != NULL)

{

ptcb -> OSTCBStkPtr[1] &= ~(1 << 5);

} ptcb = OSTCBPrioTbl[Regs[0]];

if (ptcb != NULL)

{

ptcb -> OSTCBStkPtr[1] |= (1 << 5);

} 昨天就是看到這裏,出現了壹個意識到是不能忽悠的地方就是UCOS裏面的任務控制塊OS_TCB的概念,因此今天的任務就是把這部分看看。。。 大概回憶了壹下昨天晚上的工作,開始今天的工作吧

壹點壹點來,什麽不會就學什麽,都不會就都學。。。沒有問題只要妳肯努力。。。。。。__OSStartHighRdy

MSR CPSR_c, #(NoInt | SYS32Mode) ;MSR:在ARM中只有MSR能夠直接設置狀態寄存器CPSR或SPSR,可以是立即數或者源寄存器,NoInt是禁止中斷,SYS32Mode是系統模式

;告訴uC/OS-II自身已經運行

LDR R4, =OSRunning ;OSRunning正在運行多任務的標誌,=OSRunning是把OSRunning的地址加載到R4,R4裏存的是壹個地址。。。

MOV R5, #1

STRB R5, [R4] ;將R5存儲到R4存的地址的變量即OSRunning中,也就是將OSRunning置1 BL OSTaskSwHook ;調用鉤子函數,OSTaskSwHook 是用於擴展的,在任務切換的時候執行用戶自己定義的功能。 LDR R6, =OSTCBHighRdy ;OSTCBHighRdy指向最高優先級任務的控制塊TCB的指針!!!將放指針的地址放到R6中。

LDR R6, [R6] ;將R6地址處的數據讀出即OSTCBHighRdy的地址放到R6中

B OSIntCtxSw_1 ;跳轉到OSIntCtxSw_1 AREA SWIStacks, DATA, NOINIT,ALIGN=2

SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;管理模式堆棧空間繼續昨天沒有看完的代碼OSIntCtxSw

;下面為保存任務環境

LDR R2, [SP, #20] ;獲取PC,放入R2

LDR R12, [SP, #16] ;獲取R12,//R12存的什麽東西啊?

MRS R0, CPSR MSR CPSR_c, #(NoInt | SYS32Mode) ;進入系統模式並禁止中斷

MOV R1, LR ;R1放LR值

STMFD SP!, {R1-R2} ;保存LR,PC,將R1,R2存入SP

STMFD SP!, {R4-R12} ;保存R4-R12,將R4-12存入SP MSR CPSR_c, R0 ;再回到之前的模式

LDMFD SP!, {R4-R7} ;獲取R0-R3

ADD SP, SP, #8 ;出棧R12,PC

MSR CPSR_c, #(NoInt | SYS32Mode)

STMFD SP!, {R4-R7} ;保存R0-R3

LDR R1, =OsEnterSum ;獲取OsEnterSum

LDR R2, [R1]

STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum ;保存當前任務堆棧指針到當前任務的TCB

LDR R1, =OSTCBCur

LDR R1, [R1]

STR SP, [R1] BL OSTaskSwHook ;調用鉤子函數

;OSPrioCur <= OSPrioHighRdy

LDR R4, =OSPrioCur

LDR R5, =OSPrioHighRdy

LDRB R6, [R5]

STRB R6, [R4]

;OSTCBCur <= OSTCBHighRdy

LDR R6, =OSTCBHighRdy

LDR R6, [R6]

LDR R4, =OSTCBCur

STR R6, [R4]

OSIntCtxSw_1

;獲取新任務堆棧指針

LDR R4, [R6] ;把OSTCBHighRdy指向最高優先級任務的控制塊TCB的指針給R4

ADD SP, R4, #68 ;17寄存器:CPSR,OsEnterSum,R0-R12,LR,SP

LDR LR, [SP, #-8] ;取出LR放到LR

MSR CPSR_c, #(NoInt | SVC32Mode) ;進入管理模式並且保持禁止中斷

MOV SP, R4 ;設置堆棧指針 LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum。LDMFD數據出棧,放入R4,R5

;恢復新任務的OsEnterSum

LDR R3, =OsEnterSum ;OsEnterSum的地址存入R3

STR R4, [R3] ;把R4的值賦給OsEnterSum

MSR SPSR_cxsf, R5 ;恢復CPSR;在管理模式裏是修改SPSR

LDMFD SP!, {R0-R12, LR, PC }^ ;運行新任務 ,恢復現場,異常處理返回

  • 上一篇:2022年暑假計劃表初中生精選7篇
  • 下一篇:十二生肖婚姻最佳搭配口訣,十二生肖姻緣配對
  • copyright 2024編程學習大全網