---
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)