├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── remote ├── gdbinit └── openocd.cfg └── src ├── CMakeLists.txt ├── mainloop.cpp └── stm32cube ├── CMakeLists.txt ├── stm_config.cpp ├── stm_config.h └── stm_interrupts.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ### PROJECT ################################################################### 2 | cmake_minimum_required(VERSION 3.1) 3 | enable_language(ASM) # needed for startup_*.s files 4 | project(stm32) 5 | 6 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # optional, creates compile_commands.json 7 | 8 | 9 | ### COMPILATION OPTIONS ####################################################### 10 | ## Linker 11 | # use newlib-nano - minimal implementation of libc that removes unneeded features 12 | set(LINKER_SPECS "--specs=nano.specs --specs=nosys.specs") 13 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINKER_SPECS}") 14 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_SPECS}") 15 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_SPECS}") 16 | 17 | ## Compiler 18 | # disable c++ exceptions and runtime type info (they're too expensive for embedded) 19 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti") 20 | 21 | 22 | ### TARGET #################################################################### 23 | add_executable(${CMAKE_PROJECT_NAME} "") # add sources with target_sources() 24 | stm32_set_target_properties(${CMAKE_PROJECT_NAME}) 25 | stm32_add_hex_bin_targets(${CMAKE_PROJECT_NAME}) 26 | stm32_print_size_of_targets(${CMAKE_PROJECT_NAME}) 27 | 28 | 29 | ### EXTERNAL SOURCES ########################################################## 30 | # Hardware abstraction libraries - CMSIS and STM32HAL 31 | find_package(CMSIS REQUIRED) 32 | find_package(STM32HAL COMPONENTS 33 | gpio REQUIRED 34 | ) 35 | target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${CMSIS_INCLUDE_DIRS} PUBLIC ${STM32HAL_INCLUDE_DIR}) 36 | target_sources(${CMAKE_PROJECT_NAME} PUBLIC ${CMSIS_SOURCES} PUBLIC ${STM32HAL_SOURCES}) 37 | 38 | 39 | ### INTERNAL SOURCES ########################################################## 40 | add_subdirectory(src) 41 | 42 | 43 | ### Libraries ################################################################# 44 | 45 | # ## ARM math library 46 | # # linking with prebuilt original library from https://github.com/ARM-software/CMSIS/tree/master/CMSIS/Lib/GCC 47 | # # prebuild version needs hardware float abi so... (you could compile from source too, but it takes some time - about 300 source files) 48 | # string(REPLACE "-mfloat-abi=softfp" "-mfloat-abi=hard" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") 49 | # string(REPLACE "-mfloat-abi=softfp" "-mfloat-abi=hard" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 50 | # string(REPLACE "-mfloat-abi=softfp" "-mfloat-abi=hard" CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS}") 51 | # string(REPLACE "-mfloat-abi=softfp" "-mfloat-abi=hard" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") 52 | # string(REPLACE "-mfloat-abi=softfp" "-mfloat-abi=hard" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") 53 | # string(REPLACE "-mfloat-abi=softfp" "-mfloat-abi=hard" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") 54 | # target_link_libraries(${CMAKE_PROJECT_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/path/to/prebuilt/libarm_cortexM4lf_math.a") 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 yendreij 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | ### Makefile for building cmake projects for STM32 3 | ############################################################################### 4 | 5 | ### STM32 6 | # STM32_FAMILY is not needed if STM32_CHIP is provided 7 | # default TARGET_TRIPLET = arm-none-eabi is ok 8 | STM32_CHIP = STM32F407VGT6 9 | STM32Cube_DIR = $(HOME)/opt/stm32cubemx/repo/STM32Cube_FW_F4_V1.18.0 10 | STM32_MODULE_PATH = $(HOME)/opt/stm32-cmake/cmake 11 | 12 | ### CMake 13 | CMAKE_BUILD_TYPE = Debug 14 | CMAKE_INSTALL_PREFIX = /tmp/install 15 | BUILD_DIR = ./build 16 | NJOBS = 3 17 | # "Ninja" for ninja, "Unix Makefiles" for make 18 | CMAKE_GENERATOR = Ninja 19 | # additional flags 20 | CMAKE_FLAGS = -DCMAKE_EXPORT_COMPILE_COMMANDS=1 21 | 22 | ### Remote device 23 | # copy the needed script from /usr/share/openocd/scripts/ to project directory 24 | # as for now it has to be 'remote/openocd.cfg' as it is hard-coded into gdbinit 25 | OPENOCD_CONFIG = remote/openocd.cfg 26 | # this is the cmake project name 27 | EXECUTABLE_PATH = $(BUILD_DIR)/stm32 28 | # commands to start remote debugging with gdb through openocd 29 | GDB_INIT = remote/gdbinit 30 | 31 | ############################################################################### 32 | ############################################################################### 33 | 34 | CMAKE_TOOLCHAIN_FILE = $(STM32_MODULE_PATH)/gcc_stm32.cmake 35 | CMAKE_FLAGS += \ 36 | -DSTM32_CHIP="$(STM32_CHIP)" \ 37 | -DSTM32Cube_DIR="$(STM32Cube_DIR)" \ 38 | -DCMAKE_BUILD_TYPE="$(CMAKE_BUILD_TYPE)" \ 39 | -DCMAKE_INSTALL_PREFIX="$(CMAKE_INSTALL_PREFIX)" \ 40 | -DCMAKE_MODULE_PATH="$(STM32_MODULE_PATH)" \ 41 | -DCMAKE_TOOLCHAIN_FILE="$(CMAKE_TOOLCHAIN_FILE)" \ 42 | 43 | # all targets are just commands (no specific files produced) 44 | .PHONY: build cmake clean cleanall upload gdb gdbgui rtags rtagsclear 45 | 46 | ### Main targets 47 | # only 'cmake' has to be called explicitly (if not using GLOB 48 | # it is sufficient to use 'cmake' only once at project generation) 49 | build: 50 | cmake --build $(BUILD_DIR) 51 | 52 | cmake: 53 | cmake -E make_directory $(BUILD_DIR) 54 | cd $(BUILD_DIR) && cmake $(CMAKE_FLAGS) -G "$(CMAKE_GENERATOR)" .. 55 | 56 | clean: 57 | cmake --build $(BUILD_DIR) --target clean 58 | 59 | cleanall: 60 | rm -rf $(BUILD_DIR)/* 61 | 62 | ### Connections 63 | # udev rule needed (or run with sudo) 64 | # paths are quite hard-coded 65 | upload: build 66 | openocd -f $(OPENOCD_CONFIG) -c "program $(EXECUTABLE_PATH) verify reset exit" 67 | 68 | gdb: build 69 | arm-none-eabi-gdb --command=$(GDB_INIT) $(EXECUTABLE_PATH) 70 | killall openocd || true 71 | 72 | # for https://github.com/cs01/gdbgui (but has problems with remote operation) 73 | gdbgui: build 74 | gdbgui --gdb=arm-none-eabi-gdb --gdb_cmd_file=$(GDB_INIT) $(EXECUTABLE_PATH) 75 | killall openocd || true 76 | 77 | killopenocd: 78 | killall openocd || true 79 | 80 | ### RTags: feed compile_commands.json to RDM deamon 81 | rtags: 82 | rc -J $(BUILD_DIR) 83 | 84 | rtagsclear: 85 | rc --clear 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # STM32Cube + CMake + C++ 2 | 3 | This is a template for creating projects using CMake with STM32CubeMX generation tool. It allows to use C++, but can be easily modified for pure C. For the toolchain.cmake you need to download [stm32-cmake](https://github.com/ObKo/stm32-cmake) (there's a straightforward tutorial on its usage too). 4 | 5 | Above all it is just a code snippet. You have to understand it and modify the way you need. Use it however you want. 6 | 7 | ## Usage 8 | 9 | First generate the Cube project in the directory src/stm32cube and set the project name in CMakeLists.txt there. Cube code generation options don't matter, but if not needed then set toolchain to "Other Toolchains" and don't copy libraries. 10 | 11 | Then do the standard cmake project generation & build, providing the definitions ([stm32-cmake](https://github.com/ObKo/stm32-cmake) for more details and options): 12 | 13 | * *CMAKE_BUILD_TYPE* --- release/debug build 14 | * *STM32_CHIP* --- the MCU used 15 | * *CMAKE_MODULE_PATH* --- path to the stm32-cmake module 16 | * *CMAKE_TOOLCHAIN_FILE* --- path to the gcc_stm32.cmake toolchain file 17 | * *STM32Cube_DIR* --- directory with used STM32Cube repository 18 | 19 | **Example CMake usage:** 20 | ~~~bash 21 | > mkdir build/ 22 | > cd build/ 23 | > cmake 24 | -DCMAKE_BUILD_TYPE=Debug 25 | -DSTM32_CHIP=STM32F407VGT6 26 | -DCMAKE_MODULE_PATH=/opt/stm32-cmake/cmake/ 27 | -DCMAKE_TOOLCHAIN_FILE=/opt/stm32-cmake/cmake/gcc_stm32.cmake 28 | -DSTM32Cube_DIR=/opt/STM32Cube_FW_F4_V1.17.0 29 | .. 30 | ~~~ 31 | 32 | **For linux you may use the Makefile provided** (but it may be helpful anyway). 33 | 34 | ## Remote operation 35 | 36 | Copy the necessary openocd configuration from default scripts (*/usr/share/openocd/scripts/*) and modify if needed. See example of remote operations in *Makefile*. 37 | 38 | ## Bridge between STM32Cube and user code 39 | 40 | To make the program a little less "hard-coded" you may use the example structure provided in *src/stm32cube* (*stm_config.h, stm_config.cpp, stm_interrupts.cpp*). The main idea is to group everything in one place so it can be easily modified. 41 | -------------------------------------------------------------------------------- /remote/gdbinit: -------------------------------------------------------------------------------- 1 | ### PLACE FOR USER COMMANDS 2 | 3 | 4 | 5 | ### CONNECTING TO REMOTE TARGET 6 | # connect by openocd, log openocd output to a logfile 7 | # path to openocd.cfg is hard-coded and requires gdb to be run from appropriate directory 8 | target remote | openocd -f ./remote/openocd.cfg -c "gdb_port pipe; log_output /tmp/openocd.log" 9 | # reset processor and halt it ('monitor' is used to pass commands to openocd) 10 | monitor reset halt 11 | # upload binary 12 | load 13 | # set breakpoint on main() 14 | break main 15 | # continue until main() 16 | # continue 17 | -------------------------------------------------------------------------------- /remote/openocd.cfg: -------------------------------------------------------------------------------- 1 | # This is an STM32F4 discovery board with a single STM32F407VGT6 chip. 2 | # http://www.st.com/internet/evalboard/product/252419.jsp 3 | 4 | source [find interface/stlink-v2.cfg] 5 | 6 | transport select hla_swd 7 | 8 | # increase working area to 64KB 9 | set WORKAREASIZE 0x10000 10 | 11 | source [find target/stm32f4x.cfg] 12 | 13 | reset_config srst_only 14 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # STM32Cube generated code 2 | add_subdirectory(stm32cube) 3 | 4 | # Main loop (to be added in main.c) 5 | target_sources(${CMAKE_PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/mainloop.cpp) 6 | 7 | # Project sources 8 | # add_subdirectory(yoursubdir1) 9 | # add_subdirectory(yoursubdir2) 10 | -------------------------------------------------------------------------------- /src/mainloop.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This is the "second main". Here it is possible to use regular C++. 3 | It should be called in main.c to take over controll for the rest of program, 4 | for example: 5 | 6 | / * USER CODE BEGIN 2 * / 7 | extern void mainloop(); 8 | mainloop(); 9 | / * USER CODE END 2 * / 10 | 11 | */ 12 | 13 | #include "stm_config.h" 14 | 15 | 16 | extern "C" void mainloop() { 17 | 18 | while (1) { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/stm32cube/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Adds sources generated by STM32CubeMX 2 | # the "Project Location" should be this directory 3 | # change this to "Project Name" 4 | set(STM32CUBEMX_PROJECT_NAME "Example_project") 5 | 6 | 7 | # glob all the files regardless of how many were generated 8 | # because of globbing you hve to "touch" this file when there are new files 9 | file(GLOB_RECURSE STM32CUBE_SOURCES 10 | "${CMAKE_CURRENT_SOURCE_DIR}/${STM32CUBEMX_PROJECT_NAME}/Inc/*.h" 11 | "${CMAKE_CURRENT_SOURCE_DIR}/${STM32CUBEMX_PROJECT_NAME}/Src/*.c" 12 | ) 13 | 14 | # remove any system_stm32*.c (e.g. system_stm32f4xx.c) from sources, as it is not 15 | # needed when using stm32-cmake (if included, gets compiled twice - linkage error) 16 | string(REGEX REPLACE 17 | "\/[^;]*system_stm32....\.c" # "/" + min num of any non-";" chars + the filename 18 | "" # remove it 19 | STM32CUBE_SOURCES "${STM32CUBE_SOURCES}" 20 | ) 21 | 22 | 23 | # add STM32Cube includes and sources 24 | target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${STM32CUBEMX_PROJECT_NAME}/Inc/) 25 | target_sources(${CMAKE_PROJECT_NAME} PUBLIC ${STM32CUBE_SOURCES}) 26 | 27 | # add "bridge" files 28 | target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC .) 29 | target_sources(${CMAKE_PROJECT_NAME} 30 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/stm_config.h 31 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/stm_config.cpp 32 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/stm_interrupts.cpp 33 | ) 34 | -------------------------------------------------------------------------------- /src/stm32cube/stm_config.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Here are definitions of needed peripherals. 3 | This file connects variables from Cube's C code with application's C++ code. 4 | Add any needed information about used peripherals in comments so that it is 5 | easy to recreate/modify them in Cube. 6 | */ 7 | 8 | #include "stm_config.h" 9 | 10 | 11 | // *** PERIPHERALS ************************************************************ 12 | // Only assign variables or write VERY short functions 13 | namespace stm { 14 | 15 | 16 | /* EXAMPLE: 17 | // Delay Timer: 18 | // TIM10 (APB2), set prescaler to get f = 1MHz, later only change period 19 | extern "C" TIM_HandleTypeDef htim10; 20 | TIM_HandleTypeDef &delayTimer = htim10; 21 | */ 22 | 23 | 24 | } // namespace stm 25 | -------------------------------------------------------------------------------- /src/stm32cube/stm_config.h: -------------------------------------------------------------------------------- 1 | #ifndef __STM_CONFIG__H 2 | #define __STM_CONFIG__H 3 | 4 | /* 5 | Interface for uC 6 | To be included in all other files that use uC. 7 | 8 | Wraps code generated by STM32Cube to later work independently of choosen 9 | pins/peripherals. It provides functions/variables with names corresponding 10 | to functionalities used in the application. When changing e.g. peripheral 11 | numbers, you can change only the corresponding .cpp file. 12 | */ 13 | 14 | 15 | /* Include proper HAL header */ 16 | #include "stm32f4xx_hal.h" 17 | #include "main.h" 18 | 19 | /* Example of ARM Math Library usage 20 | #ifndef __FPU_PRESENT 21 | #define __FPU_PRESENT 1 22 | #endif 23 | #define ARM_MATH_CM4 24 | #include "arm_math.h" 25 | */ 26 | 27 | // *** PERIPHERALS ************************************************************ 28 | // Here are declarations of the needed variables with meaningful names. 29 | // If there is any need for a variable/constant with fixed name (e.g htim2 30 | // or DMA2_Stream2_IRQn), then create an alias here and use the alias later. 31 | namespace stm { 32 | 33 | 34 | /* EXAMPLE 35 | // delay timer for counting microsecond delays 36 | extern TIM_HandleTypeDef &delayTimer; 37 | */ 38 | 39 | } // namespace stm 40 | 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/stm32cube/stm_interrupts.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Callback handlers. 3 | It is probably the easiest to store all callback functions in one file. 4 | */ 5 | 6 | /* Includes (or declare functions with 'extern' if it's prefered) */ 7 | #include "stm_config.h" 8 | // #include "other_header.h" 9 | 10 | 11 | extern "C" { 12 | 13 | 14 | /* Example callback: 15 | void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { 16 | if (htim->Instance == stm::delayTimer.Instance) 17 | onTimPeriodElapsed(); 18 | } 19 | */ 20 | 21 | 22 | /* 23 | Note about Priority in ARM Cortex-M 24 | 25 | High priority means low priority number, low priority means high number. 26 | In call to NVIC_SetPriority(IRQn, priority) write priority "just as is" (don't shift the value left). 27 | 28 | There are two parts of priority: preemption priority and subpriority, 29 | available priority bits are divided between these two. 30 | To do so use function NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_X). 31 | 32 | Preemption priority allows for preempting interrupts, which means that 33 | interrupt with higher preempt priority can interrupt the one with lower. 34 | Subpriority is used only for choosing which of pending interrupts to process next. 35 | 36 | Default division in HAL_Init is NVIC_PRIORITYGROUP_4, so 4 bits for preemption, 0 bits for subpriority. 37 | Default SysTick priority in HAL_Init is TICK_INT_PRIORITY from *_hal_conf.h, 38 | that is set for preemption priority (default value 0x0f - lowest priprity). 39 | */ 40 | } 41 | --------------------------------------------------------------------------------