Skip to main content
emobility.academy
M.TechEmbedded software architectureAdvanced About 10 weeksKit + add-ons

AUTOSAR-Style Layered Software (MCAL, ECU Abstraction, Application) on STM32

Students restructure EV firmware into AUTOSAR-inspired layers: an MCAL wrapping the STM32 HAL (Dio, Adc, Pwm, Can), an ECU abstraction for sensors and actuators, a small runtime interface and application components, then measure the cost in flash, RAM and latency and the benefit when porting.

Uses: GPIO · ADC · CAN

The problem

Automotive software is layered so that application logic does not depend on a particular microcontroller. Students usually call HAL functions directly from application code, which makes porting and testing hard. Layering has real costs and benefits that should be measured, not assumed.

Objectives

  • Define layers and interfaces modelled on AUTOSAR Classic concepts, without claiming compliance.
  • Implement MCAL-style drivers over the STM32 HAL for Dio, Adc, Pwm and Can.
  • Implement ECU abstraction and application components for a simple BMS-plus-throttle function.
  • Compare a layered build with a direct-HAL baseline in flash, RAM, latency and coupling.
  • Demonstrate portability by changing pin mapping and running application components in a PC unit test.

What you need

From the EV Embedded Development Kit

  • STM32F103C8T6 main controller
  • Analog potentiometer input
  • Motor driver for PWM motor-control experiments
  • CAN bus interface with screw terminal
  • RGB LEDs
  • General output relay (NO / COM / NC)
  • 16×2 LCD
  • Serial UART interface

Software

  • STM32CubeIDE with HAL drivers
  • Embedded C
  • A PC C compiler (gcc) with a unit test framework such as Unity or CppUTest
  • Python 3 with python-can

Extra parts

  • USB-CAN adapter — The board has one CAN node; the adapter receives and acknowledges frames for testing.
  • Small brushed DC motor within the motor driver's rating (optional) — To see the PWM output act on a load; an LED on the driver output is enough otherwise.

Key concepts

MCAL
The Microcontroller Abstraction Layer: drivers with standard interfaces (for example Dio_WriteChannel, Adc_ReadGroup) that hide the chip's registers or HAL.
ECU abstraction
A layer that turns MCAL signals into meaningful values such as throttle per cent or cell voltage, so application code does not know which pin or ADC channel is used.
Software component and RTE
Application logic is packaged as components that communicate only through ports provided by a runtime environment, which here is a small hand-written set of read and write functions.
Configuration versus code
Pin and channel mappings live in configuration tables, so moving a signal to another pin changes data, not logic.
Coupling metrics
Counting cross-layer includes and calls, and which files change for a given modification, quantifies how independent the layers are.

