A professional 6-layer embedded firmware framework designed for maximum modularity and hardware portability. Uses a CMake-based build system with support for multiple toolchains.
EmbeddedKit follows a strict bottom-up dependency model where upper layers depend only on lower layers. Circular dependencies are strictly prohibited.
L5_App (Application Layer)
└─> Business logic, state machines
L4_Components (Device Driver Component Layer)
└─> Hardware drivers (OLED, Flash, sensors) - Using OOP pattern
L3_Middlewares (Third-party Middleware Layer)
└─> FreeRTOS, FatFs, LVGL, etc.
L2_Core (Core & Hardware Abstraction Layer)
├─> utils/ (Data structures, memory management, logging)
└─> hal/ (Hardware abstraction: GPIO/UART/I2C/etc.)
L1_MCU (MCU Vendor Library Layer)
└─> Chip-specific vendor code (STM32 HAL, CMSIS)
L0_Assets (Resource File Layer)
└─> Static resource data (images, fonts, configs, etc.)
-
L0_Assets (Lowest Layer): Stores static resource data embedded at compile time (images, fonts, configs, etc.). Has no dependencies on other layers and can be accessed by any layer.
-
L1_MCU (MCU Vendor Library Layer): Each MCU model has an independent subdirectory containing vendor HAL/LL libraries, startup code, and
main.c. Themain.ccallsek_main()from L5_App after hardware initialization. No sharing of vendor libraries across different MCUs. -
L2_Core:
- utils/: Pure software implementations - data structures, memory management, logging
- hal/: Hardware abstraction layer providing logical-to-physical mapping
inc/*.hmust NOT include vendor headers (stm32xxxx.h)- Vendor headers only allowed in
src/*.c - All functions use
hal_prefix (e.g.,hal_gpio_write())
- Internal dependency: hal/ may depend on utils/ (allowed within same layer)
- Naming convention:
ek_prefix (utils) andhal_prefix (hal)
-
L3_Middlewares: Each middleware has an independent subdirectory with its own CMakeLists.txt. Can depend on L2_Core/hal for hardware adaptation.
-
L4_Components: Strict OOP Pattern - Use function pointers for polymorphism. The first parameter of methods must be the object pointer (
self). Define abstract interfaces to implement dependency inversion for hardware. Implemented by users based on actual hardware. -
L5_App: Implements
ek_main()as the application entry point. Should call L4 components, not L1 directly. Encapsulate business logic into modules.
- 6-Layer Architecture: Clear separation of concerns with strict dependency rules
- OBJECT Library Build Mode: Avoids static library selective linking issues
- Hardware Portability: Replace MCU by only changing the L1 layer
- OOP Design Pattern: Interface abstraction in L4 for dependency inversion
- Flexible Build System: Support for multiple toolchains (GCC, ARM Compiler 6, Clang)
- Conditional Compilation: Enable/disable features via CMake options
- Rich Data Structures: Linked list, ring buffer, stack, dynamic vector (included)
- Memory Management: TLSF-based dynamic memory allocator
- Logging System: Multi-level logging with color support
- RTOS Support: FreeRTOS integration ready
- Resource Management: Independent resource file layer supporting static data embedding
- CMake 3.20 or higher
- ARM toolchain (gcc-arm-none-eabi or STARM Clang)
- STM32CubeMX or GD32 Eclipse (for generating initialization code)
- just (optional, provides simpler build commands)
Method 1: Using just (Recommended)
# Build STM32F407VGT6 (GCC ARM)
just build
# Build GD32F470ZGT6 (GCC ARM)
just build-gd
# Build STM32F429ZIT6 (STARM Clang)
just build-starm
# Clean build directory
just clean
# Run unit tests
just testMethod 2: Using CMake
# Configure build (select MCU model via cache variable)
cmake -B build -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=cmake/gcc-arm-none-eabi.cmake \
-DMCU_MODEL=STM32F407VGT6_GCC \
-DUSE_FREERTOS=OFF \
-DUSE_FATFS=OFF \
-DUSE_LVGL=OFF
# Build
cmake --build buildcmake/gcc-arm-none-eabi.cmake- GCC ARM toolchaincmake/starm-clang.cmake- STARM Clang toolchain
Note: If you need to use ARM Compiler 6, please write your own toolchain file.
EmbeddedKit/
├── CMakeLists.txt # Main build script
├── ek_conf.h # Global configuration file
├── CLAUDE.md # Project documentation and changelog
├── LICENSE # MIT License
├── cmake/ # Toolchain files
│ ├── gcc-arm-none-eabi.cmake
│ ├── arm-compile6.cmake
│ └── starm-clang.cmake
├── L0_Assets/ # Layer 0: Resource files
├── L1_MCU/ # Layer 1: MCU vendor libraries
│ └── STM32F429ZIT6_GCC/ # STM32F429 support
├── L2_Core/ # Layer 2: Core & HAL
│ ├── utils/ # Pure software utilities
│ ├── hal/ # Hardware abstraction layer
│ └── third_party/ # Third-party libraries
├── L3_Middlewares/ # Layer 3: Middleware
│ ├── FreeRTOS/ # Real-time operating system
│ ├── FatFS/ # File system
│ └── LVGL/ # Graphics library
├── L4_Components/ # Layer 4: Device drivers (OOP)
└── L5_App/ # Layer 5: Application logic
Stores static resource data embedded at compile time, with no dependencies on other layers.
Purpose: Images, fonts, configuration data, and other static resources
Features:
- Resources provided as C arrays or const data
- Directly linked to final firmware
- Accessible by any layer
Contains vendor-provided official libraries and startup code. Each MCU model has an independent subdirectory. The main.c is responsible for calling ek_main() after hardware initialization.
Currently Supported: STM32F407VGT6_GCC, STM32F429ZIT6_STARM, GD32F470ZGT6
Build Mode: OBJECT library, linked via $<TARGET_OBJECTS:l1_mcu>
utils/ (Hardware-independent):
ek_def.h- Common definitions and cross-compiler macrosek_list.h- Doubly linked listek_ringbuf.h- Ring buffer with element countingek_stack.h- Stack data structureek_vec.h- Dynamic vector (array)ek_mem.h- TLSF-based dynamic memory managementek_log.h- Multi-level logging systemek_assert.h- Assertion moduleek_export.h- Export macros (auto-initialization)ek_io.h- IO module (based on lwprintf)ek_shell.h- Shell moduleek_str.h- String processing utilities
hal/ (Hardware Abstraction): Being implemented. Will provide logical-to-physical mapping (e.g., HAL_GPIO_1 → GPIOA PIN_5).
- GPIO, UART, SPI, I2C, Tick
third_party/:
lwprintf- Lightweight formatted output librarytlsf- Two-Level Segregated Fit memory allocator
Each middleware has an independent subdirectory and CMakeLists.txt.
Integrated Middleware:
-
FreeRTOS - Real-time operating system (✅ Fully Supported)
- Port: GCC_ARM_CM4F (for STM32F429)
- Heap implementation: heap_4.c
- Configuration: 168MHz CPU, 1000Hz Tick Rate, 32KB heap
- Enable via:
-DUSE_FREERTOS=ON
-
FatFS - File system (✅ Fully Supported)
- Enable via:
-DUSE_FATFS=ON
- Enable via:
-
LVGL - Lightweight graphics library (✅ Fully Supported)
- Version: v8.3.11
- Configuration: RGB565, 128KB memory pool
- Includes: Official examples and demos
- Enable via:
-DUSE_LVGL=ON - Note: LVGL links to L1_MCU for direct hardware access
Core Design - OOP Pattern:
- Interface abstraction using function pointers
- Object encapsulation with properties and methods
- Dependency inversion - components depend on abstract interfaces
- Polymorphism support - same component for multiple hardware types
Implementation: Implemented by users based on actual hardware
Implements ek_main() as the application entry point. Should call L2~L4 layer services, not L1 directly.
Current Implementation:
void ek_main(void)
{
ek_heap_init(); // Initialize memory heap
while (1)
{
// Business logic
}
}The global configuration file ek_conf.h in the root directory manages framework-wide settings:
// RTOS Configuration
#define EK_USE_RTOS (1)
// Memory Management
#define EK_HEAP_NO_TLSF (0)
#define EK_HEAP_SIZE (30 * 1024)
// IO Lib Management
#define EK_IO_NO_LWPRTF (0)
// Module Enable/Disable
#define EK_EXPORT_ENABLE (0)
#define EK_STR_ENABLE (1)
#define EK_LOG_ENABLE (1)
#define EK_LIST_ENABLE (1)
#define EK_VEC_ENABLE (1)
#define EK_RINGBUF_ENABLE (1)
#define EK_STACK_ENABLE (1)
#define EK_SHELL_ENABLE (1)
// Logging Configuration
#define EK_LOG_DEBUG_ENABLE (1)
#define EK_LOG_COLOR_ENABLE (1)
#define EK_LOG_BUFFER_SIZE (256)
// Assertion Configuration
#define EK_ASSERT_USE_TINY (1)
#define EK_ASSERT_WITH_LOG (1)This project uses OBJECT Library mode instead of traditional static libraries, offering the following advantages:
Why use OBJECT libraries?
Traditional static libraries have selective linking issues:
- Linker only extracts object files that resolve current undefined references
- "Skipped" symbols are not included in the final firmware
- Requires complex options like
--whole-archive,--undefinedto force symbol inclusion
OBJECT library advantages:
- Object files participate directly in final linking, no selective linking issues
- No need for
--whole-archive,--undefinedlinker options - Simpler build system, more reliable symbol resolution
CMake Implementation:
# Define each layer as OBJECT library
add_library(l0_assets OBJECT)
add_library(l1_mcu OBJECT)
add_library(l2_core OBJECT)
add_library(l4_components OBJECT)
add_library(l5_app OBJECT)
# L3 remains INTERFACE library (aggregation layer)
add_library(l3_middlewares INTERFACE)
# Final linking uses $<TARGET_OBJECTS:>
target_link_libraries(${CMAKE_PROJECT_NAME}
$<TARGET_OBJECTS:l5_app>
$<TARGET_OBJECTS:l4_components>
l3_middlewares # INTERFACE library links normally
$<TARGET_OBJECTS:l2_core>
$<TARGET_OBJECTS:l1_mcu>
$<TARGET_OBJECTS:l0_assets>
)- ✅ Complete 6-layer architecture design
- ✅ CMake build system configuration
- ✅ just command-line tool support
- ✅ L0_Assets: Resource file layer framework ready
- ✅ L1_MCU: STM32F407VGT6_GCC, STM32F429ZIT6_STARM, GD32F470ZGT6 fully supported
- ✅ L2_Core/utils: All utility modules implemented (12 modules including export, io, shell, str)
- ✅ L2_Core/third_party: lwprintf and tlsf integrated
- ✅ L2_Core/hal: GPIO, UART, SPI, I2C, Tick being implemented
- ✅ L3_Middlewares: FreeRTOS, FatFS, LVGL v8.3.11 fully supported (includes examples and demos)
- ✅ L5_App: Basic entry implementation
- ✅ Global configuration file
ek_conf.h - ✅ Detailed documentation
- ⏳ L2_Core/hal: Complete remaining peripherals (DAC, ADC, RTC, etc.)
- ⏳ L4_Components: Device driver components implemented by users based on actual hardware
- ⏳ L5_App: Specific business logic applications
Contributions are welcome! Please follow the architectural rules and coding standards described in this document.
This project is licensed under the MIT License - see the LICENSE file for details.