Skip to the content
PCBKitNew board
4 layers127 × 165 mmECU, the printed circuit board in 3D

Custom ECU (firmware and hardware) for Bespoke Electric Vehicles

Size
127 × 165 mm
Copper layers
4
Thickness
1.6 mm
Parts
258
Nets
290
Pads
986
Tracks, vias
2,518, 387
Pours
10
Routed
Yes
KiCad
KiCad 10.0 (file 20260206)
Created
7 Aug 2021
Updated
13 Aug 2026

Overview

ECU is an open-source printed circuit board by lflaherty, from the lflaherty/ev-ecu repository on GitHub, published under the Apache-2.0 licence. It is a 4-layer board of 127 × 165 mm with 258 parts on 290 nets, routed with 2,518 tracks and 387 vias.

It is built around the ISO1050DUB, SN74ABT125QDRG4Q1 and LM324DT, with the LM2903PT, CAT24C256YI-GT3 and LM1117MPX-33NOPB among its other parts. Its parts include 96 resistors, 70 capacitors, 18 ICs, 16 switches and 11 diodes. It belongs with the STM32 boards of this library.

Main components

12

README

README.md

Custom ECU for Bespoke Electric Vehicles (EVs)

build status test status code coverage

An open source reference hardware and software for building a custom ECU, designed with bespoke EVs in mind.

The ECU design is particularly tailored to FSAE/Formula Student, custom go-karts, and custom hill-climb vehicles.

Top Level Block Diagram

Cloning

This project uses submodules. Be sure to fetch submodules when cloning and pulling.

text
git clone <URL> --recurse-submodules

or

text
git pull --recurse-submodules

Directory Structure

  • firmware Firmware capable of running on the reference hardware.
  • hardware Reference hardware design.
  • tools Tools used to support operating the ECU.

For more information on sub-components, view the README in the appropriate sub-directory.

General Notes

  • The software implementation contains a state machine, fault monitoring, and throttle mapping that may not be suitable for all applications and may need changing before use. These can be found at firmware/src/vcu/vehicleLogic.
  • The ECU is also designed to be connected to specific inverter and BMS systems (Cascadia Motion Systems and OrionBMS models).
    • Drivers for these are located at firmware/src/vcu/device.
    • The hardware is designed to be flexible around the use of these devices, as they connect via CAN bus and GPIO pins.
    • The vehicle interface software layer (firmware/src/vcu/vehicleInterface) is designed to allow the drivers to be easily replaced without requiring re-work of the vehicle logic components.
    • firmware/src/vcu/startup/initialize.c dictates what software components are started in run-time.

Detailed Description

Below are detailed descriptions on the ECU hardware and firmware. They are also avilable as dedicated docs in hardware/README.md and firmware/README.md.

ECU Hardware Overview

[blocked]

[blocked]hardware/README.md

  1. Description
  2. Top Level System
  3. ECU Housing With Loom Connections
  4. Detailed Block Diagram
  5. PCBA
  6. ECU Housing
  7. PCB Fab
  8. Schematic
  9. PCB Layout
  10. ECU External Pinout
  11. Internal Microcontroller Pinout

Description

The ECU hardware provides a unified solution for several circuits required for a custom EV build:

  • A multi-channel power distribution module (PDM)
    • 6 channel
    • 24V
    • Two separate always-on 5V outputs
  • An integrated shutdown circuit (SDC)
    • A set of serial normally open relays where any one input failing will cause the circuit to open
  • ECU logic and IO unit
    • STM32 microcontroller for logical control
    • Connection ready IO:
      • 3 CAN bus
      • 7 24V GPIO inputs
        • 2x wheel speed, 4x SDC input, dashboard button
      • 4 5V ADC inputs
        • 2x accelerator pedal, 2x brake pressure
      • LED driver
      • 8 configurable 5V GPIO/ADCs
      • RS232 debug connection
      • RS232 spare unpinned connection

Top Level System

Top Level Block Diagram

ECU Housing With Loom Connections

ECU With Loom Connections

Detailed Block Diagram

Detailed Block Diagram

PCBA

ECU Housing

PCB Fab

Non-assembled PCBs. PCB Fab

Schematic

View ECU_Schematic_Print.pdf for the schematic PDF print.

PCB Layout

The PCB is laid out in 4 layers:

  1. Components and signal routing
  2. Ground
  3. 3V3
  4. Signal routing
CombinedLayer 1Layer 2Layer 3Layer 4
PCB CombinedPCB Layer 1PCB Layer 2PCB Layer 3PCB Layer 4

ECU External Pinout

