Files
Motor_v2/Robot_V2/sys/flash.cpp
T
cbb bdf03517b3 feat(CAN): 添加静默模式支持并优化节点ID处理
- 添加静默模式功能,在接收到base_id=0x0000消息时停止发送10秒
- 修正节点ID加载逻辑,区分有效节点ID(0-65534)和Flash擦除状态(0xFFFF)
- 添加静默模式相关的状态检查和时间管理功能
- 在CAN通信检查中集成静默模式处理流程

fix(FOC): 修复速度控制中的方向参数应用
- 在velocity_control、angle_control_with_velocity_limit和MIT_Mode函数中
 正确应用DIR参数调整速度误差的符号
- 添加详细注释说明DIR参数对速度误差计算的影响

refactor(systick): 添加毫秒级系统时间功能
- 实现get_systick_ms()函数提供毫秒级系统时间
- 添加SysTick中断处理程序更新系统时间计数器

chore(init): 调整初始化顺序优化系统启动流程
- 将EEPROM初始化移至CAN初始化之前,确保正确的依赖关系
2026-03-19 23:49:08 +08:00

791 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 "flash.h"
#include "FOC.h"
#include "ang_sensor.h"
#include "config.h"
#include "gd32c10x.h" // GD32 主头文件,确保外设函数声明可用
#include "gd32c10x_crc.h"
#include "gd32c10x_fmc.h"
#include "systick.h"
#include <Can.h>
#include <cstdio>
#include <cstring>
// EEP 调试宏定义
#if EEP_DEBUG
#define EEP_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define EEP_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
// OTA 调试宏定义
#if OTA_DEBUG
#define OTA_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define OTA_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
FlashEEPROM flashEEP = FlashEEPROM();
extern angle_sensor KTH7823;
extern CAN_transmiter CAN0_transmiter;
extern FOC_Controller FOC; // FOC 控制
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;
}
// FlashEEPROM 类实现
FlashEEPROM::FlashEEPROM() {
// 构造函数可以为空,或者做一些初始化工作
}
/**
* @brief 初始化 Flash EEPROM 模拟
* @param None
* @retval None
*/
u8 FlashEEPROM::init() {
rcu_periph_clock_enable(RCU_CRC);
crc_deinit(); // 初始化 CRC 模块
FlashEepromStatus status = FLASH_EEPROM_OK;
// 读取系统配置
read_saved_system_config();
if (EEPROMStatus == FLASH_EEPROM_OK) {
return FLASH_EEPROM_OK;
} else {
return FLASH_EEPROM_READ_ERROR;
}
return 1; // 正常不会返回这个,未预料的异常情况返回
}
/**
* @brief 读取 EEPROM 数据
* @param virtualAddr: 虚拟地址
* @param data: 存储读取数据的指针
* @retval EEPROMStatus: 是否成功
*/
u8 FlashEEPROM::read_saved_system_config(void) {
// 读取保存的参数
u32 CRC_NUM = 0x0000;
FlashEepromStatus status_read[CONFIG_SAVED_NUM + 1] = {FLASH_EEPROM_OK};
float data[CONFIG_SAVED_NUM] = {0.0f};
// 尝试从 Flash 读取参数,如果不存在则使用默认值
status_read[1] = readFloat(ADDR_CONTROL_MODE, &data[0]);
status_read[2] = readFloat(ADDR_VOLTAGE_LIMIT, &data[1]);
status_read[3] = readFloat(ADDR_VEL_LIMIT, &data[2]);
status_read[4] = readFloat(ADDR_ANGLE, &data[3]);
status_read[5] = readFloat(ADDR_TARGET_ANGLE, &data[4]);
status_read[6] = readFloat(ADDR_TARGET_VEL, &data[5]);
// 读取失败则使用默认值
for (u8 i = 1; i < CONFIG_SAVED_NUM + 1; i++) {
if (status_read[i] ==
FLASH_EEPROM_DATA_NOT_FOUND) { // 首次读取,不保存报错
data[i - 1] = DEFAULT_VALUE;
} else if (status_read[i] != FLASH_EEPROM_OK) { // 其余错误,保存报错
data[i - 1] = DEFAULT_VALUE;
status_read[0] = status_read[i];
}
}
// 读取到全部数据
if (status_read[0] == FLASH_EEPROM_OK) {
u32 next_address = EEPROM_END_ADDRESS;
// 遍历 Flash 页面寻找下一个可写入位置
for (u32 address = EEPROM_DATA_START_ADDRESS - SIZE_U32;
address > EEPROM_START_ADDRESS; address -= SIZE_U32) {
// 安全读取 Flash 数据
volatile u32 *flash_ptr = (volatile u32 *)address;
u32 data_word = *flash_ptr;
// 找到空位置
if (data_word == 0xFFFFFFFF) {
next_address = address + SIZE_U32; // 上一个位置为有效存储的数据
break;
}
// 更新下一个可能的写入位置(如果需要格式化)
next_address = address - SIZE_U32;
}
if (next_address == EEPROM_DATA_START_ADDRESS) {
// 从来没有存储过,直接返回
return FLASH_EEPROM_OK;
}
crc_data_register_reset(); // 确保 CRC 计算从初始状态开始
CRC_NUM = crc_block_data_calculate((uint32_t *)data, CONFIG_SAVED_NUM);
EEP_DEBUG_PRINT("CRC cmp1,0x%X_0x%x \r\n", CRC_NUM,
*((u32 *)next_address));
if (CRC_NUM != *((u32 *)next_address)) { // CRC 错误
for (u8 i = 0; i < CONFIG_SAVED_NUM; i++) {
data[i] = DEFAULT_VALUE;
}
EEPROMStatus = FLASH_EEPROM_CRC_ERROR;
} else { // CRC 正确
EEPROMStatus = FLASH_EEPROM_OK;
}
} else {
// 读取失败,报错
EEPROMStatus = status_read[0];
}
KTH7823.angle_set(0);
FOC.control_mode = (u8)data[0];
FOC.limit_voltage = data[1];
FOC.target_current = data[2];
KTH7823.angle_set(data[3]);
FOC.target_angle = data[4];
FOC.target_velocity = data[5];
return EEPROMStatus;
}
/**
* @brief 从 Flash EEPROM 读取数据
* @param virtualAddr: 虚拟地址
* @param data: 指向存储读取数据的指针
* @retval FlashEepromStatus: 操作状态
*/
FlashEepromStatus FlashEEPROM::read(u8 virtualAddr, u16 *data) const {
// 参数检查
if (data == nullptr) {
return FLASH_EEPROM_INVALID_ADDR;
}
// 从 Flash 末尾开始向前搜索,找到最新的数据
for (u32 address = EEPROM_END_ADDRESS - SIZE_U32;
address >= EEPROM_START_ADDRESS; address -= SIZE_U32) {
u32 data_word = *((u32 *)address);
// 检查是否是有效数据(非擦除状态)
if (data_word != 0xFFFFFFFF) {
u8 addr = (u8)(data_word & DATA_ADDR_MASK);
// 找到匹配的虚拟地址
if (addr == virtualAddr) {
// 右移 16 位以提取高 16 位的 u16 数据
*data = (u16)((data_word & DATA_U16_MASK) >> 16);
EEP_DEBUG_PRINT("read u16: addr=0x%x, val=%d\r\n", address, *data);
return FLASH_EEPROM_OK;
}
}
}
return FLASH_EEPROM_DATA_NOT_FOUND;
}
/**
* @brief 从 Flash EEPROM 读取 float 数据
* @param virtualAddr: 虚拟地址
* @param data: 指向存储读取数据的指针
* @retval FlashEepromStatus: 操作状态
*/
FlashEepromStatus FlashEEPROM::readFloat(u8 virtualAddr, float *data) const {
// 检查参数有效性
if (data == nullptr) {
return FLASH_EEPROM_INVALID_ADDR;
}
// 从 Flash 末尾开始向前搜索,找到最新的数据
for (u32 address = EEPROM_END_ADDRESS - SIZE_U32;
address >= EEPROM_START_ADDRESS; address -= SIZE_U32) {
// 使用 volatile 确保每次都从内存读取,防止编译器优化
volatile u32 *flash_ptr = (volatile u32 *)address;
u32 data_word = *flash_ptr;
// 检查是否是有效数据(非擦除状态)
if (data_word != 0xFFFFFFFF) {
u8 addr = (u8)(data_word & DATA_ADDR_MASK);
// 找到匹配的虚拟地址
if (addr == virtualAddr) {
// 清除最低的 8 位地址信息,保留高 24 位数据
u32 float_data = data_word & DATA_FLOAT_MASK;
// 使用内存拷贝将处理后的数据解释为 float
memcpy(data, &float_data, SIZE_FLOAT);
EEP_DEBUG_PRINT("read:0x%x=%f\r\n", address, *data);
return FLASH_EEPROM_OK;
}
}
}
// 未找到指定虚拟地址的数据
return FLASH_EEPROM_DATA_NOT_FOUND;
}
/**
* @brief 向 Flash EEPROM 写入数据
* @param virtualAddr: 虚拟地址
* @param data: 要写入的数据
* @retval FlashEepromStatus: 操作状态
*/
FlashEepromStatus FlashEEPROM::write(u8 virtualAddr, u16 data) {
// 查找下一个可写入的位置
u32 next_address = EEPROM_START_ADDRESS;
u32 data_read;
// 构建要写入的 32 位数据:格式 [data:16bit][reserved:8bit][addr:8bit]
u32 data_word_to_write = ((u32)data << 16) | (u32)virtualAddr;
for (u32 address = EEPROM_END_ADDRESS - SIZE_U32;
address >= EEPROM_DATA_START_ADDRESS; address -= SIZE_U32) {
// 安全读取 Flash 数据
volatile u32 *flash_ptr = (volatile u32 *)address;
data_read = *flash_ptr;
if ((data_read & DATA_ADDR_MASK) == (u32)virtualAddr) { // 虚拟地址相同
if ((data_read & DATA_U16_MASK) ==
(data_word_to_write & DATA_U16_MASK)) { // 数据相同
#if EEP_DEBUG
data_read = data_read & DATA_U16_MASK;
u16 float_data_read = *((u16 *)&data_read);
EEP_DEBUG_PRINT("Found data in:0x%x=%u\r\n", address,
data_read);
#endif
return FLASH_EEPROM_OK;
} else {
break;
}
}
}
// 遍历 Flash 页面寻找下一个可写入位置
for (u32 address = EEPROM_DATA_START_ADDRESS; address < EEPROM_END_ADDRESS;
address += SIZE_U32) {
// 安全读取 Flash 数据
volatile u32 *flash_ptr = (volatile u32 *)address;
u32 data_word = *flash_ptr;
// 找到空位置
if (data_word == 0xFFFFFFFF) {
next_address = address;
break;
}
// 更新下一个可能的写入位置(如果需要格式化)
next_address = address + SIZE_U32;
}
// 如果有足够空间,直接写入
if (next_address < EEPROM_END_ADDRESS) {
fmc_state_enum fmc_status =
write_ADDRESS(next_address, data_word_to_write);
if (fmc_status != FMC_READY) {
return FLASH_EEPROM_WRITE_ERROR;
}
return FLASH_EEPROM_OK;
} else { // 空间不足,需要格式化页面
EEP_DEBUG_PRINT("no enough space\r\n");
FlashEepromStatus status = format();
if (status == FLASH_EEPROM_OK) {
status = save_system_config(false);
return status;
} else {
return FLASH_EEPROM_ERASE_ERROR;
}
}
}
/**
* @brief 向 Flash EEPROM 写入 float 数据
* @param virtualAddr: 虚拟地址
* @param data: 要写入的数据
* @retval FlashEepromStatus: 操作状态
*/
FlashEepromStatus FlashEEPROM::writeFloat(u8 virtualAddr, float data) {
// 查找下一个可写入的位置
u32 next_address = EEPROM_START_ADDRESS;
u32 data_word;
// 将完整的 float 数据复制到 32 位变量中
memcpy(&data_word, &data, SIZE_FLOAT);
// 插入虚拟地址到低 8 位 格式:[data:24bit][addr:8bit]
data_word = (data_word & DATA_FLOAT_MASK) | (u32)virtualAddr;
// 从 Flash 末尾开始向前搜索,找到最新的数据
for (u32 address = EEPROM_END_ADDRESS - SIZE_U32;
address >= EEPROM_DATA_START_ADDRESS; address -= SIZE_U32) {
// 安全读取 Flash 数据
volatile u32 *flash_ptr = (volatile u32 *)address;
u32 data_word_read = *flash_ptr;
if ((data_word_read & DATA_ADDR_MASK) ==
(u32)virtualAddr) { // 虚拟地址相同
if ((data_word_read & DATA_FLOAT_MASK) ==
(data_word & DATA_FLOAT_MASK)) { // 数据相同
#if EEP_DEBUG
data_word_read = data_word_read & DATA_FLOAT_MASK;
float float_data_read = *((float *)&data_word_read);
EEP_DEBUG_PRINT("Found data in:0x%x=%f\r\n", address,
float_data_read);
#endif
return FLASH_EEPROM_OK;
} else {
break; // 找到相同地址但数据不同,需要更新
}
}
}
// 遍历 Flash 页面寻找下一个可写入位置
for (u32 address = EEPROM_DATA_START_ADDRESS; address < EEPROM_END_ADDRESS;
address += SIZE_U32) {
// 安全读取 Flash 数据
volatile u32 *flash_ptr = (volatile u32 *)address;
u32 data_word_read = *flash_ptr;
// 找到空位置
if (data_word_read == 0xFFFFFFFF) {
next_address = address;
break;
}
// 更新下一个可能的写入位置(如果需要格式化)
next_address = address + SIZE_U32;
}
// 如果有足够空间,直接写入
if (next_address < EEPROM_END_ADDRESS) {
fmc_state_enum fmc_status = write_ADDRESS(next_address, data_word);
#if EEP_DEBUG
u32 data_word_c = *((u32 *)next_address);
data_word_c = data_word_c & DATA_FLOAT_MASK;
float float_data = *((float *)&data_word_c);
EEP_DEBUG_PRINT("save:0x%x=%f\r\n", next_address, float_data);
#endif
if (fmc_status != FMC_READY) {
return FLASH_EEPROM_WRITE_ERROR;
}
return FLASH_EEPROM_OK;
} else {
// 空间不足,需要格式化页面
#if EEP_DEBUG
EEP_DEBUG_PRINT("no enough space\r\n");
#endif
FlashEepromStatus status = format();
if (status == FLASH_EEPROM_OK) {
status = save_system_config(false);
return status;
} else {
return FLASH_EEPROM_ERASE_ERROR;
}
}
}
/**
* @brief 格式化 Flash EEPROM 页面
* @param None
* @retval FlashEepromStatus: 操作状态
*/
FlashEepromStatus FlashEEPROM::format() {
fmc_state_enum fmc_status;
EEP_DEBUG_PRINT("擦除页面\r\n");
fmc_unlock();
fmc_status = fmc_page_erase(EEPROM_START_ADDRESS); // 擦除第一页
if (fmc_status != FMC_READY) {
fmc_lock();
return FLASH_EEPROM_ERASE_ERROR;
}
fmc_status = fmc_page_erase(EEPROM_DATA_PAGE2_ADDRESS); // 擦除第二页
if (fmc_status != FMC_READY) {
fmc_lock();
return FLASH_EEPROM_ERASE_ERROR;
}
fmc_lock();
return FLASH_EEPROM_OK;
}
/**
* @brief 更新系统配置并保存到 Flash
* @param config 新的系统配置值
*/
FlashEepromStatus FlashEEPROM::save_system_config(bool saved) {
u32 CRC_NUM = 0x0000;
float data[CONFIG_SAVED_NUM] = {
(float)FOC.control_mode, FOC.limit_voltage,
FOC.target_current, FOC.rt_angle,
FOC.target_angle, FOC.target_velocity};
// 如果系统配置标记已保存,则无需再次保存
if (system_config_saved) {
EEPROMStatus = FLASH_EEPROM_OK;
return FLASH_EEPROM_OK;
}
// 写入参数并检查错误
FlashEepromStatus status;
status = writeFloat(ADDR_CONTROL_MODE, data[0]);
if (status != FLASH_EEPROM_OK) {
EEPROMStatus = status;
return status;
}
status = writeFloat(ADDR_VOLTAGE_LIMIT, data[1]);
if (status != FLASH_EEPROM_OK) {
EEPROMStatus = status;
return status;
}
status = writeFloat(ADDR_VEL_LIMIT, data[2]);
if (status != FLASH_EEPROM_OK) {
EEPROMStatus = status;
return status;
}
status = writeFloat(ADDR_ANGLE, data[3]);
if (status != FLASH_EEPROM_OK) {
EEPROMStatus = status;
return status;
}
status = writeFloat(ADDR_TARGET_ANGLE, data[4]);
if (status != FLASH_EEPROM_OK) {
EEPROMStatus = status;
return status;
}
status = writeFloat(ADDR_TARGET_VEL, data[5]);
if (status != FLASH_EEPROM_OK) {
EEPROMStatus = status;
return status;
}
// 创建用于 CRC 计算的模拟数据数组,模拟实际存储到 Flash 中的数据格式
u32 crc_data_words[CONFIG_SAVED_NUM];
// 按照 writeFloat 函数的方式处理数据
for (u8 i = 0; i < CONFIG_SAVED_NUM; i++) {
// 将完整的 float 数据复制到 32 位变量中
memcpy(&crc_data_words[i], &data[i], SIZE_FLOAT);
// 去除最后 2 位精度
crc_data_words[i] = crc_data_words[i] & DATA_FLOAT_MASK;
}
crc_data_register_reset(); // 确保 CRC 计算从初始状态开始
CRC_NUM = crc_block_data_calculate(crc_data_words, CONFIG_SAVED_NUM);
writeCRC(CRC_NUM, saved);
EEPROMStatus = FLASH_EEPROM_OK;
return FLASH_EEPROM_OK;
}
/**
* @brief 读取角度数据从 Flash
* @return float: 角度数据
*/
float FlashEEPROM::read_angle() {
float angle = DEFAULT_VALUE;
if (readFloat(ADDR_ANGLE, &angle) != FLASH_EEPROM_OK) {
angle = DEFAULT_VALUE;
}
return angle;
}
/**
* @brief 保存 CRC 数据到 Flash
* @param saved: 是否一次性保存
* @return FlashEepromStatus: 状态
*/
FlashEepromStatus FlashEEPROM::writeCRC(u32 CRC_NUM, bool saved) {
static u32 next_address;
// 遍历 Flash 页面寻找下一个可写入 crc 的位置
for (u32 address = EEPROM_DATA_START_ADDRESS - SIZE_U32;
address > EEPROM_START_ADDRESS; address -= SIZE_U32) {
// 安全读取 Flash 数据
volatile u32 *flash_ptr = (volatile u32 *)address;
u32 data_word = *flash_ptr;
// 找到空位置
if (data_word == 0xFFFFFFFF) {
next_address = address;
break;
}
// 更新下一个可能的写入位置(如果需要格式化)
next_address = address - SIZE_U32;
}
// 如果当前 CRC 与现在的 crc 值不同,则写入
if (*((u32 *)next_address) != CRC_NUM) {
// 如果有足够空间,直接写入 crc 值
if (next_address > EEPROM_START_ADDRESS) {
fmc_state_enum fmc_status = write_ADDRESS(next_address, CRC_NUM);
EEP_DEBUG_PRINT("CRC cmp1,0x%X_0x%x \r\n", CRC_NUM,
*((u32 *)next_address));
// 检查写入是否成功
if (fmc_status != FMC_READY) {
EEPROMStatus = fmc_status;
return FLASH_EEPROM_WRITE_ERROR;
}
} else {
// 空间不足,需要格式化页面
EEP_DEBUG_PRINT("no enough space\r\n");
FlashEepromStatus status = format();
if (status == FLASH_EEPROM_OK) {
status = save_system_config(false);
return status;
} else {
EEPROMStatus = FLASH_EEPROM_ERASE_ERROR;
return FLASH_EEPROM_ERASE_ERROR;
}
}
}
// 重新读取 CRC 校验
if (CRC_NUM == *((u32 *)next_address)) {
system_config_saved = saved; // 标记已保存过
EEPROMStatus = FLASH_EEPROM_OK; // 设置 EEPROM 状态
return FLASH_EEPROM_OK;
} else {
EEPROMStatus = FLASH_EEPROM_CRC_ERROR; // 设置 EEPROM 错误状态
return FLASH_EEPROM_CRC_ERROR;
}
}
/**
* @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) {
EEP_DEBUG_PRINT("OTA_prepare write%08x at %08x\r\n", data, addr);
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;
EEP_DEBUG_PRINT("OTA_prepare write%08x at %08x\r\n", data, addr);
// 修复:完整输出所有 6 个配置项的值,便于调试
EEP_DEBUG_PRINT("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]);
// 4. 擦除包含所有配置参数的 Flash 页
fmc_status = fmc_page_erase(n_APP_LATEST_AVAL_ADDR);
if (fmc_status != FMC_READY) {
EEP_DEBUG_PRINT("OTA_SET - Page erase failed! Status: %d\r\n",
fmc_status);
return FLASH_EEPROM_ERASE_ERROR;
}
// 5. 重新写入所有之前读取的配置项值(恢复原始状态)
// 注意:不包括当前正在设置的地址,因为它将在最后单独处理
for (int i = 0; i < OTA_CONFIG_NUM; i++) {
u32 current_addr = n_APP_LATEST_AVAL_ADDR + 4 * i;
if (current_addr != addr and temp_val[i] != OTA_NOT_AVAL) {
fmc_status = write_ADDRESS(current_addr, temp_val[i]);
if (fmc_status != FMC_READY) {
EEP_DEBUG_PRINT("OTA_SET - Write backup value %08X failed at "
"0x%08X!status:%d\r\n",
temp_val[i], current_addr, fmc_status);
return FLASH_EEPROM_WRITE_ERROR;
}
}
}
#if EEP_DEBUG
// 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;
// 修复:完整输出所有 6 个配置项的值,便于调试
EEP_DEBUG_PRINT("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
return FLASH_EEPROM_OK;
}
/**
* @brief 跳转应用处理模块
* @param 无
* @retval 无
*/
void jumpToBOOT() {
// 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);
// 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);
// 5. 设置中断向量表偏移到应用程序起始地址
SCB->VTOR = BOOTLOADER_START_ADDR;
// 6. 设置 MSP 并跳转
uint32_t msp_value = *(__IO uint32_t *)BOOTLOADER_START_ADDR;
uint32_t reset_handler = *(__IO uint32_t *)(BOOTLOADER_START_ADDR + 4);
typedef void (*AppEntryFunc)(void);
AppEntryFunc app_entry = (AppEntryFunc)reset_handler;
__set_MSP(msp_value);
// 7. 跳转
app_entry();
// 如果程序执行到这里,说明跳转失败
EEP_DEBUG_PRINT("Jump to application failed!\r\n");
}
/**
* @brief 将 0x08011000 地址后 0xC000 大小区域内的数据搬运到 0x08005000 地址
* @param None
* @retval fmc_state_enum: 操作状态
*/
fmc_state_enum move_app_data() {
// 源地址:APPLATEST_START_ADDR (0x08011000)
// 目标地址:APPBACK_START_ADDR (0x08005000)
// 数据大小:APP_MAX_SIZE (0xC000 = 48KB)
fmc_state_enum fmc_status = FMC_READY;
OTA_DEBUG_PRINT("move_app_data\r\n");
// 关闭全局中断以防止 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 = APPBACK_START_ADDR + i * FMC_PAGE_SIZE;
// 擦除页面
fmc_status = fmc_page_erase(page_addr);
if (fmc_status != FMC_READY) {
fmc_lock();
#if OTA_DEBUG
OTA_DEBUG_PRINT("move_app_data erase error\r\n");
delay_1ms(1000);
#endif
__enable_irq(); // 重新开启中断
return fmc_status;
}
}
// 复制数据 - 按字(32 位)进行复制以提高效率
uint32_t offset = 0;
uint32_t write_count = 0; // 记录实际写入的字数
const uint32_t *src_ptr = (const uint32_t *)APPLATEST_START_ADDR;
uint32_t *dst_ptr = (uint32_t *)APPBACK_START_ADDR;
// 预计算源数据的 CRC,避免重复访问内存
crc_deinit();
crc_data_register_reset();
uint32_t src_crc = crc_block_data_calculate(
(uint32_t *)APPLATEST_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_unlock();
// 清除之前的标志位
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR |
FMC_FLAG_PGERR);
fmc_status =
fmc_word_program(APPBACK_START_ADDR + offset, src_data);
// 检查写入操作是否成功
if (fmc_status != FMC_READY) {
#if OTA_DEBUG
OTA_DEBUG_PRINT(
"Flash write failed at offset 0x%08X with status: %d\r\n",
offset, fmc_status);
delay_1ms(1000);
#endif
fmc_lock();
__enable_irq(); // 重新开启中断
return fmc_status;
}
write_count++;
}
offset += 4;
// 每写入一定数量的数据后,喂狗一次
if ((offset % 4096) == 0) { // 每 4KB 喂狗一次
fwdgt_counter_reload();
}
}
fmc_lock();
// 重新开启全局中断
__enable_irq();
crc_data_register_reset(); // 确保 CRC 计算从初始状态开始
// 计算目标数据的 CRC 值
uint32_t dst_crc = crc_block_data_calculate((uint32_t *)APPBACK_START_ADDR,
APP_MAX_SIZE / 4);
// 比较 CRC 值,确保数据完整性
if (src_crc != dst_crc) {
OTA_DEBUG_PRINT(
"APPbackup CRC mismatch: src_crc=0x%08X, dst_crc=0x%08X\r\n",
src_crc, dst_crc);
return (fmc_state_enum)0xFF; // CRC 校验失败
}
OTA_DEBUG_PRINT("APPbackup CRC OK\r\n");
// 设置备份应用可用标志
fmc_unlock();
OTA_SET(n_APP_BACKUP_AVAL_ADDR, OTA_AVAL);
OTA_SET(n_APP_BACKUP_CHECK_ADDR, OTA_AVAL);
fmc_lock();
return fmc_status;
}