ARM(IMX6U)裸机C语言版本LED驱动实验(汇编进入处理器SVC模式、SP堆内存、跳转main函数、链接起始地址)
參考:Linux之ARM(IMX6U)裸機C語言LED驅動實驗–驅動編寫,編譯
作者:一只青木呀
發布時間: 2020-08-11 11:20:17
網址:https://blog.csdn.net/weixin_45309916/article/details/107930284
目錄
- 0.簡介
- ①、匯編文件
- ②、C 語言文件
- STM32啟動匯編文件
- 1.匯編文件初始化C語言運行環境
- 1.設置處理器進入SVC模式(使用CPSR程序狀態寄存器)
- 2.設置SP指針(C語言運行需要入棧和出棧,指定一段棧內存)
- 3.跳轉到C語言
- 4.匯編實現(處理器模式和SP指針)
- 2.C 語言部分實驗程序編寫
- 3.編譯(編寫Makefile、設置程序運行起始地址)
- 4.燒寫到SD卡并驗證
- 查看反匯編文件,堆棧地址
0.簡介
前面講解了如何使用匯編來編寫LED 燈驅動,實際工作中是很少用到匯編去寫嵌入式驅動的,畢竟匯編太難,而且寫出來也不好理解,大部分情況下都是使用C 語言去編寫的。
在開始部分用匯編來初始化一下 C 語言環境,比如初始化 DDR、設置堆棧指針 SP 等等,當這些工作都做完以后就可以進入 C 語言環境,也就是運行 C 語言代碼,一般都是進入 main 函數。所以我們有兩部分文件要做:
①、匯編文件
匯編文件只是用來完成 C 語言環境搭建。
②、C 語言文件
C 語言文件就是完成我們的業務層代碼的,其實就是我們實際例程要完成的功能
C 語言文件就是完成我們的業務層代碼的,其實就是我們實際例程要完成的功能。
其實STM32 也是這樣的,只是我們在開發STM32 的時候沒有想到這一點,以STM32F103 為例,其啟動文件startup_stm32f10x_hd.s 這個匯編文件就是完成C 語言環境搭建的,當然還有一些其他的處理,比如中斷向量表等等。當startup_stm32f10x_hd.s 把C 語言環境初始化完成以后就會進入C 語言環境。
STM32啟動匯編文件
在STM32 中,啟動文件startup_stm32f10x_hd.s 就是完成C 語言環境搭建的,當然還有一些其他的處理,比如中斷向量表等等。startup_stm32f10x_hd.s 中堆棧初始化代碼如下所示:
1 Stack_Size EQU 0x00000400 2 3 AREA STACK, NOINIT, READWRITE, ALIGN=3 4 Stack_Mem SPACE Stack_Size 5 __initial_sp 6 7 ; <h> Heap Configuration 8 ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> 9 ; </h> 10 11 Heap_Size EQU 0x00000200 12 13 AREA HEAP, NOINIT, READWRITE, ALIGN=3 14 __heap_base 15 Heap_Mem SPACE Heap_Size 16 __heap_limit 17 *******************省略掉部分代碼*********************** 18 Reset_Handler PROC 19 EXPORT Reset_Handler [WEAK] 20 IMPORT __main 21 IMPORT SystemInit 22 LDR R0, =SystemInit 23 BLX R0 24 LDR R0, =__main 25 BX R0 26 ENDP第1 行代碼就是設置棧大小,這里是設置為0X400=1024 字節。
第5 行的__initial_sp 就是初始化SP 指針。
第11 行是設置堆大小。
第18 行是復位中斷服務函數,STM32 復位完成以后會執行此中斷服務函數。
第22 行調用SystemInit()函數來完成其他初始化工作。
第24 行調用__main,__main 是庫函數,其會調用main()函數。
I.MX6U 的匯編部分代碼和STM32 的啟動文件startup_stm32f10x_hd.s 基本類似的,只是本實驗我們不考慮中斷向量表(內部boot rom幫我們完成了,后面中斷章節我們會手動實現中斷向量表),只考慮初始化C 環境即可。
1.匯編文件初始化C語言運行環境
1.設置處理器進入SVC模式(使用CPSR程序狀態寄存器)
以前的 ARM 處理器有 7 種運行模型:User、FIQ、IRQ、Supervisor(SVC)、Abort、Undef和 System,其中 User 是非特權模式,其余 6 中都是特權模式。但新的 Cortex-A 架構加入了TrustZone 安全擴展,所以就新加了一種運行模式:Monitor,新的處理器架構還支持虛擬化擴展,因此又加入了另一個運行模式:Hyp,所以 Cortex-A7 處理器有 9 種處理模式,如表
| User(USR) | 用戶模式,非特權模式,大部分程序運行的時候就處于此模式。 |
| FIQ | 快速中斷模式,進入 FIQ 中斷異常 |
| IRQ | 一般中斷模式。 |
| Supervisor(SVC) | 超級管理員模式,特權模式,訪問CPU所有資源,供操作系統使用。 |
| Monitor(MON) | 監視模式?這個模式用于安全擴展模式。 |
| Abort(ABT) | 數據訪問終止模式,用于虛擬存儲以及存儲保護。 |
| Hyp(HYP) | 超級監視模式?用于虛擬化擴展。 |
| Undef(UND) | 未定義指令終止模式。 |
| System(SYS) | 系統模式,用于運行特權級的操作系統任務 |
怎么設置處理器進入 SVC 模式?
–>使用CPSR程序狀態寄存器來設置
M[4:0] :處理器模式控制位,含義如表
| 10000 | User 模式 |
| 10001 | FIQ 模式 |
| 10010 | IRQ 模式 |
| 10011 | Supervisor(SVC)模式 |
| 10110 | Monitor(MON)模式 |
| 10111 | Abort(ABT)模式 |
| 11010 | Hyp(HYP)模式 |
| 11011 | Undef(UND)模式 |
| 11111 | System(SYS)模式 |
總結:
2.設置SP指針(C語言運行需要入棧和出棧,指定一段棧內存)
設置 SVC 模式下的 SP 指針=0X80200000,因為 I.MX6U-ALPHA 開發 板 上 的 DDR3 地 址 范 圍 是 0X80000000 ~ 0XA0000000(512MB) 或 者0X80000000~0X90000000(256MB),不管是 512MB 版本還是 256MB 版本的,其 DDR3 起始地址都是 0X80000000。由于 Cortex-A7 的堆棧是向下增長的(高地址向低地址增長),所以將 SP 指針設置為 0X80200000,因此 SVC 模式的棧大小 0X80200000-0X80000000=0X200000=2MB, 2MB 的棧空間已經很大了,如果做裸機開發的話綽綽有余,不用擔心棧溢出。
總結:
3.跳轉到C語言
使用b指令,跳轉到C語言函數,比如main函數
4.匯編實現(處理器模式和SP指針)
start.s
.global _start_start:/*設置處理器進入SVC模式 */mrs r0,cpsr /*讀取cpsr的值到r0 */bic r0,r0,#0x1f /*清除cpsr的bit4--0 與運算 具體參照相關匯編指令*/orr r0,r0,#0x13 /*使用SVC模式 或運算 這是匯編的與運算*/msr cpsr,r0 /*將r0寫入到cpsr中去 *//*設置SP指針 */ /*有的芯片比如三星 還要在設置SP指針之前手動初始化DDR和SDRAM 前面分析DCD 數據的時候就已經講過了,DCD數據包含了DDR配置參數,I.MX6U 內部的Boot ROM 會讀取DCD 數據中的DDR 配置參數然后完成DDR 初始化的*/ldr sp,=0x80200000 b main /*跳轉到C語言main函數*/2.C 語言部分實驗程序編寫
C 語言部分有兩個文件 main.c 和 main.h, main.h 里面主要是定義寄存器地址,在 main.h里面輸入代碼:
main.h
在 main.h 中我們以宏定義的形式定義了要使用到的所有寄存器,后面的數字就是其地址,比如 CCM_CCGR0 寄存器的地址就是 0X020C4068
在 main.c里面輸入代碼:
main.c
main.c 文件里面一共有 7 個函數,這 7 個函數都很簡單。 clk_enable 函數是使能CCGR0~CCGR6 所控制的所有外設時鐘。 led_init 函數是初始化 LED 燈所使用的 IO,包括設置IO 的復用功能、 IO 的屬性配置和 GPIO 功能,最終控制 GPIO 輸出低電平來打開 LED 燈。led_on 和 led_off 這兩個函數看名字就知道,用來控制 LED 燈的亮滅的。 delay_short()和 delay()這兩個函數是延時函數, delay_short()函數是靠空循環來實現延時的, delay()是對 delay_short()的 簡 單 封 裝 ,在 I.MX6U 工作 在 396MHz(Boot ROM 設 置的 396MHz)的 主 頻 的 時候delay_short(0x7ff)基本能夠實現大約 1ms 的延時,所以 delay()函數我們可以用來完成 ms 延時。
main 函數就是我們的主函數了,在 main 函數中先調用函數 clk_enable()和 led_init()來完成時鐘使能和 LED 初始化,最終在 while(1)循環中實現 LED 循環亮滅,亮滅時間大約是 500ms。
3.編譯(編寫Makefile、設置程序運行起始地址)
objs := start.o main.oledc.bin:$(objs)arm-linux-gnueabihf-ld -Ttext 0X87800000 -o ledc.elf $^arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis%.o:%.sarm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<%.o:%.Sarm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<%.o:%.carm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<clean:rm -rf *.o ledc.bin ledc.elf ledc.dis第 1 行定義了一個變量 objs, objs 包含著要生成 ledc.bin 所需的材料: start.o 和 main.o,也就是當前工程下的 start.s 和 main.c 這兩個文件編譯后的.o 文件。
注意 start.o 一定要放到最前面!因為在后面鏈接的時候 start.o 要在最前面,因為 start.o 是最先要執行的文件!
第 3 行就是默認目標,目的是生成最終的可執行文件 ledc.bin, ledc.bin 依賴 start.o 和 main.o如果當前工程沒有 start.o 和 main.o 的時候就會找到相應的規則去生成 start.o 和 main.o。比如start.o 是 start.s 文件編譯生成的,因此會執行第 8 行的規則。
第 4 行是使用 arm-linux-gnueabihf-ld 進行鏈接,鏈接起始地址是 0X87800000,但是這一行用到了自動變量“” , “ ^”,“
” ,“^”的意思是所有依賴文件的集合,在這里就是 objs 這個變量的值:start.o 和 main.o。鏈接的時候 start.o 要鏈接到最前面,因為第一行代碼就是 start.o 里面的,因此這一行就相當于:
第 5 行使用 arm-linux-gnueabihf-objcopy 來將 ledc.elf 文件轉化為 ledc.bin,本行也用到了自動變量“@ ” , “ @”,“@”,“@”的意思是目標集合,在這里就是“ledc.bin”,那么本行就相當于:
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf ledc.bin第 6 行使用 arm-linux-gnueabihf-objdump 來反匯編,生成 ledc.dis 文件。
第 8~15 行就是針對不同的文件類型將其編譯成對應的.o 文件,其實就是匯編.s(.S)和.c 文件,比如 start.s 就會使用第 8 行的規則來生成對應的 start.o 文件。第 9 行就是具體的命令,這行也用到了自動變量“@ ” 和 “ @”和“@”和“<”,其中“$<”的意思是依賴目標集合的第一個文件。比如start.s 要編譯成 start.o 的話第 8 行和第 9 行就相當于:
start.o:start.sarm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o start.o start.s第 17 行就是工程清理規則,通過命令“make clean”就可以清理工程。
4.燒寫到SD卡并驗證
燒寫到SD卡并驗證參照之前的博文:ARM(IMX6U)裸機匯編LED驅動實驗——驅動編寫、編譯、燒寫bin文件到SD卡中并運行
這里燒寫到 sdb中
查看反匯編文件,堆棧地址
總結
以上是生活随笔為你收集整理的ARM(IMX6U)裸机C语言版本LED驱动实验(汇编进入处理器SVC模式、SP堆内存、跳转main函数、链接起始地址)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何下载斗鱼回放视频(纯手工)
- 下一篇: python核心编程第六章练习6-12