External PinDescriptionSignal TypeFirmware Usage
A1Power output Ch 1Power Out (24V)Power channel control - Unused, Spare sensor rear
A2Power output Ch 2Power Out (24V)Power channel control - Wheel speed sensor rear
A3Power output Ch 3Power Out (24V)Power channel control - Inverter
A4Power output Ch 4Power Out (24V)Power channel control - BMS
A5Power output Ch 5Power Out (24V)Power channel control - Spare sensor front, wheel speed sensor front
A6Power output Ch 6Power Out (24V)Power channel control - Dashboard
A7Shutdown circuit (SDC) ResetDIN (5V)
A8SDC OutDOUT (24V)Vehicle state machine - fault monitoring
A9Power output (5V) Ch 1Power Out (5V)
A10Power output (5V) Ch 2Power Out (5V)
A11GroundGround
A12LV Battery +24VPower In (24V)
B1GroundGround
B2RS232 TXOut (RS232)PC Interface
B3RS232 RXIn (RS232)PC Interface
B4Internal spare 1*
B5Programming modeDIN (5V)PC Interface (firmware updates)
B6Configurable IO 5V 1 (MPIO1)DIO/AIN (5V)Unused, Spare sensor rear
B7Configurable IO 5V 2 (MPIO2)DIO/AIN (5V)Unused, Spare sensor rear
B8Configurable IO 5V 3 (MPIO3)DIO/AIN (5V)Unused, Spare sensor rear
B9Configurable IO 5V 4 (MPIO4)DIO/AIN (5V)Unused, Spare sensor rear
B10Configurable IO 5V 5 (MPIO5)DIO/AIN (5V)Unused, Spare sensor front
B11Configurable IO 5V 6 (MPIO6)DIO/AIN (5V)Unused, Spare sensor front
B12Configurable IO 5V 7 (MPIO7)DIO/AIN (5V)Unused, Spare sensor front
C1Internal spare 2*
C2Internal spare 3*
C3CAN0 LCAN LInverter CAN bus (Cascadia motion CAN message protocol)
C4CAN0 HCAN HInverter CAN bus (Cascadia motion CAN message protocol)
C5CAN1 LCAN LBMS (custom Orion BMS CAN configuration)
C6CAN1 HCAN HBMS (custom Orion BMS CAN configuration)
C7Ground (CAN bus shielding)Ground
C8CAN2 HCAN HUnused, provisioned for dashboard
C9CAN2 LCAN LUnused, provisioned for dashboard
C10Battery current sensor for BSPDAIN (5V)
C11BMS ErrorDIN (24V)Vehicle state machine - fault monitoring
C12Insulation Monitoring Device (IMD) ErrorDIN (24V)Vehicle state machine - fault monitoring
D1Configurable IO 5V 8 (MPIO8)DIO/AIN (5V)Unused, Spare sensor front
D2Hall effect sensorDIN (24V)Wheel speed (rear) hall effect sensor
D3Hall effect sensorDIN (24V)Wheel speed (front) hall effect sensor
D4Inverter ErrorDIN (24V)Vehicle state machine - fault monitoring
D5Start buttonDIN (24V)Vehicle state machine - start button
D6Output LED1kOhm BJT sink for LEDVehicle state machine - state indicator
D7GroundGround
D8GroundGround
D9Analog sensorAIN (5V)Thottle sensor B
D10Analog sensorAIN (5V)Thottle sensor A
D11Analog sensorAIN (5V)Brake pressure (rear) sensor
D12Analog sensorAIN (5V)Brake pressure (front) sensor

Internal Microcontroller Pinout

Pin assignments for the internal STM32F7 microcontroller

STM32 Pin Assignment

ECU Firmware Overview

[blocked]

[blocked]firmware/README.md

Description

The ECU is responsible for overseeing the vehicle state and controlling the vehicle's operation from driver input and operating all devices associated with this process.

The ECU software implements:

  • Vehicle logic:
    • Vehicle State Machine
    • Torque controller (torque mapping to control inverter)
    • Fault and safety monitoring
    • Parameter configuration
  • Drivers for connected devices and sensors:
    • Inverter/motor controller
    • Battery Management System (BMS)
    • Analog sensors (throttle/brake pedals)
    • Wheel speed sensors
    • Dashboard
  • Drivers for peripheral interfaces:
    • Standard interfaces: CAN, I2C, SPI, UART, ADC, GPIO, RTC, CRC
    • Task timer
    • Logging
    • init-time dependency checking
    • EEPROM

Dependencies

  • Third-party:
    • FreeRTOS
    • STM32 HAL
    • Unity (C Unit Test framework)

Table of Contents

  1. Description
    1. Dependencies
  2. Table of Contents
  3. Architecture
  4. Directory structure
  5. Tests
  6. Software Components
    1. User Journeys - Internal Operation Examples
      1. Starting vehicle & moving into drive state
      2. Applying torque from throttle pedal
    2. RTOS Tasks and Priorities
  7. Software Components Detail
    1. System Init Layer
      1. Init
      2. Global Interrupt Handler
    2. Vehicle Logic
      1. Vehicle State Manager
        1. Vehicle State
        2. Fault Monitoring
          1. LV System Faults
          2. Drive Faults
          3. Special Faults
      2. Soft Watchdog
      3. Throttle Controller
      4. Data Logging
    3. Vehicle Interface
      1. System Configuration
      2. Vehicle Control
      3. Vehicle State
    4. Device Driver Layer
      1. Inverter
      2. BMS
      3. Discrete Sensors
      4. Wheel Speed
      5. PC Interface
        1. Frame Format
        2. Byte Description
        3. Address Names
        4. Messages Types
          1. State Broadcast
          2. Log Message
          3. Debug Console Message (PC to ECU)
      6. IMU
      7. Dashboard output
    5. Peripheral Driver/Lib Layer
      1. CAN
      2. I2C
      3. SPI
      4. UART
      5. ADC
      6. GPIO
      7. RTC
      8. Task Timer
      9. CRC
      10. Logging
      11. Depends
    6. STM32 HAL

Architecture

The firmware is comprised of the following layers:

  1. Vehicle Logic
    Implements vehicle behavior including state management and vehicle controls.
  2. Vehicle Interface
    Interfacing layer between the vehicle logic and device drivers.
    Device drivers will push data to the vehicle state, which can be pulled by vehicle logic processes. Conversely, vehicle logic processes can control the vehicle in a driver agonstic manner by using the vehicle interface layer. The vehicle interface layer will hide the driver details from the logic layer.
  3. Device Drivers
    Implements drivers for off-board devices (e.g. inverter, wheel speed), and on-board ECU specific devices (e.g. IMU, GPS, but not peripherals such as CAN or SPI). These are ECU/vehicle specific devices.
  4. Peripheral Drivers
    Implements interfaces to common STM32 peripherals. These drivers interface to the FreeRTOS scheduling enviornment, but are otherwise agnostic to the vehicle related purpose of the ECU, and can act as generalist library. The main purpose of many of these are to provide a thread-safe manner of queueing instructions to lower level peripherals, and handling interrupts where a peripheral may be used by several device drivers.
  5. STM32 HAL
    Standard HAL package provided by ST Micro.

Visualized:

Directory structure

  • doc Supporting assets for docs
  • src
    • vcu Firmware specific to vehicle control unit.
    • system-lib Symlink to common MCU firmware.
    • hal STM32 HAL.
  • test VCU tests (overlayed on top of system-lib tests)

Tests

The library is tested via a suite of unit tests contained under test. These unit tests leverge the unit testing framework, Unity.

The tests can be executed by invoking run_tests.sh

Executing the tests will generate a code coverage report using lcov.

This will also invoke the unit tests from evfirmware-lib (System/)

