This repository contains the Motor Control Unit firmware for the Polimarche Racing Team electric vehicle. It includes code for managing dual AMK inverters via CAN Bus, reading sensors via ADC with DMA, FSAE-compliant safety logic, single-pedal regenerative braking, and vehicle dynamics models in MATLAB/Simulink. The board used is the STM32 Nucleo-F756ZG.
Note: This repository covers only the MCU and the MATLAB/Simulink vehicle dynamics models. The ECU (sensor acquisition board) has been moved to its own repository.
- โ AMK Inverter Control: Full state machine (IDLE โ LV โ HV โ READY โ RUNNING) with AMK 8.2.6 error recovery sequence and automatic retry logic.
- โ Regenerative Braking: Four-stage single-pedal regen with speed fade-out, battery power limit, and DC bus voltage derating.
- โ FSAE Safety Compliance: APPS plausibility checks (T11.8.9, T11.8.10) and MCU state machine with torque interlock.
- โ ADC with DMA: Continuous multi-channel reading (APPS1, APPS2, Steering) without CPU overhead.
- โ Dual CAN Bus: CAN1 for inverter protocol, CAN2 for vehicle telemetry (dashboard, BMS, IMU, R2D).
- โ Signal Filtering: Median + Moving Average cascade on APPS and SAS for noise rejection.
- โ FreeRTOS Real-Time Architecture: 1 kHz motor control task, 1 kHz sensor task, 10 Hz logging task.
- โ Vehicle Dynamics Models: MATLAB/Simulink models for slip control, torque vectoring, and straight-line control.
The repository is organized into three main sections: mcu-stm32 (STM32 firmware), Controls (MATLAB/Simulink models), and Tests (bench test infrastructure and data).
โโโ mcu-stm32/ # STM32F756ZG firmware (main project)
โ โโโ mcu-stm32.ioc # STM32CubeMX configuration
โ โโโ Core/
โ โโโ Inc/ # Headers organized by subsystem
โ โ โโโ Config.h # Build modes, feature flags, parameters
โ โ โโโ Communication/ # CAN bus & UART serial
โ โ โโโ Drive/ # Inverter, torque control, regen, error recovery
โ โ โโโ Safety/ # MCU state machine, device state, watchdog
โ โ โโโ Sensors/ # ADC/DMA, APPS, steering angle
โ โ โโโ Tasks/ # FreeRTOS task definitions
โ โโโ Src/ # Implementations (mirrors Inc/ structure)
โ
โโโ Controls/ # MATLAB/Simulink vehicle dynamics models
โ โโโ SlipControl/ # Traction/slip ratio control
โ โโโ StraightLineControl/ # Straight-line stability
โ โโโ TorqueVectoring/ # Torque vectoring with 3D car model
โ
โโโ AIPEX/ # AMK inverter configuration files (.aipex)
โ โโโ FIXED_speed/ # Speed control mode configurations
โ โโโ FREE_torque/ # Torque control mode configurations
โ โโโ Scope Templates/ # AipexPRO oscilloscope templates
โ โโโ Errors/ # Error log files
โ
โโโ Documents/ # Datasheets, manuals, AipexPRO guides
โ
โโโ Tests/ # Test infrastructure & bench data
โ โโโ DataCollectorSoftware/ # Python/MATLAB data logging & analysis
โ โโโ ControlUnit_Nucleo_F756ZG/ # Alternative board test project
โ โโโ ESP32-WIFI-BT-communication/ # Wireless telemetry via ESP32
โ โโโ FeatherDataCollectorBancoMotori/ # Feather CAN data logger
โ โโโ FixedCan - Banco Loccioni/ # Last bench test in Speed-Control mode
โ
โโโ Regen_Pedal_Behavior.md # Detailed regen pedal behavior documentation
The firmware on the STM32F756ZG manages AMK inverter control in real time:
- Drive modules: AMK state machine, torque control loop, error recovery, regenerative braking
- Sensor acquisition: DMA-based ADC reading, APPS and steering processing
- CAN communication: Inverter setpoints on CAN1, vehicle telemetry on CAN2
- Safety: MCU state machine with torque interlock, APPS plausibility, watchdog
MATLAB/Simulink models developed to study and simulate vehicle dynamics:
- SlipControl: Traction/slip ratio control algorithms
- StraightLineControl: Straight-line torque distribution
- TorqueVectoring: Full torque vectoring model with 3D vehicle dynamics
Development, bench testing, and data collection infrastructure:
- DataCollectorSoftware: Python CSV logger + MATLAB analysis scripts
- ControlUnit_Nucleo_F756ZG: Alternative MCU implementation for testing
- ESP32-WIFI-BT-communication: Wireless data streaming from bench
- FixedCan - Banco Loccioni: Last bench test configuration (Speed-Control mode, Loccioni dyno)
The control unit implementation is located in mcu-stm32/. The architecture follows a modular design with feature-specific subfolders. Subfolders within Inc/ contain headers (.h), while subfolders within Src/ provide the corresponding sources (.c).
โโโ mcu-stm32/
โโโ mcu-stm32.ioc # STM32CubeMX configuration file
โโโ Core/
โโโ Inc/
โ โโโ Config.h # Build modes, feature flags, all parameters
โ โโโ Communication/
โ โ โโโ Can.h # CAN1 (inverters) + CAN2 (vehicle) TX/RX
โ โ โโโ Serial.h # DMA UART logging (UART3, UART5, UART6)
โ โโโ Drive/
โ โ โโโ Inverter.h # AMK state machine + CAN message decode
โ โ โโโ BaseControlMotor.h # Torque control loop, rate limiting, LEDs
โ โ โโโ ErrorRecovery.h # AMK 8.2.6 error reset state machine
โ โ โโโ Regen.h # 4-stage regenerative braking
โ โโโ Safety/
โ โ โโโ MCU_State.h # System state machine (INITโREADYโRUNNING/ERROR)
โ โ โโโ device_state.h # Device state tracking
โ โ โโโ watchdog.h # IWDG refresh
โ โโโ Sensors/
โ โ โโโ ADC_Manager.h # Centralized DMA ADC handling
โ โ โโโ APPS.h # Accelerator Pedal Position Sensors (FSAE T11.8)
โ โ โโโ SAS.h # Steering Angle Sensor with filtering
โ โโโ Tasks/
โ โโโ motors_manager.h # 1 kHz inverter control task
โ โโโ data_manager.h # 1 kHz sensor sampling task
โ โโโ data_logger.h # 10 Hz UART/CAN2 telemetry task
โโโ Src/
โโโ Communication/
โ โโโ Can.c
โ โโโ Serial.c
โโโ Drive/
โ โโโ Inverter.c
โ โโโ BaseControlMotor.c
โ โโโ ErrorRecovery.c
โ โโโ Regen.c
โโโ Safety/
โ โโโ MCU_State.c
โ โโโ device_state.c
โ โโโ watchdog.c
โโโ Sensors/
โ โโโ ADC_Manager.c
โ โโโ APPS.c
โ โโโ SAS.c
โโโ Tasks/
โ โโโ motors_manager.c
โ โโโ data_manager.c
โ โโโ data_logger.c
โโโ main.c
The project supports different build modes configured in Config.h. This allows testing with limited hardware availability.
Enable when only ADC1 CH0 (PA0) is available:
// In Config.h โ uncomment to enable:
#define TEST_MODE_SINGLE_ADCEffects:
- Uses only 1 ADC channel instead of 3
- APPS sensor 2 is simulated (duplicates sensor 1 value)
- Sensor mismatch plausibility check disabled (can't verify with 1 sensor)
- Range check disabled
- Deadzone 30% still active for realistic pedal behavior
Enable for bench testing with all ADC channels but relaxed safety thresholds:
// In Config.h โ uncomment to enable:
#define TEST_MODE_FULLEffects:
- All 3 ADC channels active (APPS1, APPS2, Steering)
- Implausibility threshold relaxed to 20% (instead of 10%)
- Implausibility timeout extended to 500 ms (instead of 100 ms)
Comment out all test modes for full FSAE compliance:
// In Config.h โ comment out all test modes:
//#define TEST_MODE_SINGLE_ADC
//#define TEST_MODE_FULL
#define RELEASE_MODEEffects:
- All 3 ADC channels active
- Full APPS plausibility check: mismatch >10% for >100 ms โ shutdown
- Full range check (open/short circuit detection)
- Deadzone 30% active
- All FSAE safety rules enforced
Located in Sensors/APPS.h and Sensors/APPS.c:
- Dual sensor reading: Two redundant potentiometers with independent transfer functions
- Signal filtering: Median filter (11 samples) + Moving Average (40 samples) cascade
- Normalization: Converts raw ADC values to 0โ100% pedal position
- FSAE Plausibility checks:
- T11.8.9: Sensor mismatch >10% for >100 ms โ motor shutdown
- T11.8.10: Auto-reset when pedal returns below 5%
- Out-of-range detection: Open/short circuit with ADC bounds checking
Located in Sensors/ADC_Manager.h and Sensors/ADC_Manager.c:
- DMA circular mode: Continuous multi-channel ADC without CPU intervention
- Thread-safe access: Getter functions for other modules
- Channels: APPS1 (PA0), APPS2 (PA1), Steering (PC3)
Located in Drive/Inverter.h and Drive/Inverter.c:
- AMK state machine: IDLE โ LV_ACTIVE โ HV_ACTIVE โ READY โ RUNNING
- CAN message decoding: Status words (SM1โSM5), speed, torque, temperatures, power
- AMK protocol: Control word bits for enable, DC activation, error reset
Located in Drive/ErrorRecovery.h and Drive/ErrorRecovery.c:
- AMK 8.2.6 sequence:
bInverterOn=0โbErrorReset=1(50 ms hold) โbErrorReset=0(falling edge triggers reset) - Retry logic: Up to 5 automatic recovery attempts with 100 ms cooldown between tries
- Per-inverter context: Independent recovery state for each motor node
Located in Drive/BaseControlMotor.h and Drive/BaseControlMotor.c:
- Dynamic torque limiting: AMK formula based on speed and DC bus voltage (
M = P / (2ฯ ร N/60)) - Rate limiting: Smooth torque transitions (10 units per ms)
- Status LED management: Visual feedback for error and running states
Located in Drive/Regen.h and Drive/Regen.c:
- 4-stage calculation: Pedal-dependent โ speed fade-out โ battery power limit โ DC bus derating
- Latch hysteresis: Entry/exit thresholds to prevent regen chattering at low speed
- Configurable intensity: CONSERVATIVE / BALANCED / AGGRESSIVE modes in
Config.h
Located in Communication/Can.h and Communication/Can.c:
- CAN1: Inverter protocol โ setpoints TX at 1 kHz, status RX at 1 kHz
- CAN2: Vehicle telemetry โ BMS, IMU, R2D RX; error snapshots and sensor data TX
- Hardware: SN65HVD230 CAN transceiver (ร2)
Located in Communication/Serial.h and Communication/Serial.c:
- DMA-based transmission: Non-blocking TX on UART3, UART5, UART6
- Per-channel enable: Each UART can be independently enabled/disabled in
Config.h - Mutex-protected: Thread-safe printf-style logging from FreeRTOS tasks
The system uses FreeRTOS with three dedicated tasks. All shared data is protected via FreeRTOS mutexes.
| Task | Period | Responsible for |
|---|---|---|
MotorsManagerTask |
10 ms | Inverter state machine, torque command, error recovery, regen |
DataManagerTask |
4 ms | ADC reading, APPS/SAS processing, sensor filtering |
DataLoggerTask |
100 ms | UART serial output and CAN2 telemetry |
The core control loop executed every 10 ms:
void MotorsManagerTask(void *argument)
{
for (;;)
{
// 1. Read sensor data (pedal %, steering %)
// 2. For each inverter:
// a. Decode CAN status (Inverter_ProcessReceivedMessage)
// b. Run state machine + error recovery (Motor_ProcessInverterControl)
// c. If RUNNING: compute torque setpoint with regen + rate limit
// d. Send CAN command (Inverter_BuildCommand + Can_SendInverterCommand)
osDelay(INVERTERS_TASK_PERIOD_MS);
}
}Runs ADC acquisition and sensor processing every 1 ms:
void DataManagerTask(void *argument)
{
for (;;)
{
// 1. Read DMA ADC buffer (ADC_Manager_GetValues)
// 2. Process APPS (APPS_Process) โ filtering + plausibility
// 3. Process SAS (SAS_Process) โ filtering + normalization
// 4. Update MCU state machine
osDelay(1);
}
}Outputs diagnostics and telemetry every 100 ms:
void DataLoggerTask(void *argument)
{
for (;;)
{
// 1. Log inverter data via UART (Serial_Log)
// 2. Transmit CAN2 telemetry messages (APPS%, steering%, inverter data, errors)
osDelay(100);
}
}- Clone the repository:
git clone https://github.com/PolimarcheRacingTeam/ev-powertrain-control.git
- Open the project with STM32CubeIDE
- Navigate to
mcu-stm32/(the main firmware project) - Select build mode in
Core/Inc/Config.h(see Build Modes section) - Compile and upload to the Nucleo-F756ZG board
- Monitor serial output at 115200 baud (e.g., using Termite or PuTTY on UART3)
- Install AipexPRO (refer to
Documents/AipexPRO/) - Load configuration from
AIPEX/FREE_torque/(torque control) orAIPEX/FIXED_speed/(speed control) - Connect to AMK inverter via CAN bus
- Use scope templates from
AIPEX/Scope Templates/for live monitoring
- Navigate to
Tests/DataCollectorSoftware/ - Run the Python logger
csv_socket_logger.pyโ streams CSV data from ESP32 over Wi-Fi - Analyze data using
analyze_test_log.min MATLAB
(see mcu-stm32/mcu-stm32.ioc for full CubeMX configuration)
- ADC1: 3-channel DMA circular mode โ APPS1 (PA0), APPS2 (PA1), Steering (PC3)
- CAN1: Inverter communication at 1 Mbit/s (SN65HVD230 transceiver)
- CAN2: Vehicle telemetry at 500 kbit/s (SN65HVD230 transceiver)
- UART3: PC debug serial at 115200 baud (DMA TX)
- UART5: Data logger / ESP32 at 115200 baud (DMA TX)
- UART6: External device (DMA TX)
- GPIO: LD2 (green), LD3 (red) status LEDs; R2D signal input
- Transceiver: SN65HVD230 (3.3 V compatible), ร2 for CAN1 + CAN2
- Baud Rates: CAN1 @ 1 Mbit/s (AMK inverter), CAN2 @ 500 kbit/s (vehicle)
- Termination: 120 ฮฉ at both ends of each bus
- Motor: AMK DD series โ 9.8 Nm nominal, 21 Nm peak, 16000 RPM max
- Inverter: AMK KW-R25 โ CAN-based torque control
- CAN node IDs: Right inverter = 1 (0x184/0x284), Left inverter = 2 (0x185/0x285)
- Dual redundant sensors with independent transfer functions
- If sensor mismatch exceeds 10% for more than 100 ms โ motor power immediately cut
- Configurable in
APPS.h:#define APPS_IMPLAUSIBILITY_THRESHOLD_PERCENT 10U // Difference threshold [%] #define APPS_IMPLAUSIBILITY_TIMEOUT_MS 100U // Time before failure [ms]
- After an implausibility event, torque cannot be restored until the pedal returns below 5%
- Prevents unintended torque restoration after driver removes foot
- Each ADC channel checked for open-circuit (value too low) and short-circuit (value too high)
- Detection uses configurable ADC margin counts
- Fault triggers shutdown independently from plausibility check
- AMK Inverter:
Documents/โ AipexPRO guides, training materials, password info - Regen behavior:
Regen_Pedal_Behavior.mdโ full pedal-speed regen interaction map - MCU Configuration:
mcu-stm32/mcu-stm32.iocโ STM32CubeMX project configuration
- Formula SAE Rules: FSAE Online
- T11.8.9 / T11.8.10: APPS sensor plausibility and reset conditions
- EV.4.7: Accelerator pedal position sensor requirements
-
NUCLEO F756ZG:
๐ Pinout -
SN65HVD230 CAN Transceiver:
๐๏ธ Video Tutorial 1
๐๏ธ Video Tutorial 2 -
STM32 ADC with DMA:
๐๏ธ Video Tutorial
๐ In-depth Guide -
AMK Motor & Inverter:
๐ AipexPRO Documentation
- Alessandro Zingaretti โ Polimarche Racing Team, UNIVPM
If you have any questions, would like to discuss this project further, or are interested in potential collaboration opportunities, please feel free to connect with us:
- LinkedIn: polimarcheracingteam
- Instagram: polimarcheracingteam
- Email: formulasae@sm.univpm.it