ARM內核控制外設寄存器
1、ARM內核寄存器組(M3為例)??
Cortex‐M3 處理器擁有?R0‐R15 的寄存器組。其中 R13 作為堆棧指針 SP。SP 有兩個,但在同一 時刻只能有一個可以看到,這也就是所謂的“banked”寄存器。
?
R0-R12:通用寄存器?
? 通用寄存器,用于數據操作。比如我們常用的加減乘除。MOV R0,#1(給R0寄存器賦值1)
R13(SP): 兩個堆棧指針:
??主堆棧指針(MSP):復位后缺省使用的堆棧指針,用于操作系統(tǒng)內核以及異常處理例程(包 括中斷服務例程)?
? 進程堆棧指針(PSP):由用戶的應用程序代碼使用。
R14(LR):連接寄存器
? ?當我們的程序調用子程序時,PC寄存器的值被改變,執(zhí)行完子程序,想要返回主程序執(zhí)行,就需要將PC修改回,調用子程序之前的地址。這個地址就存儲在LR當中。
R15? (PC):程序計數寄存器
? PC寄存器指向我們當前運行程序的地址,修改PC寄存器的值能夠改變程序的執(zhí)行位置。
?
程序的執(zhí)行就是內核根據PC中的地址,找到程序執(zhí)行位置,對內存中的程序進行讀取,期間通過通用寄存器進行數據的存儲運算。
2、內存映射(M3為例)
內核可以直接讀寫R0、R2、PC這些寄存器,但是我們在單片機的程序設計中經常需要控制片上外設,像GPIO、USART、IIC等這些外設有自己獨立的寄存器,該怎么去訪問?
???前面的內容介紹過了,內核使用通用寄存器進行數據的運算,PC的讀寫控制程序執(zhí)行的地址,返回地址用LR進行存儲,外面的寄存器肯定是不能這樣了。但是內核可以通過對內存的讀寫操作來讀寫內核外部的寄存器。下面我們先來看下M3內核的內存映射圖。
?

?在內存映射中有程序的存儲地址、 外圍設備地址等。硬件結構上,片上外設的寄存器連在了外圍設備這段地址上(0X40000000--0x5FFFFFFF)
?
?這里我們再看個片上外設的寄存器,以GPIO的為例。

?
這個寄存器是控制GPIO端口模式的,我們可以看到偏移地址為0x00,這里還需要個基地址,基地址加上偏移地址就是我們寄存器的準確地址了。
?
基地址我們可以理解為在上面的存儲器映射表中,0X40000000開始接片上外設的寄存器,這個地址就可以理解為基地址,偏移地址就是在這個地址的基礎上向上偏移了多少,接的我們需要使用的寄存器。當然實際中,這段 0X40000000開始的地址還會被細分,比如我們的GPIOA、GPIOB。但是追根到底是我們需要用到的每一個寄存器,都會在這上面找到一個具體的地址,這個地址就相當于內核對外的接口,一共排列了4G大小的長度,寄存器在某一個位置通過導線連接這樣就能夠和內核通信了。
?
??我們控制時,以上面的寄存器為例,內核對該寄存器的地址0和1位寫11就可以控制PIN0引腳為模擬模式了。這里的可以看到32位一個分成了16組,每2位控制一個PIN引腳。
?
??再舉個例子如果我們想將GPIOA的第0個引腳,即PA0配置為通用輸出模式,那么我們就可以找到GPIOA的端口模式寄存器地址,然后直接給這個地址里的0和1位分別賦值1和0。
?
?
一個完整鏡像的程序入口:
對于在flash中存儲的一個完整的程序代碼,其起始部分應該為向量表,向量表的內容格式固定如下表(參考《cortex-M3權威指南》7.3節(jié))
?
上電后的向量表:

?
由表可知,對于起始存儲地址為0的一段完整程序,其首地址處存放的是MSP的初始值,偏移4字節(jié)的地址處存放的是PC指針的初始值,我們要運行這段完整的程序,只需將這段完整程序的SP、PC初始值賦給SP和PC寄存器即可,具體實現(xiàn)的函數如下:
__asm void?boot_jump(uint32_t?address)
{
? ?LDR SP, [R0] ? ? ;
? ?LDR PC, [R0, #4] ;
}
對于此函數的解釋:__asm是MDK的編譯器提供的嵌入匯編的指令
函數體中兩行匯編代碼的功能分別為:
LDR??SP, [R0]:把R0中的值作為地址,將此地址中的值賦給SP
LDR??PC ,[R0, #4]:把R0中的值加4作為地址,將此地址中的值賦給PC
這里涉及到一個問題,r0中的值是什么?我們根據ATPCS(ARM-THUMBprocedure call standard)可知,對于參數少于等于4的函數,參數是通過R0~R3傳遞的,第一個參數放在R0中,依次類推。所以這里的R0存放的正式UserApp的起始地址,回過頭再看前面的兩行匯編代碼,它們做的事正是將UserApp的SP、PC初始值賦給相應寄存器,達到開始運行UsrApp的目的。
?