最近研究一個項目在已有電源板子上修改程序增加功能控制。硬件無法改動需要用到參數(shù)保存,就出現(xiàn)了現(xiàn)在的問題,單片機內(nèi)部flash壽命最大1萬次,頻繁讀寫參數(shù),很快就損害。于是就產(chǎn)生了下面代碼。一般代碼不可能把程序空間用完總有富裕,就利用富裕剩余空間,再劃分為多個區(qū)塊,依次存取,循環(huán)使用實現(xiàn)增加使用壽命。例如c8t6使用剩余12k空間,c8最小頁為1k可劃分為8個區(qū)塊,這樣擴展為96區(qū)塊,每個區(qū)塊保存60個16位數(shù)據(jù)。擦寫壽命96萬次足夠一般參數(shù)保存。 此項目使用的RCT6有256k使用剩余64k 每頁2k,分512區(qū)塊壽命500萬次 每個區(qū)塊保存100個16位數(shù)據(jù)。也可增大區(qū)塊字數(shù)保存更多數(shù)據(jù)。完全滿足一般需求無需增加eepram. 程序是在江科基礎(chǔ)教程上擴展修改來的,代碼項目已穩(wěn)定運行。
直徑上代碼
//===============FLASHD.H=========================
#ifndef __FLASHD_H
#define __FLASHD_H
#include <stdbool.h>
#include "stm32f10x.h" // 替換為您的設(shè)備頭文件
extern uint32_t RMnum ; //保存讀寫次數(shù) 索引
uint32_t MyFLASH_ReadWord(uint32_t Address);
uint16_t MyFLASH_ReadHalfWord(uint32_t Address);
uint8_t MyFLASH_ReadByte(uint32_t Address);
void MyFLASH_EraseAllPages(void);
void MyFLASH_ErasePage(uint32_t PageAddress);
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data);
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
// 讀取區(qū)塊
void ReadBlock(uint16_t block_index, uint16_t *block);
// 寫入?yún)^(qū)塊
void WriteBlock(uint16_t block_index, uint16_t *block);
// 查找具有最大RMnum的區(qū)塊
uint16_t FindMaxRMnumBlock(void);
// 初始化函數(shù)
extern void FlashInit(unsigned short *Dat_16);
// 讀取存檔
extern bool FlashInit_Dat(unsigned short *Dat_16);
// 保存數(shù)據(jù)函數(shù)
extern void SaveData(unsigned short *Dat_16);
// 擦除存儲區(qū)所有頁
extern void Store_Clear(void);
#endif
#include "stm32f10x.h" // 設(shè)備頭文件
#include "FLASHD.h" //
//適用與STM32F103RCT6
#define FLASH_BASE_ADDRESS 0x08030000 // Flash存儲器的基地址32F103RCT6(192K地址)剩余64K
#define FLASH_PAGE_SIZE 2048 // 每頁的大小(字節(jié))
#define FLASH_BLOCK_SIZE 256 // 每個區(qū)塊的大小(字節(jié))每個頁存8個區(qū)
#define FLASH_BLOCK_COUNT 200 // 區(qū)塊數(shù)量必須小于剩余塊數(shù),要求1頁塊數(shù)的倍數(shù) 當前剩余最多512
#define DATA_COUNT 80 // 定義Dat16內(nèi)保存參數(shù)的數(shù)量必須<=(FLASH_BLOCK_SIZE-4)/2 當前最大126
/* 適用與STM32F103C8T6
#define FLASH_BASE_ADDRESS 0x0800D000 // Flash存儲器的基地址(52K地址)剩余12K
#define FLASH_PAGE_SIZE 1024 // 每頁的大小(字節(jié))
#define FLASH_BLOCK_SIZE 128 // 每個區(qū)塊的大小(字節(jié))每個頁存8個區(qū)
#define FLASH_BLOCK_COUNT 96 // 區(qū)塊數(shù)量必須小于剩余塊數(shù),最好8的倍數(shù)最大96
#define DATA_COUNT 50 // 定義Dat16內(nèi)保存參數(shù)的數(shù)量必須<=(FLASH_BLOCK_SIZE-4)/2 根據(jù)區(qū)塊最大60
*/
uint16_t max_index=0; //操作區(qū)塊序號
uint16_t next_index=0;
uint32_t RMnum =0; //保存讀寫次數(shù) 索引
/**
* 函 數(shù):FLASH讀取一個32位的字
* 參 數(shù):Address 要讀取數(shù)據(jù)的字地址
* 返 回 值:指定地址下的數(shù)據(jù)
*/
uint32_t MyFLASH_ReadWord(uint32_t Address)
{
return *((__IO uint32_t *)(Address)); //使用指針訪問指定地址下的數(shù)據(jù)并返回
}
/**
* 函 數(shù):FLASH讀取一個16位的半字
* 參 數(shù):Address 要讀取數(shù)據(jù)的半字地址
* 返 回 值:指定地址下的數(shù)據(jù)
*/
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
return *((__IO uint16_t *)(Address)); //使用指針訪問指定地址下的數(shù)據(jù)并返回
}
/**
* 函 數(shù):FLASH讀取一個8位的字節(jié)
* 參 數(shù):Address 要讀取數(shù)據(jù)的字節(jié)地址
* 返 回 值:指定地址下的數(shù)據(jù)
*/
uint8_t MyFLASH_ReadByte(uint32_t Address)
{
return *((__IO uint8_t *)(Address)); //使用指針訪問指定地址下的數(shù)據(jù)并返回
}
/**
* 函 數(shù):FLASH全擦除
* 參 數(shù):無
* 返 回 值:無
* 說 明:調(diào)用此函數(shù)后,F(xiàn)LASH的所有頁都會被擦除,包括程序文件本身,擦除后,程序?qū)⒉粡痛嬖?br />
*/
void MyFLASH_EraseAllPages(void)
{
FLASH_Unlock(); //解鎖
FLASH_EraseAllPages(); //全擦除
FLASH_Lock(); //加鎖
}
/**
* 函 數(shù):FLASH頁擦除
* 參 數(shù):PageAddress 要擦除頁的頁地址
* 返 回 值:無
*/
void MyFLASH_ErasePage(uint32_t PageAddress)
{
FLASH_Unlock(); //解鎖
FLASH_ErasePage(PageAddress); //頁擦除
FLASH_Lock(); //加鎖
}
/**
* 函 數(shù):FLASH編程字
* 參 數(shù):Address 要寫入數(shù)據(jù)的字地址
* 參 數(shù):Data 要寫入的32位數(shù)據(jù)
* 返 回 值:無
*/
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
FLASH_Unlock(); //解鎖
FLASH_ProgramWord(Address, Data); //編程字
FLASH_Lock(); //加鎖
}
/**
* 函 數(shù):FLASH編程半字
* 參 數(shù):Address 要寫入數(shù)據(jù)的半字地址
* 參 數(shù):Data 要寫入的16位數(shù)據(jù)
* 返 回 值:無
*/
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
FLASH_Unlock(); //解鎖
FLASH_ProgramHalfWord(Address, Data); //編程半字
FLASH_Lock(); //加鎖
}
//-------------------------------------------------------------------------
/**
* 函 數(shù):讀取區(qū)塊
* 參 數(shù):block_index 要讀取的區(qū)塊索引
* 參 數(shù):block 存儲讀取數(shù)據(jù)的指針
* 返 回 值:無
*/
void ReadBlock(uint16_t block_index, uint16_t *block)
{
uint32_t address = FLASH_BASE_ADDRESS + block_index * FLASH_BLOCK_SIZE;
RMnum = *(uint32_t*)address; //讀取讀寫次數(shù)索引號
for (int i = 0; i < DATA_COUNT; i++) {
block[i] = MyFLASH_ReadHalfWord(address + 4 + i * 2); // 將閃存的數(shù)據(jù)加載回SRAM數(shù)組
}
}
/**
* 函 數(shù):寫入?yún)^(qū)塊
* 參 數(shù):block_index 要寫入的區(qū)塊索引
* 參 數(shù):block 存儲要寫入數(shù)據(jù)的指針
* 返 回 值:無
*/
void WriteBlock(uint16_t block_index, uint16_t *block)
{
uint32_t address = FLASH_BASE_ADDRESS + block_index * FLASH_BLOCK_SIZE;
MyFLASH_ProgramWord(address, RMnum); //寫入讀寫次數(shù)RMnum,兼做索引
for (int i = 0; i < DATA_COUNT; i++) {
MyFLASH_ProgramHalfWord(address + 4 + i * 2, block[i]);
}
}
/**
* 函 數(shù):讀取區(qū)塊的RMnum值
* 參 數(shù):block_index 要讀取的區(qū)塊索引
* 返 回 值:RMnum值
*/
uint32_t ReadBlockRMnum(uint16_t block_index)
{
uint32_t address = FLASH_BASE_ADDRESS + block_index * FLASH_BLOCK_SIZE;
uint32_t RMnum = MyFLASH_ReadWord(address); //讀取讀寫次數(shù)索引號
return RMnum;
}
/**
* 函 數(shù):查找具有最大RMnum值的區(qū)塊
* 參 數(shù):無
* 返 回 值:具有最大RMnum值的區(qū)塊索引
*/
uint16_t FindMaxRMnumBlock(void)
{
uint32_t max_RMnum = 0;
uint16_t max_index = 0;
uint32_t RMnum_x;
RMnum_x = ReadBlockRMnum(0);
// if (RMnum_x == 0xFFFFFFFF) {
// return 0; // 第一個區(qū)塊的RMnum為0xFFFFFFFF,認為是程序首次使用
// }
for (uint16_t i = 0; i < FLASH_BLOCK_COUNT; i++) {
RMnum_x = ReadBlockRMnum(i);
if (RMnum_x == 0xFFFFFFFF) {
continue; // 跳過未使用的區(qū)塊
}
if (RMnum_x > max_RMnum) {
max_RMnum = RMnum_x;
max_index = i;
}
}
return max_index;
}
/**
* 函 數(shù):初始化函數(shù)
* 參 數(shù):無
* 返 回 值:無
*/
void FlashInit(unsigned short *Dat_16)
{
max_index = FindMaxRMnumBlock(); //查找具有最大RMnum值的區(qū)塊
uint32_t address = FLASH_BASE_ADDRESS + max_index * FLASH_BLOCK_SIZE;
uint32_t RMnum_x = *((__IO uint32_t *)(address));
if ((RMnum_x == 0xFFFFFFFF) | (RMnum_x == 0)) {
return; // 無存檔,裝載系統(tǒng)默認值
}
ReadBlock(max_index, Dat_16);
RMnum++;
}
/**
* 函 數(shù):保存數(shù)據(jù)
* 參 數(shù):無
* 返 回 值:無
*/
void SaveData(unsigned short *Dat_16)
{
uint32_t Saddress =0;
max_index = FindMaxRMnumBlock();
if ((RMnum == 0) & (max_index == 0)) {
next_index = 0;
} else {
max_index++;
next_index = (max_index) % FLASH_BLOCK_COUNT;
}
if (next_index % (FLASH_PAGE_SIZE / FLASH_BLOCK_SIZE) == 0) {
MyFLASH_ErasePage(FLASH_BASE_ADDRESS + (next_index / (FLASH_PAGE_SIZE / FLASH_BLOCK_SIZE)) * FLASH_PAGE_SIZE);
}
Saddress = FLASH_BASE_ADDRESS + next_index * FLASH_BLOCK_SIZE;
MyFLASH_ProgramWord(Saddress, RMnum++);
for (int i = 0; i < DATA_COUNT; i++) {
MyFLASH_ProgramHalfWord(Saddress + 4 + i * 2, Dat_16[i]);
}
}
/**
* 函 數(shù):擦除存儲區(qū)所有頁
* 參 數(shù):無
* 返 回 值:無
*/
void Store_Clear(void)
{
uint8_t ii;
for (ii = 0; ii < (FLASH_BLOCK_COUNT / 8)+1; ii++) {
MyFLASH_ErasePage(FLASH_BASE_ADDRESS + ii * FLASH_PAGE_SIZE); // 擦除存儲區(qū)所有頁
}
}
#include <stdbool.h>
bool FlashInit_Dat(unsigned short *Dat_16)
{
uint16_t max_index = FindMaxRMnumBlock(); // 查找具有最大RMnum值的區(qū)塊
uint32_t address = FLASH_BASE_ADDRESS + max_index * FLASH_BLOCK_SIZE;
uint32_t RMnum_x = *((__IO uint32_t *)(address));
if ((RMnum_x == 0xFFFFFFFF) || (RMnum_x == 0)) {
return true; // 無存檔,裝載系統(tǒng)默認值
}
ReadBlock(max_index, Dat_16); //讀取存檔
RMnum++;
return false; // ,有存檔返回 false
}
/*******************main.c************************************************
unsigned short Dat16[200]; // 數(shù)據(jù)數(shù)組,參數(shù)表,485通訊用于存儲與PLC/觸摸屏/通信的數(shù)據(jù)
int main(void)
{
if(FlashInit_Dat(Dat16)) //有存檔Dat16裝載存檔
{ // 無存檔,裝載系統(tǒng)默認值
DATAstart();
load_Sysczb(); //載入初始默認校正表
}
load_XZ_voltage(); //載入修正表存檔
while (1)
{
if (KeyNum == 3) //按鍵2按下
{ SaveData(Dat16); KeyNum=0; } //保存參數(shù)
if (KeyNum == 4) //按鍵2按下
{ FlashInit(Dat16); KeyNum=0;} //讀取參數(shù)存檔
if (KeyNum == 5) //按鍵2按下
{ Store_Clear(); KeyNum=0; } // 擦除存儲區(qū)所有頁
}
***********************參數(shù)表定義**************************************/
// PLCCommunication.h
#ifndef PLC_COMMUNICATION_H
#define PLC_COMMUNICATION_H
#include <stdint.h>
// 數(shù)據(jù)數(shù)組,用于存儲與PLC/觸摸屏/通信的數(shù)據(jù)
extern unsigned short Dat16[200];
// 使用宏定義來簡化對數(shù)組元素的引用
#define on_off Dat16[0] //運行開關(guān)
#define ZERO_on Dat16[1] //校零開關(guān)
#define GVB Dat16[2] //高壓保護開關(guān)
#define DVB Dat16[3] //低壓保護開關(guān)
#define FSB Dat16[4] //溫控限流開關(guān)
#define TVB Dat16[5] //超溫停機保護開關(guān)
#define GLB Dat16[6] //功率保護開關(guān) 暫做看門狗啟用開關(guān)
#define PWMB Dat16[8] //PWM放電開關(guān)
#define LCD_ONF Dat16[9] //屏顯開關(guān)
#define SED_V (*(float*)&Dat16[10]) //設(shè)定輸出電壓
#define SED_I (*(float*)&Dat16[12]) //設(shè)定輸出電流
#define T_BH (*(float*)&Dat16[14]) //設(shè)定停機溫度
#define I_TC (*(float*)&Dat16[16]) //溫控限流減少電流
#define FPL (*(uint32_t*)&Dat16[18]) //放電PWM頻率
#define XZ48V (*(float*)&Dat16[20]) // 輸出電壓修正
#define XZ_PI (*(float*)&Dat16[22]) // 輸出電流修正
#define XZ_TI (*(float*)&Dat16[24]) // 主板溫度修正
#define W_OUT (*(float*)&Dat16[26]) // 輸出功率
#define QL_AH (*(float*)&Dat16[28]) // 輸出庫倫電量AH
#define QL_AH_1 (*(float*)&Dat16[64]) // 輸出庫倫電量AH
#define QL_AH_2 (*(float*)&Dat16[66]) // 輸出庫倫電量AH
#define QL_AH_3 (*(float*)&Dat16[68]) // 輸出庫倫電量AH
// 存放修正表
//uint16_t XOut_Voltages[i]= (*(uint16_t*)&Dat16[30+i]) //電壓輸出DAC插值
//uint16_t XOut_current[i] = (*(uint16_t*)&Dat16[35+i]) //電流DAC插值表
//float XZB_Voltages[i] = (float*)&Dat16[40 + i * 2]; //電壓修正表 采樣電壓
//float XZB_current[i] = (float*)&Dat16[50 + i * 2]; // 電流修正表 采樣電流
//uint16_t pwm_outputs[i] = (*(uint16_t*)&Dat16[60+i]); //pwm放電插值表
#define QL_WH (*(float*)&Dat16[70]) // 輸出庫倫電量WH
#define QL_WH_1 (*(float*)&Dat16[72]) // 輸出庫倫電量WH
#define QL_WH_2 (*(float*)&Dat16[74]) // 輸出庫倫電量WH
#define QL_WH_3 (*(float*)&Dat16[76]) // 輸出庫倫電量WH
#define DIS_OF Dat16[78] //息屏時間單位秒
|