GD32入門到實戰30--產品配置參數存儲方案 (EEPROM)

我們之前已經實現eeprom的驅動了,我們在應用層實現產品配置參數存儲方案

我們要實現:原本設定的modebus從機(單片機)地址是01,存儲在eeprom里,按下按鍵后修改地址為03,重新上電modebus從機(單片機)地址仍然是03

我們在app這個文件夾里創建store_app.c

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "eeprom_drv.h"
#include "mb.h"/**********EEPROM256個字節,128個為主區域,128個位備份區域**************/
/************************************************************** @brief  系統參數結構體定義* @note   保存在 EEPROM / Flash 中的配置表,整表以 magicCode 做*         有效性標記,末尾用 crcVal 做校驗,便于升級或恢復默認。***********************************************************/
typedef struct
{uint16_t magicCode;  /**< 魔數 0x5A5A:用于識別參數區是否有效 *//* ---------------- 用戶配置參數開始 ---------------- */uint8_t  modbusAddr; /**< Modbus 從機地址 1~247 *//* ---------------- 用戶配置參數結束 ---------------- */uint8_t  crcVal;     /**< 整表 CRC8/校驗和,用于完整性校驗 */
} SysParam_t;/** 默認參數常量,首次燒錄或恢復出廠時使用 */
#define MAGIC_CODE     0x5A5A
static const SysParam_t g_sysParamDefault =
{.magicCode = MAGIC_CODE,.modbusAddr = 1
};
/*==============================================================* 全局變量*============================================================*/
static SysParam_t g_sysParamCurrent;   /* 當前運行時的系統參數副本 *//*==============================================================* EEPROM 參數存儲布局*============================================================*/
#define SYSPARAM_MAX_SIZE     128      /* 參數區最大長度(字節) */
#define SYSPARAM_START_ADDR   0        /* 主參數區起始地址     */
#define BACKUP_START_ADDR     128      /* 備份參數區起始地址   */
/*==============================================================* CRC8 計算函數(多項式 0x31)* buf   : 數據首地址* len   : 參與計算的數據長度* return: 8 位 CRC 值*============================================================*/
static uint8_t CalcCrc8(uint8_t *buf, uint32_t len)
{uint8_t crc = 0xFF;                /* 初值 0xFF */for (uint8_t byte = 0; byte < len; byte++){crc ^= buf[byte];              /* 異或當前字節 */for (uint8_t i = 8; i > 0; --i){if (crc & 0x80)            /* 最高位為 1 */crc = (crc << 1) ^ 0x31;else                       /* 最高位為 0 */crc <<= 1;}}return crc;
}/*==============================================================* 帶 CRC 校驗的讀數據* readAddr : EEPROM 起始地址* pBuffer  : 數據緩沖區* numToRead: 讀取長度(含末尾 CRC 字節)* return   : true-成功 false-失敗(讀失敗或 CRC 不符)*============================================================*/
static bool ReadDataWithCheck(uint8_t readAddr, uint8_t *pBuffer, uint16_t numToRead)
{if (!ReadEepromData(readAddr, pBuffer, numToRead))   /* 讀原始數據 */return false;uint8_t crcVal = CalcCrc8(pBuffer, numToRead - 1);   /* 計算 CRC */if (crcVal != pBuffer[numToRead - 1])                /* 與末尾 CRC 比較 */return false;return true;
}
/*==============================================================* 讀取系統參數(先主區,失敗后備份區)* sysParam: 輸出參數結構體指針* return  : true-成功  false-兩區均失敗*============================================================*/
static bool ReadSysParam(SysParam_t *sysParam)
{uint16_t sysParamLen = sizeof(SysParam_t);/* 先嘗試主參數區 */if (ReadDataWithCheck(SYSPARAM_START_ADDR, (uint8_t *)sysParam, sysParamLen))return true;/* 主區失敗,再嘗試備份區 */if (ReadDataWithCheck(BACKUP_START_ADDR, (uint8_t *)sysParam, sysParamLen))return true;return false;    /* 兩區均失敗 */
}
/*==============================================================* 帶 CRC 校驗的寫數據* writeAddr : EEPROM 起始地址* pBuffer   : 數據緩沖區(最后 1 字節留空給 CRC)* numToWrite: 寫入長度(含末尾 CRC 字節)* return    : true-成功 false-失敗*============================================================*/
static bool WriteDataWithCheck(uint8_t writeAddr, uint8_t *pBuffer, uint16_t numToWrite)
{pBuffer[numToWrite - 1] = CalcCrc8(pBuffer, numToWrite - 1); /* 計算并填充 CRC */return WriteEepromData(writeAddr, pBuffer, numToWrite);      /* 寫入 EEPROM */
}
/*==============================================================* 寫系統參數到 EEPROM(主區 + 備份區)* sysParam: 待寫入參數* return  : true-成功 false-失敗*============================================================*/
static bool WriteSysParam(SysParam_t *sysParam)
{uint16_t sysParamLen = sizeof(SysParam_t);if (sysParamLen > SYSPARAM_MAX_SIZE)          /* 長度越界檢查 */return false;/* 先寫主區,失敗立即返回 */if (!WriteDataWithCheck(SYSPARAM_START_ADDR, (uint8_t *)sysParam, sysParamLen))return false;/* 主區成功后寫備份區,忽略備份區單獨失敗 */WriteDataWithCheck(BACKUP_START_ADDR, (uint8_t *)sysParam, sysParamLen);return true;
}
/*==============================================================* 系統參數初始化:* 1) 從 EEPROM 讀取有效參數 → 使用之* 2) 讀取失敗 → 載入默認參數* 3) 設置 Modbus 當前地址*============================================================*/
void InitSysParam(void)
{SysParam_t sysParam;/* 讀取成功且魔數正確 → 使用存儲參數 */if (ReadSysParam(&sysParam) && sysParam.magicCode == MAGIC_CODE){g_sysParamCurrent = sysParam;                /* 復制到運行區 */eMBSetSlaveAddr(g_sysParamCurrent.modbusAddr);/* 更新 Modbus 地址 */return;}/* EEPROM 無效 → 使用默認參數 */g_sysParamCurrent = g_sysParamDefault;eMBSetSlaveAddr(g_sysParamCurrent.modbusAddr);    /* 設置默認地址 */
}/*==============================================================* 在線修改 Modbus 地址* addr: 新地址(1~247)* return: true-成功 false-失敗* 注意:先寫 EEPROM,成功后更新協議棧;失敗則回滾*============================================================*/
bool SetModbusParam(uint8_t addr)
{if (addr == g_sysParamCurrent.modbusAddr)         /* 地址未變 */return true;SysParam_t sysParam = g_sysParamCurrent;          /* 復制當前參數 */sysParam.modbusAddr = addr;                       /* 修改地址 *//* 協議棧先試用新地址 */if (eMBSetSlaveAddr(addr) != MB_ENOERR)return false;/* 寫入 EEPROM(主+備) */if (!WriteSysParam(&sysParam)){/* 寫失敗 → 回滾地址 */eMBSetSlaveAddr(g_sysParamCurrent.modbusAddr);return false;}/* 成功 → 更新運行副本 */g_sysParamCurrent = sysParam;return true;
}

