Skip to content

AxxAxx/AxxCanFlash

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

AxxCanFlash

CAN-bus firmware update system for STM32G4 microcontrollers.

Update firmware on STM32G4 CAN nodes in the field using a standard USB-CAN adapter -- no SWD access needed after initial deployment. Flash the bootloader once, then push updates over the CAN bus forever.

License: GPL-3.0


How It Works

                  USB-CAN adapter
  PC ════════════╗    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  can_flasher    ╠════║  Node 1 β”œβ”€β”€β”€β”€β”€  Node 2 β”‚ ...
  (Python CLI)   ╝    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        250 kbps CAN bus
  1. The Python tool discovers nodes on the CAN bus
  2. Sends the target node into DFU (Device Firmware Update) mode
  3. Streams the new firmware in 1024-byte blocks with CRC verification
  4. Verifies the full image CRC on the MCU, then activates and reboots

Transfer speed: ~5.5 KB/s at 250 kbps.


Components

Directory What it is
bootloader/ Standalone 16 KB bootloader firmware (C, Makefile)
can_bootloader_lib/ Reusable C library -- drop into any STM32G4 project
can_flasher/ Python CLI host tool for firmware updates
drivers/ STM32G4 HAL and CMSIS vendor SDK
tools/ CAN bus monitor/sniffer with CANopen decoding

Flash Memory Layout

0x08000000  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚   Bootloader (16 KB) β”‚  Fixed -- flashed via SWD once
0x08004000  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
            β”‚  Application (240 KB)β”‚  Updated over CAN bus
0x0803FFFF  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
            β”‚   Reserved (254 KB)  β”‚  For future use
0x0807F800  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
            β”‚   Metadata (2 KB)    β”‚  Active slot, CRCs, versions
0x0807FFFF  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Quick Start

Prerequisites

  • ARM toolchain: arm-none-eabi-gcc (download from ARM, or sudo apt install gcc-arm-none-eabi on Ubuntu/Debian)
  • Python 3.8+ with pip (Windows, Linux, or macOS)
  • USB-CAN adapter: IXXAT or Kvaser USB-to-CAN (with vendor driver installed)
  • STM32 programmer: ST-Link or J-Link (one-time initial flash only)

1. Build the bootloader

cd bootloader
make

Output: build/bootloader.bin (~10 KB). If the toolchain isn't on PATH:

make PREFIX=/path/to/arm-none-eabi-

2. Build your application

Your application needs three things to work with the bootloader:

  1. Link at 0x08004000 -- use the reference linker script in can_bootloader_lib/STM32G491KEUX_APP.ld
  2. Relocate the vector table -- add SCB->VTOR = 0x08004000; early in main()
  3. Integrate the CAN update library -- see can_bootloader_lib/README.md for the complete step-by-step guide

After building, generate a .bin file:

arm-none-eabi-objcopy -O binary your_app.elf your_app.bin

3. Initial SWD flash (one time only)

STM32_Programmer_CLI -c port=SWD -e all
STM32_Programmer_CLI -c port=SWD -w bootloader/build/bootloader.bin 0x08000000
STM32_Programmer_CLI -c port=SWD -w your_app.bin 0x08004000

On first boot with no metadata, the bootloader enters DFU mode automatically.

4. Update firmware over CAN

cd can_flasher
pip install -r requirements.txt
python -m can_flasher

The tool is fully interactive -- it will:

  1. Detect your adapter -- auto-probes IXXAT and Kvaser USB-CAN interfaces
  2. Scan the bus -- lists all nodes with their ID, firmware version, and state
  3. Select a target -- pick which node to update
  4. Select a file -- enter the path to your .bin file (supports .hex too)
  5. Transfer -- streams firmware with a progress bar and per-block CRC checks
  6. Verify -- triggers a full-image CRC-32 check on the MCU
  7. Activate -- writes metadata and reboots the node into the new firmware

No command-line flags needed -- the tool prompts for everything. Just run it and follow the prompts.


Hardware

Parameter Value
MCU STM32G491KEU6 (Cortex-M4F, 160 MHz, 512 KB flash, 112 KB SRAM)
CAN FDCAN1 on PA11 (RX) / PA12 (TX), 250 kbps
Clock 24 MHz HSE, PLL to 160 MHz
LEDs PA6 (Status), PA7 (Heartbeat)
CAN termination PB0 (120 ohm via ADG801BRTZ, GPIO-controlled)

CAN Protocol

Standard 11-bit CAN IDs 0x7E0--0x7E3, chosen to avoid CANopen and J1939 conflicts.

CAN ID Direction Purpose
0x7E0 Host -> Node Commands
0x7E1 Node -> Host Responses
0x7E2 Host -> Node Firmware data (6 bytes/frame)
0x7E3 Node -> Host Data ACK

Frame format:

  • Commands: [cmd_code, target_node_id, payload...]
  • Responses: [source_node_id, response_code, payload...]
  • Data: [seq_lo, seq_hi, byte0..byte5]
  • Broadcast target: 0xFF

