Files
GD32_BOOT/Robot_V2/sys/OTA.cpp
T
cbb 1e6d47ddae feat(bootloader): 添加了完整的系统休眠和唤醒机制,包括:
- 实现深睡眠模式下的功耗优化
- 添加PB8引脚的外部中断唤醒功能
- 新增Flash区域用于睡眠状态管理
- 实现睡眠配置的自动检查和恢复逻辑

同时重构了OTA_SET函数,改进了Flash配置参数的写入机制,
解决了GD32 Flash特性导致的0到1写入问题,并添加了详细的
调试信息输出和错误处理。
2026-01-12 01:06:05 +08:00

766 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "OTA.h"
#include "Can.h"
#include "aes128.h"
#include "config.h"
#include "gd32c10x.h" // 主头文件,包含了gd32c10x_crc.h的内容
#include "gd32c10x_fmc.h"
#include "systick.h"
#include <cstdio>
#include <cstring>
#include <stdint.h>
extern CAN_transmiter CAN0_transmiter; // CAN初始化
OTAHandler ota;
extern "C" {
void canfd_rx_irq_handler(void);
}
inline fmc_state_enum write_ADDRESS(u32 address, u32 data) {
fmc_unlock();
fmc_flag_clear(FMC_FLAG_PGERR | FMC_FLAG_PGAERR | FMC_FLAG_WPERR);
// 确保使用对齐地址
fmc_state_enum fmc_status = fmc_word_program(address, data);
fmc_lock();
return fmc_status;
}
/**
* @brief 设置OTA配置参数到Flash
*
* 此函数用于更新存储在Flash中的OTA相关标志位(如APP可用、启用、校验等)。
* GD32的Flash特性是:只能将1写为0,不能将0写为1。若要将0变为1,必须先擦除整个页面。
* 因此,本函数通过读取同一页内的所有参数,擦除该页,然后重新写入有效的参数来实现“写1”操作。
*
* @param addr: 要设置的Flash地址
* @param data: 要写入的数据 (通常为OTA_AVAL=0x00000000 或 OTA_NOT_AVAL=0xFFFFFFFF)
* @retval FLASH_EEPROM_OK (0) 表示成功, 其他值表示失败
*/
u8 OTA_SET(u32 addr, u32 data) {
fmc_state_enum fmc_status;
u32 temp_val[OTA_CONFIG_NUM]; // 临时数组,用于存储当前所有配置参数的值
volatile u32 *flash_ptr = (volatile u32 *)addr;
// 1. 检查目标地址的值是否已与期望值相同,如果相同则无需操作
temp_val[0] = *flash_ptr;
if (temp_val[0] == data) {
return FLASH_EEPROM_OK; // 值已正确,直接返回成功
}
// 2. 尝试直接写入数据。这适用于将1修改为0的情况(因为Flash允许1->0)
if (data == OTA_AVAL) {
fmc_status = write_ADDRESS(addr, data);
// 如果写入成功,则返回成功
if (fmc_status == FMC_READY) {
return FLASH_EEPROM_OK;
}
// 如果写入失败(例如地址已被写为0),则继续执行后续的擦除-重写流程
}
// 3. 对于需要将0改回1的操作,必须先擦除整个Flash页
// 首先读取同一页内所有相关配置参数的当前状态
flash_ptr = (volatile u32 *)n_APP_LATEST_AVAL_ADDR;
temp_val[0] = *flash_ptr;
flash_ptr = (volatile u32 *)n_APP_BACKUP_AVAL_ADDR;
temp_val[1] = *flash_ptr;
flash_ptr = (volatile u32 *)n_APP_LATEST_EN_ADDR;
temp_val[2] = *flash_ptr;
flash_ptr = (volatile u32 *)n_APP_BACKUP_EN_ADDR;
temp_val[3] = *flash_ptr;
flash_ptr = (volatile u32 *)n_APP_LATEST_CHECK_ADDR;
temp_val[4] = *flash_ptr;
flash_ptr = (volatile u32 *)n_APP_BACKUP_CHECK_ADDR;
temp_val[5] = *flash_ptr;
#if EEP_DEBUG
printf("OTA_SET - Start updating configuration\r\n");
// 修复:完整输出所有6个配置项的值,便于调试
printf("LatestAval:0x%x, BackupAval:0x%x, LatestEn:0x%x, "
"BackupEn:0x%x, LatestCheck:0x%x, BackupCheck:0x%x\r\n",
temp_val[0], temp_val[1], temp_val[2], temp_val[3], temp_val[4], temp_val[5]);
#endif
// 4. 擦除包含所有配置参数的Flash页
fmc_status = fmc_page_erase(n_APP_LATEST_AVAL_ADDR);
if (fmc_status != FMC_READY) {
#if EEP_DEBUG
printf("OTA_SET - Page erase failed! Status: %d\r\n", fmc_status);
#endif
return FLASH_EEPROM_ERROR;
}
// 5. 重新写入所有之前为有效(OTA_AVAL)的配置项
// 注意:不包括当前正在设置的地址,因为它将在最后单独处理
for (int i = 0; i < OTA_CONFIG_NUM; i++) {
u32 current_addr = n_APP_LATEST_AVAL_ADDR + 4 * i;
if (temp_val[i] == OTA_AVAL && current_addr != addr) {
fmc_status = write_ADDRESS(current_addr, OTA_AVAL);
if (fmc_status != FMC_READY) {
#if EEP_DEBUG
printf("OTA_SET - Write backup value failed at 0x%08X!\r\n", current_addr);
#endif
return FLASH_EEPROM_ERROR;
}
}
}
// 6. 最后,写入本次调用的目标数据
// 即使addr为0(理论上无效),也检查一下以避免潜在问题
if (addr != 0) {
fmc_status = write_ADDRESS(addr, data);
if (fmc_status != FMC_READY) {
#if EEP_DEBUG
printf("OTA_SET - Final write to target address 0x%08X failed!\r\n", addr);
#endif
return FLASH_EEPROM_ERROR;
}
}
return FLASH_EEPROM_OK;
}
/**
* @brief 从4字节数组中提取一个32位无符号整数
* @param array: 4字节数组
* @retval 32位无符号整数
*/
static inline uint32_t extract_u32_from_4bytes(const uint8_t *array) {
return ((uint32_t)array[3] << 24) | ((uint32_t)array[2] << 16) |
((uint32_t)array[1] << 8) | (uint32_t)array[0];
}
void OTAHandler::init() {}
/**
* @brief OTA处理函数
* @param 无
* @retval 无
*/
void OTAHandler::process() {
if (ota_state == OTA_STATE_IDLE) {
ota_time = 0; // 重置计时器
static u8 zero[1] = {0};
CAN0_transmiter.CANFD_AsyncSend(CANFD_OTA_WAIT_ID, 0, zero);
#if DEBUG_OTA
printf("OTA status:%d\r\n", ota_state);
#endif
delay_1ms(10);
return;
} else {
delay_1us(100);
ota_time++;
}
// 检查是否超时(例如:10秒无数据则退出OTA模式)
if (ota_time > OTA_TIMEOUT) { // 10秒超时(10000 x 1ms)
ota_time = 0;
ota_state = OTA_STATE_IDLE;
ota_error = OTA_ERROR_TIMEOUT;
volatile u32 *flash_ptr =
(volatile u32 *)n_APP_LATEST_CHECK_ADDR; // 如果是实际可以运行
u32 if_checked = *flash_ptr;
flash_ptr = (volatile u32 *)n_APP_LATEST_AVAL_ADDR; // 如果是实际已刷入
u32 if_aval = *flash_ptr;
send_error_frame(OTA_ERROR_TIMEOUT); // OTA超时
if (if_checked == OTA_AVAL and if_aval == OTA_AVAL) {
fmc_unlock();
OTA_SET(n_APP_LATEST_AVAL_ADDR,
OTA_AVAL); // 实际可用,设置APP为启用
fmc_lock();
jumpToApplication();
}
}
}
bool validateApplication() {
u32 *app_vector = (u32 *)APPLATEST_START_ADDR;
u32 stack_ptr = app_vector[0];
u32 reset_handler = app_vector[1];
#if DEBUG_OTA
printf("APPLATEST_START_ADDR: 0x%08X\r\n", APPLATEST_START_ADDR);
printf("Stack Pointer: 0x%08X\r\n", stack_ptr);
printf("Reset Handler: 0x%08X\r\n", reset_handler);
printf("APP Range: 0x%08X - 0x%08X\r\n", APPLATEST_START_ADDR,
APPLATEST_START_ADDR + APP_MAX_SIZE);
#endif
// 简单验证:检查栈指针是否在SRAM范围内
bool stack_valid = (stack_ptr >= 0x20000000 && stack_ptr < 0x20008000);
bool reset_valid = (reset_handler >= APPLATEST_START_ADDR &&
reset_handler < (APPLATEST_START_ADDR + APP_MAX_SIZE));
#if DEBUG_OTA
if (!stack_valid) {
printf("Stack pointer validation failed!\r\n");
}
if (!reset_valid) {
printf("Reset handler validation failed!\r\n");
}
#endif
return stack_valid && reset_valid;
}
/**
* @brief 跳转应用处理模块
* @param 无
* @retval 无
*/
void OTAHandler::jumpToApplication() {
#if DEBUG_OTA
printf("try jump to app\r\n");
if (!validateApplication()) {
printf("app valid failed\r\n");
return;
} else {
printf("valid result:successful\r\n");
u8 check_data[2] = {0x0A, 0xAC};
CAN0_transmiter.CANFD_AsyncSend(CANFD_OTA_WAIT_ID, 2, check_data);
}
printf("jumping1\r\n");
#endif
fmc_unlock();
OTA_SET(n_APP_LATEST_AVAL_ADDR, OTA_AVAL); // 设置APP为可用
OTA_SET(n_APP_LATEST_EN_ADDR, OTA_AVAL); // 设置APP为激活
fmc_lock();
// 1. 关闭所有中断
__disable_irq();
for (int i = 0; i < 8; i++) {
NVIC->ICER[i] = 0xFFFFFFFF;
}
// 2. 重置外设(特别是CAN,防止冲突)
can_deinit(CAN0);
// 3. 重置系统时钟到默认状态
rcu_system_clock_source_config(RCU_CKSYSSRC_IRC8M);
#if DEBUG_OTA
printf("jumping2\r\n");
#endif
// 4. 关闭一些关键外设时钟
rcu_periph_clock_disable(RCU_GPIOA);
rcu_periph_clock_disable(RCU_GPIOB);
rcu_periph_clock_disable(RCU_GPIOC);
rcu_periph_clock_disable(RCU_USART0);
rcu_periph_clock_disable(RCU_CAN0);
rcu_periph_clock_disable(RCU_TIMER0);
rcu_periph_clock_disable(RCU_DMA0);
#if DEBUG_OTA
printf("jumping3\r\n");
#endif
// 5. 设置中断向量表偏移到应用程序起始地址
SCB->VTOR = APPLATEST_START_ADDR;
// 6. 设置MSP并跳转
uint32_t msp_value = *(__IO uint32_t *)APPLATEST_START_ADDR;
uint32_t reset_handler = *(__IO uint32_t *)(APPLATEST_START_ADDR + 4);
typedef void (*AppEntryFunc)(void);
AppEntryFunc app_entry = (AppEntryFunc)reset_handler;
#if DEBUG_OTA
printf("jumping5, MSP:0x%08X, Reset Handler:0x%08X\r\n", msp_value,
reset_handler);
#endif
__set_MSP(msp_value);
// 在跳转前清除复位标志
rcu_all_reset_flag_clear();
// 7. 跳转
app_entry();
// 如果程序执行到这里,说明跳转失败
#if DEBUG_OTA
printf("Jump to application failed!\r\n");
#endif
}
/**
* @brief 擦除应用程序区域
* @param 无
* @retval 无
*/
void OTAHandler::eraseApplicationArea() {
// 擦除APP区域
fmc_unlock();
OTA_SET(n_APP_LATEST_CHECK_ADDR, OTA_NOT_AVAL); // 设置APP为未确认
OTA_SET(n_APP_LATEST_EN_ADDR, OTA_NOT_AVAL); // 设置APP为未激活
OTA_SET(n_APP_LATEST_AVAL_ADDR, OTA_NOT_AVAL); // 设置APP为不可用
// 擦除前禁用中断,防止中断处理程序执行期间发生Flash操作导致问题
__disable_irq();
fmc_state_enum fmc_status;
for (u32 addr = APPLATEST_START_ADDR;
addr < (APPLATEST_START_ADDR + APP_MAX_SIZE); addr += FMC_PAGE_SIZE) {
fmc_status = fmc_page_erase(addr);
if (fmc_status != FMC_READY) {
// 如果擦除失败,记录错误并退出
#if DEBUG_OTA
printf("Flash erase failed at address 0x%08X, status: %d\r\n", addr,
fmc_status);
#endif
ota_error = OTA_ERROR_FLASH_ERROR;
send_error_frame(OTA_ERROR_FLASH_ERROR);
break;
}
}
// 重新启用中断
__enable_irq();
fmc_lock();
}
/**
* @brief 写入应用程序数据
* @param offset: 写入偏移量
* @param data: 数据指针
* @param size: 数据大小
* @retval 无
*/
void OTAHandler::writeApplicationData(u32 offset, u8 *data, u32 size) {
fmc_unlock();
u32 write_addr = APPLATEST_START_ADDR + offset;
u32 i = 0;
u8 *src = data;
// 32位对齐写入 - 处理完整的32位字
for (; i <= size - 4; i += 4) {
fmc_word_program(write_addr + i, *(u32 *)&src[i]);
}
// 处理剩余字节(如果有的话)
if (i < size) {
u32 word = 0;
u8 *word_ptr = (u8 *)&word;
u32 remaining_bytes = size - i;
// 直接复制剩余字节到word变量
for (u32 j = 0; j < remaining_bytes; j++) {
word_ptr[j] = src[i + j];
}
fmc_word_program(write_addr + i, word);
}
fmc_lock();
}
/**
* @brief 处理OTA数据包
* @param id: CAN帧ID
* @param data: CAN帧数据
* @param len: CAN帧数据长度
* @retval 无
*/
void OTAHandler::handleOTAMessage(u32 id, u8 *data, u8 len) {
if (id == CANFD_OTA_DATA_ID) {
ota_time = 0;
if (len == 64 and ota_state == OTA_STATE_RECEIVING_FIRMWARE) {
#if DEBUG_OTA
printf("stage8:OTA_RECEIVING_FIRMWARE\r\n");
#endif
// 使用安全的数据解包方式
u32 receive_session_num = extract_u32_from_4bytes(data);
u32 receive_packet_num = extract_u32_from_4bytes(data + 4);
u32 receive_IFEND = extract_u32_from_4bytes(data + 56);
// 直接使用数据指针,避免不必要的内存复制
const u8 *receive_encrypted_data =
data + 8; // 加密数据从偏移8字节开始
const u8 *receive_dataCRC32 = data + 60; // CRC数据从偏移60字节开始
// 解密 - 使用栈上数组存储解密结果
u8 decrypted_data[OTA_PACKET_SIZE];
u8 calculated_dataCRC32[4];
aes128_set_key(&aes_ctx, current_K_session);
aes128_ecb_decrypt_48bytes(&aes_ctx,
const_cast<u8 *>(receive_encrypted_data),
decrypted_data);
// 计算解密后CRC数据
calculate_crc32_with_key(current_K_session, decrypted_data,
calculated_dataCRC32);
// CRC数据匹配
if (memcmp(receive_dataCRC32, calculated_dataCRC32, SIZE_U32) !=
0) {
ota_error = OTA_ERROR_INVALID_CRC;
send_error_frame(OTA_ERROR_INVALID_CRC);
#if DEBUG_OTA
std::printf("CRC32 :%x,%x!\r\n", receive_dataCRC32[0],
calculated_dataCRC32[0]);
#endif
receive_packet_num -= 1;
} else {
// 写入Flash前验证数据
u32 offset = receive_packet_num * OTA_PACKET_SIZE;
if (offset + OTA_PACKET_SIZE <= APP_MAX_SIZE) {
writeApplicationData(offset, decrypted_data,
OTA_PACKET_SIZE);
} else {
ota_error = OTA_ERROR_INVALID_SIZE;
ota_state = OTA_STATE_IDLE;
send_error_frame(OTA_ERROR_INVALID_SIZE);
}
}
if (receive_IFEND == 0xFFFFFFFF) { // 接收到尾帧,发送结束信号
u8 pack_number[4] = {0xFF, 0xFF, 0xFF, 0xFF};
CAN0_transmiter.sendDATA_ACK(CANFD_OTA_ACK_ID, pack_number);
ota_state = OTA_STATE_FINISHED;
jumpToApplication();
return;
}
// 下一个请求
u32 request_packet = receive_packet_num + 1;
u8 pack_number[4];
// 封包
memcpy(&pack_number, &request_packet, 4);
CAN0_transmiter.sendDATA_ACK(CANFD_OTA_ACK_ID, pack_number);
} else if (len == 4 and ota_state == OTA_STATE_CHECKING_FIRMWARE) {
#if DEBUG_OTA
printf("stage7:OTA_STATE_CHECKING_FIRMWARE\r\n");
#endif
// OTA请求包
u32 new_size = *(u32 *)data;
if (new_size <= APP_MAX_SIZE) {
expected_size = new_size;
received_size = 0;
} else {
ota_error = OTA_ERROR_INVALID_SIZE;
ota_state = OTA_STATE_IDLE;
send_error_frame(OTA_ERROR_INVALID_SIZE);
return;
}
u8 send_data[6] = {0xBA, 0xC0, 0x00,
0x00, 0x00, 0x00}; // 确认开始传输
eraseApplicationArea();
// 发送接收包0请求
CAN0_transmiter.CANFD_AsyncSend(CANFD_OTA_ACK_ID, 6, send_data);
ota_state = OTA_STATE_RECEIVING_FIRMWARE;
} else if (len == 2 and ota_state == OTA_STATE_FINISHED) {
#if DEBUG_OTA
printf("stage--:OTA_STATE_FINISHED\r\n");
#endif
u8 send_data[2] = {0x0A, 0xAC}; // 确认开始传输
// 4. 验证成功 - 发送最终ACK
CAN0_transmiter.CANFD_AsyncSend(CANFD_OTA_ACK_ID, 2, send_data);
} else {
ota_error = OTA_ERROR_INVALID_PACKET;
ota_state = OTA_STATE_IDLE;
send_error_frame(OTA_ERROR_INVALID_PACKET);
}
} else if (id == CANFD_OTA_REQ_ID) {
ota_time = 0;
#if DEBUG_OTA
printf("stage2:OTA_STATE_WAITING_RANDOM\r\n");
#endif
if (data[0] == 0x00 and data[1] == 0xAC and len == 2 and
ota_state == OTA_STATE_IDLE) { // 收到发起信号
u8 send_data[2] = {0x00, 0xac}; // 确认握手开始,请求随机数
CAN0_transmiter.CANFD_AsyncSend(CANFD_OTA_ACK_ID, 2,
send_data); // 发送握手开始信号
ota_state = OTA_STATE_WAITING_RANDOM;
} else if (len == 16 and ota_state == OTA_STATE_WAITING_RANDOM) {
#if DEBUG_OTA
printf("stage4:OTA_STATE_WAITING_CONFIRM\r\n");
#endif
while (len-- > 0) {
nonce_server[len] = data[len];
}
// 1. 获取设备唯一码
getDeviceSerialNumber(device_uid);
// 2. 初始化AES上下文(设置主密钥)
aes128_set_key(&aes_ctx, K_master);
// 3. 生成会话密钥 K_session (16字节)
u8 K_session[16];
aes128_encrypt_block(&aes_ctx, nonce_server,
K_session); // 用主密钥加密随机数
// 4. 用主密钥加密明文 (AES-128-ECB)
u8 encrypted_Ksession[16];
u8 temp_block[AES_BLOCK_SIZE];
// 加密第一个16字节块
aes128_encrypt_block(&aes_ctx, K_session, temp_block);
memcpy(encrypted_Ksession, temp_block, 16);
// 5. 构造握手响应包 (36字节)
u8 handshake_response[36];
// 5.1 前12字节:设备UID
memcpy(handshake_response, device_uid, 12);
// 5.2 接下来8字节:nonce后半部分
memcpy(handshake_response + 12, nonce_server + 8, 8);
// 5.3 最后16字节:加密数据的K-session
memcpy(handshake_response + 20, encrypted_Ksession, 16);
// 6. 通过CAN发送响应
CAN0_transmiter.CANFD_AsyncSend(CANFD_OTA_ACK_ID, 36,
handshake_response);
// 7. 保存会话密钥供后续使用
memcpy(current_K_session, K_session, 16);
// 8. 更新状态机
ota_state = OTA_STATE_WAITING_CONFIRM;
} else if (len == 16 and ota_state == OTA_STATE_WAITING_CONFIRM) {
#if DEBUG_OTA
printf("stage6:OTA_STATE_CHECKING_SESSION\r\n");
#endif
u8 plaintext[16];
u8 expected[16];
// 1. 用K_session解密
u8 encrypted_confirm[16];
memcpy(encrypted_confirm, data, 16);
aes128_set_key(&aes_ctx, current_K_session);
aes128_decrypt_block(&aes_ctx, encrypted_confirm, plaintext);
// 2. 计算期望值 (nonce_server XOR 0x55)
// 注意:需要保存原始的nonce_server
for (int i = 0; i < 16; i++) {
expected[i] = nonce_server[i] ^ 0x55;
}
// 3. 验证解密结果
if (memcmp(plaintext, expected, 16) != 0) {
// 验证失败 - 可能密钥错误或攻击
ota_error = OTA_ERROR_INVALID_KEY;
ota_state = OTA_STATE_IDLE;
send_error_frame(OTA_ERROR_INVALID_KEY); // 验证失败
return;
}
u8 send_data[2] = {0xBA, 0xC0}; // 确认开始传输
// 4. 验证成功 - 发送最终ACK
CAN0_transmiter.CANFD_AsyncSend(CANFD_OTA_ACK_ID, 2, send_data);
// 5. 更新状态 - 进入固件接收阶段
ota_state = OTA_STATE_CHECKING_FIRMWARE;
} else {
// 其他握手异常
ota_error = OTA_ERROR_INVALID_HANDSHAKE;
ota_state = OTA_STATE_IDLE;
send_error_frame(OTA_ERROR_INVALID_HANDSHAKE);
return;
}
}
}
/**
* @brief 发送错误帧
* @param error: 错误码
* @retval 无
*/
void send_error_frame(u8 error) {
u8 error_frame[1] = {error};
CAN0_transmiter.CANFD_AsyncSend(CANFD_OTA_ACK_ID, 1, error_frame);
}
/**
* @brief 获取GD32设备唯一码
* @param buffer 存储唯一码的缓冲区,至少需要12字节
* @return void
*/
void getDeviceSerialNumber(u8 *buffer) {
uint32_t serial_msb = DEVICE_SERIAL_ADDR_MSB;
uint32_t serial_mid = DEVICE_SERIAL_ADDR_MID;
uint32_t serial_lsb = DEVICE_SERIAL_ADDR_LSB;
// 将96位唯一ID复制到缓冲区 (以小端序存储)
for (int i = 0; i < 4; i++) {
buffer[i] = (u8)(serial_lsb >> (i * 8));
buffer[i + 4] = (u8)(serial_mid >> (i * 8));
buffer[i + 8] = (u8)(serial_msb >> (i * 8));
}
}
/**
* @brief 获取GD32设备唯一码为字符串格式
* @param str 存储唯一码字符串的缓冲区,至少需要25字节(包含结束符)
* @return void
*/
void getDeviceSerialNumberString(char *str) {
u8 serial[12];
getDeviceSerialNumber(serial);
// 将字节序列转换为十六进制字符串表示
for (int i = 0; i < 12; i++) {
sprintf(&str[i * 2], "%02X", serial[i]);
}
str[24] = '\0'; // 确保字符串结束
}
/**
* @brief 将0x08010500地址后0xC000大小区域内的数据搬运到0x08011000地址
* @param None
* @retval fmc_state_enum: 操作状态
*/
fmc_state_enum move_app_data() {
fmc_state_enum fmc_status = FMC_READY;
// 关闭全局中断以防止Flash操作期间的中断干扰
__disable_irq();
fmc_unlock();
// 首先擦除目标地址的Flash页面
uint32_t num_pages =
(APP_MAX_SIZE + FMC_PAGE_SIZE - 1) / FMC_PAGE_SIZE; // 向上取整
for (uint32_t i = 0; i < num_pages; i++) {
uint32_t page_addr = APPLATEST_START_ADDR + i * FMC_PAGE_SIZE;
// 擦除页面
fmc_status = fmc_page_erase(page_addr);
if (fmc_status != FMC_READY) {
fmc_lock();
#if OTA_DEBUG
printf("move_app_data erase error\r\n");
#endif
__enable_irq(); // 重新开启中断
return fmc_status;
}
}
// 复制数据 - 按字(32位)进行复制以提高效率
uint32_t offset = 0;
uint32_t write_count = 0; // 记录实际写入的字数
const uint32_t *src_ptr = (const uint32_t *)APPBACK_START_ADDR; // 从备份区域读取
// 预计算源数据的CRC,避免重复访问内存
crc_deinit();
crc_data_register_reset();
uint32_t src_crc = crc_block_data_calculate((uint32_t *)APPBACK_START_ADDR,
APP_MAX_SIZE / 4);
while (offset < APP_MAX_SIZE) {
uint32_t src_data = src_ptr[offset / 4]; // 从备份区域读取数据
// 检查源地址是否为有效的Flash数据(非0xFFFFFFFF表示有数据)
if (src_data != 0xFFFFFFFF) {
// 清除之前的标志位
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR |
FMC_FLAG_PGERR);
fmc_status =
fmc_word_program(APPLATEST_START_ADDR + offset, src_data); // 写入到最新APP区域
// 检查写入操作是否成功
if (fmc_status != FMC_READY) {
#if OTA_DEBUG
printf(
"Flash write failed at offset 0x%08X with status: %d\r\n",
offset, fmc_status);
#endif
fmc_lock();
__enable_irq(); // 重新开启中断
return fmc_status;
}
write_count++;
}
offset += 4;
}
fmc_lock();
// 重新开启全局中断
__enable_irq();
crc_data_register_reset(); // 确保CRC计算从初始状态开始
// 计算目标数据的CRC值
uint32_t dst_crc = crc_block_data_calculate(
(uint32_t *)APPLATEST_START_ADDR, APP_MAX_SIZE / 4);
// 比较CRC值,确保数据完整性
if (src_crc != dst_crc) {
#if OTA_DEBUG
printf("CRC mismatch: src_crc=0x%08X, dst_crc=0x%08X\r\n", src_crc,
dst_crc);
#endif
return (fmc_state_enum)0xFF; // CRC校验失败
}
return fmc_status;
}
/**
* 带密钥的CRC32校验函数
*
* @param key 16字节密钥
* @param data 48字节待校验数据
* @param output 4字节输出缓冲区,存储CRC32校验值
*
* 特点:
* - 使用标准CRC32算法
* - 16字节密钥用于初始化CRC32状态
*/
void calculate_crc32_with_key(const uint8_t *key, const uint8_t *data,
u8 *output) {
// CRC32查找表(标准多项式0xEDB88320)
static const uint32_t crc_table[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D};
// 用16字节密钥初始化CRC32状态
uint32_t crc = 0xFFFFFFFF;
for (int i = 0; i < 16; i++) {
crc = crc_table[(crc ^ key[i]) & 0xFF] ^ (crc >> 8);
}
// 计算48字节数据的CRC32
for (int i = 0; i < 48; i++) {
crc = crc_table[(crc ^ data[i]) & 0xFF] ^ (crc >> 8);
}
// 取反得到最终结果
crc = ~crc;
// 将CRC32结果拆分为4字节(小端格式)
output[0] = (uint8_t)(crc >> 0);
output[1] = (uint8_t)(crc >> 8);
output[2] = (uint8_t)(crc >> 16);
output[3] = (uint8_t)(crc >> 24);
}