.h

#ifndef _STORE_APP_H_
#define _STORE_APP_H_#include <stdint.h>
#include <stdbool.h>bool SetModbusParam(uint8_t addr);
void InitSysParam(void);#endif

這是有人問了up,up這個要怎么才可以實現修改從機地址呀?

那我們當然是要寫個修改從機地址的函數,我們在mb.c添加

/************************************************************* @brief   設置 Modbus 從機地址* @param   ucSlaveAddress:新地址(1~247)* @return  MB_ENOERR      成功*          MB_EINVAL      地址非法(廣播地址或越界)* @note    僅更新全局變量,立即生效;無需重啟協議棧**********************************************************/
eMBErrorCode eMBSetSlaveAddr(UCHAR ucSlaveAddress)
{eMBErrorCode eStatus = MB_ENOERR;/* 地址合法性檢查:禁止廣播地址與越界值 */if ((ucSlaveAddress == MB_ADDRESS_BROADCAST) ||(ucSlaveAddress < MB_ADDRESS_MIN) ||(ucSlaveAddress > MB_ADDRESS_MAX)){eStatus = MB_EINVAL;        /* 參數錯誤 */}else{ucMBAddress = ucSlaveAddress;  /* 更新全局從機地址 */}return eStatus;
}

我們在用戶交互函數里進行修改:

#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(2)){printf("SetModbusParam sucess\n");}else{printf("SetModbusParam fail\n");}break;case KEY1_LONG_PRESS:TurnOffLed(LED1);break;case KEY2_SHORT_PRESS: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;}
}

main

