---
# System prepended metadata

title: cm4hbm
tags: [' stm32f429zit6', stm32]

---

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