Skip to content

Commit 8c75d5b

Browse files
committed
Initial import
1 parent 07c53a8 commit 8c75d5b

10 files changed

+340
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
_build

CMakeLists.txt

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
cmake_minimum_required(VERSION 3.0)
2+
3+
# project name
4+
project(bare_metal)
5+
6+
# select linker script
7+
set(LINKER_SCRIPT "src/startup/stm32f031x6.ld")
8+
9+
add_compile_options(
10+
-Wall
11+
-pedantic
12+
-Wextra
13+
-fstrict-volatile-bitfields
14+
-ffunction-sections
15+
)
16+
17+
# define compiler options more info in: tools/gcc-arm/share/doc/gcc-arm-none-eabi/readme.txt
18+
set(CPU_OPTIONS "${CPU_OPTIONS} -mthumb -mcpu=cortex-m0")
19+
20+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CPU_OPTIONS}")
21+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
22+
23+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CPU_OPTIONS}")
24+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
25+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fms-extensions")
26+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
27+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
28+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-use-cxa-atexit")
29+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics")
30+
31+
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T ${LINKER_SCRIPT}")
32+
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nostartfiles")
33+
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nostdlib")
34+
35+
# optimizations (-O0 -O1 -O2 -O3 -Os -Ofast -Og -flto)
36+
set(OPTIMIZATION_DEBUG "-Og")
37+
set(OPTIMIZATION_RELEASE "-O2 -flto")
38+
39+
set(CMAKE_C_FLAGS_DEBUG "${OPTIMIZATION_DEBUG} -g -DDEBUG")
40+
set(CMAKE_CXX_FLAGS_DEBUG "${OPTIMIZATION_DEBUG} -g -DDEBUG")
41+
set(CMAKE_C_FLAGS_RELEASE "${OPTIMIZATION_RELEASE}")
42+
set(CMAKE_CXX_FLAGS_RELEASE "${OPTIMIZATION_RELEASE}")
43+
44+
# select build type, can be Debug, Release
45+
set(CMAKE_BUILD_TYPE Debug)
46+
47+
include_directories(
48+
${CMAKE_SOURCE_DIR}
49+
)
50+
51+
link_directories(
52+
${CMAKE_SOURCE_DIR}
53+
)
54+
55+
# list of modules to build final firmware (without extension .c or .cpp)
56+
add_executable(${PROJECT_NAME}.elf
57+
src/main
58+
src/startup/startup
59+
src/startup/stack
60+
src/startup/handlers_cm
61+
)
62+
63+
include("cmake/flash.cmake")

