簡單的STM32 匯編程序—閃爍LED


要移植操作系統,匯編是道不得不跨過去的坎。所以承接上篇的思路,我准備用匯編寫一個簡單的閃爍LED燈的程式。以此練習匯編,為操作系統做准備。

第一步,還是和上篇一樣,建立一個空的文件夾。

第二步,因為是要用匯編來寫程式,所以不需要啟動代碼,這里選擇否。

第三步,建立一個.s文件,並把文件添加到工程中。

第四步,在LED.s文件中添加如下代碼。

LED0 EQU 0x422101a0 
RCC_APB2ENR EQU 0x40021018
GPIOA_CRH EQU 0x40010804



Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp







                AREA    RESET, DATA, READONLY

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                    
                    
                AREA    |.text|, CODE, READONLY
                    
                THUMB
                REQUIRE8
                PRESERVE8
                    
                ENTRY
Reset_Handler 
                BL LED_Init
MainLoop        BL LED_ON
                BL Delay
                BL LED_OFF
                BL Delay
                
                B MainLoop
             
LED_Init
                PUSH {R0,R1, LR}
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x04
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]
                
                LDR R0,=GPIOA_CRH
                BIC R0,R0,#0x0F
                LDR R1,=GPIOA_CRH
                STR R0,[R1]
                
                LDR R0,=GPIOA_CRH
                ORR R0,R0,#0x03
                LDR R1,=GPIOA_CRH
                STR R0,[R1]
                
                MOV R0,#1 
                LDR R1,=LED0
                STR R0,[R1]
             
                POP {R0,R1,PC}

             
LED_ON
                PUSH {R0,R1, LR}    
                
                MOV R0,#0 
                LDR R1,=LED0
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED_OFF
                PUSH {R0,R1, LR}    
                
                MOV R0,#1 
                LDR R1,=LED0
                STR R0,[R1]
             
                POP {R0,R1,PC}             
             
Delay
                PUSH {R0,R1, LR}
                
                MOVS R0,#0
                MOVS R1,#0
                MOVS R2,#0
                
DelayLoop0        
                ADDS R0,R0,#1

                CMP R0,#330
                BCC DelayLoop0
                
                MOVS R0,#0
                ADDS R1,R1,#1
                CMP R1,#330
                BCC DelayLoop0

                MOVS R0,#0
                MOVS R1,#0
                ADDS R2,R2,#1
                CMP R2,#15
                BCC DelayLoop0
                
                
                POP {R0,R1,PC}    
             
    ;         NOP
             END
                 
                 

 

///////////////////////////////////////////////////////

代碼的簡單講解

1,預定義

LED0 EQU 0x422101a0 ;PA8的Bit-Bond地址。

RCC_APB2ENR EQU 0x40021018

GPIOA_CRH EQU 0x40010804

為方便操作,給每個需要用到的寄存器地址定義一個名字,類似於C語言的#define。PA8的Bit-Bond地址的計算方法可按上篇文章中C語言的算法算出。后面的兩個地址時固定的,可從STM32的手冊查詢,或者根據ST官方的庫文件查找計算。

2,分配棧空間

Stack_Size EQU 0x00000400

 

AREA STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem SPACE Stack_Size

__initial_sp

這一段摘自啟動文件。要讀懂這段代碼,首先要了解兩個命令。

AREA命令:AREA 命令指示匯編器匯編一個新的代碼段或數據段。段是獨立的、指定的、不可見的代碼或數據塊,它們由鏈接器處理。格式如下:

AREA 段名,段屬性1,段屬性2,段屬性3。。。

AREA STACK, NOINIT, READWRITE, ALIGN=3

NOINIT: = NO Init,不初始化。

READWRITE : 可讀,可寫。

ALIGN =3 : 2^3 對齊,即8字節對齊。

SPACE命令:SPACE 命令保留一個用零填充的存儲器塊。

所以整段的意思為:分配一個STACK段,該段不初始化,可讀寫,按8字節對齊。分配一個大小為Stack_Size的存儲空間,並使棧頂的地址為__initial_sp。

3,分配向量表

AREA RESET, DATA, READONLY

 

__Vectors DCD __initial_sp ; Top of Stack

DCD Reset_Handler ; Reset Handler

這里的向量可參考我之前寫的《STM32向量表詳細分析》。

4,開始代碼段

AREA |.text|, CODE, READONLY

通知匯編器,開始代碼段。

THUMB

REQUIRE8

PRESERVE8

這段的意思是,匯編器支持THUMB指令,代碼段按8字節對齊。

ENTRY命令:聲明整個程式的入口點,入口點有且僅有一個。不管哪種語言,編譯器都得有個入口點,這沒什么好說的。

5,程序正式開始。

后面的代碼,皆用標准的THUMB2匯編指令。首先了解下代碼中用到的指令。

BL:帶鏈接的跳轉指令。當使用該指令跳轉時,當前地址(PC)會自動送入LR寄存器。

B:無條件跳轉。

PUSH和POP:可以看到,所有的子程序都是由PUSH和POP包起來的。借用一張圖解釋下這兩個指令。

據上可知,PUSH {R0,R1, LR}的意思即把R0,R1,LR中的值放入堆棧中。由於主程式中使用BL跳轉指令,所以LR中的值實際上就是當前PC的值。而POP {R0,R1,PC}的意思即是將棧中之前存的R0,R1,LR的值返還給R0,R1,PC。這樣就完成了子程序的返回。

LDR和STR:寄存器的裝載和存儲指令。

LDR是把地址裝載到寄存器中(比如R0)。

STR是把值存儲到寄存器所指的地址中。

舉個例子:

MOV R0,#1 ;將立即數1送入R0.

LDR R1,=LED0;將PA8 bit-bond的地址送入R1.

STR R0,[R1];將R0的值,也就是1,送給R1中的值所指向的地址中,也就是PA8的bit-bond地址。

上面三句話的意思即是將PA8置1。

ORR和BIC:

ORR 按位或操作。ORR R0,R0,#0x04意思即將R0中的數或上0x04,再將結果送往R0中。實際意思就是將R0的第二位置1,其他位不變。

BIC 先把立即數取反,再按位與。

CMP和BCC:CMP是比較兩個數,相等或大於則將標志位C置位,否則將C清零。BCC是個組合指令,實際為B+CC,意思是如果C=0則跳轉。

CMP R2,#15; 計算R2-15的值,若是R2<15,則C=0;若是R2>=15,則C=1。

BCC DelayLoop0;若是C=0,則跳到DelayLoop0,若是c=1,則不跳轉。

以上就是代碼段相關指令的介紹,相信了解了這些指令的含義,要理解代碼並不困難。

整個代碼的結構和上篇用C語言寫的基本是一樣的。可參照理解

////////////////////////////////////////////////////

第五步,編譯,下載。

編譯后,會有一個警告 No section matches pattern……可不用管。下載后,LED燈正常閃爍。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM