# STM32 Development ###### tags: `stm32` [TOC] ## Get Library (Code Generator) ### Why - The reason we need to get libraries is because we want to simplify the process of setting register. - Functions can help us interacte with hardware. - Code generator can help us generate those function automatically and setup Makefile for us. ### Tools #### STM32 Library - Now There are 3 libraries for stm32: **HAL**, LL, SPL - There difference and comparison can be found [here](https://itexp.blog.csdn.net/article/details/54613202). - We choose HAL as our library because it is the easiest to use, despite its inefficiency. - [HAL library introduction](https://itexp.blog.csdn.net/article/details/55213616) - [HAL library official describtion for F4](https://www.st.com/resource/en/user_manual/dm00105879-description-of-stm32f4-hal-and-ll-drivers-stmicroelectronics.pdf) - [HAL library F4 source code](https://github.com/STMicroelectronics/STM32CubeF4/tree/master/Drivers/STM32F4xx_HAL_Driver) - [HAL library official describtion for F7](https://www.st.com/content/ccc/resource/technical/document/user_manual/45/27/9c/32/76/57/48/b9/DM00189702.pdf/files/DM00189702.pdf/jcr:content/translations/en.DM00189702.pdf) #### Code Generator - [**STM32CubeMX**](https://www.st.com/en/development-tools/stm32cubemx.html) - [Reference Video](https://youtu.be/FkqQpBqkSns) ### Further Study - Due the inefficiency of the HAL library, we can try using more low-level library like LL or directly use CMSIS, a library provide by ARM and it is the lowest library we can get. ## Editor - We are going to use **VSCode** as the primary editor because it has great intellisense and also we can integrate debugger into. The latter will be discuss in the [Flash](#Flash) & [Debug](#Debug) section. ### Configuring VScode - [Reference Video](https://youtu.be/jcy5TpbXfAY) #### c_cpp_properties.json ```json= { "env": { "arm_gcc_path": "/home/ben/stm32_tools/abe_build/builds/destdir/x86_64-pc-linux-gnu" }, "configurations": [ { "name": "Linux", "includePath": [ "${workspaceFolder}/Core/Inc", "${workspaceFolder}/Drivers/STM32F4xx_HAL_Driver/Inc", "${workspaceFolder}/Drivers/CMSIS/Include", "${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F4xx/Include", "${arm_gcc_path}/arm-none-eabi/**", "${arm_gcc_path}/include/**", "${arm_gcc_path}/lib/gcc/arm-none-eabi/11.2.1/include/**", "${arm_gcc_path}/lib/gcc/arm-none-eabi/11.2.1/include-fixed/**" ], "defines": [ "USE_HAL_DRIVER", "STM32F407xx" ], "compilerPath": "${arm_gcc_path}/bin/arm-none-eabi-g++", "cStandard": "gnu17", "cppStandard": "gnu++14", "intelliSenseMode": "linux-gcc-x64" } ], "version": 4 } ``` >Note: If you have install "Makefile Tools" extension and cannot configure, delete this line `"configurationProvider": "ms-vscode.makefile-tools"` in your "c_cpp_properties.json" file ## Compiler - There are several compiler for compiling ARM chips. Details can be found [here](https://blog.csdn.net/ZCShouCSDN/article/details/89553323). (other refer: https://youtu.be/imUiQkO9YHM?si=xaaTE8C6mMjiJRlM&t=142) ### GNU Arm Embedded Toolchain - We are going to use [**GNU Arm Embedded Toolchain**](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/downloads) as our compier tool chain. - The reason is because it is open source and free, and it matches the interface of gcc, which many of us are already familiar with. And also, it provides a good debugger tool, gdb, that can be integrade into many environment for debugging purpose. #### Download Precompile Executable - Look for keyword like `x86_64 Linux` and `arm-none-eabi)` #### Install from Source - If something go wrong and you cannot solve it, you can try install from source. This method has a lot of chance to solve most problems. - There is a instruction in the "release note". 1. `wget https://raw.githubusercontent.com/git/git/master/contrib/workdir/git-new-workdir` 2. `sudo mv git-new-workdir /usr/local/bin` 3. `sudo chmod +x /usr/local/bin/git-new-workdir ` 4. `git clone https://git.linaro.org/toolchain/abe.git` 5. `mkdir abe_build && cd abe_build` 6. `../abe/configure` (inside abe_build) - If there is any package you don't have, install it. 7. `wget https://developer.arm.com/-/media/Files/downloads/gnu/11.2-2022.02/manifest/gcc-arm-arm-none-eabi-abe-manifest.txt` - version dependent 9. `../abe/abe.sh --manifest gcc-arm-arm-none-eabi-abe-manifest.txt --build all` - dependend on the last step 11. The executable will be locate in `abe_builds/destdir/x86_64-pc-linux-gnu/bin/` 12. Edit `~/.bashrc` and put `export PATH=$PATH:~/<the directory you keep the gnu_arm_toolchain>/abe_build/builds/destdir/x86_64-pc-linux-gnu/bin ` into it. ### Makefile Adjustment to compile C++ 1. Add CPP_Source_file after C_Source_file ```cmake= CPP_SOURCES = \ Core/Src/main.cpp ``` 2. Change "binaries" section - add g++ complier and modify assembler ```cmake= ifdef GCC_PATH CC = $(GCC_PATH)/$(PREFIX)gcc #+++ CPP = $(GCC_PATH)/$(PREFIX)g++ #+++ AS = $(GCC_PATH)/$(PREFIX)g++ -x assembler-with-cpp CP = $(GCC_PATH)/$(PREFIX)objcopy SZ = $(GCC_PATH)/$(PREFIX)size else CC = $(PREFIX)gcc #+++ CPP = $(PREFIX)g++ #+++ AS = $(PREFIX)g++ -x assembler-with-cpp CP = $(PREFIX)objcopy SZ = $(PREFIX)size endif ``` 3. Add CPP_Includes & CPP_Flags ```cmake= ... # CPP includes #+++ CPP_INCLUDES = $(C_INCLUDES) # compile gcc flags ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections CFLAGS += $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections #+++ CPPFLAGS = $(MCU) $(C_DEFS) $(CPP_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections ifeq ($(DEBUG), 1) CFLAGS += -g -gdwarf-2 endif #+++ ifeq ($(DEBUG), 1) #+++ CPPFLAGS += -g -gdwarf-2 #+++ endif # Generate dependency information CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" #+++ CPPFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" ``` 4. Add Linker Flag - "-specs=**nosys**.specs" ```cmake= LDFLAGS = $(MCU) -specs=nano.specs -specs=nosys.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections ``` - If there is any error regarding to "lc_nano", delete the `-specs=nano.specs` line > https://blog.csdn.net/zoomdy/article/details/100727279 > The source-install compiler seems to be lack of nano library. 5. Add Object file ```cmake= OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(CPP_SOURCES:.cpp=.o))) vpath %.cpp $(sort $(dir $(CPP_SOURCES))) ``` 7. Add and modify compile command ```cmake= $(BUILD_DIR)/%.o: %.cpp Makefile | $(BUILD_DIR) $(CPP) -c $(CPPFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.cpp=.lst)) $< -o $@ ``` ```cmake= $(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile $(CPP) $(OBJECTS) $(LDFLAGS) -o $@ ``` #### Makefile Example ```cmake= ########################################################################################################################## # File automatically-generated by tool: [projectgenerator] version: [3.16.0] date: [Sun May 08 20:39:50 CST 2022] ########################################################################################################################## # ------------------------------------------------ # Generic Makefile (based on gcc) # # ChangeLog : # 2017-02-10 - Several enhancements + project update mode # 2015-07-22 - first version # ------------------------------------------------ ###################################### # target ###################################### TARGET = C_Test1 ###################################### # building variables ###################################### # debug build? DEBUG = 1 # optimization OPT = -Og ####################################### # paths ####################################### # Build path BUILD_DIR = build ###################################### # source ###################################### # C sources C_SOURCES = \ Core/Src/gpio.c \ Core/Src/usart.c \ Core/Src/stm32f4xx_it.c \ Core/Src/stm32f4xx_hal_msp.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c \ Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_exti.c \ Core/Src/system_stm32f4xx.c # CPP sources CPP_SOURCES = \ Core/Src/main.cpp # ASM sources ASM_SOURCES = \ startup_stm32f407xx.s ####################################### # binaries ####################################### PREFIX = arm-none-eabi- # The gcc compiler bin path can be either defined in make command via GCC_PATH variable (> make GCC_PATH=xxx) # either it can be added to the PATH environment variable. ifdef GCC_PATH CC = $(GCC_PATH)/$(PREFIX)gcc CPP = $(GCC_PATH)/$(PREFIX)g++ AS = $(GCC_PATH)/$(PREFIX)g++ -x assembler-with-cpp CP = $(GCC_PATH)/$(PREFIX)objcopy SZ = $(GCC_PATH)/$(PREFIX)size else CC = $(PREFIX)gcc CPP = $(PREFIX)g++ AS = $(PREFIX)g++ -x assembler-with-cpp CP = $(PREFIX)objcopy SZ = $(PREFIX)size endif HEX = $(CP) -O ihex BIN = $(CP) -O binary -S ####################################### # CFLAGS ####################################### # cpu CPU = -mcpu=cortex-m4 # fpu FPU = -mfpu=fpv4-sp-d16 # float-abi FLOAT-ABI = -mfloat-abi=hard # mcu MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI) # macros for gcc # AS defines AS_DEFS = # C defines C_DEFS = \ -DUSE_HAL_DRIVER \ -DSTM32F407xx # AS includes AS_INCLUDES = # C includes C_INCLUDES = \ -ICore/Inc \ -IDrivers/STM32F4xx_HAL_Driver/Inc \ -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy \ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include \ -IDrivers/CMSIS/Include # CPP includes CPP_INCLUDES = $(C_INCLUDES) # compile gcc flags ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections CFLAGS += $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections CPPFLAGS = $(MCU) $(C_DEFS) $(CPP_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections ifeq ($(DEBUG), 1) CFLAGS += -g -gdwarf-2 endif ifeq ($(DEBUG), 1) CPPFLAGS += -g -gdwarf-2 endif # Generate dependency information CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" ####################################### # LDFLAGS ####################################### # link script LDSCRIPT = STM32F407VETx_FLASH.ld # libraries LIBS = -lc -lm -lnosys LIBDIR = LDFLAGS = $(MCU) -specs=nano.specs -specs=nosys.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections # default action: build all all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin ####################################### # build the application ####################################### # list of objects OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o))) vpath %.c $(sort $(dir $(C_SOURCES))) OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(CPP_SOURCES:.cpp=.o))) vpath %.cpp $(sort $(dir $(CPP_SOURCES))) # list of ASM program objects OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o))) vpath %.s $(sort $(dir $(ASM_SOURCES))) $(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) $(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@ $(BUILD_DIR)/%.o: %.cpp Makefile | $(BUILD_DIR) $(CPP) -c $(CPPFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.cpp=.lst)) $< -o $@ $(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR) $(AS) -c $(CFLAGS) $< -o $@ $(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile $(CPP) $(OBJECTS) $(LDFLAGS) -o $@ $(SZ) $@ $(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR) $(HEX) $< $@ $(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR) $(BIN) $< $@ $(BUILD_DIR): mkdir $@ ####################################### # clean up ####################################### clean: -rm -fR $(BUILD_DIR) ####################################### # dependencies ####################################### -include $(wildcard $(BUILD_DIR)/*.d) ####################################### # flash ####################################### flash: $(BUILD_DIR)/$(TARGET).bin STM32_Programmer_CLI -c port=swd -w $< 0x08000000 -v -rst # *** EOF *** ``` ### Integrate into VSCode - tasks.json ```json= { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "Build", "group": "build", "type": "shell", "command": "make", "args": ["all", "-j4"] }, { "label": "Clean", "group": "build", "type": "shell", "command": "make", "args": ["clean"] } ] } ``` ## Flash ### STM32CubeProg - ST provide a program software: [**STM32CubeProg**](https://www.st.com/en/development-tools/stm32cubeprog.html) 1. Use to flash executable 2. check ROM memory 3. Update firmware - [ST LINK is not in the DFU mode plesse restart it](https://www.796t.com/content/1545114008.html) >1. 拔掉stlink >2. 插上stlink >3. 不要點其他的,直接點選ST-LINK->Firmware update - [reference video](https://youtu.be/CI1BcIN7qC4) ### STM32ProgrammerCLI - We are going to use its command line tool to integrate it into VSCode. - Edit `~/.bashrc` and put `export PATH=$PATH:~/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin` into it. - [Official Manual](https://www.st.com/resource/en/user_manual/dm00403500-stm32cubeprogrammer-software-description-stmicroelectronics.pdf) #### Usage 1. Command: `STM32_Programmer_CLI -c port=swd -w <executable> 0x08000000 -v -rst` - -c: connect to the chip - -w: write to the chip (0x08000000 is the starting address of the ROM of STM32) - -v: verify the program - -rst: automatically reset after flash 2. Modify Makefile - Add make command ```cmake= ####################################### # flash ####################################### flash: $(BUILD_DIR)/$(TARGET).bin STM32_Programmer_CLI -c port=swd -w $< 0x08000000 -v -rst ``` 3. Integrate into VSCode - add the following code into "tasks" block in task.json. ```json= { "label": "Flash", "group": "build", "type": "shell", "command": "make", "args": ["flash", "-j4"] } ``` ## Debug ### Install "Cortex-Debug" extension - [Github Page](https://github.com/Marus/cortex-debug) ### Install "STM32CubeIDE" to get ST-LINK GDB server - [Official Website](https://www.st.com/en/development-tools/stm32cubeide.html) ### Add `launch.json` file - add executable - select servertype as "stlink" - select interface as "swd" - Example ```json= { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "ST-Link Debug", "cwd": "${workspaceFolder}", "executable": "./build/CPP_Test.elf", "device": "STM32F407VE", "request": "launch", "type": "cortex-debug", "runToEntryPoint": "main", "servertype": "stlink", "interface": "swd" } ] } ``` #### Bug Fixing - If you cannot launch the debugger sucefully within VSCode, try run `arm-none-eabi-gdb --version` on your terminal to check whether the gdb is working properly. - If encouter the error regarding to the lack of "libncursesw5", try `sudo apt install libncursesw5 libncursesw5-dev` to install the library. ### Further Study - It seems that J-link & OpenOCD are other good tools but I haven't have the time to take a good look at it. ## Other Option There are other option for us to program stm32, for example: 1. Keil uVision IDE 2. STM32 Cube IDE