前言
在 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 1FLASH_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个扇区各16KBsector = 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;// 解锁FlashHAL_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只有Bank1erase.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);// 锁定FlashHAL_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. 获取芯片唯一IDget_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字节都要先解密才能送入sha256uint8_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;}// 解锁FlashHAL_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) {// 填充剩余字节为0xFFfor(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);// 锁定FlashHAL_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 */