This Internal Interface Wrap (IID) provides a unified interface for common MCU peripherals,
enabling seamless portability between different microcontroller platforms.
Originally designed for the HardFOC board which needs to support multiple MCU types,
this abstraction layer allows developers to write hardware-agnostic code while maintaining optimal
performance.
π Core Benefits
π MCU Portability - Write once, run on multiple MCU platforms
π― Unified APIs - Consistent interface across all peripheral types
β‘ Performance - Zero-cost abstractions with compile-time optimization
π‘οΈ Type Safety - Strong typing with project-specific type system
π Extensible - Easy to add new MCUs and peripheral drivers
π Complete Coverage - 14+ peripheral interfaces for comprehensive hardware control
π¨ Design Philosophy
1
2
3
4
5
6
// Write hardware-agnostic codeBaseGpio*led=GpioFactory::Create(GPIO_PIN_2,GPIO_OUTPUT);led->SetHigh();// Same code works on ESP32, STM32, or any supported MCU// The factory handles MCU-specific implementation selection
// Application code remains the same across MCUsclassMotorController{BaseGpio*enable_pin;BasePwm*speed_control;BaseAdc*current_sensor;public:voidInitialize(){enable_pin=GpioFactory::Create(MOTOR_ENABLE_PIN,GPIO_OUTPUT);speed_control=PwmFactory::Create(PWM_CHANNEL_1,1000);// 1kHzcurrent_sensor=AdcFactory::Create(ADC_CHANNEL_1);}voidSetSpeed(hf_u16_tspeed_percent){speed_control->SetDutyCycle(speed_percent);}};
2. External Driver Support
The base classes can be extended for external chips:
1
2
3
4
5
6
7
8
9
10
11
// External motor driver chipclassDRV8302_Driver:publicBasePwm{BaseI2c*i2c_bus;BaseSpi*spi_bus;public:// Implement BasePwm interface using external chipvoidSetDutyCycle(hf_u16_tduty)override{// Send PWM command to DRV8302 via SPI/I2C}};
// Project uses consistent type definitionstypedefuint8_thf_u8_t;typedefuint16_thf_u16_t;typedefuint32_thf_u32_t;// Enums use snake_case with *t suffixenumclasshf_gpio_state_t{LOW=0,HIGH=1};
Error Handling
1
2
3
4
5
6
7
8
9
10
11
12
// All operations return status codesenumclasshf_gpio_err_t{SUCCESS=0,INVALID_PIN,ALREADY_CONFIGURED,HARDWARE_ERROR};hf_gpio_err_tresult=gpio->Configure(GPIO_MODE_OUTPUT);if(result!=hf_gpio_err_t::SUCCESS){Logger::GetInstance().LogError("GPIO configuration failed");}
Factory Pattern
The factory pattern enables completely MCU-agnostic code by automatically selecting the correct
implementation at compile time.
Factories support both dynamic allocation (heap-based) and static allocation (stack-based)
patterns:
// Dynamic allocation (heap-based)classDynamicController{BaseGpio*motor_enable;BasePwm*motor_speed;public:voidInitialize(){motor_enable=GpioFactory::Create(GPIO_PIN_5,GPIO_OUTPUT);motor_speed=PwmFactory::CreateMotorControl(PWM_CH_0,GPIO_PIN_18);}~DynamicController(){GpioFactory::Destroy(motor_enable);PwmFactory::Destroy(motor_speed);}};// Static allocation (stack-based, deterministic memory)classStaticController{HardwarePoolhardware;// All objects created in constructorpublic:voidInitialize(){// Objects already created in hardware pool constructor// Just configure themhardware.motor_enable->SetHigh();hardware.motor_pwm->SetDutyCycle(0);}voidRunMotor(hf_u16_tspeed){hardware.motor_pwm->SetDutyCycle(speed);hf_u16_tcurrent=hardware.current_adc->ReadRaw();Logger::GetInstance().LogInfo("Motor speed: %d%%, Current: %d",speed,current);}// Destructor automatically called, no manual cleanup needed};// Real-time system with pre-allocated poolvoidRealTimeTask(){staticStaticGpioFactory<8>gpio_pool;// Max 8 GPIOs, stack allocatedBaseGpio*led1=gpio_pool.Create(GPIO_PIN_2,GPIO_OUTPUT);BaseGpio*led2=gpio_pool.Create(GPIO_PIN_3,GPIO_OUTPUT);BaseGpio*button=gpio_pool.Create(GPIO_PIN_0,GPIO_INPUT);// No heap allocation, deterministic timingwhile(true){if(button->Read()==GPIO_HIGH){led1->SetHigh();led2->SetLow();}else{led1->SetLow();led2->SetHigh();}vTaskDelay(pdMS_TO_TICKS(10));}}
Pre-allocated: Known hardware layout, maximum determinism
C++23 Compatibility Note:
The examples above use modern C++23 syntax with alignas() and std::byte arrays instead of the
deprecated std::aligned_storage.
As noted in
P1413R3,
std::aligned_storage is deprecated due to API issues and potential undefined behavior.
The replacement pattern alignas(T) std::byte[sizeof(T)] provides the same functionality with
better type safety and constexpr compatibility.
// PWM Factory with motor control optimizationclassPwmFactory{public:staticBasePwm*CreateMotorControl(hf_u8_tchannel,hf_u8_tgpio_pin){// Automatically configures optimal settings for motor control// ESP32: 20kHz, 12-bit resolution// STM32: 20kHz, 16-bit resolution // nRF: 20kHz, 10-bit resolution}staticBasePwm*CreateServoControl(hf_u8_tchannel,hf_u8_tgpio_pin){// Automatically configures for servo control (50Hz, precise timing)}};// Communication Factory with bus managementclassCommFactory{public:staticBaseI2c*CreateSensorBus(hf_u8_tbus_num){// Optimized I2C settings for sensor communication// Handles MCU-specific pin assignments automatically}staticBaseUart*CreateDebugPort(){// Creates standard debug UART on each MCU's debug pins// ESP32: UART0 on GPIO1/3// STM32: USART2 on PA2/PA3// nRF: UART0 on P0.06/P0.08}};
π§ Building
Build System Features
Multi-MCU Support - Single build system for all platforms
Automated Testing - Comprehensive test suites
CI/CD Integration - Automated builds and validation
// Simple GPIO control - works on any MCUvoidBlinkLED(){BaseGpio*led=GpioFactory::Create(GPIO_PIN_2,GPIO_OUTPUT);while(true){led->SetHigh();vTaskDelay(pdMS_TO_TICKS(500));led->SetLow();vTaskDelay(pdMS_TO_TICKS(500));}GpioFactory::Destroy(led);}// PWM motor control - MCU-optimized automaticallyvoidControlMotor(){BasePwm*motor=PwmFactory::CreateMotorControl(PWM_CH_0,GPIO_PIN_5);BaseAdc*current=AdcFactory::Create(ADC_CHANNEL_1);motor->SetDutyCycle(75);// 75% speedhf_u16_tcurrent_ma=current->ReadMillivolts()/10;// Convert to mALogger::GetInstance().LogInfo("Motor current: %d mA",current_ma);}
classHardFOCController{// Hardware interfaces - MCU agnosticBaseGpio*motor_enable;BaseGpio*fault_pin;BasePwm*motor_speed;BaseAdc*current_sensor;BaseAdc*voltage_sensor;BaseUart*debug_port;BaseI2c*sensor_bus;BaseWifi*telemetry;public:hf_gpio_err_tInitialize(){// Factory creates MCU-specific implementations automaticallymotor_enable=GpioFactory::Create(MOTOR_EN_PIN,GPIO_OUTPUT);fault_pin=GpioFactory::CreateWithInterrupt(FAULT_PIN,fault_callback);motor_speed=PwmFactory::CreateMotorControl(PWM_CH_0,MOTOR_PWM_PIN);current_sensor=AdcFactory::Create(CURRENT_ADC_CH);voltage_sensor=AdcFactory::Create(VOLTAGE_ADC_CH);debug_port=CommFactory::CreateDebugPort();sensor_bus=CommFactory::CreateSensorBus(I2C_BUS_0);telemetry=WifiFactory::Create();// Validate all interfaces created successfullyif(!motor_enable||!motor_speed||!current_sensor){returnhf_gpio_err_t::HARDWARE_ERROR;}debug_port->Printf("HardFOC Controller initialized on %s\n",MCU_NAME);returnhf_gpio_err_t::SUCCESS;}voidRunMotor(hf_u16_tspeed_percent){// Enable motor drivermotor_enable->SetHigh();// Set motor speedmotor_speed->SetDutyCycle(speed_percent);// Read diagnosticshf_u16_tcurrent_ma=current_sensor->ReadMillivolts()/10;hf_u16_tvoltage_mv=voltage_sensor->ReadMillivolts();// Log locallydebug_port->Printf("Speed: %d%%, Current: %dmA, Voltage: %dmV\n",speed_percent,current_ma,voltage_mv);// Send telemetry if connectedif(telemetry&&telemetry->IsConnected()){telemetry->SendData("motor_speed",speed_percent);telemetry->SendData("motor_current",current_ma);telemetry->SendData("bus_voltage",voltage_mv);}}voidEmergencyStop(){motor_enable->SetLow();motor_speed->SetDutyCycle(0);debug_port->Printf("EMERGENCY STOP - Motor disabled\n");}~HardFOCController(){// Clean up all resourcesEmergencyStop();GpioFactory::Destroy(motor_enable);GpioFactory::Destroy(fault_pin);PwmFactory::Destroy(motor_speed);AdcFactory::Destroy(current_sensor);AdcFactory::Destroy(voltage_sensor);CommFactory::DestroyComm(debug_port);CommFactory::DestroyComm(sensor_bus);WifiFactory::Destroy(telemetry);}};// Same code compiles and runs on ESP32, STM32, nRF!HardFOCControllercontroller;controller.Initialize();controller.RunMotor(50);// 50% speed