Follow + star our public account to not miss exciting content
Source | Embedded Intelligence Bureau
Today, I would like to share a firmware versioning method for microcontrollers that I frequently use. I believe some friends, when starting at small companies, typically encounter version definitions like V1, V2, etc. However, as projects iterate and software expands, such simplistic version definitions can no longer meet the needs and lead to a host of issues, such as:
• Device bricking: A factory mistakenly flashed firmware for <span>hardware V3</span> onto <span>V2</span> devices, resulting in a complete batch being scrapped.
• Difficulty in problem tracing: When customers report device anomalies, engineers spend 3 days locating a <span>v1.2.3</span> PWM driver bug.
• Production chaos: The production line simultaneously has <span>test</span>/<span>mass production</span> firmware, with a misflash rate as high as 15%.
• Inefficient collaboration: After hardware engineers change sensors, the software team fails to update the version, leading to communication failures.
In summary, these issues primarily lead to four types of risks:
- Incompatibility of versions causing device functionality anomalies.
- Inability to quickly confirm the version of on-site devices.
- Inability to find the corresponding code version when reproducing issues.
- Inability to meet version traceability requirements for medical/industrial control devices.
I. Comprehensive Version Number Design Scheme
1.1 Version Number Definition Template
<span>Major.Minor.Patch-hwHardwareVersion+Date.gitHash.crcCheck</span> Example:<span>2.3.5-hw4+20250504.8a3f2c1.78A2B9C4</span>
1.2 Field Explanation Table
| Field | Rule Description | Technical Implementation |
|---|---|---|
| Major Version | Incremented on architecture-level incompatible updates | Manually set, reset minor/patch |
| Minor Version | Incremented when new backward-compatible features are added | Manually set, reset patch |
| Patch | Incremented on bug fixes/optimizations | Automatically generated based on Git commit count |
| hwHardware | PCB version number (V4.2→hw4) | Read from hardware configuration file |
| Date | Firmware compilation date (YYYYMMDD) | Automatically fetch system time |
| gitHash | First 7 characters of commit ID | <span>git rev-parse --short=7</span> |
| crcCheck | Firmware integrity check code | Calculate the CRC32 value of the entire binary file |
II. How to Implement?
2.1 Firmware Code Implementation
Currently, microcontrollers primarily use C language, focusing on efficiency. Below is an explanation using a structure:
2.1.1 Version Information Structure
// version.h
#pragma once
#include <stdint.h>
// Stored at Flash address 0x0800F000
typedef struct __attribute__((packed)) {
uint8_t major; // Major version
uint8_t minor; // Minor version
uint16_t patch; // Patch number (automatically generated)
uint8_t hw_version; // Hardware major version
uint32_t build_date; // Build date
char git_sha[8]; // Git commit hash (7 characters + null terminator)
uint32_t file_crc; // Firmware file CRC32 check code
} FirmwareVersion;
// Access version information via pointer
#define FW_VERSION ((FirmwareVersion*)0x0800F000)
2.1.2 CRC Check Integration
// Reserve CRC storage area in the linker script
LR_ROM 0x08000000 0x100000 {
ER_CRC 0x0800FFF0 EMPTY 0x00000004 { }
}
// Automatically inject CRC value after compilation
$ arm-none-eabi-objcopy --update-section .CRC=checksum.bin firmware.hex
It’s quite simple; the basic method is to reserve a section in a fixed area of flash for subsequent automated tools to fill in. Of course, you can fill it in yourself, but it can be a bit cumbersome and prone to errors.
2.2 Creating Automation Tools
Tools that you create yourself are definitely more useful. For each design, I mainly consider the following four functional aspects, which I believe are very necessary.
• 1. Ability to automatically package firmware files with version numbers.
• 2. Read hardware version from the PCB configuration file.
• 3. Integrity protection, automatically calculate and inject CRC check code.
• 4. Traceability support, embed Git commit information and build time.
2.2.3 Core Code Snippet
For the development of automation tools, I won’t go into too much detail here, as there are many ways in Windows. Here, I will only provide some rough pseudocode for reference. The idea is simple: combine the bin or hex files generated by the IDE to obtain version information and fill it into the bin and hex files.
# Automatically generate version information
def generate_version():
# Get Git information
git_sha = subprocess.check_output(
['git', 'rev-parse', '--short=7', 'HEAD']
).decode().strip()
# Read hardware version
with open('hw_config.json') as f:
hw_ver = json.load(f)['main_version']
# Calculate CRC32
with open('firmware.bin', 'rb') as f:
crc_val = zlib.crc32(f.read()) & 0xFFFFFFFF
return {
"version": f"{major}.{minor}.{patch}",
"hw_version": hw_ver,
"build_date": datetime.now().strftime("%Y%m%d"),
"git_sha": git_sha,
"crc": f"{crc_val:08X}"
}
III. Utilizing the Fields
Since we have designed the version number, we should definitely utilize each field. This also needs to be combined with the actual project requirements. For example, I usually have a verification logic in the Bootloader as shown in the following code:
// Validate during firmware upgrade
int validate_firmware(FirmwareVersion *new_ver) {
// Hardware version check
if (new_ver->hw_version != CURRENT_HW_VERSION) {
send_error("ERR_HW_MISMATCH");
return -1;
}
// CRC integrity check
uint32_t calculated_crc = calculate_crc(new_ver);
if (calculated_crc != new_ver->file_crc) {
send_error("ERR_CRC_FAIL");
return -2;
}
// Prevent version rollback
if (compare_version(new_ver, current_ver) < 0) {
send_error("ERR_VERSION_ROLLBACK");
return -3;
}
return 0;
}
In this way, during mass production, the hardware version is automatically verified, preventing misflashing; each version contains the Git hash to quickly lock down the problematic code version; and CRC checks can intercept some tampering and anomalies.
Of course, based on the above, further refinements can be made, such as integrating the packaging tool into Jenkins/GitLab CI, setting up a version dashboard on the intranet to display the status of each version in real-time, etc. This depends on how much each company values versioning.
———— END ————

● Column “Embedded Tools”
● Column “Embedded Development”
● Column “Keil Tutorials”
● Selected Tutorials from the Embedded Column
Follow our public account Reply “Add Group” to join the technical exchange group according to the rules, reply “1024” to see more content.
Click “Read the original text” to see more shares.