前言
在 STM32 嵌入式开发中,Bootloader 是一个不可或缺的模块。ST 公司为 STM32 提供了功能完备的官方 Bootloader,支持多种通信接口(如 USART、USB DFU、I2C、SPI 等),适用于标准的固件更新方案。
然而,随着系统需求的多样化和定制化,自定义 Bootloader 越来越成为项目开发者的首选。
方案比较
官方 bootloader 优点
- 开箱即用:无需编写任何代码,即可通过 ROM 中的 Bootloader 进行下载。
- 支持多种接口:如 USART、USB、I2C 等,适合简单的量产烧录。
- 稳定性高:由 ST 官方提供,经过长期验证。
官方 bootloader 局限
- 不可修改
官方 Bootloader 存储在芯片的系统 ROM 中,属于只读区域,无法添加用户逻辑或扩展功能。 - 接口受限
实际项目中可能需要使用 CAN、以太网、BLE 等接口更新固件,而这些通常未被官方 Bootloader 支持或实现较复杂。 - 协议封闭
官方 Bootloader 通信协议较为复杂,难以与高层系统(如云 OTA、手机 App)灵活集成。 - 安全性不足
默认无固件加密或签名机制,容易被反编译和恶意刷写。 - 启动方式有限
官方 Bootloader 通常通过 BOOT 引脚或 Option Byte 来触发,不够灵活。例如,无法通过软件条件(如特定按键组合)进入 Bootloader 模式。
通过自定义bootloader,可以实现多种方式控制触发升级逻辑:
- 按键时序组合
- 接收特定指令
- Flash 中标志位
- RTC Backup 寄存器
- Watchdog Timeout
- 远程 OTA
主要思想
本项目实现了一个从 TF 读取升级固件并更新到 flash 中的功能,同时包括了绑定 chipID 加密操作,提升操作便利性的同时提供了绝对的安全可靠性。
主要思想史利用单片机的唯一的ChipID
于用户自定义的密钥输入sha256算法得出32字节的加密密钥,然后用这个密钥对固件进行异或运算加密
设计细节
固件加密
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <shlobj.h>
#include <commctrl.h>
extern "C" {
#include "../Core/Inc/sha256.h"
}
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "shell32.lib")
// 控件ID定义
#define IDC_BROWSE_CID 101
#define IDC_BROWSE_APP 102
#define IDC_BROWSE_OUTPUT 103
#define IDC_START 104
#define IDC_EDIT_CID 105
#define IDC_EDIT_APP 106
#define IDC_EDIT_OUTPUT 107
#define IDC_LOG 108
#define HASH_SIZE 32
// 全局变量
uint32_t chipID[3];
uint8_t encryptionKey[HASH_SIZE];
HWND g_hEditLog = NULL;
const char* userKey = "Your_Password";
// 函数声明
void generate_encryption_key(const uint32_t* chipID, const char* userKey, uint8_t* encryptionKey);
void xor_encrypt(FILE* src, FILE* dst, const uint8_t* key);
void calculate_file_hash(FILE* file, uint8_t* hash);
int parse_chip_id(const wchar_t* filename, uint32_t* chipID);
void create_firmware_file(FILE* src, const uint32_t* chipID, const uint8_t* key, const wchar_t* outputPath);
int process_all_cid_files(const wchar_t* cidDir, const wchar_t* appPath, const wchar_t* outputDir);
wchar_t* browse_folder(const wchar_t* title);
wchar_t* browse_file(const wchar_t* title, const wchar_t* filter);
void LogMessage(const wchar_t* format, ...);
// 生成加密密钥
void generate_encryption_key(const uint32_t* chipID, const char* userKey, uint8_t* encryptionKey) {
SHA256_CTX ctx;
sha256_init(&ctx);
sha256_update(&ctx, (const uint8_t*)chipID, sizeof(uint32_t) * 3);
sha256_update(&ctx, (const uint8_t*)userKey, strlen(userKey));
sha256_final(&ctx, encryptionKey);
}
// 异或加密
void xor_encrypt(FILE* src, FILE* dst, const uint8_t* key) {
uint8_t buffer[4096];
size_t bytes_read;
size_t key_index = 0;
fseek(src, 0, SEEK_SET);
while ((bytes_read = fread(buffer, 1, sizeof(buffer), src)) > 0) {
for (size_t i = 0; i < bytes_read; i++) {
buffer[i] ^= key[key_index];
key_index = (key_index + 1) % HASH_SIZE;
}
fwrite(buffer, 1, bytes_read, dst);
}
}
// 计算文件哈希
void calculate_file_hash(FILE* file, uint8_t* hash) {
SHA256_CTX ctx;
sha256_init(&ctx);
uint8_t buffer[4096];
size_t bytes_read;
fseek(file, 0, SEEK_SET);
while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
sha256_update(&ctx, buffer, bytes_read);
}
sha256_final(&ctx, hash);
}
// 解析芯片ID
int parse_chip_id(const wchar_t* filename, uint32_t* chipID) {
FILE* cidFile = _wfopen(filename, L"r");
if (!cidFile) {
LogMessage(L"错误: 无法打开芯片ID文件: %s\n", filename);
return 0;
}
char line[50];
if (!fgets(line, sizeof(line), cidFile)) {
LogMessage(L"读取文件错误\n");
fclose(cidFile);
return 0;
}
fclose(cidFile);
char* token = strtok(line, "-");
for (int i = 0; i < 3 && token != NULL; i++) {
char* hex_str = (token[0] == '0' && token[1] == 'x') ? token + 2 : token;
chipID[i] = strtoul(hex_str, NULL, 16);
token = strtok(NULL, "-");
}
return 1;
}
// 创建固件文件
void create_firmware_file(FILE* src, const uint32_t* chipID, const uint8_t* key, const wchar_t* outputPath) {
wchar_t firmware_filename[MAX_PATH];
swprintf(firmware_filename, MAX_PATH,
L"%s\\0x%08X-0x%08X-0x%08X.bin",
outputPath, chipID[0], chipID[1], chipID[2]);
FILE* dst = _wfopen(firmware_filename, L"wb");
if (!dst) {
LogMessage(L"错误: 无法创建固件文件: %s\n", firmware_filename);
return;
}
// 计算并写入文件哈希
uint8_t fileHash[HASH_SIZE];
calculate_file_hash(src, fileHash);
fseek(dst, 0, SEEK_SET);
if (fwrite(fileHash, 1, HASH_SIZE, dst) != HASH_SIZE) {
LogMessage(L"写入哈希头失败\n");
fclose(dst);
return;
}
// 执行加密并保存
xor_encrypt(src, dst, key);
fclose(dst);
LogMessage(L"已创建: %s\n", firmware_filename);
}
// 处理所有CID文件
int process_all_cid_files(const wchar_t* cidDir, const wchar_t* appPath, const wchar_t* outputDir) {
WIN32_FIND_DATAW findData;
wchar_t searchPath[MAX_PATH];
swprintf(searchPath, MAX_PATH, L"%s\\*.cid", cidDir);
HANDLE hFind = FindFirstFileW(searchPath, &findData);
if (hFind == INVALID_HANDLE_VALUE) {
LogMessage(L"在目录中未找到CID文件: %s\n", cidDir);
return 0;
}
FILE* appSrc = _wfopen(appPath, L"rb");
if (!appSrc) {
LogMessage(L"错误: 无法打开app.bin文件: %s\n", appPath);
FindClose(hFind);
return 0;
}
int processed = 0;
do {
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
continue;
wchar_t cidPath[MAX_PATH];
swprintf(cidPath, MAX_PATH, L"%s\\%s", cidDir, findData.cFileName);
LogMessage(L"\n处理: %s\n", findData.cFileName);
if (!parse_chip_id(cidPath, chipID)) {
LogMessage(L"跳过: %s (解析错误)\n", findData.cFileName);
continue;
}
generate_encryption_key(chipID, userKey, encryptionKey);
create_firmware_file(appSrc, chipID, encryptionKey, outputDir);
processed++;
fseek(appSrc, 0, SEEK_SET);
} while (FindNextFileW(hFind, &findData));
fclose(appSrc);
FindClose(hFind);
return processed;
}
// 文件夹浏览对话框
wchar_t* browse_folder(const wchar_t* title) {
BROWSEINFOW bi = { 0 };
bi.lpszTitle = title;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
LPITEMIDLIST pidl = SHBrowseForFolderW(&bi);
if (pidl) {
static wchar_t path[MAX_PATH];
SHGetPathFromIDListW(pidl, path);
CoTaskMemFree(pidl);
return path;
}
return NULL;
}
// 文件浏览对话框
wchar_t* browse_file(const wchar_t* title, const wchar_t* filter) {
OPENFILENAMEW ofn = { 0 };
static wchar_t path[MAX_PATH] = L"";
ofn.lStructSize = sizeof(OPENFILENAMEW);
ofn.lpstrTitle = title;
ofn.lpstrFilter = filter;
ofn.nFilterIndex = 1;
ofn.lpstrFile = path;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
if (GetOpenFileNameW(&ofn)) {
return path;
}
return NULL;
}
// 日志输出函数
void LogMessage(const wchar_t* format, ...) {
va_list args;
va_start(args, format);
wchar_t buffer[1024];
vswprintf(buffer, ARRAYSIZE(buffer), format, args);
// 获取当前文本长度
int len = GetWindowTextLength(g_hEditLog);
SendMessage(g_hEditLog, EM_SETSEL, len, len);
SendMessage(g_hEditLog, EM_REPLACESEL, FALSE, (LPARAM)buffer);
va_end(args);
}
// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
// 创建CID目录控件
CreateWindowW(L"STATIC", L"CID文件目录:", WS_VISIBLE | WS_CHILD,
20, 20, 100, 25, hWnd, NULL, NULL, NULL);
CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
130, 20, 300, 25, hWnd, (HMENU)IDC_EDIT_CID, NULL, NULL);
CreateWindowW(L"BUTTON", L"浏览...", WS_VISIBLE | WS_CHILD,
440, 20, 80, 25, hWnd, (HMENU)IDC_BROWSE_CID, NULL, NULL);
// 创建APP文件控件
CreateWindowW(L"STATIC", L"APP文件:", WS_VISIBLE | WS_CHILD,
20, 55, 100, 25, hWnd, NULL, NULL, NULL);
CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
130, 55, 300, 25, hWnd, (HMENU)IDC_EDIT_APP, NULL, NULL);
CreateWindowW(L"BUTTON", L"浏览...", WS_VISIBLE | WS_CHILD,
440, 55, 80, 25, hWnd, (HMENU)IDC_BROWSE_APP, NULL, NULL);
// 创建输出目录控件
CreateWindowW(L"STATIC", L"输出目录:", WS_VISIBLE | WS_CHILD,
20, 90, 100, 25, hWnd, NULL, NULL, NULL);
CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
130, 90, 300, 25, hWnd, (HMENU)IDC_EDIT_OUTPUT, NULL, NULL);
CreateWindowW(L"BUTTON", L"浏览...", WS_VISIBLE | WS_CHILD,
440, 90, 80, 25, hWnd, (HMENU)IDC_BROWSE_OUTPUT, NULL, NULL);
// 创建操作按钮
CreateWindowW(L"BUTTON", L"开始处理", WS_VISIBLE | WS_CHILD,
200, 130, 100, 30, hWnd, (HMENU)IDC_START, NULL, NULL);
// 创建日志框
g_hEditLog = CreateWindowW(L"EDIT", L"",
WS_VISIBLE | WS_CHILD | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_VSCROLL,
20, 170, 500, 200, hWnd, (HMENU)IDC_LOG, NULL, NULL);
// 设置日志框字体
HFONT hFont = CreateFont(14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, L"宋体");
SendMessage(g_hEditLog, WM_SETFONT, (WPARAM)hFont, TRUE);
break;
}
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDC_BROWSE_CID: {
wchar_t* path = browse_folder(L"选择包含CID文件的文件夹");
if (path) {
SetDlgItemTextW(hWnd, IDC_EDIT_CID, path);
LogMessage(L"已选择CID目录: %s\n", path);
}
break;
}
case IDC_BROWSE_APP: {
wchar_t* path = browse_file(L"选择app.bin文件", L"Bin文件\0*.bin\0所有文件\0*.*\0");
if (path) {
SetDlgItemTextW(hWnd, IDC_EDIT_APP, path);
LogMessage(L"已选择APP文件: %s\n", path);
}
break;
}
case IDC_BROWSE_OUTPUT: {
wchar_t* path = browse_folder(L"选择输出文件夹");
if (path) {
SetDlgItemTextW(hWnd, IDC_EDIT_OUTPUT, path);
LogMessage(L"已选择输出目录: %s\n", path);
}
break;
}
case IDC_START: {
wchar_t cidDir[MAX_PATH] = { 0 };
wchar_t appPath[MAX_PATH] = { 0 };
wchar_t outputDir[MAX_PATH] = { 0 };
GetDlgItemTextW(hWnd, IDC_EDIT_CID, cidDir, MAX_PATH);
GetDlgItemTextW(hWnd, IDC_EDIT_APP, appPath, MAX_PATH);
GetDlgItemTextW(hWnd, IDC_EDIT_OUTPUT, outputDir, MAX_PATH);
if (!cidDir[0] || !appPath[0] || !outputDir[0]) {
LogMessage(L"错误: 请先选择所有路径!\n");
break;
}
LogMessage(L"\n=== 批量处理开始 ===\n");
LogMessage(L"CID目录: %s\n", cidDir);
LogMessage(L"App文件: %s\n", appPath);
LogMessage(L"输出目录: %s\n\n", outputDir);
int count = process_all_cid_files(cidDir, appPath, outputDir);
LogMessage(L"\n完成: 已处理 %d 个文件\n", count);
break;
}
}
break;
}
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hWnd, msg, wParam, lParam);
}
return 0;
}
// 程序入口
int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
HWND console = GetConsoleWindow();
if (console) {
ShowWindow(console, SW_HIDE); // 隐藏现有控制台
}
// 初始化通用控件
INITCOMMONCONTROLSEX icc = { sizeof(INITCOMMONCONTROLSEX), ICC_STANDARD_CLASSES };
InitCommonControlsEx(&icc);
// 注册窗口类
WNDCLASSEXW wc = { sizeof(WNDCLASSEX) };
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = L"FirmwareEncryptor";
if (!RegisterClassExW(&wc)) {
return 0;
}
// 创建主窗口
HWND hWnd = CreateWindowW(wc.lpszClassName, L"Bootloader固件加密工具",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, 550, 450,
NULL, NULL, hInstance, NULL);
if (!hWnd) {
return 0;
}
// 显示窗口
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// 新增居中代码
RECT rect;
GetWindowRect(hWnd, &rect); // 获取窗口尺寸
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
int x = (screenWidth - (rect.right - rect.left)) / 2;
int y = (screenHeight - (rect.bottom - rect.top)) / 2;
SetWindowPos(hWnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); // 移动到中心
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
固件解密
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "fatfs.h"
#include "sdio.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include "stm32f4xx_hal.h"
#include "stdio.h"
#include "sha256.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define MODE_PORT GPIOD
#define MODE_PIN GPIO_PIN_13
#define LED_PORT GPIOD
#define LED_PIN GPIO_PIN_14
#define READ_MODE_PIN() HAL_GPIO_ReadPin(MODE_PORT, MODE_PIN)
#define SET_LED_ON() HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET)
#define SET_LED_OFF() HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET)
#define TOGGLE_LED() HAL_GPIO_TogglePin(LED_PORT, LED_PIN)
#define LED_IS_ON() (HAL_GPIO_ReadPin(LED_PORT, LED_PIN) == GPIO_PIN_SET)
#define F407VE_FLASH_SIZE 0x0007FFFF
#define APP_START_ADDR 0x08020000
#define APP_END_ADDR 0x0807FFFF
#define BOOTLOADER_SIZE 0x10000 // 64KB bootloader空间
#define CPU_ID_BASE_ADDR (uint32_t*)(0x1FFF7A10)
#define FLASH_PAGE_SIZE 128 // STM32F407 Flash编程最小单位为128位(16字节)
#define HASH_SIZE 32
const char user_local_key[] = "Your_Password";
char cid_filename[48] = {0};
char cid_content[48] = {0};
char bin_filename[48] = {0};
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
uint32_t chipID[3];
uint8_t decryptionKey[HASH_SIZE];
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
FATFS fs; // FatFs文件系统对象
FIL file; // 文件对象
FIL InfoFile;
FIL AuthFile;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int fputc(int ch, FILE *f) {
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1);
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 1);
HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 1);
return ch;
}
void Enable_RDP_Level1(void) {
// 1. 解锁Flash和选项字节
HAL_FLASH_Unlock();
HAL_FLASH_OB_Unlock();
// 2. 配置RDP Level 1
FLASH_OBProgramInitTypeDef ob_config;
HAL_FLASHEx_OBGetConfig(&ob_config); // 获取当前配置(可选)
ob_config.OptionType = OPTIONBYTE_RDP;
ob_config.RDPLevel = OB_RDP_LEVEL_1;
// 3. 应用配置
if (HAL_FLASHEx_OBProgram(&ob_config) == HAL_OK) {
HAL_FLASH_OB_Launch(); // 触发重载
}
// 4. 重新锁定
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
}
void Check_ReadProtection(void) {
FLASH_OBProgramInitTypeDef obConfig;
HAL_FLASHEx_OBGetConfig(&obConfig);
if (obConfig.RDPLevel == OB_RDP_LEVEL_0)
{
// 读保护未启用,则手动开启
Enable_RDP_Level1();
}
}
/**
* @brief 根据地址获取扇区号
* @param address: Flash地址
* @retval 扇区号 (0-11)
*/
static uint32_t Get_Sector(uint32_t address)
{
uint32_t sector = 0;
if((address < FLASH_BASE) || (address > FLASH_BASE + F407VE_FLASH_SIZE)) {
Error_Handler();
}
address -= FLASH_BASE; // 转换为偏移地址
if(address < 0x10000) {
// 前4个扇区各16KB
sector = address / 0x4000;
} else if(address < 0x20000) {
sector = FLASH_SECTOR_4; // 64KB扇区
} else {
// 剩余128KB扇区 (Sector5-11)
sector = ((address - 0x20000) / 0x20000) + FLASH_SECTOR_5;
}
return sector;
}
/**
* @brief 擦除应用程序区域
* @retval HAL status: HAL_OK 成功, HAL_ERROR 失败
*/
HAL_StatusTypeDef Erase_Application_Sectors(void)
{
FLASH_EraseInitTypeDef erase;
uint32_t sector_error = 0;
HAL_StatusTypeDef status;
// 解锁Flash
HAL_FLASH_Unlock();
// 清除所有错误标志
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
// 计算需要擦除的扇区范围
uint32_t start_sector = Get_Sector(APP_START_ADDR);
uint32_t end_sector = Get_Sector(APP_END_ADDR);
// 设置擦除参数
erase.TypeErase = FLASH_TYPEERASE_SECTORS;
erase.Banks = FLASH_BANK_1; // F407只有Bank1
erase.Sector = start_sector;
erase.NbSectors = end_sector - start_sector + 1;
erase.VoltageRange = FLASH_VOLTAGE_RANGE_3; // 适用于3.3V
// 执行扇区擦除
status = HAL_FLASHEx_Erase(&erase, §or_error);
// 锁定Flash
HAL_FLASH_Lock();
return status;
}
void mount_file_system()
{
/* 挂载文件系统 */
FRESULT res;
const TCHAR* SDPath = "0:"; // SD 卡路径
res = f_mount(&SDFatFS, SDPath, 1);
if (res != FR_OK)
{
printf("Bootloader TF Card Mount Failed! f_mount error code: %d\r\n", res);
Error_Handler();
}
else
{
printf("Bootloader TF Card Mount success\r\n");
}
}
void get_chipid(uint8_t* id_buf) {
uint32_t* id_ptr = (uint32_t*)CPU_ID_BASE_ADDR;
memcpy(id_buf, id_ptr, 12);
}
// 生成加密密钥
void generate_encryption_key(const uint32_t* chipID, const char* userKey, uint8_t* encryptionKey) {
SHA256_CTX ctx;
sha256_init(&ctx);
sha256_update(&ctx, (const uint8_t*)chipID, sizeof(uint32_t) * 3);
sha256_update(&ctx, (const uint8_t*)userKey, strlen(userKey));
sha256_final(&ctx, encryptionKey);
}
void FormatIDString(uint32_t *id, char *output) {
sprintf(output, "0x%08X-0x%08X-0x%08X", id[0], id[1], id[2]);
}
ErrorStatus Get_File_Name()
{
// 1. 获取芯片唯一ID
get_chipid((uint8_t *)chipID);
FormatIDString(chipID, cid_filename);
strcat(cid_filename, ".cid");
FormatIDString(chipID, bin_filename);
strcat(bin_filename, ".bin");
FormatIDString(chipID, cid_content);
return SUCCESS;
}
FRESULT CreateChipIDFile() {
FIL file;
FRESULT fr;
UINT bytesWritten;
fr = f_open(&file, cid_filename, FA_CREATE_ALWAYS | FA_WRITE);
if (fr == FR_OK) {
fr = f_write(&file, cid_content, 32, &bytesWritten);
f_close(&file);
} else {
printf("CreateChipIDFile failed! Error: %d\n", fr);
}
if (bytesWritten == 32)
{
printf("CreateChipIDFile done!\r\n");
return FR_OK;
}
else
{
printf("CreateChipIDFile failed! code: %d\r\n", fr);
return FR_DENIED;
}
}
ErrorStatus DeleteFile() {
FRESULT fr;
fr = f_unlink(bin_filename); // 删除文件
if(fr != FR_OK) {
printf("delete .bin Failed! Error: %d\r\n", fr);
return ERROR;
}
fr = f_unlink(cid_filename); // 删除文件
if(fr != FR_OK) {
printf("delete .cid Failed! Error: %d\r\n", fr);
return ERROR;
}
f_mount(&SDFatFS, "", 0); // 卸载文件系统
return SUCCESS;
}
ErrorStatus Check_Firmware_License()
{
FIL file;
FRESULT fr;
UINT bytes_read;
uint32_t file_size;
fr = f_open(&file, bin_filename, FA_READ);
if(fr != FR_OK) {
printf("Open Firmware File Failed! Error: %d\r\n", fr);
return ERROR;
}
// 3. 获取文件大小
file_size = f_size(&file);
if(file_size <= HASH_SIZE) {
printf("Invalid firmware size: %d bytes\r\n", file_size);
f_close(&file);
return ERROR;
}
// 4. 生成解密密钥(SHA256)
generate_encryption_key(chipID, user_local_key, decryptionKey); // 密钥生成函数
// 5. 读取并解密许可证区域(前32字节)
uint8_t stored_hash_enc[HASH_SIZE];
fr = f_read(&file, stored_hash_enc, HASH_SIZE, &bytes_read);
if(fr != FR_OK || bytes_read != HASH_SIZE) {
printf("Read license failed! Error: %d\r\n", fr);
f_close(&file);
return ERROR;
}
uint8_t stored_hash[HASH_SIZE];
for(int i=0; i<HASH_SIZE; i++) {
stored_hash[i] = stored_hash_enc[i] ^ decryptionKey[i];
}
// 初始化SHA-256上下文
SHA256_CTX ctx;
sha256_init(&ctx);
// 读取并计算文件内容哈希(每次32字节),包括解密过程,每读取32字节都要先解密才能送入sha256
uint8_t hash[HASH_SIZE];
uint32_t bytes_processed = 0;
uint32_t content_size = file_size - HASH_SIZE; // 实际内容大小(减去许可证)
static UINT key_index = 0;
while(bytes_processed < content_size) {
UINT to_read = HASH_SIZE;
if(content_size - bytes_processed < HASH_SIZE) {
to_read = content_size - bytes_processed;
}
fr = f_read(&file, hash, to_read, &bytes_read);
if(fr != FR_OK || bytes_read == 0) {
printf("Read error at pos %d: %d\r\n", bytes_processed, fr);
f_close(&file);
return ERROR;
}
// 解密数据块(按字节异或)
for(UINT i=0; i<bytes_read; i++) {
hash[i] ^= decryptionKey[key_index];
key_index = (key_index + 1) % HASH_SIZE; // 循环使用密钥
}
sha256_update(&ctx, hash, bytes_read);
bytes_processed += bytes_read;
}
// 获取最终哈希值
uint8_t calculated_hash[HASH_SIZE];
sha256_final(&ctx, calculated_hash);
// 关闭文件
f_close(&file);
// 比较哈希值
uint8_t diff = 0;
for(int i = 0; i < HASH_SIZE; i++) {
diff |= stored_hash[i] ^ calculated_hash[i];
}
ErrorStatus hash_valid = (diff == 0) ? SUCCESS : ERROR;
if(!hash_valid) {
// 处理许可证无效的情况
printf("Firmware license verification failed!\n");
printf("Take appropriate action: halt system, use safe mode, or notify administrator.\n");
return ERROR;
}
return SUCCESS;
}
/**
* @brief 修复版SD卡固件烧录函数
* @retval HAL_OK 成功, HAL_ERROR 失败
*/
HAL_StatusTypeDef Update_Firmware_From_SD(void)
{
FRESULT fr;
UINT bytes_read;
uint32_t total_words = 0; // 改为按字计数
uint8_t buffer[512]; // 512字节缓冲区
uint8_t tail_buffer[4]; // 用于处理文件尾部的非完整字
uint8_t tail_count = 0;
static UINT key_index = 0;
// 打开固件文件
fr = f_open(&file, bin_filename, FA_READ);
if(fr != FR_OK) {
printf("Open Frimware File Failed!\r\n");
return HAL_ERROR;
}
// 先读取32字节的hash值,将文件指针跳过32字节
fr = f_read(&file, buffer, HASH_SIZE, &bytes_read);
if(fr != FR_OK || bytes_read != HASH_SIZE) {
return HAL_ERROR;
}
// 解锁Flash
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
// 开始编程
while(1) {
// 处理上一块剩余的字节(如果有)
if(tail_count > 0) {
// 读取新数据,补齐4字节
UINT bytes_to_read = 4 - tail_count;
fr = f_read(&file, tail_buffer + tail_count, bytes_to_read, &bytes_read);
if(fr != FR_OK || bytes_read == 0) {
// 文件已结束,但还有部分数据需要写入
if(tail_count > 0) {
// 填充剩余字节为0xFF
for(int i = tail_count; i < 4; i++) {
tail_buffer[i] = 0xFF;
}
// 写入最后一个不完整的字
uint32_t tail_data;
memcpy(&tail_data, tail_buffer, 4);
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,
APP_START_ADDR + total_words * 4,
tail_data) != HAL_OK) {
goto update_error;
}
total_words++;
}
break;
}
// 现在有完整的4字节数据
uint32_t tail_data;
memcpy(&tail_data, tail_buffer, 4);
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,
APP_START_ADDR + total_words * 4,
tail_data) != HAL_OK) {
goto update_error;
}
total_words++;
tail_count = 0;
}
// 读取文件数据
fr = f_read(&file, buffer, sizeof(buffer), &bytes_read);
if(fr != FR_OK || bytes_read == 0) {
break;
}
// 解密数据块(按字节异或)
for(UINT i=0; i<bytes_read; i++) {
buffer[i] ^= decryptionKey[key_index];
key_index = (key_index + 1) % HASH_SIZE; // 循环使用密钥
}
// 处理完整的4字节块
uint32_t full_words = bytes_read / 4;
for(uint32_t i = 0; i < full_words; i++) {
uint32_t addr = APP_START_ADDR + total_words * 4;
uint32_t data;
memcpy(&data, &buffer[i * 4], 4);
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, data) != HAL_OK) {
goto update_error;
}
total_words++;
}
// 处理剩余的不完整字
tail_count = bytes_read % 4;
if(tail_count > 0) {
memcpy(tail_buffer, &buffer[full_words * 4], tail_count);
}
}
// 关闭文件
f_close(&file);
// 锁定Flash
HAL_FLASH_Lock();
return HAL_OK;
update_error:
f_close(&file);
HAL_FLASH_Lock();
return HAL_ERROR;
}
/**
* @brief 跳转到应用程序
*/
void Jump_To_Application()
{
// for (int i = 0; i < 16; i++) {
// NVIC_DisableIRQ((IRQn_Type)i);
// }
if(((*(uint32_t*)APP_START_ADDR) & 0x2FF00000) != 0x20000000) {
return;
}
SCB->VTOR = APP_START_ADDR;
// // 关闭所有中断
// __disable_irq();
__set_MSP(*(__IO uint32_t*)APP_START_ADDR);
typedef void (*pFunction)(void);
pFunction app_entry;
app_entry = (pFunction)(*(__IO uint32_t*)(APP_START_ADDR + 4));
app_entry();
while(1);
}
/**
* @brief 主更新流程
*/
ErrorStatus Perform_Firmware_Update(void)
{
mount_file_system();
Get_File_Name();
if (Check_Firmware_License() == ERROR)
{
printf("Check_Firmware_License Error!\r\n");
printf("Please Retry\r\n");
Error_Handler();
return ERROR;
}
if(Erase_Application_Sectors() != HAL_OK) {
printf("Erase App Sectors Failed!\r\n");
Error_Handler();
return ERROR;
}
printf("Erase_Application_Sectors done!\r\n");
if(Update_Firmware_From_SD() != HAL_OK) {
printf("Update Frimware Failed!\r\n");
Error_Handler();
return ERROR;
}
DeleteFile();
return SUCCESS;
}
void delay_ms(uint32_t ms)
{
const uint32_t hsi_freq = 16000000;
volatile uint32_t cycles = ms * (hsi_freq / 6000);
while(cycles--);
}
/*
* 时序检测:上电时刻如果检测到1,在3~5s内检测到下降沿,则成功进入升级模式
*
*/
ErrorStatus Check_Firmware_Upgrade()
{
if (READ_MODE_PIN() == GPIO_PIN_RESET)
{
return ERROR;
}
delay_ms(3000);
if (READ_MODE_PIN() == GPIO_PIN_RESET)
{
return ERROR;
}
delay_ms(2000);
if (READ_MODE_PIN() == GPIO_PIN_RESET)
{
return SUCCESS;
}
return ERROR;
}
void Succeed_Light()
{
while (1) {
const uint16_t cycle = 8; // 缩短PWM周期至8ms(125Hz)
const uint16_t steps = 80; // 减少呼吸阶段步数
const uint16_t min_step = 2; // 设置最小亮度阈值
// 渐暗阶段优化
for (uint16_t i = steps; i > min_step; i--) {
uint16_t on_time = ((uint32_t)cycle * i * i) / (steps * steps); // 二次曲线加快变化速度
SET_LED_ON();
if(on_time > 0) HAL_Delay(on_time);
SET_LED_OFF();
uint16_t off_time = cycle - on_time;
if(off_time > 0) HAL_Delay(off_time);
}
// 渐亮阶段优化
for (uint16_t i = min_step; i < steps; i++) {
uint16_t on_time = ((uint32_t)cycle * i * i) / (steps * steps); // 二次曲线加快变化速度
SET_LED_ON();
if(on_time > 0) HAL_Delay(on_time);
SET_LED_OFF();
uint16_t off_time = cycle - on_time;
if(off_time > 0) HAL_Delay(off_time);
}
}
}
void Failed_Light()
{
for (uint16_t i = 0; i < 10; i++) {
TOGGLE_LED();
HAL_Delay(200);
}
SET_LED_OFF();
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
// enable memory read protection.
Check_ReadProtection();
MX_GPIO_Init();
if (Check_Firmware_Upgrade() == ERROR)
{
Jump_To_Application();
for (uint16_t i = 0; i < 10; i++) {
TOGGLE_LED();
delay_ms(200);
}
SET_LED_OFF();
while(1);
}
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_SDIO_SD_Init();
MX_FATFS_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
setvbuf(stdout, NULL, _IONBF, 0);
printf("Start to Upgrade Firmware\r\n");
if (Perform_Firmware_Update() == SUCCESS)
{
printf("Perform_Firmware_Update done!\r\n");
printf("Please Restart the Device\r\n");
Succeed_Light();
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
CreateChipIDFile();
Failed_Light();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */