This is a demonstrative project of software architecture based on 4 independent layers.
┌─────────────────────────────────────────┐
│ APP LAYER │
│ (Application / Orchestration) │
│ - Main logic │
│ - Task coordination │
└─────────────────────────────────────────┘
↓ can ONLY call
┌─────────────────────────────────────────┐
│ CONTROL LAYER │
│ (Control / Business Logic) │
│ - Algorithms │
│ - State machines │
│ - Data processing │
└─────────────────────────────────────────┘
↓ can call ↓
┌────────────────────┐ ┌────────────────────┐
│ DRIVER LAYER │ │ DRIVER LAYER │
│ (Sensors) │ │ (Actuators) │
└────────────────────┘ └────────────────────┘
↓ ↓
└───────────┬───────────┘
↓ can call
┌─────────────────────────────────────────┐
│ MCU-PERIPHERALS LAYER │
│ (Microcontroller HAL) │
│ - GPIO, ADC, UART, SPI, I2C │
│ - Vendor libs (emlib, HAL) │
└─────────────────────────────────────────┘
- APP can call CONTROL only
- CONTROL can call DRIVER and MCU-PERIPHERALS
- DRIVER can call MCU-PERIPHERALS only
- APP CANNOT call DRIVER or MCU-PERIPHERALS directly
- DRIVER CANNOT call CONTROL or APP
- MCU-PERIPHERALS CANNOT call anything (base layer)
- CONTROL CANNOT call APP
- System initialization
- Main application loop
- Coordination between CONTROL modules
- Global state management
- User interface (if applicable)
- Never accesses hardware directly
- Control algorithm implementation
- Business logic
- Sensor data processing
- State machines
- Validations and business rules
- Bridge between APP and DRIVER
- External sensor drivers (temperature, pressure, humidity via I2C)
- Logical actuator drivers (LED, fan, heater, alarm)
- External module drivers (displays, SD cards, etc)
- High-level device interface
- Maps logical concepts (ACTUATOR_FAN) to physical hardware (PORT_B, PIN_0)
- Uses MCU-PERIPHERALS to access hardware
- See: docs/actuator_driver_rationale.md - Explains why actuator_driver exists in DRIVER
- Lowest layer - microcontroller hardware abstraction
- Implementation/wrapper of vendor libraries
- GPIO, ADC, UART, SPI, I2C, PWM, Timers, etc
- Direct access to MCU registers
- ✨ Modularity: Each layer has well-defined responsibility
- 🔄 Reusability: Drivers can be reused in other projects
- 🧪 Testability: Layers can be tested independently
- 📝 Maintainability: Easy to locate and fix issues
- 🔌 Portability: Easy to change hardware - MCU changes affect only MCU-PERIPHERALS layer, external devices affect only DRIVER layer
architecture-example/
├── .clang-format # Code formatting configuration (Barr C Standard)
├── Makefile # Build system with validation
├── README.md # Project documentation
├── template.c # Template for .c files
├── template.h # Template for .h files
├── docs/
│ ├── 4-layer-architecture.md # Architecture documentation
│ ├── architecture_diagram.md # Visual diagrams
│ └── layer_rules.md # Layer rules reference
├── build/ # Build artifacts (auto-generated)
│ ├── obj/ # Object files
│ └── architecture-example.elf # Executable
└── src/
├── app/ # APPLICATION LAYER
│ ├── inc/
│ │ └── app.h # APP layer public interface
│ └── src/
│ ├── main.c # Entry point
│ ├── app_manager.c # Application coordinator
│ ├── app_cooling.c # Cooling management module
│ ├── app_hmi.c # HMI (buttons/LEDs) module
│ └── app_msg.c # Message/communication module
├── control/ # CONTROL LAYER
│ ├── inc/
│ │ └── control.h # CONTROL layer public interface
│ └── src/
│ ├── board_init.c # Board initialization
│ ├── temperature_control.c # Temperature control algorithm
│ ├── alarm_control.c # Alarm management
│ ├── actuator_control.c # Actuator control abstraction
│ └── hmi_control.c # HMI control abstraction
├── driver/ # DRIVER LAYER
│ ├── inc/
│ │ └── driver.h # DRIVER layer public interface
│ └── src/
│ ├── temperature_sensor_driver.c # LM75 I2C sensor
│ ├── humidity_sensor_driver.c # SHT31 I2C sensor
│ ├── pressure_sensor_driver.c # BMP280 I2C sensor
│ ├── actuator_driver.c # LED, fan, heater drivers
│ ├── button_driver.c # Button event driver (interrupt-based)
│ └── uart_driver.c # UART communication
└── mcu-peripherals/ # MCU-PERIPHERALS LAYER (HAL)
├── inc/
│ └── mcu_hal.h # MCU HAL public interface
└── src/
├── mcu_peripheral_init.c # Peripheral initialization
├── mcu_peripheral_internal.h # Internal HAL declarations
├── gpio.c # GPIO HAL implementation
├── adc.c # ADC HAL implementation
├── uart.c # UART HAL implementation
└── i2c.c # I2C HAL implementation
- This is an example/demonstrative project
- The code is simplified for learn purposes
- The architecture can be adapted according to project needs
This project uses make to automate compilation and validation. Available commands:
Compiles the entire project and generates the executable.
make build- Compiles all
.cfiles from the 4 layers - Generates object files in
build/obj/ - Links and creates the executable
build/architecture-example.elf
Removes all compiled files.
make clean- Deletes the entire
build/folder - Useful for doing a clean build from scratch
Validates if architecture rules are being respected.
make validateChecks:
- ✅ APP does not include DRIVER or MCU-PERIPHERALS directly
- ✅ CONTROL does not include APP
- ✅ DRIVER does not include CONTROL or APP
- ✅ MCU-PERIPHERALS does not include any upper layer
Auto-formats all source files according to Barr C Coding Standard.
make format- Applies consistent code formatting across all
.cand.hfiles - Uses
clang-formatwith modified Barr C Coding Standard - Ensures 2-space indentation, 80 character line limit
Shows project information and dependencies.
make infoDisplays:
- List of source files for each layer
- Allowed dependencies between layers
- Project structure overview
Shows help message with all available commands.
make help# Clean, compile and validate everything
make clean build validate
# Build and validate architecture
make build validate
# Format code and build
make format build
# Full workflow: format, clean, build, validate
make format clean build validate# Run in simulation mode
./build/architecture-example.elf
# Run for a limited time (3 seconds)
timeout 3 ./build/architecture-example.elfThe program runs in simulation mode (-DSIMULATION) printing messages about hardware operations.