#include <stdint.h>
#include <stdio.h>
#include "led_drv.h"
#include "key_drv.h"
#include "systick.h"
#include "usb2com_drv.h"
#include "rtc_drv.h"
#include "delay.h"
#include "sensor_drv.h"
#include "eeprom_drv.h"
#include "hmi_app.h"
#include "sensor_app.h"
#include "modbus_app.h"
#include "store_app.h"
/******實驗現象:原本設定的modebus從機(單片機)地址是01,存儲在eeprom里,按下按鍵后修改地址為03******/
typedef struct
{uint8_t run;                // 調度標志,1:調度,0:掛起uint16_t timCount;          // 時間片計數值uint16_t timRload;          // 時間片重載值void (*pTaskFuncCb)(void);  // 函數指針變量,用來保存業務功能模塊函數地址
} TaskComps_t;static TaskComps_t g_taskComps[] = 
{{0, 5,  5,   HmiTask},{0, 1000, 1000,  SensorTask},{0, 1,    1,     ModbusTask},/* 添加業務功能模塊 */
};#define TASK_NUM_MAX   (sizeof(g_taskComps) / sizeof(g_taskComps[0]))static void TaskHandler(void)
{for (uint8_t i = 0; i < TASK_NUM_MAX; i++){if (g_taskComps[i].run)                  // 判斷時間片標志{g_taskComps[i].run = 0;              // 標志清零g_taskComps[i].pTaskFuncCb();        // 執行調度業務功能模塊}}
}/**
***********************************************************
* @brief 在定時器中斷服務函數中被間接調用,設置時間片標記,需要定時器1ms產生1次中斷
* @param
* @return 
***********************************************************
*/
static void TaskScheduleCb(void)
{for (uint8_t i = 0; i < TASK_NUM_MAX; i++){if (g_taskComps[i].timCount){g_taskComps[i].timCount--;if (g_taskComps[i].timCount == 0){g_taskComps[i].run = 1;g_taskComps[i].timCount = g_taskComps[i].timRload;}}}
}static void DrvInit(void)
{DelayInit();LedDrvInit();KeyDrvInit();Usb2ComDrvInit();RtcDrvInit();SensorDrvInit();EepromDrvInit();SystickInit();
}
static void AppInit(void)
{TaskScheduleCbReg(TaskScheduleCb);ModbusAppInit();InitSysParam();
}int main(void)
{	DrvInit();AppInit();while (1){TaskHandler();}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/97982.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/97982.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/97982.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

find_code 插件 react_vite

find_code 插件 react_vite const fs require("fs"); const path require("path"); const parser require("babel/parser"); const traverse require("babel/traverse").default; const generate require("babel/generator&…

手機秒變全棧IDE:Claude Code UI的深度體驗

還在為只能在命令行中使用Claude Code而苦惱嗎&#xff1f;想在移動設備上繼續你的AI編程對話嗎&#xff1f;Claude Code UI的出現徹底改變了這一切。這個開源項目為Anthropic官方的Claude Code CLI工具提供了現代化的Web界面&#xff0c;讓你能夠在任何設備、任何地方與AI編程…

F5發布后量子API安全解決方案,以AI驅動全面防護應對量子計算威脅

量子計算的飛速演進&#xff0c;正對傳統加密體系構成日益嚴峻的安全威脅。Gartner預測顯示&#xff0c;到2029年&#xff0c;量子計算機有望攻破目前普遍采用的公鑰加密算法&#xff0c;這一風險正倒逼全球企業加速密碼體系的更迭與升級。面對這一挑戰&#xff0c;F5公司——應…

深度剖析 DC - DC 轉換器在新能源汽車中的關鍵應用

在新能源汽車的發展進程中&#xff0c;DC - DC 轉換器扮演著至關重要的角色。以下將詳細介紹其在新能源汽車上的應用&#xff0c;包括作用、電路組成以及工作原理等方面。DC - DC 轉換器的作用簡單來說&#xff0c;新能源汽車上的 DC - DC 轉換器是一個 “降壓型電壓變換器”。…

【標準項目】在線五子棋對決(下)

在線五子棋對決一. 項目介紹及鏈接二. 項目結構設計項目模塊劃分業務處理模塊的子模塊劃分項目流程圖玩家流程圖服務器流程圖三. 數據管理模塊數據庫設計創建 user_table 類四. 在線用戶管理模塊五. 游戲房間管理模塊游戲房間類實現游戲房間管理類實現六. Session 管理模塊Sess…

重構導航之核:高德地圖的深度學習架構解析 導論:從數字化世界到可計算世界

導論&#xff1a;從數字化世界到可計算世界 數字地圖的演進&#xff0c;本質上是一場關于“世界可計算性”的持續探索。第一代地圖的核心任務是數字化轉錄&#xff08;Digital Transcription&#xff09;&#xff0c;它成功地將物理世界的靜態元素——道路、建筑、興趣點&#…

邏輯回歸(sigmoid函數、混淆矩陣、精確率召回率F1)

目錄 一、概述 1、邏輯回歸 2、激活函數 sigmoid函數 3、最大似然估計 二、邏輯回歸 1、原理 2、損失函數 3、代碼 三、混淆矩陣 1、定義 2、舉例 3、代碼 四、分類評估方法 1、精確率&#xff08;Precision&#xff09; 2、召回率&#xff08;Recall&#xff09; 3、F1&#…

Redis底層實現原理之五大基礎結構

文章目錄1. 基礎結構和編碼類型2. 編碼類型和數據結構實現2.1 字符串&#xff08;String&#xff09;2.2 壓縮列表&#xff08;listpack&#xff09;2.3 哈希表&#xff08;hashtable&#xff09;2.4 快速列表&#xff08;quicklist&#xff09;2.5 整數集合&#xff08;intset…

火山引擎數據智能體DataAgent總結分享

數據的冰山:看得見的資產與看不見的鴻溝 這張圖片用“冰山”類比的方式展示了數據資產管理中的可見與不可見問題,并突出了數據利用的核心挑戰與潛在陷阱。 1. 冰山之上的“看得見的資產” 內容:數據庫、報表、指標等結構化、顯性的數據資源。 核心挑戰: 需要從“采集存儲”…

100種高級數據結構 (速查表)

一、 基礎結構的擴展與組合 (Advanced Linear Structures) 這些結構在數組、鏈表、隊列、棧等基礎結構上增加了特定功能或約束。雙端隊列 (Deque - Double-Ended Queue) 介紹&#xff1a;允許在隊列的前后兩端都進行插入和刪除操作的線性結構。應用場景&#xff1a;工作竊取算法…

一個開源的企業官網簡介

簡介一個完美的企業官網系統,支持手機端和電腦端展示企業風采,還可以展示企業產品/企業新聞資訊等等.普通用戶PC端展示普通用戶手機端展示管理后臺

TCP實現線程池競爭任務

服務端&#xff1a;#include<stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<netinet/ip.h> #include<strings.h> #include<unistd.h> #include<ctype.h> #include<arpa/inet.h&…

Redis C++ 實現筆記(F篇)

Implementing Redis in C : F Redis C 實現筆記&#xff08;F篇&#xff09; 前言 本章代碼及思路均來自Build Your Own Redis with C/C 本文章只闡述我的理解想法&#xff0c;以及需要注意的地方。 本文章為續<<Implementing Redis in C : E>>所以本文章不再…

finally 與 return的執行順序

一、第一次試驗public static void main(String[] args) throws InterruptedException {System.out.println(aaa(null));}private static StringBuilder aaa(Integer i) throws InterruptedException {StringBuilder sb new StringBuilder();try {i.toString();return sb;} ca…

Git安裝教程

簡介 Git 是目前全球最流行的分布式版本控制系統&#xff08;Distributed Version Control System, DVCS&#xff09;&#xff0c;核心作用是追蹤文件修改歷史、支持多人協同開發&#xff0c;并能高效管理代碼&#xff08;或任何文本類文件&#xff09;的版本迭代。它由 Linux…

Linux安裝RTL8821CE無線網卡驅動

1. 查看網卡芯片$ lspci | grep Net 01:00.0 Network controller: Realtek Semiconductor Co., Ltd. RTL8821CE 802.11ac PCIe Wireless Network Adapter2. 預備配套sudo apt install -y dkms git3. 下載驅動并安裝git clone https://github.com/tomaspinho/rtl8821ce.git cd r…

vue3存儲/獲取本地或會話存儲,封裝存儲工具,結合pina使用存儲

目錄 一、基本用法&#xff08;原生 API&#xff09; 1. 存儲數據 2. 獲取數據 3. 刪除數據 二、Vue3 中封裝成工具函數&#xff08;推薦&#xff09; 三、以上工具函數在 Vue3 組件中使用 1. 在選項式 API 中使用 2. 在組合式 API&#xff08;setup 語法糖&#xff09;…

【Flink】DataStream API:基本轉換算子、聚合算子

目錄基本轉換算子映射&#xff08;map&#xff09;過濾&#xff08;filter&#xff09;扁平映射聚合算子按鍵分區&#xff08;keyBy&#xff09;簡單聚合&#xff08;sum/min/max/minBy/maxBy&#xff09;規約聚合&#xff08;reduce&#xff09;基本轉換算子 有如下POJO類用來…

從淘寶推薦到微信搜索:查找算法如何支撐億級用戶——動畫可視化

本篇技術博文摘要 &#x1f31f; 本文通過動畫可視化深入解析數據結構中的核心查找算法&#xff0c;從基礎概念到高階應用&#xff0c;全面覆蓋順序查找、折半查找、分塊查找、B樹/B樹及散列查找的核心原理與實現細節。文章以動態演示為核心工具&#xff0c;直觀展現算法執行過…

圖像正向扭曲反向扭曲

在圖像處理領域&#xff0c;正向扭曲&#xff08;Forward Warping&#xff09;和反向扭曲&#xff08;Backward Warping&#xff09;是兩種核心的圖像坐標映射與像素重采樣技術&#xff0c;核心區別在于“像素映射的方向”——是從“原始圖像”到“目標圖像”&#xff0c;還是從…