当前位置: 首页 > news >正文

GD32入门到实战33--用单片机内部FLASH保护产品参数

inflash.c

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "gd32f30x.h"#define FLASH_PAGE_SIZE  		   		 	0x800       // 2K(1页大小)
#define FLASH_END_ADDRESS					0x0807FFFF  // 512K(结束地址)/**
*******************************************************************
* @function 指定地址开始读出指定个数的数据
* @param    readAddr,读取地址
* @param    pBuffer,数组首地址
* @param    numToRead,要读出的数据个数
* @return   
*******************************************************************
*/
bool FlashRead(uint32_t readAddr, uint8_t *pBuffer, uint32_t numToRead)
{	if ((readAddr + numToRead) > FLASH_END_ADDRESS)//如果超过结束地址{return false;}uint32_t addr = readAddr; for (uint32_t i = 0; i < numToRead; i++) //读数据{*pBuffer = *(uint8_t *)addr;//强转成U8的指针,再访问地址addr = addr + 1;    //读取地址+1pBuffer++;          //数组+1}return true;
}/**
*******************************************************************
* @function 指定地址开始写入指定个数的数据
* @param    writeAddr,写入地址
* @param    pBuffer,数组首地址
* @param    numToWrite,要写入的数据个数
* @return                                                         
*******************************************************************
*/
bool FlashWrite(uint32_t writeAddr, uint8_t *pBuffer, uint32_t numToWrite)
{	if ((writeAddr + numToWrite) > FLASH_END_ADDRESS)//如果超过结束地址{return false;}if (writeAddr % 2 == 1)   // 半字(2字节)写入,地址要对齐{return false;}uint16_t temp;fmc_state_enum  fmcState = FMC_READY;fmc_unlock();for (uint32_t i = 0; i < numToWrite / 2; i++) {fmc_flag_clear(FMC_FLAG_BANK0_END | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_PGERR);//清除标志位fmcState = fmc_halfword_program(writeAddr, *(uint16_t *)pBuffer);//传入要写入的地址if (fmcState != FMC_READY){fmc_lock(); //flash上锁return false;}pBuffer += 2;writeAddr += 2;}if (numToWrite % 2)//如果写入数据为奇数个{fmc_flag_clear(FMC_FLAG_BANK0_END | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_PGERR);temp = *pBuffer | 0xff00;fmcState = fmc_halfword_program(writeAddr, temp);//写入低字节}fmc_lock();//flash上锁return true;
}/**
*******************************************************************
* @function 擦除从eraseAddr开始到eraseAddr + numToErase的页
* @param    eraseAddr,地址
* @param    numToErase,对应写入数据时的个数
* @return                                                         
*******************************************************************
*/
bool FlashErase(uint32_t eraseAddr, uint32_t numToErase)
{if (numToErase == 0 || (eraseAddr + numToErase) > FLASH_END_ADDRESS)//如果超过结束地址{return false;} uint8_t pageNum;uint8_t addrOffset = eraseAddr % FLASH_PAGE_SIZE; 	// mod运算求余在一页内的偏移,若eraseAddr是FLASH_PAGE_SIZE整数倍,运算结果为0fmc_state_enum fmcState = FMC_READY;fmc_unlock();if (numToErase > (FLASH_PAGE_SIZE - addrOffset))           // 跨页{fmc_flag_clear(FMC_FLAG_BANK0_END | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_PGERR);fmcState = fmc_page_erase(eraseAddr);           // 擦本页if (fmcState != FMC_READY){goto erase_err;}eraseAddr += FLASH_PAGE_SIZE - addrOffset;   // 对齐到页地址numToErase -= FLASH_PAGE_SIZE - addrOffset;pageNum = numToErase / FLASH_PAGE_SIZE;      //计算还有多少个没擦除的页while (pageNum--) //不断擦除剩余页的数据{fmc_flag_clear(FMC_FLAG_BANK0_END | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_PGERR);fmcState = fmc_page_erase(eraseAddr);if (fmcState != FMC_READY){goto erase_err;}eraseAddr += FLASH_PAGE_SIZE;}if (numToErase % FLASH_PAGE_SIZE != 0)//擦除剩下不是整页的数据{fmc_flag_clear(FMC_FLAG_BANK0_END | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_PGERR);fmcState = fmc_page_erase(eraseAddr);          if (fmcState != FMC_READY){goto erase_err;}}}else  // 没有跨页{fmc_flag_clear(FMC_FLAG_BANK0_END | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_PGERR);fmcState = fmc_page_erase(eraseAddr);if (fmcState != FMC_READY){goto erase_err;}}/* lock the main FMC after the erase operation */fmc_lock();return true;erase_err:/* lock the main FMC after the erase operation */fmc_lock();return false;
}#define BUFFER_SIZE                   3000
#define FLASH_TEST_ADDRESS            0x0807F004  
void FlashDrvTest(void)
{uint8_t bufferWrite[BUFFER_SIZE];uint8_t bufferRead[BUFFER_SIZE];printf("flash writing data:\n");for (uint16_t i = 0; i < BUFFER_SIZE; i++){ bufferWrite[i] = i + 1;printf("0x%02X ", bufferWrite[i]);}printf("\n开始写入\n");if (!FlashErase(FLASH_TEST_ADDRESS, BUFFER_SIZE)){printf("Flash写数据故障,请排查!\n");return;}if (!FlashWrite(FLASH_TEST_ADDRESS, bufferWrite, BUFFER_SIZE)){printf("Flash写数据故障,请排查!\n");return;}printf("开始读取\n");if (!FlashRead(FLASH_TEST_ADDRESS, bufferRead, BUFFER_SIZE)){printf("Flash读数据故障,请排查!\n");return;}for (uint16_t i = 0; i < BUFFER_SIZE; i++){if (bufferRead[i] != bufferWrite[i]){printf("0x%02X ", bufferRead[i]);printf("Flash测试故障,请排查!\n");return;}printf("0x%02X ", bufferRead[i]);}printf("\nFlash测试通过!\n");
}

store_app.c

/********************************************************************************** @file    sys_param.c* @brief   基于 **片上 Flash** 的系统参数管理(双区备份 + CRC 校验)*          主参数区:0x0807F000(2 KB)*          备份区:  0x0807F800(2 KB)*          结构体末尾带 CRC8,保证完整性********************************************************************************/#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "inflash_drv.h"
#include "mb.h"/* -------------------- 参数表定义 -------------------- */
/*** @brief 系统参数结构体* @note  整个结构体最后 1 字节为 CRC8 校验*/
typedef struct
{uint16_t magicCode;  /**< 魔数 0x5A5A:标记参数区有效 */uint8_t  modbusAddr; /**< Modbus 从机地址 1~247 */uint8_t  crcVal;     /**< 整表 CRC8(含本字节前的所有数据) */
} SysParam_t;/* 出厂默认参数 */
static const SysParam_t g_sysParamDefault =
{.magicCode  = 0x5A5A,.modbusAddr = 1
};/* 运行期副本 */
static SysParam_t g_sysParamCurrent;/* -------------------- 存储布局 -------------------- */
#define SYSPARAM_MAX_SIZE    2048U          /* 一页 2 KB */
#define SYSPARAM_START_ADDR  0x0807F000U    /* 主参数区 */
#define BACKUP_START_ADDR    0x0807F800U    /* 备份区 *//* -------------------- CRC8 计算 -------------------- */
/*** @brief  CRC8 计算(多项式 0x31)* @param  buf 数据指针* @param  len 数据长度(不含 CRC 本身)* @return CRC8 值*/
static uint8_t CalcCrc8(uint8_t *buf, uint32_t len)
{uint8_t crc = 0xFF;          /* 初值 0xFF */for (uint32_t i = 0; i < len; i++){crc ^= buf[i];           /* 异或当前字节 */for (uint8_t j = 0; j < 8; j++){if (crc & 0x80)crc = (crc << 1) ^ 0x31;elsecrc <<= 1;}}return crc;
}/* -------------------- 带 CRC 的读 -------------------- */
/*** @brief  读取并校验数据* @param  readAddr   Flash 起始地址* @param  pBuffer    接收缓冲区* @param  numToRead  读取长度(含末尾 CRC)* @return true 校验成功;false 失败*/
static bool ReadDataWithCheck(uint32_t readAddr, uint8_t *pBuffer, uint32_t numToRead)
{/* 读原始数据 */if (!FlashRead(readAddr, pBuffer, numToRead))return false;/* 计算 CRC 并比较 */uint8_t crcCalc = CalcCrc8(pBuffer, numToRead - 1);return (crcCalc == pBuffer[numToRead - 1]);
}/* -------------------- 参数读取 -------------------- */
/*** @brief  从 Flash 读取参数(先主区,失败后备份区)* @param  sysParam 输出参数结构体指针* @return true 成功;false 两区均失效*/
static bool ReadSysParam(SysParam_t *sysParam)
{uint16_t len = sizeof(SysParam_t);/* 先尝试主区 */if (ReadDataWithCheck(SYSPARAM_START_ADDR, (uint8_t *)sysParam, len))return true;/* 主区失败 → 尝试备份区 */if (ReadDataWithCheck(BACKUP_START_ADDR, (uint8_t *)sysParam, len))return true;return false;    /* 两区均失败 */
}/* -------------------- 带 CRC 的写 -------------------- */
/*** @brief  写入数据并自动计算 CRC* @param  writeAddr   Flash 起始地址* @param  pBuffer     数据缓冲区(最后 1 字节留空给 CRC)* @param  numToWrite  写入长度(含 CRC)* @return true 成功;false 失败*/
static bool WriteDataWithCheck(uint32_t writeAddr, uint8_t *pBuffer, uint32_t numToWrite)
{/* 计算 CRC 并填充到末尾 */pBuffer[numToWrite - 1] = CalcCrc8(pBuffer, numToWrite - 1);/* 擦除 + 写入 */if (!FlashErase(writeAddr, numToWrite))return false;if (!FlashWrite(writeAddr, pBuffer, numToWrite))return false;return true;
}/* -------------------- 参数写入 -------------------- */
/*** @brief  同时写入主区与备份区* @param  sysParam 待写入参数* @return true 成功;false 失败*/
static bool WriteSysParam(SysParam_t *sysParam)
{uint16_t len = sizeof(SysParam_t);if (len > SYSPARAM_MAX_SIZE)          /* 长度越界检查 */return false;/* 先写主区,失败立即返回 */if (!WriteDataWithCheck(SYSPARAM_START_ADDR, (uint8_t *)sysParam, len))return false;/* 主区成功 → 写备份区(备份区单独失败不影响整体) */WriteDataWithCheck(BACKUP_START_ADDR, (uint8_t *)sysParam, len);return true;
}/* -------------------- 系统初始化 -------------------- */
/*** @brief  系统上电时初始化参数* @note   1) 优先读取 Flash 有效参数*         2) 失败则使用默认参数*         3) 更新 Modbus 地址*/
void InitSysParam(void)
{SysParam_t temp;/* 读取成功且魔数正确 → 使用存储参数 */if (ReadSysParam(&temp) && temp.magicCode == 0x5A5A){g_sysParamCurrent = temp;}else{/* 使用默认参数 */g_sysParamCurrent = g_sysParamDefault;}/* 设置 Modbus 从机地址 */eMBSetSlaveAddr(g_sysParamCurrent.modbusAddr);
}/* -------------------- 在线修改地址 -------------------- */
/*** @brief  在线修改 Modbus 地址(带掉电保存)* @param  addr 新地址 1~247* @return true 成功;false 失败(地址未变或写入失败)*/
bool SetModbusParam(uint8_t addr)
{/* 地址未变 */if (addr == g_sysParamCurrent.modbusAddr)return true;SysParam_t temp = g_sysParamCurrent;temp.modbusAddr = addr;/* 先让协议栈试用新地址 */if (eMBSetSlaveAddr(addr) != MB_ENOERR)return false;/* 写入 Flash(主+备) */if (!WriteSysParam(&temp)){/* 失败 → 回滚地址 */eMBSetSlaveAddr(g_sysParamCurrent.modbusAddr);return false;}/* 成功 → 更新运行副本 */g_sysParamCurrent = temp;return true;
}