Software Components

Expanding on the high level firmware stack from above, we can see all the software components:

Firmware Components

User Journeys - Internal Operation Examples

To visualize the flow of data through this system, we can consider a few examples:

Starting vehicle & moving into drive state

The driver will:

  1. Turn power on
  2. Press brake, then simultaneously press dashboard button

The ECU firmware will, internally:

  1. Power on
    1. Upon powering on, the init code will initialize all code modules.
    2. Devices will report their status to the vehicle state module.
    3. The vehicle state manager will query these fields until it is satisfied that the vehicle is in an idle, ready, and non fault state.
    4. When it transitions to this ready state, it instructs the vehicle control module to flash in a manner that indicates this state.
    5. The vehicle control module relays this requrest to the dashboard output.
    6. The state machine should be in the correct state now, and the driver is shown this state.
  2. Driver moves vehicle into drive
    1. The driver physically presses the brake and dashboard button.
    2. At a specific polling period, the discrete sensors module is recording the ADC measurements from the brake sensor ADC input, and the dashboard button input. These measurements are regularly being updated in the vehicle state.
    3. Simultaneously, the vehicle state manager is monitoring vehicle state for these fields. If the brake pressure is appropriately high, and the dashboard button has been simultaneously pressed, the vehicle state manager will move through it's drive train power on process (more details in the vehicle state manager doc), and if successful, will transition to the drive state. While transitioning, it instructs the vehicle control module on what it needs the drive train to be doing.
    4. Once in the drive state, the vehicle state manager will instruct the vehicle control module to indicate on the dashboard that the car is in drive. This invokes a method in dashboard output to update the indicator LED.

Applying torque from throttle pedal

Once the driver puts the vehicle into it's drive state (as above), then pressing the accelerator should apply power to the wheels.

The process internal to the ECU:

  • At a regular periodic interval, the discrete sense module will sample all of the ADC input sensors, including the throttle pedal sensors.
    • The sensor values are averaged. If no fault condition is found (i.e. the sensors disagree), the discrete sense module will push the latest sensor value to the vehicle state module.
  • Simultaneously, the vehicle control state will:
    1. Periodically (the task nominally runs at 100Hz/10ms) request the latest throttle sensor value. The vehicle control task and sensor tasks run at the same rate. The sensors have a higher priority, so the RTOS should execute sensors first. The sensor field in vehicle state is protected via a mutex.
    2. With the latest throttle sensor value, the throttle control module applies a torque mapping, converting the pedal depression percentage to a requested inverter torque in Nm.
    3. The throttle control module invokes the vehicle contorl module to apply this value of Nm to the drive train.
    4. The vehicle control will then invoke the inverter driver to send a request for this value of torque.
    5. The inverter driver constructs a CAN bus message for the requested torque output, and sends it.
  • Simultaneously, the vehicle state manager is monitoring the vehicle state for any fault conditions or requested state changes.

To visualize this flow of data needed by the vehicle control module:

Detailed View - Control Example

RTOS Tasks and Priorities

The modules with RTOS tasks are arranged into the following priorities.

The RTOS (FreeRTOS) uses preemption and task priorities, and round robin scheduling for equal priorities. Only modules that have RTOS tasks are shown here. The remaining drivers are invoked within an existing task context.

RTOS Task Priorities

The priorities are selected to achieve:

  • Initialization is performed first, and at a higher priority than any other tasks that are started during init.
    This requires the init task to have the highest priority.
  • We want the latest sensor data available for the logical tasks. This gives us the desire to have Prio(RT critical sensors) > Prio(RT critical logic).
  • We want real-time critical processes to have a high priority. The system performs RT critical work (i.e. driving the vehicle) alongside non-RT critical work (i.e. logging, or checking the PC interface). The RT critical work should always be performed ahead of other work, and non-RT critical work can fill the idle time in-between (most RT critical work is done at 10ms intervals, so the time after RT work has completed until the next 10ms step is available).
  • The purpose of the soft watchdog is to capture a fault or hang in a critical task. All critical tasks regularly report to the soft watchdog. It is desirable to place the watchdog task as Prio(soft watchdog) > Prio(all RT critical code) such that:
    • Upon correct operation, the watchdog runs nominally in line with RT critical code.
    • Upon a single task having a fault where it hangs, the scheduler will context switch to the soft watchdog, whose internal counter will identify a fault.
    • If the fault causes the entire system (including scheduler) to lock up, the hardware watchdog will take over.
  • Non RT critical tasks can be arranged on lower priorities.
    • Non RT critical sensors (useful for log data) are put on a higher priority than the log task for the same reason as above with the RT critical sensors/logic.

These priorities, in conjunction with the scheduler, enact the following de facto state machine:

RTOS Scheduling State Machine

This state machine is not explicitly coded as a state machine in the source, it is deliberate emergent behavior of the scheduler.

All critical logic registers with the watchdog handler. The watchdog handler will trigger a system fault if any critical logic is not handled in a timely manner.

Software Components Detail

System Init Layer

Init

As the name suggests, this module is responsible for calling the init method of all other required modules in the system, and doing so in the correct order.

The init module maintains local ownership of the data structures required for all devices. It does not expose them in the global context.

The init module will create an init task, where all initialization methods are invoked from. Many init methods (for other modules) will create further RTOS tasks. Upon init completion, the init task will be deleted, however the data storage will remain.

Global Interrupt Handler

This is really a sub-component of the init module, but broken out explicitly for clairty. Certain higher level drivers require some code to run from an interrupt handler. In some instances, the timing and frequency of these events would prohibit this from being done via RTOS task notifications (for example, the wheel speed sensors may run at several kHz, but perform very simple code for each ISR routine). The global interrupt handler simply implements the ISR routine, and calls each module's ISR as needed.

Vehicle Logic

Vehicle State Manager

The Vehicle State Manager is responsible for two tasks:

  • Managing the vehicle state
  • Monitoring fault conditions (and handling them)
Vehicle State

Vehicle State Machine

The state diagram essentially follows:

  1. Power up and wait for external devices to power up/become ready.
  2. At user request, engage HV system and wait for HV to become ready.
  3. Switch between driving states at user request.
  4. Or enter fault state from any of the above if a fault is observed.

