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

GD32入门到实战32--产品配置参数存储方案 (NORFLASH)

我们之前已经实现norflash的驱动了,我们在应用层实现产品配置参数存储方案

我们要实现:原本设定的modebus从机(单片机)地址是01,存储在norflash里,按下按键后修改地址为02,重新上电modebus从机(单片机)地址仍然是02

我们在app这个文件夹里创建store_app.c

/********************************************************************************** @file    sys_param.c* @brief   基于 SPI-NOR-Flash 的系统参数管理(双区备份 + CRC 校验)*          主参数区:0x00000000*          备份区: 0x00001000(4 KB 偏移)*          结构体末尾带 CRC8,保证完整性********************************************************************************/#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "norflash_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    4096U   /* 一个扇区 4 KB */
#define SYSPARAM_START_ADDR  0x00000000U
#define BACKUP_START_ADDR    0x00001000U   /* 主区 + 4 KB *//* -------------------- 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;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, uint16_t numToRead)
{ReadNorflashData(readAddr, pBuffer, numToRead);uint8_t crcCalc = CalcCrc8(pBuffer, numToRead - 1);return (crcCalc == pBuffer[numToRead - 1]);
}/* -------------------- 带 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, uint16_t numToWrite)
{/* 计算 CRC 并填充到末尾 */pBuffer[numToWrite - 1] = CalcCrc8(pBuffer, numToWrite - 1);/* 擦除 + 写入 */EraseNorflashForWrite(writeAddr, numToWrite);WriteNorflashData(writeAddr, pBuffer, numToWrite);return true;
}/* -------------------- 参数读取 -------------------- */
/*** @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;
}/* -------------------- 参数写入 -------------------- */
/*** @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){/* Flash 参数有效 */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;
}

人机交互函数:

#include <stdint.h>
#include <stdio.h>
#include "rtc_drv.h"
#include "sensor_drv.h"
#include "led_drv.h"
#include "key_drv.h"
#include "store_app.h"/**
***********************************************************
* @brief 人机交互任务处理函数
* @param 
* @return 
***********************************************************
*/
void HmiTask(void)
{
//	SensorData_t sensorData;
//	GetSensorData(&sensorData);
//	printf("\n temp is %.1f, humi is %d.\n", sensorData.temp, sensorData.humi);uint8_t keyVal;keyVal = GetKeyVal();switch (keyVal){case KEY1_SHORT_PRESS:TurnOnLed(LED1);if (SetModbusParam(1)){printf("SetModbusParam sucess1\n");}else{printf("SetModbusParam fail\n");}break;case KEY1_LONG_PRESS:TurnOffLed(LED1);break;case KEY2_SHORT_PRESS:if (SetModbusParam(2)){printf("SetModbusParam sucess2\n");}else{printf("SetModbusParam fail\n");}TurnOnLed(LED2);break;case KEY2_LONG_PRESS:TurnOffLed(LED2);break;case KEY3_SHORT_PRESS:TurnOnLed(LED3);break;case KEY3_LONG_PRESS:TurnOffLed(LED3);break;default:break;}
}

http://www.xdnf.cn/news/20214.html

相关文章:

  • rabbitmq 入门知识点
  • Go 自建库的使用教程与测试
  • 脑卒中目标检测含完整数据集
  • CSS 优先级详解:理解选择器权重和层叠规则
  • 鸿蒙NEXT动画开发指南:组件与页面典型动画场景解析
  • 【C++练习】06.输出100以内的所有素数
  • Java 攻克 PDF 表格数据提取:从棘手挑战到自动化实践
  • 深度学习——数据增强
  • devcpp 5.11的详细安装步骤
  • 上位机知识篇---conda run
  • 文华财经多空提示指标公式 变色K线多空明确指标 文华wh6赢顺多空买卖提示指标
  • 平衡车 --- 测量减速比 M法、T法测速
  • 当公司在你电脑上安装了IP-guard,你必须知道的事
  • 【面试题】你在项目中做过哪些相关性优化?
  • 【sft技巧】模型微调技巧
  • 零样本学习与少样本学习
  • 下载及交叉编译libconfig
  • 惊爆!耐达讯自动化RS485转Profinet,电机连接的“逆天神器”?
  • C++17无锁编程实战
  • 2025数学建模国赛A题思路首发!
  • 【赛题已出】2025高教社杯全国大学生数学建模竞赛ABCDE赛题已发布!
  • 阵列信号处理之均匀面阵波束合成方向图的绘制与特点解读
  • 从零开始学大模型之预训练语言模型
  • [科普] 卫星导航系统的授时原理与精度分析
  • 【案例分享】外国使馆雷电综合防护系统改造方案(四)
  • 2025年数学建模国赛B题超详细解题思路
  • 【LVGL】从HTML到LVGL:嵌入式UI的设计迁移与落地实践
  • C# FileInfo 类深度解析文件时间属性
  • NIPT 的时点选择与胎儿的异常判定
  • leetcode162.寻找峰值