README.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Bare metal example for ARM cortex-M using C++
2+
3+
This example show how to write simple startup code in C++ and use Cmake for building.
4+
Presented example is very simple "Hello blinky" program for STM32F0xx which blink LED connected to GPIO A15 and blink on maximum speed (no delay or timers are used).
5+
6+
## C++ advantages for embedded
7+
8+
- Possible to use latest standards (C++17)
9+
- Very small compiled code (comparable with C)
10+
11+
### Limitations, or not recommended C++ features in embedded
12+
13+
- Do not use new and delete (unless you define your own on heap or in static array - memory pool)
14+
- No exceptions are allowed and also no components which using exceptions (like most of `std::`, ...)
15+
- no RTTI, ..
16+
17+
*Everything from this is also possible to use, but then is necessary to implement some stub functions for heap management and basic IO for `newlib`, after program can be much bigger in memory (often more than 80KB).*
18+
19+
## Code structure
20+
21+
- `CmakeLists.txt` is Cmake building script
22+
- `src` folder contain application code
23+
- `src/startup` folder contain all startup code + linker script
24+
- `cmake/arm-none-eabi.cmake` is platform file which define compilers and configuration of cmake for this project
25+
26+
## Dependencies
27+
28+
- arm-gcc for embedded [download here](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads)
29+
- [cmake](https://cmake.org/download/)
30+
31+
- no need any dependencies like CMSIS library for building of this example.
32+
33+
## Building
34+
35+
```
36+
mkdir _build
37+
cd _build
38+
cmake -D CMAKE_TOOLCHAIN_FILE=../cmake/arm-none-eabi.cmake ..
39+
make
40+
```
41+
42+
or use `make bin`, `srec`, `hex`, `flash`, ...

cmake/arm-none-eabi.cmake

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
set(CMAKE_SYSTEM_NAME Generic)
2+
set(CMAKE_SYSTEM_PROCESSOR arm)
3+
4+
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
5+
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
6+
set(CMAKE_C_COMPILER_ID GNU)
7+
set(CMAKE_CXX_COMPILER_ID GNU)
8+
set(CMAKE_C_COMPILER_FORCED TRUE)
9+
set(CMAKE_CXX_COMPILER_FORCED TRUE)

cmake/flash.cmake

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# cmake script for generating binary and hex format and for flashing
2+
3+
add_custom_command(OUTPUT ${PROJECT_NAME}.srec
4+
DEPENDS ${PROJECT_NAME}.elf
5+
COMMAND ${CMAKE_OBJCOPY} -Osrec ${PROJECT_NAME}.elf ${PROJECT_NAME}.srec
6+
)
7+
add_custom_command(OUTPUT ${PROJECT_NAME}.hex
8+
DEPENDS ${PROJECT_NAME}.elf
9+
COMMAND ${CMAKE_OBJCOPY} -Oihex ${PROJECT_NAME}.elf ${PROJECT_NAME}.hex
10+
)
11+
add_custom_command(OUTPUT ${PROJECT_NAME}.bin
12+
DEPENDS ${PROJECT_NAME}.elf
13+
COMMAND ${CMAKE_OBJCOPY} -Obinary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin
14+
)
15+
add_custom_command(OUTPUT ${PROJECT_NAME}.lst
16+
DEPENDS ${PROJECT_NAME}.elf
17+
COMMAND ${CMAKE_OBJDUMP} -S ${PROJECT_NAME}.elf > ${PROJECT_NAME}.lst
18+
)
19+
add_custom_command(OUTPUT ${PROJECT_NAME}.sym
20+
DEPENDS ${PROJECT_NAME}.elf
21+
COMMAND ${CMAKE_NM} -C -l -n -S ${PROJECT_NAME}.elf > ${PROJECT_NAME}.sym
22+
)
23+
24+
add_custom_target(srec
25+
DEPENDS ${PROJECT_NAME}.srec
26+
)
27+
add_custom_target(hex
28+
DEPENDS ${PROJECT_NAME}.hex
29+
)
30+
add_custom_target(bin
31+
DEPENDS ${PROJECT_NAME}.bin
32+
)
33+
add_custom_target(lst
34+
DEPENDS ${PROJECT_NAME}.lst
35+
)
36+
add_custom_target(sym
37+
DEPENDS ${PROJECT_NAME}.sym
38+
)
39+
add_custom_target(flash
40+
pystlink flash:erase:verify:${PROJECT_NAME}.srec
41+
DEPENDS ${PROJECT_NAME}.srec
42+
)

src/main.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// define used registers
2+
#define RCC_AHB1 *(volatile unsigned *)(0x40021014)
3+
#define GPIOA_MODER *(volatile unsigned *)(0x48000000)
4+
#define GPIOA_BSRR *(volatile unsigned *)(0x48000018)
5+
6+
// application
7+
void main_app() {
8+
RCC_AHB1 |= 1 << 17; // enable clock for GPIOA
9+
GPIOA_MODER |= 1 << (15 * 2); // set output on GPIOA.P15
10+
while (true) {
11+
GPIOA_BSRR = 1 << 15; // set output on GPIOA.P15
12+
GPIOA_BSRR = 1 << (15 + 16); // clear output on GPIOA.P15
13+
}
14+
}

src/startup/handlers_cm.cpp

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// handlers definition code
2+
3+
// undefined handler point to this function, this stop MCU
4+
// this function name must by not mangled, so must be C
5+
extern "C" void __stop() { while (true); }
6+
7+
// handlers for cortex-M core
8+
// these handler are 'weak' and can be overwritten by non-week function
9+
// default is __stop() function
10+
__attribute__((weak, alias("__stop"))) void RESET_handler();
11+
__attribute__((weak, alias("__stop"))) void NMI_handler();
12+
__attribute__((weak, alias("__stop"))) void HARDFAULT_handler();
13+
__attribute__((weak, alias("__stop"))) void MEMMANAGE_handler();
14+
__attribute__((weak, alias("__stop"))) void BUSFAULT_handler();
15+
__attribute__((weak, alias("__stop"))) void USAGEFAULT_handler();
16+
__attribute__((weak, alias("__stop"))) void SVCALL_handler();
17+
__attribute__((weak, alias("__stop"))) void DEBUGMONITOR_handler();
18+
__attribute__((weak, alias("__stop"))) void PENDSV_handler();
19+
__attribute__((weak, alias("__stop"))) void SYSTICK_handler();
20+
21+
// dummy handler
22+
__attribute__((weak, alias("__stop"))) void DUMMY_handler();
23+
24+
// vector table for handlers
25+
__attribute__((section(".vectors"), used)) void (*isr_vectors[])() = {
26+
RESET_handler,
27+
NMI_handler,
28+
HARDFAULT_handler,
29+
MEMMANAGE_handler,
30+
BUSFAULT_handler,
31+
USAGEFAULT_handler,
32+
DUMMY_handler,
33+
DUMMY_handler,
34+
DUMMY_handler,
35+
DUMMY_handler,
36+
SVCALL_handler,
37+
DEBUGMONITOR_handler,
38+
DUMMY_handler,
39+
PENDSV_handler,
40+
SYSTICK_handler,
41+
};

src/startup/stack.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// stack definition code
2+
3+
// top of stack
4+
extern unsigned _stacktop;
5+
6+
// initial stack pointer is first address of program
7+
__attribute__((section(".stack"), used)) unsigned *stack_init = &_stacktop;

src/startup/startup.cpp

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// very simple startup code with definition of handlers for all cortex-m cores
2+
3+
// location of these variables is defined in linker script
4+
extern unsigned _data_load;
5+
6+
extern unsigned _data_start;
7+
extern unsigned _data_end;
8+
9+
extern unsigned _bss_start;
10+
extern unsigned _bss_end;
11+
12+
extern unsigned _init_array_start;
13+
extern unsigned _init_array_end;
14+
15+
extern unsigned _fini_array_start;
16+
extern unsigned _fini_array_end;
17+
18+
// main application
19+
extern void main_app();
20+
21+
// reset handler
22+
void RESET_handler() {
23+
unsigned *src, *dst;
24+
25+
// copy data
26+
src = &_data_load;
27+
dst = &_data_start;
28+
while (dst < &_data_end) {
29+
*dst++ = *src++;
30+
}
31+
32+
// zero bss
33+
dst = &_bss_start;
34+
while (dst < &_bss_end) {
35+
*dst++ = 0;
36+
}
37+
38+
// get current stack pointer
39+
register unsigned *msp_reg;
40+
__asm__("mrs %0, msp\n" : "=r" (msp_reg) );
41+
42+
// fill unused SRAM
43+
dst = &_bss_end;
44+
while (dst < msp_reg) {
45+
*dst++ = 0x55555555;
46+
}
47+
48+
// call constructors for static instances
49+
dst = &_init_array_start;
50+
while (dst < &_init_array_end) {
51+
((void (*)())*dst++)();
52+
}
53+
54+
// run application
55+
main_app();
56+
57+
// call destructors for static instances
58+
dst = &_fini_array_start;
59+
while (dst < &_fini_array_end) {
60+
((void (*)())*dst++)();
61+
}
62+
63+
// stop
64+
while (true);
65+
}

src/startup/stm32f031x6.ld

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
MEMORY {
2+
FLASH(rx) : ORIGIN = 0x08000000, LENGTH = 32K
3+
SRAM(rwx) : ORIGIN = 0x20000000, LENGTH = 4K
4+
}
5+
6+
SECTIONS {
7+
. = ORIGIN(FLASH);
8+
.text : {
9+
KEEP(*(.stack))
10+
KEEP(*(.vectors))
11+
KEEP(*(.vectors*))
12+
KEEP(*(.text))
13+
*(.text*)
14+
KEEP(*(.rodata))
15+
*(.rodata*)
16+
} >FLASH
17+
18+
.init_array ALIGN(4): {
19+
_init_array_start = .;
20+
KEEP(*(.init_array))
21+
_init_array_end = .;
22+
} >FLASH
23+
24+
.fini_array ALIGN(4): {
25+
_fini_array_start = .;
26+
KEEP(*(.fini_array))
27+
_fini_array_end = .;
28+
} >FLASH
29+
}
30+
31+
SECTIONS {
32+
_stacktop = ORIGIN(SRAM) + LENGTH(SRAM);
33+
_data_load = LOADADDR(.data);
34+
. = ORIGIN(SRAM);
35+
36+
.data ALIGN(4) : {
37+
_data_start = .;
38+
*(.data)
39+
*(.data*)
40+
. = ALIGN(4);
41+
_data_end = .;
42+
} >SRAM AT >FLASH
43+
44+
.bss ALIGN(4) (NOLOAD) : {
45+
_bss_start = .;
46+
*(.bss)
47+
*(.bss*)
48+
. = ALIGN(4);
49+
_bss_end = .;
50+
*(.noinit)
51+
*(.noinit*)
52+
} >SRAM
53+
54+
. = ALIGN(4);
55+
_heap_start = .;
56+
}

0 commit comments

Comments
 (0)