As noted in the diagram, there is no reverse drive state due to hill-climb/FSAE/etc vehicles having no rear visibility & no need to move in reverse.

Fault Monitoring

Faults are split into two categories: Drive Faults and LV System Faults.

  • Drive faults will always invoke a transition to the fault state, regardless of origin state.
  • LV System faults will only invoke a transition to the fault state only from the LV ready state and onwards. Earlier during init (i.e. LV startup state), it is assumed that the LV systems may be in unsable states as they are initializing. After LV startup, all critical systems must remain in a known good state.

All faults are encoded into an error value. They are encoded as a binary one-hot system (i.e. individual faults are all a power of 2) such that multiple errors can be expressed as a logical OR of their individual error codes. Fault codes are encoded as a 32-bit word. LV System Faults occupy the lower 8 bits and Drive Faults oppupy top 24 bits.

LV System Faults
ComponentFault ConditionConfigurationError ValueNotes
Inverter/motorCAN message timeoutTimeout period configurable0x00000001
BMSCAN message timeoutTimeout period configurable0x00000002
InverterInverter state is unexpected0x00000004This code is only generated when the vehicle state is LV Startup. An unexpected inverter state from any other vehicle state would be error 0x01000000.
Soft Watchdog TimeoutThe soft watchdog task indicates a critical task is hung (not vehicle state manager)0x00000008A fault in the vehicle state manager is handled as a special fault (described in Special Faults).
Drive Faults
ComponentFault ConditionConfigurationError ValueNotes
Accelerator PedalOutside of calibrated rangeCalibration of pedal can be configured0x00000100A failure mode of a disconnected sensor would be handled here.
Accelerator PedalRedundant sensors disagreeDisagreement tolerance can be configured0x00000200
Brake PedalOutside of calibrated rangeCalibration of pedal can be configured0x00000400A failure mode of a disconnected sensor would be handled here.
Brake PedalRedundant sensors disagreeDisagreement tolerance can be configured0x00000800
Brake PedalAccel/brake pedal abuse (both pedals simultaneously pressed)Can be disabled0x00001000
BMSAny cell above temperature thresholdLimit configurable0x00002000
BMSCurrent draw too highLimit configurable0x00004000
BMSAny cell above threshold voltageLimit configurable0x00008000
BMSState of charge too lowLimit configurable0x00010000
BMSBMS fault indicator0x00020000
Inverter/motorInverter internal temperature too highLimit configurable0x00040000
Inverter/motorIGBTs above allowable tempLimit configurable0x00080000Thermal throttling could be considered before this occurs (not currently implemented)
Inverter/motorMotor above allowable tempLimit configurable0x00100000Thermal throttling could be considered before this occurs (not currently implemented)
Inverter/motorCurrent draw too highLimit configurable0x00200000
Inverter/motorInverter fault indicator0x00400000
Inverter/motorInverter fault message0x00800000
Inverter/motorInverter state is unexpected0x01000000This would result in a 0x00000004 if it occurs during the LV Startup vehicle state.
Special Faults

A number of scenarios require more extreme management of a fault:

  • A CPU fault
    The code is configured to handle CPU faults (with a custom CPU hard fault handler).
  • A hardware watchdog timeout
    There is a hardware watchdog in use in addition to the soft watchdog.
    The hard watchdog will catch more extreme system hangs where the soft watchdog or RTOS scheduler has hung
  • A microcontroller brown-out
  • The vehicle state manager has transitioned into a fault state, but peripherals indicate that they are still in fault state
  • A soft watchdog has been triggered, but the vehicle state manager has not transitioned to it's fault state.

If these occur, the firmware will immeidately run a special fault handler where it moves the vehicle into a safe state by:

  • Powering off inverter power channel
  • Enabling the ECU fault output (this will open the SDC relays)
  • Apply a solid LED output to indicate that the ECU is stuck
  • Hold in an infinite loop - we no longer proceeed past this point.

These operations are done with as little firmware layers as possible - they will write directly to hardware (i.e. they bypass the Vehicle Control component, and the drivers below this, as it is likely unclear what has caused this fault).

Soft Watchdog

The soft watchdog is an indepent RTOS task where, during init, other critical tasks will register to the soft watchdog. During runtime, these critical tasks must "feed" the watchdog (just call a notification task to reset their respective counters). The watchdog will count how long it has been since a feed from each critical task. If the count exceeds a timeout, it will update a flag in the vehicle state, where the state machine manager component will read this and act accordingly.

The soft watchdog will monitor the vehicle state. If a fault state is not entered within a small amount of time, the special fault handler will be invoked.

The soft watchdog task is also responsible for feeding the hardware watchdog. The soft watchdog should be able to account for all faults that don't prevent the task from running, which is where the hardware watchdog will take over (and run the special fault handler).

Throttle Controller

This module will read the throttle pedal sensor value, translate this via a torque map to a request of Nm, and requests this from the inverter. This operation (or whether the module sits idle) is controlled via the API, and is called by the vehicle state manager.

The module contains a torque map. The exact torque value is found by linearly interpolating between entries of the torque map.

The torque map implemented is:

Accelerator pedal %Torque (Nm)
0%0 Nm
10%0 Nm
50%100 Nm
70%200 Nm
100%500 Nm

  • The configured torque is based on a motor with a maximum torque of 500 Nm.
  • A deadzone of 0 Nm is created between 0 and 10% pedal depression. To account for very light movement and ADC noise, a deadzone is applied.
  • To allow for more precise control at slower speeds, the output torque up to 50% pressed is fairly limited.
  • If the driver wants to accelerate rapidly, the torque increases rapidly after this point.

Single pedal driving and regenerative braking are currently not supported. Only mechanical braking is supported.

Any regenerative braking would be added to this module, however.

Data Logging

The data logging module simply makes a copy of system state and logs it to a file on the SDMMC at on a regular periodic interval.

Vehicle Interface

System Configuration

Note: The system configuration as described here is is not currently implemented. The code currently implements a default config data structure.

The system configuration is a table of values used by the rest of the system during init. The table is populated by fields stored on the extermal EEPROM.

