簡介:Arm上電時處于ARM狀態(tài),故無論指令為ARM集或Thumb集,都先強制成ARM集,待init.s初始化完成后 ;再根據(jù)用戶的編譯配置轉(zhuǎn)換成相應(yīng)的指令模式。
=========================================
; NAME: 2440INIT.S
; DESC: C start up codes
;Configure memory, ISR ,stacks
; Initialize C-variables
;完全注釋
; HISTORY:
; 2002.02.25:kwtark: ver 0.0
; 2002.03.20:purnnamu: Add some functions for testing STOP,Sleep mode
; 2003.03.14:DonGo: Modified for 2440.
; 2009 06.24:Tinko Modified
;=========================================
;匯編不能使用include包含頭文件,所有用Get
;匯編也不認(rèn)識*.h 文件,所有只能用*.inc
GET option.inc;定義芯片相關(guān)的配置
GET memcfg.inc;定義存儲器配置
GET 2440addr.inc;定義了寄存器符號
;REFRESH寄存器[22]bit : 0- auto refresh; 1 - self refresh
BIT_SELFREFRESH EQU (1<<22) ;用于節(jié)電模式中,SDRAM自動刷新
;處理器模式常量: CPSR寄存器的后5位決定目前處理器模式 M[4:0]
USERMODEEQU 0x10
FIQMODEEQU 0x11
IRQMODEEQU 0x12
SVCMODEEQU 0x13
ABORTMODEEQU 0x17
UNDEFMODEEQU 0x1b
MODEMASKEQU 0x1f;M[4:0]
NOINTEQU 0xc0
;定義處理器各模式下堆棧地址常量
UserStackEQU(_STACK_BASEADDRESS-0x3800);0x33ff4800 ~_STACK_BASEADDRESS定義在option.inc中
SVCStackEQU(_STACK_BASEADDRESS-0x2800);0x33ff5800 ~
UndefStackEQU(_STACK_BASEADDRESS-0x2400);0x33ff5c00 ~
AbortStackEQU(_STACK_BASEADDRESS-0x2000);0x33ff6000 ~
IRQStackEQU(_STACK_BASEADDRESS-0x1000);0x33ff7000 ~
FIQStackEQU(_STACK_BASEADDRESS-0x0);0x33ff8000 ~
;arm處理器有兩種工作狀態(tài) 1.arm:32位 這種工作狀態(tài)下執(zhí)行字對準(zhǔn)的arm指令 2.Thumb:16位 這種工作狀
;態(tài)執(zhí)行半字對準(zhǔn)的Thumb指令
;因為處理器分為16位 32位兩種工作狀態(tài) 程序的編譯器也是分16位和32兩種編譯方式 所以下面的程序用
;于根據(jù)處理器工作狀態(tài)確定編譯器編譯方式
;code16偽指令指示匯編編譯器后面的指令為16位的thumb指令
;code32偽指令指示匯編編譯器后面的指令為32位的arm指令
;
;Arm上電時處于ARM狀態(tài),故無論指令為ARM集或Thumb集,都先強制成ARM集,待init.s初始化完成后
;再根據(jù)用戶的編譯配置轉(zhuǎn)換成相應(yīng)的指令模式。為此,定義變量THUMBCODE作為指示,跳轉(zhuǎn)到main之前
;根據(jù)其值切換指令模式
;
;這段是為了統(tǒng)一目前的處理器工作狀態(tài)和軟件編譯方式(16位編譯環(huán)境使用tasm.exe編譯
;Check if tasm.exe(armasm -16...@ADS1.0) is used.
GBLLTHUMBCODE ;定義THUMBCODE全局變量注意EQU所定義的宏與變量的區(qū)別
[ {CONFIG} = 16 ;如果發(fā)現(xiàn)是在用16位代碼的話(編譯選項中指定使用thumb指令)
THUMBCODE SETL {TRUE} ;一方面把THUMBCODE設(shè)置為TURE
CODE32 ;另一方面暫且把處理器設(shè)置成為ARM模式,以方便初始化
| ;(|表示else)如果編譯選項本來就指定為ARM模式
THUMBCODE SETL {FALSE};把THUMBCODE設(shè)置為FALSE就行了
] ;結(jié)束
MACRO;一個根據(jù)THUMBCODE把PC寄存的值保存到LR的宏
MOV_PC_LR;宏名稱
[ THUMBCODE;如果定義了THUMBCODE,則
bx lr ;在ARM模式中要使用BX指令轉(zhuǎn)跳到THUMB指令,并轉(zhuǎn)換模式.
;bx指令會根據(jù)PC最后1位來確定是否進入thumb狀態(tài)
|;否則,
movpc,lr;如果目標(biāo)地址也是ARM指令的話就采用這種方式
]
MEND;宏定義結(jié)束標(biāo)志
MACRO ;和上面的宏一樣,只是多了一個相等的條件
MOVEQ_PC_LR
[ THUMBCODE
bxeq lr
|
moveq pc,lr
]
MEND
;=======================================================================================
;下面這個宏是用于第一次查表過程的實現(xiàn)中斷向量的重定向,如果你比較細心的話就是發(fā)現(xiàn)
;在_ISR_STARTADDRESS=0x33FF_FF00里定義的第一級中斷向量表是采用型如Handle***的方式的.
;而在程序的ENTRY處(程序開始處)采用的是b Handler***的方式.
;在這里Handler***就是通過HANDLER這個宏和Handle***建立聯(lián)系的.
;這種方式的優(yōu)點就是正真定義的向量數(shù)據(jù)在內(nèi)存空間里,而不是在ENTRY處的ROM(FLASH)空間里,
;這樣,我們就可以在程序里靈活的改動向量的數(shù)據(jù)了.
;========================================================================================
;;這段程序用于把中斷服務(wù)程序的首地址裝載到pc中,有人稱之為“加載程序”。
;本初始化程序定義了一個數(shù)據(jù)區(qū)(在文件最后),34個字空間,存放相應(yīng)中斷服務(wù)程序的首地址。
;每個字空間都有一個標(biāo)號,以Handle***命名。
;在向量中斷模式下使用“加載程序”來執(zhí)行中斷服務(wù)程序。
;這里就必須講一下向量中斷模式和非向量中斷模式的概念
;向量中斷模式是當(dāng)cpu讀取位于0x18處的IRQ中斷指令的時候,系統(tǒng)自動讀取對應(yīng)于該中斷源確定
;地址上的指令取代0x18處的指令,通過跳轉(zhuǎn)指令系統(tǒng)就直接跳轉(zhuǎn)到對應(yīng)地址
;函數(shù)中 節(jié)省了中斷處理時間提高了中斷處理速度標(biāo) 例如 ADC中斷的向量地址為0xC0,則在0xC0處
;代放如下碼:ldr PC,=HandlerADC 當(dāng)ADC中斷產(chǎn)生的時候系統(tǒng)會自動跳轉(zhuǎn)到HandlerADC函數(shù)中
;非向量中斷模式處理方式是一種傳統(tǒng)的中斷處理方法,當(dāng)系統(tǒng)產(chǎn)生中斷的時候,系統(tǒng)將interrupt
;pending寄存器中對應(yīng)標(biāo)志位置位 然后跳轉(zhuǎn)到位于0x18處的統(tǒng)一中斷函數(shù)中
; 該函數(shù)通過讀取interrupt pending寄存器中對應(yīng)標(biāo)志位 來判斷中斷源 并根據(jù)優(yōu)先級關(guān)系再跳到
;對應(yīng)中斷源的處理代碼中
;
;H|------|H|------|H|------|H|------|H|------|
; |/ / / ||/ / / ||/ / / ||/ / / ||/ / / |
; |------|<----sp|------||------||------||------|<------sp
;L|||------|<----sp L|------||-isr--||------| isr==>pc
; |||||--r0--|<----sp|---r0-|<----spL|------| r0==>r0
;(0)(1)(2)(3)(4)
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel ;標(biāo)號
sub sp,sp,#4 ;(1)減少sp(用于存放轉(zhuǎn)跳地址)
stmfd sp!,{r0};(2)把工作寄存器壓入棧(lr does not push because it return to original address)
ldrr0,=$HandleLabel;將HandleXXX的址址放入r0
ldrr0,[r0];把HandleXXX所指向的內(nèi)容(也就是中斷程序的入口)放入r0
strr0,[sp,#4];(3)把中斷服務(wù)程序(ISR)壓入棧
ldmfdsp!,{r0,pc};(4)用出棧的方式恢復(fù)r0的原值和為pc設(shè)定新值(也就完成了到ISR的轉(zhuǎn)跳)
MEND
;=========================================================================================
;在這里用IMPORT偽指令(和c語言的extren一樣)引入|Image$$RO$$Base|,|Image$$RO$$Limit|...
;這些變量是通過ADS的工程設(shè)置里面設(shè)定的RO Base和RW Base設(shè)定的,
;最終由編譯腳本和連接程序?qū)氤绦?
;那為什么要引入這玩意呢,最簡單的用處是可以根據(jù)它們拷貝自已
;==========================================================================================
;Image$$RO$$Base等比較古怪的變量是編譯器生成的。RO, RW, ZI這三個段都保存在Flash中,但RW,ZI在Flash中
;的地址肯定不是程序運行時變量所存儲的位置,因此我們的程序在初始化時應(yīng)該把Flash中的RW,ZI拷貝到RAM的
;對應(yīng)位置。一般情況下,我們可以利用編譯器替我們實現(xiàn)這個操作。比如我們跳轉(zhuǎn)到main()時,使用 b__Main,
;編譯器就會在__Main和Main之間插入一段匯編代碼,來替我們完成RW,ZI段的初始化。 如果我們使用 bMain,
;那么初始化工作要我們自己做。編譯器會生成如下變量告訴我們RO,RW,ZI三個段應(yīng)該位于什么位置,但是它并
;沒有告訴我們RW,ZI在Flash中存儲在什么位置,實際上RW,ZI在Flash中的位置就緊接著RO存儲。我們知道了
;Image$$RO$$Base,Image$$RO$$Limit,那么Image$$RO$$Limit就是RW(ROM data)的開始。
IMPORT|Image$$RO$$Base| ; Base of ROM code
IMPORT|Image$$RO$$Limit|; End of ROM code (=start of ROM data)
IMPORT|Image$$RW$$Base|; Base of RAM to initialise
IMPORT|Image$$ZI$$Base|; Base and limit of area
IMPORT|Image$$ZI$$Limit|; to zero initialise
;這里引入一些在其它文件中實現(xiàn)在函數(shù),包括為我們所熟知的main函數(shù)
;IMPORT MMU_SetAsyncBusMode
;IMPORT MMU_SetFastBusMode ;hzh
IMPORTMain
;從這里開始就是正真的代碼入口了!
AREAInit,CODE,READONLY ;這表明下面的是一個名為Init的代碼段
ENTRY;定義程序的入口(調(diào)試用)
EXPORT __ENTRY;導(dǎo)出符號_ENTRY,但在那用到就還沒查明
__ENTRY
ResetEntry
;1)The code, which converts to Big-endian, should be in little endian code.
;2)The following little endian code will be compiled in Big-Endian mode.
; The code byte order should be changed as the memory bus width.
;3)The pseudo instruction,DCD can not be used here because the linker generates error.
;條件編譯,在編譯成機器碼前就設(shè)定好
ASSERT:DEF:ENDIAN_CHANGE;判斷ENDIAN_CHANGE是否已定義
[ ENDIAN_CHANGE;如果已經(jīng)定義了ENDIAN_CHANGE,則(在Option.inc里已經(jīng)設(shè)為FALSE )
ASSERT:DEF:ENTRY_BUS_WIDTH;判斷ENTRY_BUS_WIDTH是否已定義
[ ENTRY_BUS_WIDTH=32;如果已經(jīng)定義了ENTRY_BUS_WIDTH,則判斷是不是為32
bChangeBigEndian;DCD 0xea000007
]
;在bigendian中,地址為A的字單元包括字節(jié)單元A,A+1,A+2,A+3,字節(jié)單元由高位到低位為A,A+1,A+2,A+3
;地址為A的字單元包括半字單元A,A+2,半字單元由高位到低位為A,A+2
[ ENTRY_BUS_WIDTH=16
andeqr14,r7,r0,lsl #20;DCD 0x0007ea00也是bChangeBigEndian指令,只是由于總線不一樣而取機器碼
];的順序不一樣,先取低位->高位上述指令是通過機器碼裝換而來的
[ ENTRY_BUS_WIDTH=8
streqr0,[r0,-r10,ror #1] ;DCD 0x070000ea 也是bChangeBigEndian指令,只是由于總線不一樣而取機器碼
];的順序不一樣
|
bResetHandler;我們的程序由于ENDIAN_CHANGE設(shè)成FALSE就到這兒了,轉(zhuǎn)跳到復(fù)位程序入口
]
bHandlerUndef;handler for Undefined mode;0x04
bHandlerSWI;handler for SWI interrupt;0x08
bHandlerPabort;handler for PAbort;0x0c
bHandlerDabort;handler for DAbort;0x10
b.;reserved 注意小圓點;0x14
bHandlerIRQ;handler for IRQ interrupt;0x18
bHandlerFIQ;handler for FIQ interrupt;0x1c
;@0x20
bEnterPWDN; Must be @0x20.
;==================================================================================
;下面是改變大小端的程序,這里采用直接定義機器碼的方式,至說為什么這么做就得問三星了
;反正我們程序里這段代碼也不會去執(zhí)行,不用去管它
;==================================================================================
;通過設(shè)置CP15的C1的位7,設(shè)置存儲格式為Bigendian,三種總線方式
ChangeBigEndian ;//here ENTRY_BUS_WIDTH=16
;@0x24
[ ENTRY_BUS_WIDTH=32
DCD0xee110f10;0xee110f10 => mrc p15,0,r0,c1,c0,0
DCD0xe3800080;0xe3800080 => orr r0,r0,#0x80;//Big-endian
DCD0xee010f10;0xee010f10 => mcr p15,0,r0,c1,c0,0
;對存儲器控制寄存器操作,指定內(nèi)存模式為Big-endian
;因為剛開始CPU都是按照32位總線的指令格式運行的,如果采用其他的話,CPU別不了,必須轉(zhuǎn)化
;但當(dāng)系統(tǒng)初始化好以后,則CPU能自動識別
]
[ ENTRY_BUS_WIDTH=16
DCD 0x0f10ee11
DCD 0x0080e380
DCD 0x0f10ee01
;因為采用Big-endian模式,采用16位總線時,物理地址的高位和數(shù)據(jù)的地位對應(yīng)
;所以指令的機器碼也相應(yīng)的高低對調(diào)
]
[ ENTRY_BUS_WIDTH=8
DCD 0x100f11ee
DCD 0x800080e3
DCD 0x100f01ee
]
DCD 0xffffffff;swinv 0xffffff is similar with NOP and run well in both endian mode.
DCD 0xffffffff
DCD 0xffffffff
DCD 0xffffffff
DCD 0xffffffff
b ResetHandler
;====================================================================================
; Function for entering power down mode
; 1. SDRAM should be in self-refresh mode.
; 2. All interrupt should be maksked for SDRAM/DRAM self-refresh.
; 3. LCD controller should be disabled for SDRAM/DRAM self-refresh.
; 4. The I-cache may have to be turned on.
; 5. The location of the following code may have not to be changed.
;void EnterPWDN(int CLKCON);
EnterPWDN
mov r2,r0;r2=rCLKCON 保存原始數(shù)據(jù) 0x4c00000c 使能各模塊的時鐘輸入
tst r0,#0x8;測試bit[3] SLEEP mode? 1=>sleep
bne ENTER_SLEEP;C=0,即TST結(jié)果非0,bit[3]=1
;//進入PWDN后如果不是sleep則進入stop
;//進入Stop mode
ENTER_STOP
ldr r0,=REFRESH;0x48000024DRAM/SDRAM refresh config
ldr r3,[r0];r3=rREFRESH
mov r1, r3
orr r1, r1, #BIT_SELFREFRESH;Enable SDRAM self-refresh
str r1, [r0];Enable SDRAM self-refresh
mov r1,#16;wait until self-refresh is issued. may not be needed.
0
subs r1,r1,#1
bne %B0
;//wait 16 fclks for self-refresh
ldr r0,=CLKCON;enter STOP mode.
str r2,[r0]
mov r1,#32
0
subs r1,r1,#1;1) wait until the STOP mode is in effect.
bne %B0;2) Or wait here until the CPU&Peripherals will be turned-off
;Entering SLEEP mode, only the reset by wake-up is available.
ldr r0,=REFRESH ;exit from SDRAM self refresh mode.
str r3,[r0]
MOV_PC_LR;back to main process
ENTER_SLEEP
;NOTE.
;1) rGSTATUS3 should have the return address after wake-up from SLEEP mode.
ldr r0,=REFRESH
ldr r1,[r0];r1=rREFRESH
orr r1, r1, #BIT_SELFREFRESH
str r1, [r0];Enable SDRAM self-refresh
;//Enable SDRAM self-refresh
mov r1,#16;Wait until self-refresh is issued,which may not be needed.
0
subs r1,r1,#1
bne %B0
;//Wait until self-refresh is issued,which may not be needed
ldrr1,=MISCCR;IO register
ldrr0,[r1]
orrr0,r0,#(7<<17);Set SCLK0=1, SCLK1=1, SCKE=1.
strr0,[r1]
ldr r0,=CLKCON; Enter sleep mode
str r2,[r0]
b .;CPU will die here.
;//進入Sleep Mode,1)設(shè)置SDRAM為self-refresh
;//2)設(shè)置MISCCR bit[17] 1:sclk0=sclk 0:sclk0=0
;//bit[18] 1:sclk1=sclk 0:sclk1=0
;//bit[19] 1:Self refresh retain enable
;//0:Self refresh retain disable
;//When 1, After wake-up from sleep, The self-refresh will be retained.
WAKEUP_SLEEP
;Release SCLKn after wake-up from the SLEEP mode.
ldrr1,=MISCCR
ldrr0,[r1]
bicr0,r0,#(7<<17);SCLK0:0->SCLK, SCLK1:0->SCLK, SCKE:0->=SCKE.
strr0,[r1]
;//設(shè)置MISCCR
;Set memory control registers
;ldrr0,=SMRDATA
adrl r0, SMRDATA
ldrr1,=BWSCON;BWSCON Address;//總線寬度和等待控制寄存器
addr2, r0, #52;End address of SMRDATA
0
ldrr3, [r0], #4;數(shù)據(jù)處理后R0自加4,[R0]->R3,R0+4->R0
strr3, [r1], #4
cmpr2, r0
bne%B0
;//設(shè)置所有的memory control register,他的初始地址為BWSCON,初始化
;//數(shù)據(jù)在以SMRDATA為起始的存儲區(qū)
mov r1,#256
0
subs r1,r1,#1;1) wait until the SelfRefresh is released.
bne %B0
;//1) wait until the SelfRefresh is released.
ldr r1,=GSTATUS3 ;GSTATUS3 has the start address just after SLEEP wake-up
ldr r0,[r1]
mov pc,r0
;//跳出Sleep Mode,進入Sleep狀態(tài)前的PC
;=================================================================================
;如上所說,這里采用HANDLER宏去建立Hander***和Handle***之間的聯(lián)系
上一篇:ARM研發(fā)常見問題集
下一篇:ARM嵌入式平臺的VGA接口設(shè)計(ADV7120)
推薦閱讀
史海拾趣
設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦
- 榜單!1700萬輛座艙域控「紅利」,誰是大贏家?龍頭格局揭曉!
- 多點觸摸感應(yīng)技術(shù)給人機界面帶來的改變
- H.265:網(wǎng)絡(luò)視頻的高清時代
- 大聯(lián)大友尚集團推出基于ST產(chǎn)品的工業(yè)PLC方案
- 電視棒工作原理很簡單 有網(wǎng)絡(luò)就能變身智能電視
- 牛B的手勢操控是如何煉成的
- Bourns 擴展符合 AEC-Q200 標(biāo)準(zhǔn)車規(guī)級電流檢測電阻產(chǎn)品線
- 基于G.729壓縮語音流隱蔽通信系統(tǒng)設(shè)計方案
- 觸摸控制器如何滿足大屏觸控設(shè)備抗噪新需求?
- Bourns 擴展半屏蔽功率電感產(chǎn)品組合,推出具更高最大感值新系列
- Follow me第二季第3期來啦!與得捷一起解鎖高性能開發(fā)板【EK-RA6M5】超能力!
- 有獎直播:ams投影照明(MLA)增強汽車與道路的溝通
- 降碳行動:尋找身邊8位單片機,搶樓贏ST最新M0開發(fā)板!
- 有獎看視頻|2022臺北國際電腦展——美光主題演講精選
- Microchip喊你探索dsPIC33A 芯片,70份好禮等你贏!
- 【主題月活動】小電阻,大智慧!
- ST傳感器闖天下之驅(qū)動移植大賽+骨振動傳感器評測,分高者勝
- 有獎直播|TI Sitara™ 產(chǎn)品在智能電網(wǎng)中的應(yīng)用
- 西門子|加速數(shù)字化轉(zhuǎn)型電子產(chǎn)品,智能制造解決方案
- 電荷泵是如何升壓的?原理非常簡單,一看就會
- DC-DC的輸出電壓和輸出阻抗問題
- 總結(jié):DC/DC轉(zhuǎn)換電路設(shè)計10大原則(圖文+案例)
- 頻率跟蹤的幾種方法,哪個精通這些的,哪怕知道PLL鎖相環(huán)也好,講講實現(xiàn)的原理
- 壓電陶瓷片參數(shù)如何測試
- 【博流BL606P音視頻開發(fā)板】 HelloWord項目DownLoad Flash出現(xiàn)問題,期待解決
- 【行空板 Python編程學(xué)習(xí)主控板評測五】pinpong庫使用
- 【MPS商城鉅惠體驗季】開箱
- 【ST NUCLEO-U575ZI-Q 測評 】LUA移植