文章转载自:

http://hd5kuPGd.fqcdh.cn
http://n49Wvh0Y.fqcdh.cn
http://V3m2hbns.fqcdh.cn
http://3brocHI9.fqcdh.cn
http://7Q8bY2HM.fqcdh.cn
http://cyamNlbO.fqcdh.cn
http://iJdalBAA.fqcdh.cn
http://yTWOXwtW.fqcdh.cn
http://lNQR5ROa.fqcdh.cn
http://DDL7baxv.fqcdh.cn
http://i4jeEQRh.fqcdh.cn
http://OQsi57BL.fqcdh.cn
http://AHJTqAWw.fqcdh.cn
http://9AnEfzQl.fqcdh.cn
http://z07V0y3N.fqcdh.cn
http://MayqtBEK.fqcdh.cn
http://xKqJ26jL.fqcdh.cn
http://NeX8oK8y.fqcdh.cn
http://idd4zBrP.fqcdh.cn
http://CBFbZYon.fqcdh.cn
http://IEAifmjL.fqcdh.cn
http://poPWezrK.fqcdh.cn
http://0BKveiVa.fqcdh.cn
http://4FawBaCS.fqcdh.cn
http://ThKjfLxG.fqcdh.cn
http://yMCjLBiZ.fqcdh.cn
http://Xd7tdbAs.fqcdh.cn
http://5i0rj79S.fqcdh.cn
http://TDNsdKrX.fqcdh.cn
http://udsQyyQZ.fqcdh.cn
http://www.dtcms.com/a/368778.html