Configuration options include the pedal calibration, or fault timeout periods. The full list of configurations are available in firmware/src/vcu/vehicleInterface/config/configData.h.

Configuration values are stored on the 256Kbit (32KiB) EEPROM. To simplify development and modification, the external EEPROM is loaded with an instance of littlefs. The configuration items are stored as files under /config/. Their paths represent the field values and the file contents are the configuration value. Each file has a corresponding *.crc32 checksum file.

E.g. for the calibration of accelerator pedal sensor A, the files are:

  • /config/inputs/accelPedal/calibrationA/rawLower Raw ADC value corresponding to the pedal being 0% pressed.
  • /config/inputs/accelPedal/calibrationA/rawLower.crc32 32-bit CRC of rawLower
  • /config/inputs/accelPedal/calibrationA/rawUpper Raw ADC value corresponding to the pedal being fully pressed.
  • /config/inputs/accelPedal/calibrationA/rawUpper.crc32 32-bit CRC of rawUpper

The PC interface can also query and update the values on the EEPROM, but they are only loaded into the system configuration module at startup.

Vehicle Control

The Vehicle Control module is really just a shim. It contains APIs that allow control over any aspect of the car. Internally, it will just invoke one of the device drivers to achieve the requested funtionality.

Current operations supported:

  • Enable inverter
  • Disable inverter
  • Request motor torque - set torque in Nm and motion direction
  • Set power channel - set power channel number and enable/disable
  • Set ECU error - set to enabled or disabled
  • Set dash LED output - set to on or off

Vehicle State

The vehicle state module holds the current physical state of the car and owns the sychronization primitives controlling thread-safe access to these variables.

You can safely copy the state by invoking VehicleState_CopyState. Alternatively, individual elements can be accessed by using VehicleState_AccessAcquire and then reading or writing values. VehicleState_AccessRelease must always be used after a successful acquire.

The vehicle state is conceuptialized as a tree, and currently contains:

  • Input sensors
    • Averaged accelerator input [0.0 - 1.1]
    • Accelerator A input [0.0 - 1.1]
    • Accelerator B input [0.0 - 1.1]
    • Accelerator A raw ADC reading
    • Accelerator B raw ADC reading
    • Accelerator valid [bool]
  • Dash inputs
    • Dash button pressed [bool]
  • Vehicle sensors
    • GPS
      • UTC Time
      • Latitude
      • Longitude
      • Position fix?
      • Number of satellites
    • SDC
      • BMS fault state [bool, true = fault condition]
      • BSPD fault state
      • IMD fault state
      • ECU fault output active
    • Wheel speed
      • Wheel speed front (RPM)
      • Wheel speed rear (RPM)
      • Wheel speed front count
      • Wheel speed rear count
  • GLV state
    • PDM channel state 1-8 [bool, true = power enabled]
  • Battery state
    • Maximum cell voltage [Volts]
    • Maximum cell voltage - Cell ID
    • Maximum cell temperature [Celsius]
    • Maximum cell temperature - Cell ID
    • Minimum cell voltage [Volts]
    • Minimum cell voltage - Cell ID
    • Minimum cell temperature [Celsius]
    • Minimum cell temperature - Cell ID
    • DC Current [Amps]
    • DC Voltage [Volts]
    • BMS Fault indicator [bool]
    • BMS number of populated cells
    • BMS message counter
    • BMS failsafe status
  • Motor
    • Temperature [Celsius]
    • Angle [Degrees]
    • Speed [rpm]
    • Phase A current [Amps]
    • Phase B current [Amps]
    • Phase C current [Amps]
    • Calculated torque [Nm]
  • Inverter
    • Module A temperature [Celsius]
    • Module B temperature [Celsius]
    • Module C temperature [Celsius]
    • Gate driver temperature [Celsius]
    • Control board temperature [Celsius]
    • Output frequency [Hz]
    • DC bus current [Amps]
    • DC bus voltage [Amps]
    • Output voltage [Line-neutral voltage]
    • D-axis voltage [Volts]
    • Q-axis voltage [Volts]
    • Commanded flux [Wb]
    • Feedback flux [Wb]
    • D-axis current feedback [Amps]
    • Q-axis current feedback [Amps]
    • D-axis commanded current [Amps]
    • Q-axis commanded current [Amps]
    • Commanded torque [Nm]
    • Modulation index
    • Flux weakening output [Amps]
    • Inverter VSM state
    • Inverter state
    • Discharge state
    • Inverter enabled [bool]
    • Motion direction
    • Timer counts (incremented every 3ms)
    • Run faults (inverter fault codes)
    • Post fault (inverter fault codes)

Device Driver Layer

Inverter

This module will handle CAN bus messages from the inverter, updating the state machine, and will construct CAN bus messages to send to the inverter based on the driver API.

The inverter driver is based a Cascadia Motion Sytems CM200 inverter.

BMS

This module will handle CAN bus messages from the inverter, updating the state machine.

The BMS driver is based on an OrionBMS 2 with the following CAN message configuration:

CAN IDMessage NameFrequencyData[0]Data[1]Data[2]Data[3]Data[4]Data[5]Data[6]Data[7]
0x301Max cell state100 HzMax cell temperature LSBMax cell temperature MSBMax temperature cell IDMax cell voltage LSBMax cell voltage MSBMax voltage cell ID00
0x302Min cell state100 HzMin cell temperature LSBMin cell temperature MSBMin temperature cell IDMin cell voltage LSBMin cell voltage MSBMin voltage cell ID00
0x303Pack state100 HzDC current LSBDC current MSBBus voltage LSBBus voltage MSBState of charge LSBState of charge MSB00
0x304Status100 HzCounterPopulated CellsFailsafe status LSBFailsafe status MSB0000

All fields size >1 byte are sent LSB first.

Data Formats:

FieldData TypeEncodingRangeNotes
Temperatureint16Temperature in degrees C x10-3276.8 °C to +3276.7 °C
Cell Voltageint16Voltage in Volts x100-327.68 V to +327.67 V
Cell IDuint8Raw cell number0 to 255
Pack Currentint16Current in Amps x10-3276.8 A to +3276.7 A
Pack Voltageint16Current in Volts x10-3276.8 V to +3276.7 V
State of chargeuint16Percentage x1000% to 655.35%
Counteruint8Raw value0 to 255Increments for every message

