日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

ARM(IMX6U)裸机C语言版本LED驱动实验(汇编进入处理器SVC模式、SP堆内存、跳转main函数、链接起始地址)

發布時間:2023/12/10 c/c++ 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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] :處理器模式控制位,含義如表

M[4:0]處理器模式
10000User 模式
10001FIQ 模式
10010IRQ 模式
10011Supervisor(SVC)模式
10110Monitor(MON)模式
10111Abort(ABT)模式
11010Hyp(HYP)模式
11011Undef(UND)模式
11111System(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 的??臻g已經很大了,如果做裸機開發的話綽綽有余,不用擔心棧溢出。

總結:

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

#ifndef __MAIN_H #define __MAIN_H/*外設時鐘寄存器*/ #define CCM_CCGR0 *((volatile unsigned int *)0X020C4068) #define CCM_CCGR1 *((volatile unsigned int *)0X020C406C) #define CCM_CCGR2 *((volatile unsigned int *)0X020C4070) #define CCM_CCGR3 *((volatile unsigned int *)0X020C4074) #define CCM_CCGR4 *((volatile unsigned int *)0X020C4078) #define CCM_CCGR5 *((volatile unsigned int *)0X020C407C) #define CCM_CCGR6 *((volatile unsigned int *)0X020C4080)/* * IOMUX 復用相關寄存器 */#define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068) #define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0X020E02F4)/* * GPIO1相關寄存器 這里實際就用了前面兩行定義的寄存器 */#define GPIO1_DR *((volatile unsigned int *)0X0209C000) #define GPIO1_GDIR *((volatile unsigned int *)0X0209C004) #define GPIO1_PSR *((volatile unsigned int *)0X0209C008) #define GPIO1_ICR1 *((volatile unsigned int *)0X0209C00C) #define GPIO1_ICR2 *((volatile unsigned int *)0X0209C010) #define GPIO1_IMR *((volatile unsigned int *)0X0209C014) #define GPIO1_ISR *((volatile unsigned int *)0X0209C018) #define GPIO1_EDGE_SEL *((volatile unsigned int *)0X0209C01C)#endif

在 main.h 中我們以宏定義的形式定義了要使用到的所有寄存器,后面的數字就是其地址,比如 CCM_CCGR0 寄存器的地址就是 0X020C4068

在 main.c里面輸入代碼:
main.c

#include "main.h"/*使能所有外設時鐘*/ void clk_enable(void) {CCM_CCGR0 = 0xFFFFFFFF;CCM_CCGR1 = 0xFFFFFFFF;CCM_CCGR2 = 0xFFFFFFFF;CCM_CCGR3 = 0xFFFFFFFF;CCM_CCGR4 = 0xFFFFFFFF;CCM_CCGR5 = 0xFFFFFFFF;CCM_CCGR6 = 0xFFFFFFFF; }/*初始化LED燈*/ void led_init(void) {SW_MUX_GPIO1_IO03 = 0x5; /*復用為GPIO1--IO03 */SW_PAD_GPIO1_IO03 = 0x10B0; /*設置GPIO1__IO03電氣屬性*/GPIO1_GDIR = 0x8; //設置為輸出GPIO1_DR = 0x0; //默認打開LED燈} /*短延時*/ void delay_short(volatile unsigned int n) {while(n--){}} /** 延時 一次循環大概是1ms 在主頻396MHz下測試的* n:延時ms數 */ void delay(volatile unsigned int n) {while (n--){delay_short(0x7ff);}} /*打開LED燈*/ void led_on(void) {GPIO1_DR &= ~(1<<3); //bit3清零} /*關閉LED燈*/ void led_off(void ) {GPIO1_DR |= (1<<3); //bit3置1 }int main() {clk_enable(); //使能外設時鐘led_init(); //初始化LED// led_off(); while(1){led_off(); delay(500);led_on();delay(500);}return 0; }

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 里面的,因此這一行就相當于:

arm-linux-gnueabihf-ld -Ttext 0X87800000 -o ledc.elf start.o main.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函数、链接起始地址)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。