CanManager Implementation
=========================
Define the implementation architecture for the type-safe CAN transport layer.
This section specifies the CanManager design, template architecture, and integration with the modm hardware abstraction layer.
Design Goals
------------
The CanManager implementation achieves:
1. Complete abstraction of FDCAN hardware from application code.
2. Zero-cost compile-time type checking of message permissions.
3. Automatic hardware initialization via template parameters.
4. Automatic filter configuration from CAN map metadata.
5. No dynamic allocation in transmission or reception paths.
6. Deterministic bounded execution time.
7. Separation of transport logic from application logic.
Software Stack
--------------
.. graphviz::
digraph CanManagerStack {
// Graph settings
rankdir=TB;
splines=polyline;
nodesep=1.2;
ranksep=1.0;
bgcolor="#FAFAFA";
node [fontname="Helvetica", fontsize=13];
edge [fontname="Helvetica", fontsize=11];
// ================================================================
// MAIN STACK
// ================================================================
// USER CODE
UserCode [
label=<
| 👤 User Application |
|
| SensorNode::StrainData msg; |
| msg.fx = 123; |
| can.send(msg); |
|
| can.processMessages(); |
|
| Work with CAN map structs only |
>,
shape=box,
style="filled,rounded",
fillcolor="#EBF5FB:#D6EAF8",
gradientangle=90,
penwidth=2,
color="#3498DB",
width=4,
height=2
];
// CANMANAGER
CanManager [
label=<
| 🔧 CanManager |
|
| send<Message>(msg) |
| • Type checking • Serialization |
|
| processMessages() |
| • Poll FIFOs • Deserialize • Dispatch |
|
| Constructor |
| • Init hardware • Configure filters |
>,
shape=box,
style="filled,rounded",
fillcolor="#FEF5E7:#FCE4B6",
gradientangle=90,
penwidth=2,
color="#F39C12",
width=4,
height=2.2
];
// GENERATED CODE
Generated [
label=<
| ⚙️ Generated from YAML |
|
| can0.hpp - Message structs |
| can0_serialize.hpp - Pack/unpack |
| NodeTraits - Permissions |
|
| ✓ Single source of truth |
| ✓ Compile-time validation |
>,
shape=box,
style="filled,rounded",
fillcolor="#E8F8F5:#D5F5E3",
gradientangle=90,
penwidth=2,
color="#27AE60",
width=4,
height=2
];
// MODM
Modm [
label=<
| 🔌 modm HAL |
|
| Fdcan1::sendMessage() |
| Fdcan1::getMessage() |
| Fdcan1::initialize() |
|
| STM32G4 hardware drivers |
>,
shape=box,
style="filled,rounded",
fillcolor="#F4ECF7:#EBDEF0",
gradientangle=90,
penwidth=2,
color="#8E44AD",
width=4,
height=1.8
];
// HARDWARE
Hardware [
label=<
| 🔩 Hardware |
|
| STM32G4 FDCAN Peripheral |
| CAN Bus (1M/5M bit/s) |
|
| Physical layer |
>,
shape=box,
style="filled,rounded",
fillcolor="#EAECEE:#D5D8DC",
gradientangle=90,
penwidth=2,
color="#5D6D7E",
width=4,
height=1.5
];
// ================================================================
// SIDE ELEMENTS
// ================================================================
// YAML SOURCE
YAML [
label=<
| 📄 can_map.yaml |
|
| messages: |
| - name: StrainData |
| id: 0x201 |
| fields: [...] |
>,
shape=note,
style="filled",
fillcolor="#FFFACD",
penwidth=1.5,
color="#F39C12",
width=2.5
];
// TX FLOW
TxFlow [
label=<
| 📤 Send Flow |
|
| 1. Create struct |
| 2. send(msg) |
| 3. Serialize |
| 4. Hardware TX |
| 5. CAN bus |
>,
shape=box,
style="filled,rounded",
fillcolor="#FADBD8",
penwidth=0,
width=1.8
];
// RX FLOW
RxFlow [
label=<
| 📥 Receive Flow |
|
| 1. CAN bus |
| 2. Hardware RX |
| 3. processMessages() |
| 4. Deserialize |
| 5. onMessage(msg) |
>,
shape=box,
style="filled,rounded",
fillcolor="#D6EAF8",
penwidth=0,
width=1.8
];
// KEY PRINCIPLE
Principle [
label=<
| ✨ Key Abstraction |
| User Sees ✓ |
Hidden ✗ |
• CAN map structs • Normal C++ types • send(), processMessages()
|
• modm::can::Message • Bit shifting • Hardware registers
|
>,
shape=plaintext
];
// ================================================================
// CONNECTIONS
// ================================================================
// Main vertical flow with large arrows
UserCode -> CanManager [
penwidth=3,
color="#E74C3C:#3498DB",
arrowsize=1.2,
label=< API calls >
];
CanManager -> Generated [
penwidth=2,
color="#7F8C8D",
style=dashed,
arrowsize=1,
label=< uses >
];
CanManager -> Modm [
penwidth=3,
color="#8E44AD",
arrowsize=1.2,
label=< calls >
];
Modm -> Hardware [
penwidth=3,
color="#5D6D7E",
arrowsize=1.2,
label=< controls >
];
// YAML generation flow
YAML -> Generated [
penwidth=2.5,
color="#F39C12",
style=dashed,
arrowsize=1.2,
label=<generates>
];
// ================================================================
// LAYOUT
// ================================================================
// Position side elements
{rank=same; UserCode; TxFlow;}
{rank=same; CanManager; YAML;}
{rank=same; Hardware; RxFlow;}
{rank=min; Principle;}
// Invisible edges for layout
TxFlow -> RxFlow [style=invis];
YAML -> Principle [style=invis];
}
Layer Descriptions
------------------
User Application Layer
^^^^^^^^^^^^^^^^^^^^^^^
The user works exclusively with CAN map structs and normal C++ types.
Key characteristics:
* Direct field access: ``msg.fx = 123``
* Type-safe API: ``can.send(msg)``
* Automatic message processing: ``can.processMessages()``
* No bit manipulation required
* No hardware knowledge needed
CanManager (Transport Layer)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Template-based transport layer providing:
* **send(msg)** - Type-checked message transmission with automatic serialization
* **processMessages()** - Polls hardware FIFOs, deserializes, and dispatches to handlers
* **Constructor** - Automatic hardware initialization and filter configuration
All hardware complexity is hidden from the user.
Generated Code Layer
^^^^^^^^^^^^^^^^^^^^
Auto-generated from YAML source:
* **can0.hpp** - Message struct definitions with metadata
* **can0_serialize.hpp** - Deterministic pack/unpack functions
* **NodeTraits** - Compile-time permissions (TxMessages, RxMessages)
Provides:
* Single source of truth
* Compile-time validation
* Type safety guarantees
modm HAL Layer
^^^^^^^^^^^^^^
Hardware abstraction layer providing:
* FDCAN peripheral drivers
* GPIO configuration
* Interrupt handling
* Platform-specific implementations
The CanManager calls modm APIs but hides them from the user.
Hardware Layer
^^^^^^^^^^^^^^
Physical layer:
* STM32G4 FDCAN peripheral
* CAN bus interface
* 1 Mbit/s nominal bitrate
* 5 Mbit/s data phase (CAN-FD)
Data Flows
----------
Send Flow (TX)
^^^^^^^^^^^^^^
1. User creates message struct
2. Calls ``can.send(msg)``
3. CanManager checks compile-time permissions
4. Automatic serialization via generated code
5. modm transmits frame
6. Hardware sends to CAN bus
Receive Flow (RX)
^^^^^^^^^^^^^^^^^
1. CAN bus receives frame
2. Hardware stores in RX FIFO
3. User calls ``can.processMessages()``
4. CanManager polls FIFO
5. Matches ID to message type
6. Automatic deserialization
7. Calls user's ``onMessage(msg)`` handler
Initialization Flow
^^^^^^^^^^^^^^^^^^^
Single constructor call handles:
1. GPIO pin connection
2. FDCAN peripheral initialization
3. CAN-FD mode configuration
4. Reads RxMessages from NodeTraits
5. Configures hardware filters automatically
6. Ready to send/receive!
Key Abstraction Principle
--------------------------
What the User Sees
^^^^^^^^^^^^^^^^^^
- CAN map structs (``SensorNode::StrainData``)
- Normal C++ types (``int32_t``, ``float``, ``uint8_t``)
- Simple API (``send()``, ``processMessages()``)
What is Hidden
^^^^^^^^^^^^^^
- ``modm::can::Message``
- Bit shifting and masking
- Hardware register access
- Filter configuration
- Serialization logic
The user works at a high level of abstraction, never touching low-level details.