How to Update Firmware Over UART in Embedded C
To update firmware over
UART in embedded C, implement a bootloader that receives firmware data packets via UART, verifies them, and writes them to flash memory. Use a communication protocol with start, data, checksum, and end markers to ensure data integrity during transfer.Syntax
The basic steps to update firmware over UART involve initializing UART communication, receiving data packets, verifying data integrity, and writing to flash memory.
- UART_Init(): Sets up UART hardware.
- UART_Receive(): Reads incoming bytes.
- Verify_Checksum(): Confirms data correctness.
- Flash_Write(): Writes data to program memory.
c
void UART_Init(void); int UART_Receive(uint8_t *buffer, int length); int Verify_Checksum(uint8_t *data, int length, uint8_t checksum); int Flash_Write(uint32_t address, uint8_t *data, int length);
Example
This example shows a simple bootloader loop that receives firmware data packets over UART, checks a simple checksum, and writes the data to flash memory.
c
#include <stdint.h> #include <stdbool.h> #include <string.h> #define PACKET_SIZE 64 #define FLASH_START_ADDRESS 0x08010000 // Mock functions for UART and Flash void UART_Init(void) { /* Initialize UART hardware */ } int UART_Receive(uint8_t *buffer, int length) { // Blocking receive simulation // In real code, read from UART hardware return length; // Return number of bytes received } bool Verify_Checksum(uint8_t *data, int length, uint8_t checksum) { uint8_t sum = 0; for (int i = 0; i < length; i++) { sum += data[i]; } return (sum == checksum); } int Flash_Write(uint32_t address, uint8_t *data, int length) { // Simulate flash write // In real code, unlock flash, erase sector, write data return 0; // 0 for success } int main(void) { UART_Init(); uint8_t packet[PACKET_SIZE + 2]; // Data + checksum + end byte uint32_t flash_address = FLASH_START_ADDRESS; while (true) { // Receive packet: [data...][checksum][end_marker] int received = UART_Receive(packet, PACKET_SIZE + 2); if (received != PACKET_SIZE + 2) { continue; // Wait for full packet } uint8_t *data = packet; uint8_t checksum = packet[PACKET_SIZE]; uint8_t end_marker = packet[PACKET_SIZE + 1]; if (end_marker != 0x04) { // EOT (End of Transmission) marker continue; // Invalid packet } if (!Verify_Checksum(data, PACKET_SIZE, checksum)) { continue; // Checksum failed } if (Flash_Write(flash_address, data, PACKET_SIZE) != 0) { // Handle flash write error break; } flash_address += PACKET_SIZE; // Add condition to break loop when firmware update is complete } // Optionally reset device to run new firmware return 0; }
Common Pitfalls
- Not verifying data integrity: Skipping checksum or CRC checks can cause corrupted firmware to be written.
- Writing to flash incorrectly: Flash memory requires unlocking, erasing sectors, and writing carefully; skipping steps can brick the device.
- Ignoring UART framing errors: Noise or framing errors can corrupt data; handle UART errors properly.
- No bootloader fallback: Without a safe bootloader, failed updates can leave the device unusable.
c
/* Wrong: Writing flash without erase or unlock */ int Flash_Write(uint32_t address, uint8_t *data, int length) { // Missing flash unlock and erase for (int i = 0; i < length; i++) { *((volatile uint8_t *)(address + i)) = data[i]; // Unsafe write } return 0; } /* Right: Proper flash write sequence (pseudo-code) */ int Flash_Write(uint32_t address, uint8_t *data, int length) { Flash_Unlock(); Flash_Erase_Sector(address); Flash_Program(address, data, length); Flash_Lock(); return 0; }
Quick Reference
- Initialize UART before receiving data.
- Use a packet format with data, checksum, and end marker.
- Verify checksum or CRC before writing to flash.
- Unlock, erase, write, and lock flash memory properly.
- Implement a bootloader to safely handle firmware updates.
Key Takeaways
Always verify firmware data integrity with checksum or CRC before flash writing.
Proper flash memory handling (unlock, erase, write, lock) is critical to avoid device bricking.
Use a bootloader that listens on UART and safely updates firmware in chunks.
Design a simple communication protocol with start, data, checksum, and end markers.
Test firmware update process thoroughly to handle errors and recover safely.