Track Operations
Quick reference for bringing the board up, checking it's healthy, and recognizing known failure signatures without re-deriving them from first principles every time. Written from real debugging sessions on this exact board.
Display DIP switch (IM0/IM1/IM2)
The ILI9488's interface mode is set by three onboard switches (SW200A/B/C = IM0/IM1/IM2), each pulled up to 3V3 with a switch to GND.
| IM2 | IM1 | IM0 | Interface |
|---|---|---|---|
| 0 | 0 | 0 | MCU18 |
| 0 | 0 | 1 | MCU9 |
| 0 | 1 | 0 | MCU16 |
| 0 | 1 | 1 | MCU8, the correct setting for this firmware |
| 1 | 0 | 1 | 3-line SPI |
| 1 | 1 | 1 | 4-line SPI |
The firmware is built for 8-bit parallel (MCU8). If the DIP is on any other setting, the panel will render something (each wrong mode reinterprets the same byte stream differently: clean-looking but wrong colors on some settings, corrupted-looking on others), which can be misleading during bring-up. Always confirm the DIP first before chasing a software bug.
Bring-up checklist, in order
Confirm each stage before moving to the next. This order reflects the actual dependency chain, and skipping ahead wastes time debugging a symptom whose real cause is one stage back.
- DIP on MCU8. See table above.
- TE is toggling. Scope PF13, should show a clean ~60 Hz signal once the panel is in the correct interface mode and scanning normally. If TE looks dead or static, the DIP is probably still wrong, not the TE circuit.
- DMA is completing. Breakpoint
DMA_TxCpltCallbackinTouchGFXHAL.cpp, it should hit repeatedly during rendering. If TE is confirmed good but DMA never completes, check GPDMA channel config againstCore/Src/gpdma.cand the FMC bring-up page. - Full-screen coverage, correct colors. If TE and DMA both check out but the image is still wrong, see the failure-signature table below before assuming it's a driver bug. The two real display faults found on this board were both physical, not firmware.
Failure signatures (display)
| Symptom | Likely cause | What to check |
|---|---|---|
| Only a fixed fraction of the screen renders (for example 15-70%), rest stays blank. Same physical region regardless of what pattern is sent, and regardless of DMA vs. CPU-loop writes | Marginal physical connection (bad solder joint or FPC seating) on a control line (CS/WR/DCX). Not a firmware/timing bug | Reseat and reflow the display FPC connector (J200), focusing on CS (PD7), WR (PD5), DCX (PE4): these are single points of failure for the whole rest of a transfer if they glitch. Confirmed root cause on this board; slowing FMC timing 10x and lowering GPIO drive strength both had zero effect, which is what pointed away from a timing/software explanation |
| Full coverage but wrong or inverted colors | Possible R/B swap, not yet confirmed on this board. Do not "fix" it with a per-pixel CPU swap (blocks DMA) | Do a clean solid-red-then-solid-blue full-screen test and judge by eye, not through a phone camera which color-casts. If genuinely swapped, fix it at the panel's MADCTL BGR bit or in TouchGFX asset generation, not in the blit path |
| Coverage is a position-encoded pattern (for example vertical color bars) that still only shows in a limited strip | Same connector/solder issue as above. Use a position-encoded test pattern instead of solid color specifically because it lets you see where the cutoff is, not just that one exists | See FMC Hardware Bring-Up for the diagnostic pattern technique |
Failure signatures (CAN / RTOS)
| Symptom | Likely cause | What to check |
|---|---|---|
RX ISR fires (confirmed via breakpoint) but the corresponding task never runs, task handle reads NULL in the debugger, and this is permanent, not intermittent | configTOTAL_HEAP_SIZE in FreeRTOSConfig.h too small: osThreadNew() fails silently and returns NULL, no crash, no log | Check the FreeRTOS heap budget against the sum of every osThreadNew/osTimerNew/osSemaphoreNew stack/object size actually created, with margin. See CAN |
| Button touch doesn't produce a CAN frame, and received values never update the UI, at the same time | Same root cause as above (heap exhaustion killed both canRxTask and canTxTask) | Same fix. This is a single bug with two symptoms, don't chase them separately |
| A CAN signal shows a wrong-looking but not obviously garbage value (off by a scale factor, wrong sign, or reading a neighboring signal's data) | Byte-offset or endianness bug in the manual decode in can_comm.c | Cross-check the exact start_bit/@0 (big-endian/Motorola) vs @1 (little-endian/Intel) against MAIN_DBC.dbc. Most signals here are @0, but not all (GPS_Speed is @1). Found three real instances of this: Pack_Inst_Voltage/Pack_Current reading the wrong byte offset entirely, landing on a different, valid neighboring signal (Pack_Open_Voltage/Average_Current, which is why it looked plausible instead of obviously broken), and GPS_Speed decoded with the wrong endianness |
Regenerating from CubeMX / TouchGFX Designer: what's safe
- CubeMX "Generate Code": safe, as long as the
.iocitself (not just hand-edited source) reflects the setting you want. Anything outsideUSER CODE BEGIN/ENDmarkers gets fully replaced from the.ioc, including GPIO pin lists and peripheralInitstruct fields likeMemoryDataWidthorStdFiltersNbr. Change the setting in the CubeMX GUI first, then generate. Don't hand-patch generated files and expect them to survive a regen. - TouchGFX Designer "Generate Code": touches
TouchGFX/generated/**only. Confirmed safe on this board when the.touchgfxproject itself hasn't changed, but always diff after, since this is the one regen path that could touch the hand-tuned partial-framebuffer/TE-pacing driver work if the project file ever changes. - Always
git diffafter either regen and review it before flashing.
