--- tags : stm32, stm32f429zit6 --- # cm4hbm Arm cortex-m4 hello world bare metal ## 安裝流程 ### 安裝 arm-none-eabi-gcc 首先先去下載原始壓縮檔 ```shell $ sudo apt-get install gcc-arm-none-eabi ``` 原本的安裝的不會有 GDB,要自己額外去裝才可以用 先安裝套件 ```shell $ sudo apt install -y libncursesw5 $ sudo add-apt-repository ppa:deadsnakes/ppa $ sudo apt install -y python3.8 $ which arm-none-eabi-gcc /usr/bin/arm-none-eabi-gcc ``` 接著去 [arm developer download](https://developer.arm.com/downloads/-/gnu-rm) 下載整個壓縮檔,接著把 gdb 的 elf 執行檔移動到 `/usr/bin/arm-none-eabi-gcc` 這個目錄夾下就可以用 gdb 了 ### 安装 lsb-core工具 ```shell $ sudo apt-get update $ sudo apt-get install lsb-core ``` ### 安装 OpenOCD 工具 ```shell $ sudo apt-get update $ sudo apt-get install openocd ``` 或是從源碼進行編譯 :+1: ```shell $ git clone git://git.code.sf.net/p/openocd/code openocd $ sudo apt-get install build-essential pkg-config autoconf automake libtool libusb-dev libusb-1.0-0-dev libhidapi-dev $ sudo apt-get install libtool libsysfs-dev $ cd openocd && ./bootstrap $ ./configure $ make $ sudo make install $ openocd -v ``` ### 安裝 putty ```shell $ sudo apt-get install putty ``` ### 安裝 stlink ```shell $ sudo apt-get install git cmake libusb-1.0-0-dev $ git clone https://github.com/stlink-org/stlink && cd stlink $ cmake . $ make $ cd bin && sudo cp st-* /usr/local/bin $ cd ../lib && sudo cp *.so* /lib32 $ cd ../config/udev/rules.d/ && sudo cp 49-stlinkv* /etc/udev/rules.d/ ``` ### vscode 下載插件 `cortex debug` 插件 在 vscode 的擴充插件中搜尋 `cortex debug`,看到這個圖示就可以安裝了 ![](https://i.imgur.com/NsRk2Yc.jpg) ### 配置 Debug 的 json 文件 ``` 待補 ``` ## 添加 `SRC`, `INC`, `Startup`等等資料夾 ## 添加 linker-script 先使用從 IDE 那裡的 linker script :::spoiler stm32f429zit6 ```c /* Entry Point */ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ _estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */ _Min_Heap_Size = 0x200; /* required amount of heap */ _Min_Stack_Size = 0x400; /* required amount of stack */ /* Memories definition */ MEMORY { CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K } /* Sections */ SECTIONS { /* The startup code into "FLASH" Rom type memory */ .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } >FLASH /* The program code and other data into "FLASH" Rom type memory */ .text : { . = ALIGN(4); *(.text) /* .text sections (code) */ *(.text*) /* .text* sections (code) */ *(.glue_7) /* glue arm to thumb code */ *(.glue_7t) /* glue thumb to arm code */ *(.eh_frame) KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(4); _etext = .; /* define a global symbols at end of code */ } >FLASH /* Constant data into "FLASH" Rom type memory */ .rodata : { . = ALIGN(4); *(.rodata) /* .rodata sections (constants, strings, etc.) */ *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ . = ALIGN(4); } >FLASH .ARM.extab : { . = ALIGN(4); *(.ARM.extab* .gnu.linkonce.armextab.*) . = ALIGN(4); } >FLASH .ARM : { . = ALIGN(4); __exidx_start = .; *(.ARM.exidx*) __exidx_end = .; . = ALIGN(4); } >FLASH .preinit_array : { . = ALIGN(4); PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); . = ALIGN(4); } >FLASH .init_array : { . = ALIGN(4); PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); . = ALIGN(4); } >FLASH .fini_array : { . = ALIGN(4); PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(SORT(.fini_array.*))) KEEP (*(.fini_array*)) PROVIDE_HIDDEN (__fini_array_end = .); . = ALIGN(4); } >FLASH /* Used by the startup to initialize data */ _sidata = LOADADDR(.data); /* Initialized data sections into "RAM" Ram type memory */ .data : { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ *(.data) /* .data sections */ *(.data*) /* .data* sections */ *(.RamFunc) /* .RamFunc sections */ *(.RamFunc*) /* .RamFunc* sections */ . = ALIGN(4); _edata = .; /* define a global symbol at data end */ } >RAM AT> FLASH _siccmram = LOADADDR(.ccmram); /* CCM-RAM section * * IMPORTANT NOTE! * If initialized variables will be placed in this section, * the startup code needs to be modified to copy the init-values. */ .ccmram : { . = ALIGN(4); _sccmram = .; /* create a global symbol at ccmram start */ *(.ccmram) *(.ccmram*) . = ALIGN(4); _eccmram = .; /* create a global symbol at ccmram end */ } >CCMRAM AT> FLASH /* Uninitialized data section into "RAM" Ram type memory */ . = ALIGN(4); .bss : { /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; /* define a global symbol at bss end */ __bss_end__ = _ebss; } >RAM /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */ ._user_heap_stack : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); } >RAM /* Remove information from the compiler libraries */ /DISCARD/ : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } .ARM.attributes 0 : { *(.ARM.attributes) } } ``` ::: ## 添加 Makefile :::spoiler Makefile # Generic Makefile OUT ?= build BIN ?= main # c source file INPUT_C_SRC ?= Src/main.c C_SRC = Src/syscalls.c \ Src/myusart.c \ $(INPUT_C_SRC) INPUT_C_INC ?= C_INC = -Iinc \ $(INPUT_C_INC) # assembly file ASM_SRC = Src/startup.s # linker script LDSCRIPT = Src/link.ld VPATH = $(dir $(C_SRC)) VPATH += $(dir $(ASM_SRC)) # object file OBJS = $(patsubst %.c, $(OUT)/%.o, $(notdir $(C_SRC))) OBJS += $(patsubst %.s, $(OUT)/%.o, $(notdir $(ASM_SRC))) # toolchain TOOLCHAIN = arm-none-eabi- CC = $(TOOLCHAIN)gcc SZ = $(TOOLCHAIN)size # toolchain options MCU = -mcpu=cortex-m4 CFLAGS = $(MCU) -mthumb $(C_INC) -O0 -Wall LDFLAGS = $(MCU) -specs=nano.specs -T $(LDSCRIPT) -lc -lm -lnosys all: $(OUT)/$(BIN) # compile $(OUT)/%.o: %.c $(CC) -c $(CFLAGS) $< -o $@ $(OUT)/%.o: %.s $(CC) -c $(CFLAGS) $< -o $@ # link $(OUT)/$(BIN): $(OUT) $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o $@ $(SZ) $@ $(OUT): mkdir $@ .PHONY: disassembly load upload clean disassembly: $(OUT)/$(BIN) $(TOOLCHAIN)objdump -d $^ > $(OUT)/$(BIN).S debug: openocd -f board/st_nucleo_f4.cfg upload: openocd -f /board/st_nucleo_f4.cfg clean: -@rm -r $(OUT) ::: ## 使用 openOCD Debug 首先先進行編譯 ```shell $ make mkdir build arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -Iinc -O0 -Wall Src/syscalls.c -o build/syscalls.o arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -Iinc -O0 -Wall Src/myusart.c -o build/myusart.o arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -Iinc -O0 -Wall Src/main.c -o build/main.o arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -Iinc -O0 -Wall Src/startup.s -o build/startup.o arm-none-eabi-gcc build/syscalls.o build/myusart.o build/main.o build/startup.o -mcpu=cortex-m4 -specs=nano.specs -T Src/link.ld -lc -lm -lnosys -o build/main arm-none-eabi-size build/main text data bss dec hex filename 13360 120 1728 15208 3b68 build/main ``` 完成之後就進行 openOCD debug ```sehll $ make upload openocd -f /board/st_nucleo_f4.cfg Open On-Chip Debugger 0.12.0+dev-00150-g91bd43134 (2023-04-26-13:20) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD srst_only separate srst_nogate srst_open_drain connect_deassert_srst Info : Listening on port 6666 for tcl connections Info : Listening on port 4444 for telnet connections Info : clock speed 2000 kHz Info : STLINK V2J41M27 (API v2) VID:PID 0483:374B Info : Target voltage: 3.253997 Info : [stm32f4x.cpu] Cortex-M4 r0p1 processor detected Info : [stm32f4x.cpu] target has 6 breakpoints, 4 watchpoints Info : starting gdb server for stm32f4x.cpu on 3333 Info : Listening on port 3333 for gdb connections ``` 接著有幾種方式可以進行連接 client 端 1. GDB 2. telnet ### GDB 先啟動 GDB ```shell $ arm-none-eabi-gdb ``` 再進行 gdb 連線 ```sehll (gdb) target remote localhost:3333 ``` 再進行燒錄 ```shell (gdb) monitor flash write_image erase build/main ``` 或是 ```shell (gdb) load build/main ``` 再執行 ``` (gdb) monitor reset run ``` 接著列出比較常用的指令 - Reset 1. reset 2. reset run 3. reset halt 4. reset init - Halt: 暫停目前的執行 ```shell (gdb) monitor halt ``` - Resume: 結束 halt 並開始執行程式 ```shell (gdb) monitor resume ``` ### telnet 先進行安裝 ```shell $ sudo apt-get install telnet ``` 接著在終端機輸入 ```shell $ telnet localhost 4444 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Open On-Chip Debugger > ``` 接著所有的操作指令都與 GDB 的相同,只是前面不須加上 `monitor` 跳出 telnet 的方式為 **Ctrl + ]** ,接著出現 telnet> 的時候再輸入 `q` 按 Enter 即可 ## Usart + putty 原本的寫法 ```c ``` ## Usart + vputty 由於 python 的輸入與換行與 putty 不同,因此需要對 syscall.c 中的 `_write` 與 `_read` 做修改 - syscall.c ```c int _write(int file, char *ptr, int len) { MYUSART_SendData((uint8_t*)ptr, len); return len; } int _read(int file, char *ptr, int len) { for (int i = 0; i < len; i++) { *ptr = (char)MYUSART_ReceiveData(); if(*ptr++ == '\n') break; /* read Enter */ } return len; } ``` - vputty.py ```python import serial import sys ser = serial.Serial('/dev/ttyACM0', 38400) def read_serial(): return ser.readline().decode('utf-8').rstrip() def write_serial(data): ser.write(data) while True: # 讀取 serial 資料 received_data = read_serial() if received_data is not None: print(received_data) while True: data_to_send = sys.stdin.buffer.read(1) write_serial(data_to_send) if data_to_send == b'\n': print(read_serial()) break ``` 透過這樣就可以達到與 putty 相同的功能! 在此感謝 [Risheng1128](https://github.com/Risheng1128) 協助 debug :+1: ## 參考資料 - [STM32开发环境搭建(ARM-GCC)](https://microdynamics.github.io/1.%20Breeze%20Mini%E5%9B%9B%E8%BD%B4%E9%A3%9E%E8%A1%8C%E5%99%A8/2.2%20STM32%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA(ARM-GCC)/) - [(arm-none-eabi-gcc + GNU make + Makefile +嵌入式單片機)環境搭建篇(linux+windows)](https://blog.csdn.net/qq_35333978/article/details/110398968) - [Ubuntu中gcc-arm-none-eabi的安装、移除和版本切换](https://blog.csdn.net/qq_20016593/article/details/125343260)