Discrete Sensors

This module perodically reads discrete sensors (GPIO inputs and ADC inputs) and pushes the results to the vehicle state.

This module currently handles:

  • Accelerator pedal A
  • Accelerator pedal B
  • Average accelerator pedal
  • Brake pressure front
  • Brake pressure rear
  • Bashboard button

The module updates these values on a regular 100Hz update tick.

Wheel Speed

The wheel speed sensor measures the speed of a hall effect wheel speed sensor. The sensor driver assumes that the spacing of the teeth is uniform, and that the high/low size of the sensor teeth are equal. The number of teeth is configurable at init. The sensor will produce a new reading every 1 second.

The voltage input appears as a PWM signal. The frequency of the PWM signal encodes the wheel speed.

The module implements an ISR (that is invoked by the Global Interrupt Handler). This does a GPIO read on the wheel speed sensor hall effect inputs, and then pushes these values to an RTOS queue. In the main task, the driver pulls these readings off, and calculates the number of 0V -> 24V transitions over the 1s period, informing a simple conversion to RPM.

Power Distribution Module (PDM)

This module is simply an abstraction to access the correct GPIO pins to control the 24V power output channels. When updating a power channel, the module will also reflect the change in the Vehicle State module.

Shutdown Circuit (SDC)

The module serves two purposes:

  • A simple abstraction to access the ECU error output
  • Update the Vehicle State when any of the SDC error inputs (BMS error, IMD error, SDC error) is asserted.

The Global Interrupt Handler invokes the SDC ISR method when the GPIO interrupt is triggered. The SDC ISR method will read the state of all the SDC inputs via a simple GPIO read. If any errors are asserted, a FreeRTOS task notification is used to awake the SDC task, finally updating the field in the Vehicle State.

PC Interface

The PC interface implements the serial (RS232) interface to a debug/control computer.

The following functions can currently be performed over the interface:

  • Sending ECU log messages
  • Sending ECU state updates
  • Debug terminal

The serial interface communication is performed via a Modbus protocol. This allows the debug PC to multiplex the different types of data and handle independent operations. It also allows easier expansion of the PC interface.

Frame Format

Words are transmitted MSB first.

The console is configured to 115200 bps.

ByteContent
0Start
1Address[1]
2Address[0]
3Function[1]
4Function[0]
5 .. 5+(n-1)Data[n..0]
6+(n-1)CRC[3]
7+(n-1)CRC[2]
8+(n-1)CRC[1]
9+(n-1)CRC[0]
10+(n-1)End[1]
11+(n-1)End[0]
Byte Description
SectionLength (Bytes)Description
Start1Payload begin. Equal to 0x80
Address2Address of receiver
Function2Message type
DatanMessage data
CRC432-bit cyclic redundancy check
End2Frame end. Equal to
Address Names
AddressTarget
0x01ECU
0x02Debug PC
Messages Types
State Broadcast
Message NameState Broadbast
Function0x01
Transmit Rate1Hz per data field
Send Address0x01
Target Address0x02
Data Length7
Message Length18
DescriptionTransmit live vehicle data from the Vehicle Interface layer.
Data[0]Data[1]Data[2]Data[3]Data[4]Data[5]Data[6]
FieldID[1]FieldID[0]FieldSizeData[3]Data[2]Data[1]Data[0]
  • FieldID ID of field. IDs are located at firmware/src/vcu/device/pcinterface/fieldId.h.
  • FieldSize size of Data (bytes).
  • Data[3..0] latest contents of field.
Log Message
Message NameLog Message
Function0x02
Transmit RateVariable (upon ECU log event)
Send Address0x01
Target Address0x02
Data Length32
Message Length43
DescriptionContinuously dumps print/log messages. Transmits up to 32 bytes at once - no more to avoid data corruption. Unused bytes are left as zeros and are ignored. Note that this message could be read with an incorect CRC when displaying output as it just contains ASCII text.
Data[0..31]
LogChar[0..31]
  • LogChar ASCII byte
Debug Console Message (PC to ECU)
Message NameDebug Message (ECU -> PC)
Function0x09
Transmit RateVariable (upon response generated)
Send Address0x02
Target Address0x01
Data Length8
Message Length17
DescriptionTransmits a text command to the ECU. For help with the debug terminal, issue the command help. View more details at firmware/src/vcu/device/pcinterface/debugtermcommands.c.
Data[0..7]
CmdStr[0..7]
  • CmdStr String to append to the internal command buffer.
Debug Console Message (ECU -> PC)

This message ID is currently TBD. The current implementation prints log messages when handling debug console requests.

Multi-purpose IO (MPIO)

Implements a simple wrapper around the multi-purpose IO components on the ECU board. Allows an ADC read operation, or simple GPIO read/write.

The module will set the output mode pin. As with the ADC/GPIO drivers, the STM32-specific pin configuration is done seperately.

IMU

The board contains a MPU-6050 IMU. The IMU interfaces over the I2C bus.

The data from the IMU (accelerometer and gyroscope) would be pushed into the vehicle state.

This data is currently not used for any vehicle control and exists for offboard analysis.

This module is currently unimplemented.

Dashboard output

The current dashboard consists of a single LED output and button input. The dashboard output controls a single GPIO pin.

Peripheral Driver/Lib Layer

CAN

The CAN bus module provides a thread safe wrapper around the CAN bus hardware on the STM32. The CAN interface is accessed via the STM32 HAL internally.

FreeRTOS queues and semaphores are used to gate access to the peripheral, with access to three in parallel possible. The internal queues allow any FreeRTOS thread/task to enqueue a CAN tx message by making CAN_SendMessage thread safe. The module does this by using CAN_SendMessage to place pending messages on a queue and automatically sending queued messages when Tx complete interrupts trigger.

This module also implements the CAN rx ISRs. Other code modules can register to "listen" for CAN messages. The other code modules register a queue where their CAN messages can be placed and a FreeRTOS notification structure to notify. When the rx ISR is triggered, the CAN message is read, the appropriate target queues are found via the registered CAN IDs, the message is copied to the appropriate queues, and the appropriate task notifications are made.

