Skip to main content

Command Palette

Search for a command to run...

8Bytes is not too bad!

CAN CC Bootloader.

Updated
3 min read
8Bytes is not too bad!

Last year, I was working on developing Bootloader to receive firmware updates over CAN(Controller Area Network). Though the MCU I was working on supported CAN FD(Flexible Data rate), the USB to CAN Adapter had only supported CAN CC(Classic). This means, the CAN payload can only be 8bytes maximum unlike the CAN FD which can support upto 64 bytes. This might not seem like a huge problem at first, I mean instead of 1 cycle, the CAN CC takes 8 cycles. Big deal? Well, it was. The existing architecture required a block of 69 bytes to write to FLASH.

2B Offset + 1B Length + 64B Data + 2B CRC = 69B. This meant that the 8 byte payloads need to be packaged into a 69byte frame before sending over for flashing.

To handle this, I developed a Python script on the host side that divides the firmware hex file into chunks of 64 bytes. Since the hex file size won't always be a multiple of 64, there will typically be a final chunk smaller than 64 bytes. So we get n full chunks of 64 bytes, where n=hex_file_size //64. and one last chunk of size = hex_file_size % 64. The script takes these chunks and initiates transmission over the USB-CAN adapter in smaller frames of 8 bytes each.

On the firmware side, the CAN driver needs to know when a full 64-byte chunk has been received, assemble the frame, and pass it on for flashing. The simplest approach would be to initialise a 64-byte buffer and build the frame once it's full, but this breaks down for the last chunk, which may never fill the buffer and would therefore never be processed. There's also no natural place in this scheme to include the chunk offset and CRC during transmission.

To tackle this, I introduced two special frames: a Start of Frame (SoF) and an End of Frame (EoF). The SoF consists of a predefined SoF command byte, the chunk offset, and the chunk length. When the firmware receives this command, recognised because the same command definitions exist on both the host and firmware sides, it saves the chunk offset and initialises a buffer of the specified length. The script then sends a series of data frames, each up to 8 bytes, containing the actual hex file data, which the firmware appends to the buffer. Once the full chunk is sent, the script sends an EoF consisting of a predefined EoF command byte and the chunk CRC.

On receiving the EoF, the firmware assembles the complete 69-byte frame and passes it to the bootloader application, where the CRC is verified and the data, offset, and length are passed on to the flash driver. It's worth noting that CAN's built-in error checking only covers each individual 8-byte frame and the chunk-level CRC is what ensures the integrity of the fully reassembled 64-byte block. This process repeats until the entire hex file has been transmitted.

The SoF/EoF framing approach turned out to be a clean solution to what initially seemed like a minor hardware limitation. By pushing the fragmentation and reassembly logic into a lightweight protocol layer, the firmware remained largely agnostic to whether it was receiving data in 8-byte CAN CC frames or larger CAN FD payloads (only the host-side script would need to change). For anyone working under similar constraints, the key takeaway is that a minimal framing protocol can make a fragile chunking scheme into something robust and extensible.

Workstation

Part 1 of 1

Practical system builds, architecture decisions, and implementation details. Where ideas are tested against real constraints.