相关文章:

  • Python的RSS/Atom源解析库feedparser
  • 抓虫:loongarch64架构selinux强防开启程序执行报错execmod
  • 酷柚易汛ERP 2025-09-05系统升级日志
  • STM32——WDG看门狗
  • Redis 发布订阅:社区的 “通知栏与分类订阅” 系统
  • WordPress性能优化全攻略:从插件实战到系统级优化
  • [新启航]激光频率梳 3D 轮廓测量 - 蓝光机械 3D 扫描的工作原理及优缺点
  • 3DEXPERIENCE平台五大实用技巧指南
  • 彻底搞懂深度学习-模型压缩(减枝、量化、知识蒸馏)
  • 概率论第二讲——一维随机变量及其分布
  • ChartGPT深度体验:AI图表生成工具如何高效实现数据可视化与图表美化?
  • 【AndroidStudio】官网下载免安装版,AndroidStudio压缩版的配置和使用
  • Android Activity的启动流程
  • 将 Android 设备的所有系统日志(包括内核日志、系统服务日志等)完整拷贝到 Windows 本地
  • NGUI--三大基础控件
  • 服务器IP暴露被攻击了怎么办?
  • Transformer实战——使用 run_glue.py 微调模型
  • SQLalachemy 错误 - Lost connection to MySQL server during query
  • 门控MLP(Qwen3MLP)与稀疏混合专家(Qwen3MoeSparseMoeBlock)模块解析
  • React Hooks useContext
  • 【Linux】Linux 的 cp -a 命令的作用
  • 基于FPGA实现CRC校验码算法(以MODBUS中校验码要求为例)verilog代码+仿真验证
  • LeetCode刷题-top100( 矩阵置零)
  • 算法模板(Java版)_DFS与BFS
  • 一分钟了解Modbus 转 IEC61850 网关
  • Webpack 有哪些特性?构建速度?如何优化?
  • 2025精选5款AI视频转文字工具,高效转录秒变文字!
  • 【最新版】发烧级完美解码播放器PureCodec v2025.08.29 中文免费版_电脑播放器影音解码包
  • 阿里云国际代理:阿里云的云数据库是什么?
  • 盲盒抽卡机小程序功能版块设计的合理性评估维度