I2C

The I2C driver provides a thread safe wrapper of the STM32 HAL DMA I2C interface. The driver will wait for the hardware to become free (up to a timeout).

One difference to the CAN bus driver is that it works synchronously with Read and Write operations, and does not allow for registering for responses (the I2C comms are completed inline).

SPI

The SPI driver works similarly to the I2C driver in that it provides a thread safe wrapper around the SPI peripherals. However, a simple SPI transfer (and a blocking transfer) are provided.

The blocking transfer uses the STM32 HAL blocking API and will busy loop while waiting for the transfer to complete.

The non blocking transfer uses DMA to perform the transfer and immediately returns when the transfer has begun.

UART

The UART driver works almost entirely the same as the CAN driver:

  • The UART_SendMessage will either send the message directly to the UART hardware, or place it in a software queue if the hardware is already performing a transfer.
  • For receive, the API allows for setting a receive data stream. The receive interrupt (which can handle variable length data using the frame end detection) will place data on the stream as it receives data. Unlike CAN, only one consumer can be applied per UART interface.

The STM32 hardware allows the U(S)ART to be managed via DMA, so this driver utilizes the DMA controller to run the UART interfaces.

ADC

The ADC driver configures the ADC peripherals to read via DMA and provide a thread safe interface (a simple ADC_Get once the device is running).

A helper function to apply linear scaling is also provided.

GPIO

This module of code simply adds an abstraction layer above the STM32 HAL GPIO code to improve code portability. This layer is intended to fully cover the peripheral capabilities, so a simple GPIO wrapper is needed.

RTC

Similarly to the GPIO, this module wraps around the STM32 HAL RTC driver.

Task Timer

The task timer module implements the timer ISR and sends FreeRTOS task notifications. Tasks can notify for period notifications (e.g. tasks that run at 100Hz) that allow them to run exactly at a specific frequency.

The depdendent tasks can pend on a notification until the task timer notifies them that they can run. This allows 100Hz tasks to run exactly at 10ms intervals without delay (regardless of run-time jitter).

The hardware is configured with an external crystal, making the timing highly accurate. The timer has been tested to maintain timing accuracy up to 20kHz.

When timing is above 1kHz, FreeRTOS scheduling must be recalculated. This currently isn't in place as the ECU only requires 100Hz.

CRC

This module is a simple wrapper around the STM32 CRC calculation hardware.

Logging

The logging module's use is to provide the Log_Print method which replaces printf.

The logging module will opertionally output the log data to two sources:

  • SWD SWO - the SWD header is wired to support the SWO pin, so when using a debugger, SWO signals can be monitored, and the logging module can copy all log data to this interface.
  • An arbitrary stream - this can be configured to copy data to any software stream. In practice, the PC Interface module will invoke Log_SetSerialStream to consume the log data, and will subsequently encode the logs onto a special serial structure for a connected PC on the UART interface.

Depends

Each module can use the REGISTER* macros to get a module ID which can be depended on by other module. Modules' init methods can invoke a DEPEND_ON or DEPEND_ON_STATIC method to ensure that other code that a particular module depends on has actually been initialized.

STM32 HAL

The STM32 HAL is simply the ST Micro provided HAL for the STM32 F7 microcontroller.

Parts

