Module Index
The dashboard runs FreeRTOS (via the CMSIS-RTOS2 wrapper), no superloop. TouchGFX gets its own task, CAN RX/TX get their own tasks, and they all talk through two shared structs: can_input_data (read-only outside the CAN RX path) and ui_data (driver-intent and UI state, written by screens and read by the CAN TX path). This page is the map of every task and where it's documented.
Tasks
Application tasks are created in MX_FREERTOS_Init() (app_freertos.c) and init_can_tasks() (can_tasks.c), both called before osKernelStart(). Lower osPriority_t value means lower priority.
| Task | Priority | Rate / trigger | Job | Docs |
|---|---|---|---|---|
| GUI_Task | Normal | TouchGFX internal (~60 Hz target) | Runs the TouchGFX task entry, renders dirty screen regions, drives the display DMA/TE pipeline | TouchGFX Integration |
| canRxTask | AboveNormal | on RX ISR flag | Drains the RX ring buffer, decodes recognized CAN IDs into can_input_data | CAN |
| canTxTask | Normal | on button-change flag, or every 1 ms | Packs ui_data into TX frames, runs the periodic TX scheduler, drains the TX ring buffer into the FDCAN FIFO | CAN |
| defaultTask | Normal | free-running | Idle placeholder (LED blink stub, currently a no-op osDelay(1) loop) | (none) |
| Startup frame timer | (software timer, not a task) | one-shot, 3000 ms after boot | Fires the first DASH_Buttons CAN frame after boot, deliberately delayed | CAN |
Interrupts vs. tasks
Peripherals (FDCAN, the display's GPDMA channel, the TE line) do the minimum in their ISRs: copy a frame into a ring buffer, advance a DMA block, or signal a semaphore/flag. The heavier work of decoding CAN payloads and transmitting the next framebuffer block happens in the corresponding task once it wakes.
Shared state, not message passing
Unlike a strict message-passing design, most cross-task communication here goes through two long-lived structs plus a flag:
can_input_data: written only bycanRxTaskinsideprocess_can_frames(), read byModel::tick()on the TouchGFX side.Model::tick()runsmemcmpagainst the previous snapshot and only pushes an update to the active screen's presenter when something actually changed.ui_data: written by screenViews (viaModelsetters) when the driver touches something, and read bycanTxTaskwhen packing TX frames. Abuttons_updated_flagplus a thread-flag wake (CAN_TX_WAKE_FLAG) tellscanTxTaskthere's something new to send without it having to poll.
This keeps both directions simple: the CAN side never blocks on the UI, and the UI never blocks on the CAN side.
Modules by group
- Display: TouchGFX Integration, FMC Hardware Bring-Up
- UI: Screens
- Communication: CAN
- Lap Timing: Lap Tracking
- Storage: Flash Config
Bringing up a board or chasing a hardware fault? Skip the theory and go straight to Track Operations: DIP switch settings, the bring-up checklist, and a failure-signature reference built from real debugging sessions on this board.
