2025-10-13 12:32 AM
Hello Sir/Madam,
My project is using Nucleo-F767Zi + X-NUCLEO-IKS4A1. The calibration data need to store in STM32F767ZI flash memory. I have coded writing and loading function for this purpose. However, the data read out from the flash memory are different from the data written into with forementioned functions. I do appreciate your help for debugging my codes and sorted out the issues. My codes are:
/**
******************************************************************************
* @file motion_ac_flash.c
* @author ChatGPT
* @brief 实现 MotionAC 加速度计校准参数的 Flash 存取功能
* - 使用 STM32F767ZI 的内部 Flash(Sector 11)
* - 支持写入后逐字校验
* - 保证读取与写入数据完全一致
* - 自动刷新 D-Cache,避免缓存导致读出旧数据
******************************************************************************
*/
#include "motion_ac_manager.h"
#include "motion_ac.h"
#include "stm32f7xx_hal.h"
#include <string.h>
#include <stdio.h>
/* --------------------------------------------------------------------------
* Flash 存储地址设置(根据 STM32F767ZI 的 Flash 空间布局)
* STM32F767ZI 总 Flash 容量为 2MB (0x08000000 ~ 0x081FFFFF)
* 每个扇区大小为 128KB
* 本程序使用最后一个扇区 (Sector 11, 起始地址 0x080E0000)
* -------------------------------------------------------------------------- */
#define MOTION_AC_FLASH_SECTOR_ADDR ((uint32_t)0x080E0000) // 扇区起始地址
#define MOTION_AC_FLASH_SECTOR FLASH_SECTOR_11 // 扇区编号
#define MOTION_AC_FLASH_BANK FLASH_BANK_1 // 所属 Bank
/**
* @brief 从 Flash 读取加速度计校准参数
* @PAram data_size 数据大小(字节)
* @PAram data 指向目标缓存区的指针 (MAC_output_t*)
* @retval 0: 成功读取到有效参数
* 1: 读取失败或数据无效
*/
char MotionAC_LoadCalFromNVM(unsigned short int data_size, unsigned int *data)
{
if (data == NULL || data_size == 0)
return (char)1;
/* 清除DCache,防止读取到旧缓存数据 */
SCB_InvalidateDCache_by_Addr((uint32_t *)MOTION_AC_FLASH_SECTOR_ADDR, data_size);
/* 将 Flash 中的数据拷贝到 RAM 缓存 */
memcpy((void *)data, (void *)MOTION_AC_FLASH_SECTOR_ADDR, data_size);
/* 转换为 MotionAC 输出结构体指针,便于验证 */
MAC_output_t *p = (MAC_output_t *)data;
/* 校验数据是否为有效的校准参数(通过 CalQuality 判断) */
if (p->CalQuality == MAC_CALQSTATUSGOOD ||
p->CalQuality == MAC_CALQSTATUSOK)
{
printf("[MotionAC] 成功加载校准参数,质量等级 = %d\r\n", p->CalQuality);
return (char)0;
}
printf("[MotionAC] 无有效校准参数,需要重新校准。\r\n");
return (char)1;
}
/**
* @brief 将加速度计校准参数保存到 Flash
* @PAram data_size 数据大小(字节)
* @PAram data 指向源数据的指针 (MAC_output_t*)
* @retval 0: 保存并校验成功
* 1: 写入失败或校验不通过
*/
char MotionAC_SaveCalInNVM(unsigned short int data_size, unsigned int *data)
{
if (data == NULL || data_size == 0)
return (char)1;
HAL_StatusTypeDef status;
uint32_t address = MOTION_AC_FLASH_SECTOR_ADDR;
/* 确保写入长度是4字节对齐(Flash按字写入) */
if (data_size % 4 != 0)
data_size = ((data_size + 3) / 4) * 4;
/* 解锁 Flash,允许擦写操作 */
HAL_FLASH_Unlock();
/* ----------------- 第1步:擦除指定扇区 ----------------- */
FLASH_EraseInitTypeDef eraseInitStruct;
uint32_t sectorError = 0;
eraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
eraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; // 电压范围:2.7V-3.6V
eraseInitStruct.Sector = MOTION_AC_FLASH_SECTOR;
eraseInitStruct.Banks = MOTION_AC_FLASH_BANK;
eraseInitStruct.NbSectors = 1; // 擦除1个扇区
status = HAL_FLASHEx_Erase(&eraseInitStruct, §orError);
if (status != HAL_OK)
{
HAL_FLASH_Lock();
printf("[MotionAC] Flash 擦除失败!错误码=%lu\r\n", sectorError);
return (char)1;
}
/* ----------------- 第2步:逐字写入数据 ----------------- */
for (uint32_t i = 0; i < data_size / 4; i++)
{
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data[i]);
if (status != HAL_OK)
{
HAL_FLASH_Lock();
printf("[MotionAC] Flash 写入失败!地址=0x%08lX\r\n", address);
return (char)1;
}
address += 4;
}
/* ----------------- 第3步:关闭Flash写保护 ----------------- */
HAL_FLASH_Lock();
/* ----------------- 第4步:刷新Cache ----------------- */
SCB_CleanDCache_by_Addr((uint32_t *)MOTION_AC_FLASH_SECTOR_ADDR, data_size);
SCB_InvalidateDCache_by_Addr((uint32_t *)MOTION_AC_FLASH_SECTOR_ADDR, data_size);
/* ----------------- 第5步:逐字校验Flash内容 ----------------- */
uint32_t *src=(uint32_t *)data; // 源数据(RAM)
uint32_t *dst = (uint32_t *)MOTION_AC_FLASH_SECTOR_ADDR; // 写入后Flash数据
for (uint32_t i = 0; i < data_size / 4; i++)
{
if (src[i] != dst[i]) // 校验不一致
{
printf("[MotionAC] 校验失败 @0x%08lX: 写=0x%08lX, 读=0x%08lX\r\n",
(unsigned long)&dst[i],
(unsigned long)src[i],
(unsigned long)dst[i]);
return (char)1;
}
}
printf("[MotionAC] 校准参数保存成功并校验通过。\r\n");
return (char)0;
}