258
QtyValueFootprintReferences
36100nCapacitor_SMD:C_0805_2012MetricC1, C2, C5, C24, C25, C26, C27, C28, C29, C30, C39, C48, C54, C55, C56, C57 +20
251kResistor_SMD:R_0805_2012MetricR1, R2, R10, R17, R18, R21, R22, R29, R30, R31, R32, R33, R37, R39, R41, R103 +9
134k7Resistor_SMD:R_0805_2012MetricR19, R20, R23, R24, R25, R27, R28, R36, R40, R51, R53, R55, R102
1212kResistor_SMD:R_0805_2012MetricR64, R65, R66, R67, R78, R79, R82, R83, R86, R88, R91, R92
121n2Capacitor_SMD:C_0805_2012MetricC35, C36, C37, C38, C40, C41, C42, C43, C44, C45, C46, C47
126k2Resistor_SMD:R_0805_2012MetricR60, R61, R62, R63, R76, R77, R80, R81, R84, R85, R89, R90
82k2Resistor_SMD:R_0805_2012MetricR34, R35, R93, R94, R95, R96, R97, R98
83.3VDiode_SMD:D_MiniMELFZD5, ZD10, ZD11, ZD12, ZD13, ZD16, ZD17, ZD18
7PBHV8115T,215Package_TO_SOT_SMD:SOT-23Q1, Q2, Q3, Q4, Q5, Q6, Q7
60154010.DRLSamacSys_Parts:0154010DRLF1, F2, F3, F5, F6, F7
610kResistor_SMD:R_0805_2012MetricR4, R7, R26, R43, R100, R101
6420RResistor_SMD:R_0805_2012MetricR11, R12, R13, R14, R15, R16
647kResistor_SMD:R_0805_2012MetricR71, R72, R73, R74, R75, R99
6CPC1706YSamacSys_Parts:CPC1706YK11, K12, K13, K14, K15, K16
51uCapacitor_SMD:C_0805_2012MetricC7, C8, C9, C74, C75
5LEDLED_SMD:LED_0805_2012MetricD7, D8, D9, D14, D15
412pCapacitor_SMD:C_0805_2012MetricC33, C34, C52, C53
45-1419124-7SamacSys_Parts:514191247K1, K2, K3, K5
4AQW212EHASamacSys_Parts:SOP254P962X275-8NK6, K7, K8, K10
4DDiode_SMD:D_SMAD1, D2, D3, D4
310KSamacSys_Parts:3306F1103RV1, RV2, RV3
3120RResistor_SMD:R_0805_2012MetricR57, R58, R59
32n2Capacitor_SMD:C_0805_2012MetricC49, C50, C51
3ISO1050DUBPackage_SO:SOP-8_6.62x9.15mm_P2.54mmU4, U5, U6
3LM324DTPackage_SO:SOIC-14_3.9x8.7mm_P1.27mmU7, U14, U15
3MountingHoleMountingHole:MountingHole_2.5mmH1, H2, H3
2100 @ 100MHzInductor_SMD:L_0805_2012MetricFB1, FB2
210uCapacitor_SMD:C_0805_2012MetricC79, C80
2220RResistor_SMD:R_0805_2012MetricR44, R87
222uCapacitor_SMD:C_1206_3216MetricC17, C18
22u2Capacitor_SMD:C_0805_2012MetricC31, C32
2JS202011CQNSamacSys_Parts:JS202011CQNS1, S2
2LM2903PTPackage_SO:TSSOP-8_4.4x3mm_P0.65mmU10, U11
2SN74ABT125QDRG4Q1Package_SO:SOIC-14_3.9x8.7mm_P1.27mmIC7, IC8
1100kResistor_SMD:R_0805_2012MetricR42
1100uCapacitor_SMD:C_1206_3216MetricC10
110nCapacitor_SMD:C_0805_2012MetricC82
110u/35VCapacitor_SMD:C_0805_2012MetricC16
116MHzCrystal:Crystal_SMD_3225-4Pin_3.2x2.5mmY1
124VTestPoint:TestPoint_THTPad_D2.5mm_Drill1.2mmTP7
132.768kHz (ECS-.327-9-34QN-TR)Crystal:Crystal_SMD_3215-2Pin_3.2x1.5mmY2
13V/CR2032Battery:BatteryHolder_Keystone_1060_1x2032BT1
13V3TestPoint:TestPoint_THTPad_D2.5mm_Drill1.2mmTP9
1470RResistor_SMD:R_0805_2012MetricR38
14u7Capacitor_SMD:C_0805_2012MetricC76
15VTestPoint:TestPoint_THTPad_D2.5mm_Drill1.2mmTP8
1619540-1SamacSys_Parts:6195401J5
1680RResistor_SMD:R_0805_2012MetricR3
174HC14D,653Package_SO:SOIC-14_3.9x8.7mm_P1.27mmU9
1790SamacSys_Parts:790IC6
1ARM_SWDConnector_PinHeader_1.27mm:PinHeader_2x05_P1.27mm_Vertical_SMDJ4
1B5819WDiode_SMD:D_SOD-123D6
1BOOTJumper:SolderJumper-3_P1.3mm_Open_RoundedPad1.0x1.5mm_NumberLabelsJP6
1BOOT_EXTJumper:SolderJumper-2_P1.3mm_Open_RoundedPad1.0x1.5mmJP1
1BRKTestPoint:TestPoint_THTPad_D2.5mm_Drill1.2mmTP3
1CAT24C256YI-GT3Package_SO:TSSOP-8_4.4x3mm_P0.65mmU16
1CURRTestPoint:TestPoint_THTPad_D2.5mm_Drill1.2mmTP2
1DT13-48PABCD-R015_PHYSICALDT1348PABCDR015:DT1348PABCDR015_PHYSICALJ1
1ExpansionConnector_PinHeader_2.54mm:PinHeader_2x09_P2.54mm_VerticalJ2
1GNDTestPoint:TestPoint_THTPad_D2.5mm_Drill1.2mmTP1
1LM1117MPX-33NOPBPackage_TO_SOT_SMD:SOT-223-3_TabPin2U12
1MAX3232IDWRPackage_SO:SOIC-16W_7.5x10.3mm_P1.27mmU2
1MPU-6050Sensor_Motion:InvenSense_QFN-24_4x4mm_P0.5mmU8
1Micro_SD_Card_DetConnector_Card:microSD_HC_Molex_104031-0811J3
1NC7S02M5XSamacSys_Parts:SOT95P280X125-5NU1
1RCTestPoint:TestPoint_THTPad_D2.5mm_Drill1.2mmTP4
1STM32F765ZITxPackage_QFP:LQFP-144_20x20mm_P0.5mmU3
1V15P45S-M3_86ASamacSys_Parts:V15P45SM387AD19
1mEZDPD3603ASamacSys_Parts:mEZDPD3603APS1

Questions

How big is the ECU PCB?

It measures 127 × 165 mm and has 4 copper layers, 1.6 mm thick.

What parts does the ECU use?

258 parts, built around the ISO1050DUB, SN74ABT125QDRG4Q1, LM324DT and LM2903PT. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the ECU KiCad files?

From the lflaherty/ev-ecu repository on GitHub, at the commit this page was made from: the board is hardware/kicad_project/ECU.kicad_pcb and its schematic hardware/kicad_project/BSPD.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the ECU design in my own project?

Its repository publishes it under the Apache-2.0 licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).

Similar boards

  • 2 layers76.1 × 45.5 mmKick, the printed circuit board in 3D

    Kick

    Gamu2124/RoboCupJunior2026-RobotDatas

    Complete data for the robot that won the Japan and World championships / 日本大会優勝 & 世界大会優勝したロボットの全データ

    26 parts1
  • 2 layers63.2 × 38.5 mmHades, the printed circuit board in 3D

    Hades

    vmangelschots/hades-poolcontroller-pcb

    The pcb for the hades poolcontroller

    25 parts1
  • 4 layers150 × 100 mmPnP Mother Board, the printed circuit board in 3D

    PnP Mother Board

    Arnaud-Apu-Fanovard/Pick-Place

    Pick and Place automatisée pour PCB designer

    356 parts
  • 4 layers80.1 × 47.1 mm8 Way Relay Board, the printed circuit board in 3D

    8 Way Relay Board

    Pan-Robotics/CPC1706Y-8-Way-Relay-Board

    CPC1706Y 8 Way Solid State Relay Board

    61 parts
  • 2 layers121 × 75.5 mmJlctest, the printed circuit board in 3D

    Jlctest

    cjheath/jlcbasic

    JLC Basic parts library for Kicad fetched and converted from EasyEDA

    46 parts5
  • 4 layers89 × 58 mmSH ESP32, the printed circuit board in 3D

    SH ESP32

    hatlabs/SH-ESP32-hardware

    Sailor Hat for ESP32 hardware

    321 parts49