Step-by-step guide

  1. 1

    Define the function and layer rules

    Pick a function that touches several peripherals: read throttle and a battery-related voltage, compute a PWM motor command with limits, drive a fault relay and RGB status LED, and send a status frame on CAN. Write the layering rules: application components include only RTE headers; the ECU abstraction includes only MCAL headers; only MCAL includes HAL headers. Note this is AUTOSAR-inspired, not an AUTOSAR-compliant stack.

    Checkpoint: A layer diagram, the rule list and the list of signals with their owners.

  2. 2

    Build the direct-HAL baseline

    First implement the function the usual way: one main.c calling HAL_ADC, TIM PWM, HAL_GPIO and HAL_CAN functions directly. Configure ADC, a timer channel for PWM to the motor driver, GPIO for the relay and LEDs and CAN at 500 kbit/s in CubeMX, and set the DIP switches for these blocks. This baseline is what you compare against. Save its timing measurements as well.

    Checkpoint: The baseline works end to end and its flash and RAM use are recorded from the map file.

  3. 3

    Design the MCAL interfaces

    Write headers for Dio (Dio_ReadChannel, Dio_WriteChannel), Adc (Adc_StartGroupConversion, Adc_ReadGroup), Pwm (Pwm_SetDutyCycle with a 0 to 0x8000 duty scale as in AUTOSAR Pwm) and Can (Can_Write with a PDU structure, plus a receive callback). Put channel mappings in Dio_Cfg.c, Adc_Cfg.c and similar configuration tables. Use your own typedefs, such as Dio_ChannelType and Std_ReturnType, so the upper layers never see STM32 types. Keep each module's public header small and document every function's behaviour and error returns before writing code.

    Checkpoint: Headers compile on their own and the configuration tables list every channel used.

  4. 4

    Implement the MCAL over HAL

    Implement each MCAL module by calling the STM32 HAL underneath, translating channel IDs through the configuration tables. Keep error handling consistent: return E_OK or E_NOT_OK and report errors to a small Det-style error hook. Do not let HAL types leak into MCAL headers. Add simple development checks, such as rejecting an invalid channel ID, that can be compiled out in release builds. Keep interrupt handling, such as the CAN receive callback, inside the MCAL and pass received data upward through a defined callback.

    Checkpoint: A test application calling only MCAL functions toggles LEDs, reads the ADC, sets PWM and sends a CAN frame.

  5. 5

    Implement the ECU abstraction

    Write IoHwAb-style functions: IoHwAb_GetThrottlePercent, IoHwAb_GetBatteryVoltage_mV, IoHwAb_SetMotorDemand, IoHwAb_SetFaultRelay and IoHwAb_SetStatusLed. These apply scaling, filtering and plausibility checks (for example throttle out of range raises a fault). Add a small communication layer that packs signals into a CAN PDU, modelled on the Com module idea. Put scaling constants and limits in a configuration file. Each function should return a status as well as a value, so components can tell a valid reading from a failed one and react, for example by commanding zero torque.

    Checkpoint: Abstraction functions return correctly scaled values that match the baseline's readings.

  6. 6

    Write the RTE and application components

    Write a simple RTE header with Rte_Read and Rte_Write functions for each port, implemented as copies to and from shared buffers with interrupts briefly disabled. Write two components: ThrottleManager (throttle to torque demand with ramp and limits) and SafetyMonitor (voltage and plausibility checks, fault relay and LED). Schedule their runnables from a fixed 10 ms and 100 ms task table.

    Checkpoint: The layered build behaves identically to the baseline in a side-by-side test with the same inputs.

  7. 7

    Unit-test components on the PC

    Because components depend only on RTE headers, compile them with gcc on the PC against a test RTE that feeds chosen inputs. Use Unity or CppUTest to test ramp limits, fault thresholds and plausibility logic, including edge cases that are hard to create on hardware. Use a coverage tool such as gcov to measure branch coverage. Run the tests automatically before every firmware build so a change to a component is checked before it reaches hardware.

    Checkpoint: A PC test suite runs and passes, with coverage of every branch in the two components.

  8. 8

    Measure the cost of layering

    Compare baseline and layered builds: flash and RAM from the map file, worst-case execution time of the 10 ms task with the DWT cycle counter, and input-to-PWM latency measured with GPIO toggles. Try different optimisation levels and see how much inlining recovers. Measure the same paths in both builds with the same probes and conditions. Identify where the overhead comes from, such as extra function calls, copies in the RTE or configuration table lookups, by profiling individual functions with the DWT counter.

    Checkpoint: A cost table for both builds at two optimisation levels.

  9. 9

    Measure the benefit of layering

    Make three changes to both builds: move the throttle to another ADC channel, move the status LED to another pin, and change the CAN frame layout. Count the files and lines changed in each build. Also count include dependencies across layers with a script to show the rules were followed. Count lines changed with git diff statistics, and note whether any application component had to change at all.

    Checkpoint: A change-impact table and a dependency report showing no forbidden includes.

  10. 10

    Analyse and document

    Discuss the trade-off between measured overhead and maintainability, which layers gave most benefit, and how a real AUTOSAR toolchain would generate the RTE and configuration. Include diagrams, results and the test suite. Present the cost and benefit numbers side by side and state the conclusion they support, including cases where layering was not worth it. Add the layer rules, interface headers and configuration tables as appendices, and note the parts of real AUTOSAR, such as BSW services, that were left out.

    Checkpoint: A report with layer diagram, cost and benefit tables and a reasoned conclusion.

Testing & validation

  • Layered and baseline builds produce identical outputs for the same input sequences.
  • No forbidden cross-layer includes (checked by script).
  • PC unit tests pass with full branch coverage of the components.
  • Overhead in flash, RAM and latency measured, not estimated.

Expected results

A layered EV firmware on the STM32 with measured overhead against a direct-HAL baseline, PC-tested application components and quantified change impact when hardware mapping changes.

Research angle for your M.Tech thesis

Research questions: what runtime and memory overhead does AUTOSAR-style layering add on a small Cortex-M3, how much does compiler optimisation recover, and how much does layering reduce change impact? Metrics: flash, RAM, WCET, input-to-output latency, files and lines changed per modification and coupling counts. Baseline: direct-HAL implementation of the same function. A strong thesis would test several functions and change scenarios and report the trade-off with clear methodology.

Take it further

  • Generate the configuration tables from a YAML or ARXML-like description with a Python script.
  • Add a simple OS-style scheduler with task timing monitoring.
  • Port the MCAL to another STM32 board and show the application compiles unchanged.

Viva and review questions

What is the role of the MCAL?

It gives the upper layers a standard driver interface so they do not depend on the specific microcontroller's registers or HAL.

Why can the components be tested on a PC?

They only use RTE interfaces, so a test RTE can supply inputs without any STM32 hardware.

Is your stack AUTOSAR compliant?

No. It follows AUTOSAR Classic layering ideas and naming, but it is hand-written and not generated or certified.

What did the layering cost?

The measured extra flash, RAM and cycles in the cost table, partly recovered with higher optimisation and inlining.

Related EV projects

All 100 EV projects