# STM32 Bare-Metal Programming with GCC and Makefile github:https://github.com/lisyuanhao/STM32-Bare-Metal-Programming-with-GCC-and-Makefile ## 前言 此學習筆記為實作成大Jserv教授的教材:[嵌入式系統建構:開發運作於stm32的韌體程式](http://wiki.csie.ncku.edu.tw/embedded/Lab19/stm32-prog.pdf),**就是Bare-Metal Programming,不使用[ST](https://www.st.com/en/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus.html)提供的IDE(STM32CubeIDE)與HAL,從編譯的makefile、連結的linker、到燒錄位置都要手動設定**。目標為點亮LED,因此需要查規格書,知道有哪些暫存器會控制LED、會從哪裡開機(Boot area)、要燒錄到Flash的哪個位址。 ## 作業環境 1. 作業系統:Windows11 2. 開發版:STM32H745ZIQ 3. 編譯器:GNU Arm Embedded Toolchain (arm-none-eabi-gcc) 4. 燒錄工具:OpenOCD + ST-Link ![image](https://hackmd.io/_uploads/HJCCDg2nge.png) ## Flash開機位址 ### Option bytes 要先提到option bytes,因為這會決定我們要將程式碼燒錄到Flash的哪個位址,閱讀[Reference manual](https://www.st.com/resource/en/reference_manual/rm0399-stm32h745755-and-stm32h747757-advanced-armbased-32bit-mcus-stmicroelectronics.pdf)中的4.4.1及4.4.2章節,Option bytes是Flash中的一段非揮發性記憶體,會在上電後reset被載入到Flash configuration registers當中,並且可以根據應用需求修改。因此可以透過設定Boot address option bytes來設定要在哪個位址開機。 > The embedded flash memory includes a set of non-volatile option bytes. They are loaded at power-on reset and can be read and modified only through onfiguration registers. These option bytes are configured by the end-user depending on the application requirements. > When the user application successfully modifies the option byte content through the embedded flash memory registers, the non-volatile option bytes are programmed and the embedded flash memory automatically reloads all option bytes to update the option registers. 以下為兩個核心各自可以設定的option bytes,因為我們只會用CM7核心,因此都看CM7相關的部分。首先,要啟用CM7 core,必須要設定BOOT_CM7 bit為1(預設為1)。 > 4.4.7 Description of boot address option bytes > Below the list of option bytes that can be used to configure the appropriate boot address for your application: > • Arm® Cortex®-M7 boot options > – BOOT_CM7: option bit to enable Arm® Cortex®-M7 boot, when set to 1. > – BOOT_CM7_ADD0/1: MSB of the Arm® Cortex®-M7 boot address when the BOOT pin is low (respectively high) ![image](https://hackmd.io/_uploads/HJl5a8C2ex.png) 至於位址的部分則取決於BOOT_CM7_ADD0/1,依照手冊說明,若boot pin為0,則看BOOT_CM7_ADD0。內文提到: > BOOT_CM7_ADD0/1: MSB of the Arm® Cortex®-M7 boot address when the BOOT pin is low (respectively high)。 這裡的MSB,並不是指most significant bit,而是代表boot address的高16位,且其預設值為0x0800(BOOT_ADD0BOOT_CM7_ADD0[15:0]),查詢4.9.17 FLASH register boot address有寫到: > Bits 15:0 BOOT_CM7_ADD0: Arm® Cortex®-M7 boot address 0 > These bits reflect the MSB of the Arm® Cortex®-M7 boot address when the BOOT pin is low. > ![image](https://hackmd.io/_uploads/H1p-CIRnee.png) ![image](https://hackmd.io/_uploads/r1xzqT0nxg.png) 也可以從以下的Table 9看出Boot=0,Boot area是在Flash的0x0800 0000開始的,這就我們後續在linker中要寫的位址。 ![image](https://hackmd.io/_uploads/r1exZDkaee.png) 接著就要查BOOT pin,沒有在Reference manual,而是在[User manual](https://www.st.com/resource/en/user_manual/um2408-stm32h7-nucleo144-boards-mb1363-stmicroelectronics.pdf) ,預設為Low,若要拉高要接到3V3_VDD(pin 5)。 | ![image](https://hackmd.io/_uploads/SJ-3G12ixx.png) | ![image](https://hackmd.io/_uploads/Hy9GQGahel.png) | | --------------------------------------------------- | --------------------------------------------------- | > The default state of BOOT0 is LOW. It can be set to HIGH when a jumper is ON between pins 5 and 7 of CN11. Refer to the warning at the end of Section 7.4.8: Internal SMPS/LDO configuration. ### RAM(Random-access memory)起始位址 使用AXI SRAM(Advanced eXtensible interface static random access memory),起始位址為0x2400 0000, ![image](https://hackmd.io/_uploads/r1ePNIZpex.png) ## 專案結構 ``` ├── blink.c ├── startup.c ├── reg.h ├── simple.ld └── Makefile ``` ## LED GPIO ### GPIO與AHB4匯流排 要點亮LED1,閱讀User manual的7.6 LEDs章節說明: > User LD1: a green user LED is connected to the STM32H7 I/O PB0 (SB65 OFF and SB54 ON) or PA5 (SB65 ON and SB54 OFF) corresponding to the ST Zio D13. 我們要找到GPIO B的第0號Pin,從[datasheet](https://www.st.com/resource/en/datasheet/stm32h745zg.pdf)可以找到 Figure 1. STM32H745xI/G block diagram,其中GPIO B部分是由AHB4匯流排控制,因此從Reference manual的Table 7. Register boundary addresses,找到**RCC(Reset and Clock Control)** 與**GPIO B**的基底位址。 ![image](https://hackmd.io/_uploads/rkq7iyTjel.png) ![image](https://hackmd.io/_uploads/ry-_5cJTgg.png) ### RCC AHB4ENR register 要啟用GPIO B的clock要定位到RCC_AHB4ENR暫存器,方法為從剛剛找到的RCC基底位址+offset=0x58024400+0x0E0,下方表格可定位到RCC_AHB4ENR或RCC_C1_AHB4ENR,沒有C1後綴屬於Common register bank,兩個CPU都能存取。此暫存器的bit 1位置為GPIOBEN,因此這部分的程式碼為: ```c! #define AHB4_RCC 0x58024400 #define RCC_AHB4ENR_OFFSET 0x0E0 #define RCC_AHB4ENR (*((volatile unsigned long *)(AHB4_RCC + RCC_AHB4ENR_OFFSET))) RCC_AHB4ENR |= (1 << 1); ``` ![image](https://hackmd.io/_uploads/BkxsPsJael.png) > The RCC block manages the clock and reset generation for the whole microcontroller, which embeds two CPUs: an Arm® Cortex®-M7 and an Arm® Cortex®-M4, called CPU1 and CPU2, respectively. CPU1 can allocate a peripheral for itself by setting the dedicated PERxEN bit in: > • RCC_DnxxxxENR registers or > • RCC_C1_DnxxxxENR registers. ### GPIOB register 接著就是GPIO B相關的暫存器,我們需要從Table 7. Register boundary addresses找到GPIO B的基底位址:0x58020400,並根據各暫存器的offset來定位到該位址。總共有3個暫存器要設定: 1. GPIOx_MODER:設成輸出模式(General purpose output mode) 2. GPIOx_OTYPER:設成推挽模式(Output push-pull) 3. GPIOx_BSRR:設定Port x 為1(Port x reset I/O pin y ) ### GPIOx_MODER GPIOx_MODER的offset為0x00,需要將MODER0兩個bit設成01,因此程式碼為: ```c! #define AHB4_GPIOB 0x58020400 #define GPIO_MODER_OFFSET 0x00 #define GPIO_MODER (*((volatile unsigned long *)(AHB4_GPIOB + GPIO_MODER_OFFSET))) GPIO_MODER &= ~(0x3 << 0);//先清除此2 bit GPIO_MODER |= (0x1 << 0);//將bit 0設為1 ``` ![image](https://hackmd.io/_uploads/B1ARzWb6el.png) ### GPIOx_OTYPER GPIOx_OTYPER的offset為0x04,需要將OT0 bit設成0,因此程式碼為: ```c! #define GPIO_OTYPER_OFFSET 0x04 #define GPIO_OTYPER (*((volatile unsigned long *)(AHB4_GPIOB + GPIO_OTYPER_OFFSET))) GPIO_OTYPER &= ~(0x1 << 0); ``` ![image](https://hackmd.io/_uploads/SkULr-Zale.png) ### GPIOx_BSRR GPIOx_BSRR的offset為0x18, 點亮LED:BS0設成1, 熄滅LED:BR0設成1,因此程式碼為: ```c! #define GPIO_BSRR_OFFSET 0x18 #define GPIO_BSRR (*((volatile unsigned long *)(AHB4_GPIOB + GPIO_BSRR_OFFSET))) #define LED_ON() (GPIO_BSRR = (0x1)) #define LED_OFF() (GPIO_BSRR = (0x1 << 16)) ``` ![image](https://hackmd.io/_uploads/BJCpF-ball.png) ### reg.h ```c #define AHB4_GPIOB 0x58020400 #define AHB4_RCC 0x58024400 #define RCC_AHB4ENR_OFFSET 0x0E0 #define GPIO_MODER_OFFSET 0x00 #define GPIO_OTYPER_OFFSET 0x04 #define GPIO_BSRR_OFFSET 0x18 #define RCC_AHB4ENR (*((volatile unsigned long *)(AHB4_RCC + RCC_AHB4ENR_OFFSET))) #define GPIO_MODER (*((volatile unsigned long *)(AHB4_GPIOB + GPIO_MODER_OFFSET))) #define GPIO_OTYPER (*((volatile unsigned long *)(AHB4_GPIOB + GPIO_OTYPER_OFFSET))) #define GPIO_BSRR (*((volatile unsigned long *)(AHB4_GPIOB + GPIO_BSRR_OFFSET))) #define LED_ON() (GPIO_BSRR = (0x1)) #define LED_OFF() (GPIO_BSRR = (0x1 << 16)) ``` ### blink.c ```c #include "reg.h" int global = 123; int main(void){ unsigned int c = 0; c = global; RCC_AHB4ENR |= (1 << 1); GPIO_MODER &= ~(0x3 << 0); GPIO_MODER |= (0x1 << 0); GPIO_OTYPER &= ~(0x1 << 0); while (1){ LED_ON(); for(c = 0; c < 1000000; c++); LED_OFF(); for(c = 0; c < 1000000; c++); } } ``` ## Linker 會先使用關鍵字:MEMORY來定義兩個記憶體區間,亦即`FLASH`與`RAM`區域,定義如下,起始位址為0x0800 0000,大小為1MB,RAM的起始位址為0x2400 0000,大小為512KB。 ### simple.ld ``` MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024k RAM (rwx) : ORIGIN = 0x24000000, LENGTH = 512k } SECTIONS { .flash :{ KEEP(*(.isr_vector)); KEEP(*(.reset_handler)); _load_text = .; } > FLASH .text_ram : AT(_load_text) { _text_ram = .; *(.text) *(.text*) _load_data = .; /* Flash 中 data section 的載入位址 */ } > RAM .data :AT (_load_data) { _data = .; *(.data) *(.data*) _edata = . ; } > RAM .bss : { _bss = . ; *(.bss) *(.bss*) _ebss = . ; } > RAM _end = . ; _estack = ORIGIN(RAM) + LENGTH(RAM); /DISCARD/ : { *(.glue_7) *(.glue_7t) *(.vfp11_veneer) *(.v4_bx) *(.iplt) *(.rel.dyn) *(.rel.iplt) *(.igot.plt) } } ``` 我們需要定義text section、data section需要放的地方。需注意的是,若有定義已初始化的全域變數,會先將其放置於Flash,需要將其複製到RAM這段可讀可寫的區域中。MCU上電reset後,會依照先前設定的option byte跳到指定位址找vector table,並且讀取前兩個entry: 1. Main stack pointer 2. Reset handler ### 中斷向量表(vector table) 以下為[programming manual](https://www.st.com/resource/en/programming_manual/pm0253-stm32f7-series-and-stm32h7-series-cortexm7-processor-programming-manual-stmicroelectronics.pdf#page=31.10)中的vector table,前兩個entry為Initial SP value、Reset,也因此我們自己宣告的vector table前兩個entry也要為此。 ![image](https://hackmd.io/_uploads/HyW82PWaxg.png) > On system reset, the vector table is at address 0x00000000. Privileged software can write to the VTOR to relocate the vector table start address to a different memory location, in the range 0x00000000 to 0xFFFFFF80. ```c! extern uint32_t _estack; extern uint32_t _load_text; extern uint32_t _text_ram; extern uint32_t _edata; extern uint32_t _bss; extern uint32_t _ebss; __attribute__((section(".isr_vector"))) uint32_t *vector_table[] = { (uint32_t *) &_estack, (uint32_t *) reset_handler, (uint32_t *) nmi_handler, }; __attribute__((section(".reset_handler"))) void reset_handler(void){ uint32_t *src, *dst; src = &_load_text; dst = &_text_ram; while (dst < &_edata) *dst++ = *src++; for(dst = &_bss; dst < &_ebss; dst++) *dst = 0; main(); } void nmi_handler(void){ while(1); } ``` * 前面加`__attribute__((section(".isr_vector")))`可讓開發者自行定義一個section,讓linker知道這一section放的是中斷向量表。 > You can place code and data by separating them into their own objects without having to use toolchain-specific pragmas or attributes. However, you can also use __attribute__((section("name"))) to place an item in a separate ELF section. You can then use a scatter file to place the named sections at specific locations. > *Ref*:https://developer.arm.com/documentation/dui0377/c/using-scatter-files/using---attribute----section--name---- * _estack是在linker中定義的變數,`_estack = ORIGIN(RAM) + LENGTH(RAM);`,代表top of stack,對應到前面提到的Initial SP value。 * reset_handler負責將data section從Flash搬到RAM * Jserv教授的講義中有提到: >大家如果開發過ARM9上的 bootloader 的話,應該都有過類似的經驗。這類 bootloader 的實作中,開始運行的第一步就是把text section,data section全部移到 RAM 中,尤其是對有些從 NAND FLASH 啟動的開發板,這一步是無法避免的。這個例子比前一個例子只是多了一個text section的複製,看起來並不算複雜,關鍵是對中斷向量表及中斷服務例程的特殊處理,讀者可以做為練習自己解說一下。給出的參考例程中,中斷向量表和中斷服務例程還是運行在 FLASH 上的,感興趣的話可以,把除了ResetISR()的中斷服務例程也放在 SRAM中。自己試試吧! 因為中斷向量表和中斷服務例程的reset handler還是運行在 Flash 上的,所以會將這兩個段獨立出來,nmi_handler則不需要,因為這個ISR是要被移到RAM的。**因此linker部分會依照講義說的:** **1. 把除了ResetISR()的中斷服務例程也放在 SRAM中** **2. 把text section,data section全部移到 RAM 中,如下圖:** ![image](https://hackmd.io/_uploads/rJQdP0mAxx.png =80%x) 以下為將中斷向量表isr_vector及reset_handler的中斷服務例程放在Flash中,最後面的"`> FLASH`"代表是要放在Flash中。 ``` .flash :{ KEEP(*(.isr_vector)); KEEP(*(.reset_handler)); _load_text = .; } > FLASH ``` > 在.isr_vector 上加KEEP是為了防止連結器的垃圾回收(garbage collection)功能啟用時,忽略.isr_vector section > GNU linker ld參考手冊定義了以下兩種位址: > ● LMA (Load Memory Address): 表示section被保存在記憶體上的位址 > ● VMA (Virtual Memory Address): 表示程式碼執行時期的位址 > 大多數情況來說(也就是不需要section搬移的情況),這兩類位址可視為相同,一如目的檔由GNU/Linux或其他作業系統提供的載入器去載入的情況,或者像上面那個例子,沒有作業系統載入器,但不需要作data section搬移的動作,指令直接從Flash被讀取並執行。不過,在本節探討的案例中,由於data section需要從Flash中搬移到SRAM中,因此data section的LMA和VMA不會相同。text section儲存在Flash上的位址是LMA,搬移到SRAM裡頭的位址則是VMA。 以下為reset_handler以後的text section,因為是要移到RAM,因此最後面的符號為"`> RAM`"。而 `AT(...)` 後面接的是載入位址(LMA),也就是這段內容在 Flash 中的實際儲存位置。代表`.text_ram` 這段雖然執行時會放在 RAM,但它的初始內容存放在 Flash 的 `_load_text` 位置。而 `_load_text` 是在前一段 `.flash` 段中定義的符號(`_load_text` = .;),它記錄了 Reset Handler 結束時 Flash 內的位址,因此從這個位址開始的程式碼(也就是 Reset Handler 之後的所有`.text` 區段)都會被視為需要搬到 RAM 執行的程式碼。 ``` .text_ram : AT(_load_text){ _text_ram = .; *(.text) *(.text*) _load_data = .; /* Flash 中 data section 的載入位址 */ } > RAM ``` 以下為data section,段的起始位址為_load_data,也就是上一個段:.text_ram的結束位址 ``` .data :AT (_load_data){ _data = .; *(.data) *(.data*) _edata = . ; } > RAM ``` 以下為bss section,因為直接在RAM處理(清0),因此只須要記錄下起始位址`_bss` 與結束位址`_ebss`。 ``` .bss : { _bss = . ; *(.bss) *(.bss*) _ebss = . ; } > RAM ``` ### Reset handler 以下僅列出.reset_handler段與linker中定義的變數,其中的`_load_text`、`_text_ram`、`_edata`、`_bss`、`_ebss`皆為linker中定義的變數,用來代表各個段的起始或結束位址。 `_load_text`(src)代表的是.reset_handler段的結束位址,同時也代表其他text段在Flash的起始位址,我們要從這個位址開始複製。要複製到的地方為`.text_ram`(dst)在RAM中的位址,因此在while迴圈內寫`*dst++ = *src++;`,直到在RAM中的位址`_text_ram`(dst)到達data section在RAM中的結束位址`_edata`。並且將bss section的值都清0後,跳到`main()`,執行我們的blink.c。 ```c! extern uint32_t _estack; extern uint32_t _load_text; extern uint32_t _text_ram; extern uint32_t _edata; extern uint32_t _bss; extern uint32_t _ebss; __attribute__((section(".reset_handler"))) void reset_handler(void){ uint32_t *src, *dst; src = &_load_text; dst = &_text_ram; while (dst < &_edata) *dst++ = *src++; for(dst = &_bss; dst < &_ebss; dst++) *dst = 0; main(); } ``` ### startup.c ```c #include <stdint.h> extern uint32_t _estack; extern uint32_t _load_text; extern uint32_t _text_ram; extern uint32_t _edata; extern uint32_t _bss; extern uint32_t _ebss; void reset_handler(void); void nmi_handler(void); int main(void); __attribute__((section(".isr_vector"))) uint32_t *vector_table[] = { (uint32_t *) &_estack, (uint32_t *) reset_handler, (uint32_t *) nmi_handler, }; __attribute__((section(".reset_handler"))) void reset_handler(void){ uint32_t *src, *dst; src = &_load_text; dst = &_text_ram; while (dst < &_edata) *dst++ = *src++; for(dst = &_bss; dst < &_ebss; dst++) *dst = 0; main(); } void nmi_handler(void){ while(1); } ``` ## Makefile * arm-none-eabi-gcc > ARM architecture,no vendor,not target an operating system,complies with the ARM EABI 用於編譯 ARM 架構的裸機系統(包括 ARM Linux 的 boot、kernel,不適用編譯 Linux 應用 Application),一般適合 ARM7、Cortex-M 和 Cortex-R 內核的芯片使用,所以不支持那些跟操作系統關係密切的函數,比如fork(2),他使用的是 newlib 這個專用於嵌入式系統的C庫。 > *Ref:* https://jasonblog.github.io/note/toolchain/173.html * -mcpu:使用ARM Cortex-M7產生的指令 * -mthumb:Cortex-M7只支援thumb指令集 >The ARMv7-M Thumb instruction set, defined in the ARM®v7-M Architecture Reference Manual. >*Ref:*[ARM® Cortex®-M7 Processor](https://developer.arm.com/documentation/ddi0489/f/introduction/about-the-cortex-m7-processor/features?lang=en) * -nostartfiles: > 要求連結階段不要使用標準系統起始檔案(starup file),這在沒有作業系統支援的環境是必要的,因為我們自己處理C語言程式main()函式之前的種種準備動作。 * -Map:blink.map,輸出一份記憶體配置報告。下一小節會根據此檔案分析startup.c與linker分配的記憶體位址是否正確。 * 使用openocd進行燒錄: 1. -f interface/stlink.cfg:使用 ST-Link 作為連線介面 1. -f target/stm32h7x.cfg:設定目標為 STM32H7 系列晶片 1. init : 初始化介面與 target,建立連線 1. reset init : 重置並暫停 MCU,確保可安全燒錄 1. flash write_image erase blink.out : 把程式寫進 Flash 1. reset run : 寫完後讓 MCU 執行程式 1. shutdown : 關閉 OpenOCD ``` CROSS_COMPILE ?= arm-none-eabi- blink.o: blink.c $(CROSS_COMPILE)gcc -mcpu=cortex-m7 -mthumb -nostartfiles -c blink.c -o blink.o startup.o: startup.c $(CROSS_COMPILE)gcc -mcpu=cortex-m7 -mthumb -nostartfiles -c startup.c -o startup.o blink.out: blink.o startup.o simple.ld $(CROSS_COMPILE)ld -T simple.ld -Map=blink.map --gc-sections --strip-all -o blink.out blink.o startup.o blink.bin: blink.out $(CROSS_COMPILE)objcopy -j .text -O binary blink.out blink.bin $(CROSS_COMPILE)size blink.out flash: blink.bin openocd \ -f interface/stlink.cfg \ -f target/stm32h7x.cfg \ -c "init" \ -c "reset init" \ -c "flash write_image erase blink.out" \ -c "reset run" \ -c "shutdown" clean: -del /Q *.o *.bin *.out *.elf *.lst *.map blink 2>nul || echo. ``` ## Memory layout ![image](https://hackmd.io/_uploads/SkvZdCm0gg.png =80%x) ``` Discarded input sections .bss 0x00000000 0x0 blink.o .data 0x00000000 0x0 startup.o .bss 0x00000000 0x0 startup.o Memory Configuration Name Origin Length Attributes FLASH 0x08000000 0x00100000 xr RAM 0x24000000 0x00080000 xrw *default* 0x00000000 0xffffffff Linker script and memory map .flash 0x08000000 0x78 *(.isr_vector) .isr_vector 0x08000000 0xc startup.o 0x08000000 vector_table *(.reset_handler) .reset_handler 0x0800000c 0x64 startup.o 0x0800000c reset_handler .reset_handler.__stub 0x08000070 0x8 linker stubs 0x08000078 _load_text = . ``` * 以上為定義的Flash與RAM的記憶體位址與其大小,以及中斷向量表(.isr_vector)與reset handler放在Flash的記憶體layout。 * 以下為將其餘的text section與其餘ISR(nmi_handler)放入RAM的記憶體layout * 可以看到被放到RAM的text section在原先的Flash位址為0x08000078,跟上方的結尾位址相同。 * 以及data section有一個`0x4 load address 0x24000096`,此為我們在blink.c中宣告的全域變數`int global = 123;`其放置於Flash中的位址,並且其後置於RAM當中,`0x24000098 0x4 blink.o`也顯示此變數是來自於blink.o,使得RAM的記憶體位置由`0x24000098`變成`0x2400009c` ``` .text_ram 0x24000000 0x96 load address 0x08000078 0x24000000 _text_ram = . *(.text) .text 0x24000000 0x90 blink.o 0x24000000 main .text 0x24000090 0x6 startup.o 0x24000090 nmi_handler *(.text*) 0x24000096 _load_data = . .data 0x24000098 0x4 load address 0x24000096 0x24000098 _data = . *(.data) .data 0x24000098 0x4 blink.o 0x24000098 global *(.data*) 0x2400009c _edata = . .bss 0x2400009c 0x0 0x2400009c _bss = . *(.bss) *(.bss*) 0x2400009c _ebss = . 0x2400009c _end = . 0x24080000 _estack = (ORIGIN (RAM) + LENGTH (RAM)) /DISCARD/ *(.glue_7) *(.glue_7t) *(.vfp11_veneer) *(.v4_bx) *(.iplt) *(.rel.dyn) *(.rel.iplt) *(.igot.plt) LOAD blink.o LOAD startup.o OUTPUT(blink.out elf32-littlearm) LOAD linker stubs .comment 0x00000000 0x45 .comment 0x00000000 0x45 blink.o 0x46 (size before relaxing) .comment 0x00000045 0x46 startup.o .ARM.attributes 0x00000000 0x2e .ARM.attributes 0x00000000 0x2e blink.o .ARM.attributes 0x0000002e 0x2e startup.o ``` ## 結語 之前在用MCU都是用廠商提供的整合式IDE,其包含了HAL,且能夠一鍵編譯-連結-燒錄,此時會跳出很多程式碼與燒錄位置,但卻知其然而不知其所以然,不知道背後發生了什麼事。此專題讓我學會進行裸機開發,了解過程中的每個步驟該如何寫。也更深入的閱讀規格書,光是開機位址就找了很久,因為講義用的stm32版本有memory aliasing,linker可以寫到0x0,其他位址能夠映射過去,但是stm32H745系列則是會用vector table offset register(VTOR)+0x0跳到指定位址,而VTOR的值又會跟option byte一樣,因此linker沒辦法寫0x0,而是需要依照實際的boot設定進行調整。做完這次專題才明白整合式IDE幫我們做了多少事,像是編譯、連結、搬移 .`data`到 RAM、初始化 `.bss` 等動作,才真正理解從開機到執行的全貌。 ## 參考資料 [[基礎] OpenOCD 與 STM32](https://loserembedded.blogspot.com/2016/07/openocd-stm32.html) [mini-arm-os](https://github.com/jserv/mini-arm-os) [Reference manual](https://www.st.com/resource/en/reference_manual/rm0399-stm32h745755-and-stm32h747757-advanced-armbased-32bit-mcus-stmicroelectronics.pdf) [User manual](https://www.st.com/resource/en/user_manual/um2408-stm32h7-nucleo144-boards-mb1363-stmicroelectronics.pdf) [programming manual](https://www.st.com/resource/en/programming_manual/pm0253-stm32f7-series-and-stm32h7-series-cortexm7-processor-programming-manual-stmicroelectronics.pdf#page=31.10) [ARM® Cortex®-M7 Processor](https://developer.arm.com/documentation/ddi0489/f/introduction/about-the-cortex-m7-processor/features?lang=en) [ARM Compiler toolchain v4.1 for µVision Using the Linker](https://developer.arm.com/documentation/dui0377/c/using-scatter-files/using---attribute----section--name----) [【1】Jserv mini-arm-os 學習筆記_開機流程x脫離IDEx你好](https://hackmd.io/@nfUUgsYRTGy81y5d9AYOyg/rkJtpGdAT)