Update sequence

  Host                              Node
   |                                  |
   |--- SCAN_REQUEST (0x01) -------->|
   |<--- SCAN_RESPONSE (0x01) -------|
   |                                  |
   |--- ENTER_DFU (0x02) ----------->|
   |<--- DFU_READY (0x02) -----------|  Node reboots into bootloader
   |                                  |
   |--- FW_HEADER (0x03) ----------->|  Size + CRC upper 16 bits
   |--- FW_HEADER_EXT (0x04) ------->|  CRC lower 16 bits + version
   |<--- FW_HEADER_ACK (0x03) -------|
   |                                  |
   |--- FW_DATA frames ------------->|  1024-byte blocks, 171 frames each
   |--- FW_BLOCK_DONE (0x11) ------->|  Block number + CRC-32
   |<--- FW_BLOCK_ACK (0x11) --------|  Per-block verification
   |         ... repeat ...           |
   |                                  |
   |--- FW_VERIFY (0x20) ----------->|
   |<--- FW_VERIFY_RESULT (0x20) ----|  Full-image CRC-32 check
   |                                  |
   |--- FW_ACTIVATE (0x21) --------->|
   |<--- FW_ACTIVATE_ACK (0x21) -----|  Metadata updated, node reboots
   |                                  |

DFU entry

Three ways to enter DFU mode:

  1. CAN command -- send ENTER_DFU to the running application (normal path)
  2. Boot window -- bootloader listens on CAN for 100 ms on every boot
  3. No valid app -- missing or corrupt metadata triggers DFU automatically

Adding CAN Updates to Your Own Project

See can_bootloader_lib/README.md for the full integration guide.

In short:

  1. Copy can_bootloader.c and can_bootloader.h into your project
  2. Add the FDCAN RX filter for IDs 0x7E0--0x7E3
  3. Call can_bootloader_init() after FDCAN starts
  4. Call can_bootloader_process() in your main loop
  5. Link at 0x08004000 (see reference linker script in can_bootloader_lib/)

CAN Monitor

Standalone bus sniffer with CANopen frame decoding:

python tools/can_monitor.py --interface kvaser --bitrate 250000

Supports --interface ixxat or --interface kvaser, and --fd for CAN-FD.


Project Structure

AxxCanFlash/
β”œβ”€β”€ bootloader/                 Standalone 16 KB bootloader
β”‚   β”œβ”€β”€ Makefile                Build with arm-none-eabi-gcc
β”‚   β”œβ”€β”€ Src/main.c              Boot logic, CAN, flash, clock init
β”‚   β”œβ”€β”€ Inc/                    HAL configuration headers
β”‚   β”œβ”€β”€ Startup/                Cortex-M4 startup assembly
β”‚   └── STM32G491KEUX_BOOTLOADER.ld
β”‚
β”œβ”€β”€ can_bootloader_lib/         Reusable C library
β”‚   β”œβ”€β”€ can_bootloader.c        Protocol state machine, flash ops
β”‚   β”œβ”€β”€ can_bootloader.h        Public API, constants, memory map
β”‚   β”œβ”€β”€ STM32G491KEUX_APP.ld    Reference application linker script
β”‚   └── README.md               Integration guide
β”‚
β”œβ”€β”€ can_flasher/                Python host tool
β”‚   β”œβ”€β”€ can_flasher.py          Interactive update workflow
β”‚   β”œβ”€β”€ can_protocol.py         CAN frame builders and parsers
β”‚   β”œβ”€β”€ firmware_utils.py       Binary/hex loading, CRC, validation
β”‚   └── requirements.txt        python-can, colorama
β”‚
β”œβ”€β”€ drivers/                    STM32G4 HAL + CMSIS vendor SDK
β”œβ”€β”€ tools/can_monitor.py        CAN bus sniffer
β”œβ”€β”€ LICENSE                     GPL-3.0
└── README.md

Implementation Notes

  • FDCAN clock -- defaults to HSE on STM32G4. Both bootloader and application set it to PCLK1 via __HAL_RCC_FDCAN_CONFIG(RCC_FDCANCLKSOURCE_PCLK1).
  • FDCAN filter -- the RX filter for 0x7E0--0x7E3 must be in MX_FDCAN1_Init(), not after FDCAN start. Required when a CAN stack (e.g. CANopen) re-initializes the peripheral.
  • Deferred flash operations -- flash erase, write, and CRC verification run in the main loop (can_bootloader_process()), not in the CAN RX interrupt. The ISR only buffers data and sets flags.
  • Single-slot operation -- firmware always stages to Slot A (0x08004000) since the binary is position-dependent.
  • CRC-32 -- standard zlib-compatible (polynomial 0x04C11DB7). The STM32 hardware CRC is configured with byte-reversal to match Python's zlib.crc32().

Troubleshooting

Problem Solution
No nodes found Check CAN wiring, 120 ohm termination at each end, baud rate (250 kbps)
Transfer fails mid-way MCU times out after 60s and reboots. Retry the transfer.
Bootloader too large Run make size. Compiled with -Os, must be under 16 KB.
DFU mode stuck 60-second timeout auto-resets and tries booting the app.
CRC verification fails Ensure .bin is built for 0x08004000. Don't use .elf directly.
Adapter not detected Install IXXAT VCI V4 or Kvaser CANlib driver.

License

This project is licensed under the GNU General Public License v3.0.

About

CAN-bus firmware update system for STM32G4 -- bootloader, C library, and Python flash tool.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages