一般代码不可能把程序空间用完总有富裕,就利用富裕剩余空间,再划分为多个区块,依次存取,循环使用实现增加使用寿命。例如c8t6使用剩余12k空间,c8最小页为1k可划分为8个区块,这样扩展为96区块,每个区块保存60个16位数据。擦写寿命96万次足够一般参数保存。 此项目使用的RCT6有256k使用剩余64k 每页2k,分512区块寿命500万次 每个区块保存100个16位数据。也可增大区块字数保存更多数据。完全满足一般需求无需增加eepram. 程序是在江科基础教程上扩展修改来的,代码项目已稳定运行。
直径上代码
//===============FLASHD.H=========================
#ifndef __FLASHD_H
#define __FLASHD_H
#include <stdbool.h>
#include "stm32f10x.h" // 替换为您的设备头文件
extern uint32_t RMnum ; //保存读写次数 索引
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);
// 读取区块
void ReadBlock(uint16_t block_index, uint16_t *block);
// 写入区块
void WriteBlock(uint16_t block_index, uint16_t *block);
// 查找具有最大RMnum的区块
uint16_t FindMaxRMnumBlock(void);
// 初始化函数
extern void FlashInit(unsigned short *Dat_16);
// 读取存档
extern bool FlashInit_Dat(unsigned short *Dat_16);
// 保存数据函数
extern void SaveData(unsigned short *Dat_16);
// 擦除存储区所有页
extern void Store_Clear(void);
#endif
#include "stm32f10x.h" // 设备头文件
#include "FLASHD.h" //
//适用与STM32F103RCT6
#define FLASH_BASE_ADDRESS 0x08030000 // Flash存储器的基地址32F103RCT6(192K地址)剩余64K
#define FLASH_PAGE_SIZE 2048 // 每页的大小(字节)
#define FLASH_BLOCK_SIZE 256 // 每个区块的大小(字节)每个页存8个区
#define FLASH_BLOCK_COUNT 200 // 区块数量必须小于剩余块数,要求1页块数的倍数 当前剩余最多512
#define DATA_COUNT 80 // 定义Dat16内保存参数的数量必须<=(FLASH_BLOCK_SIZE-4)/2 当前最大126
/* 适用与STM32F103C8T6
#define FLASH_BASE_ADDRESS 0x0800D000 // Flash存储器的基地址(52K地址)剩余12K
#define FLASH_PAGE_SIZE 1024 // 每页的大小(字节)
#define FLASH_BLOCK_SIZE 128 // 每个区块的大小(字节)每个页存8个区
#define FLASH_BLOCK_COUNT 96 // 区块数量必须小于剩余块数,最好8的倍数最大96
#define DATA_COUNT 50 // 定义Dat16内保存参数的数量必须<=(FLASH_BLOCK_SIZE-4)/2 根据区块最大60
*/
uint16_t max_index=0; //操作区块序号
uint16_t next_index=0;
uint32_t RMnum =0; //保存读写次数 索引
/**
* 函 数:FLASH读取一个32位的字
* 参 数:Address 要读取数据的字地址
* 返 回 值:指定地址下的数据
*/
uint32_t MyFLASH_ReadWord(uint32_t Address)
{
return *((__IO uint32_t *)(Address)); //使用指针访问指定地址下的数据并返回
}
/**
* 函 数:FLASH读取一个16位的半字
* 参 数:Address 要读取数据的半字地址
* 返 回 值:指定地址下的数据
*/
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
return *((__IO uint16_t *)(Address)); //使用指针访问指定地址下的数据并返回
}
/**
* 函 数:FLASH读取一个8位的字节
* 参 数:Address 要读取数据的字节地址
* 返 回 值:指定地址下的数据
*/
uint8_t MyFLASH_ReadByte(uint32_t Address)
{
return *((__IO uint8_t *)(Address)); //使用指针访问指定地址下的数据并返回
}
/**
* 函 数:FLASH全擦除
* 参 数:无
* 返 回 值:无
* 说 明:调用此函数后,FLASH的所有页都会被擦除,包括程序文件本身,擦除后,程序将不复存在
*/
void MyFLASH_EraseAllPages(void)
{
FLASH_Unlock(); //解锁
FLASH_EraseAllPages(); //全擦除
FLASH_Lock(); //加锁
}
/**
* 函 数:FLASH页擦除
* 参 数:PageAddress 要擦除页的页地址
* 返 回 值:无
*/
void MyFLASH_ErasePage(uint32_t PageAddress)
{
FLASH_Unlock(); //解锁
FLASH_ErasePage(PageAddress); //页擦除
FLASH_Lock(); //加锁
}
/**
* 函 数:FLASH编程字
* 参 数:Address 要写入数据的字地址
* 参 数:Data 要写入的32位数据
* 返 回 值:无
*/
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
FLASH_Unlock(); //解锁
FLASH_ProgramWord(Address, Data); //编程字
FLASH_Lock(); //加锁
}
/**
* 函 数:FLASH编程半字
* 参 数:Address 要写入数据的半字地址
* 参 数:Data 要写入的16位数据
* 返 回 值:无
*/
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
FLASH_Unlock(); //解锁
FLASH_ProgramHalfWord(Address, Data); //编程半字
FLASH_Lock(); //加锁
}
//-------------------------------------------------------------------------
/**
* 函 数:读取区块
* 参 数:block_index 要读取的区块索引
* 参 数:block 存储读取数据的指针
* 返 回 值:无
*/
void ReadBlock(uint16_t block_index, uint16_t *block)
{
uint32_t address = FLASH_BASE_ADDRESS + block_index * FLASH_BLOCK_SIZE;
RMnum = *(uint32_t*)address; //读取读写次数索引号
for (int i = 0; i < DATA_COUNT; i++) {
block[i] = MyFLASH_ReadHalfWord(address + 4 + i * 2); // 将闪存的数据加载回SRAM数组
}
}
/**
* 函 数:写入区块
* 参 数:block_index 要写入的区块索引
* 参 数:block 存储要写入数据的指针
* 返 回 值:无
*/
void WriteBlock(uint16_t block_index, uint16_t *block)
{
uint32_t address = FLASH_BASE_ADDRESS + block_index * FLASH_BLOCK_SIZE;
MyFLASH_ProgramWord(address, RMnum); //写入读写次数RMnum,兼做索引
for (int i = 0; i < DATA_COUNT; i++) {
MyFLASH_ProgramHalfWord(address + 4 + i * 2, block[i]);
}
}
/**
* 函 数:读取区块的RMnum值
* 参 数:block_index 要读取的区块索引
* 返 回 值: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); //读取读写次数索引号
return RMnum;
}
/**
* 函 数:查找具有最大RMnum值的区块
* 参 数:无
* 返 回 值:具有最大RMnum值的区块索引
*/
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; // 第一个区块的RMnum为0xFFFFFFFF,认为是程序首次使用
// }
for (uint16_t i = 0; i < FLASH_BLOCK_COUNT; i++) {
RMnum_x = ReadBlockRMnum(i);
if (RMnum_x == 0xFFFFFFFF) {
continue; // 跳过未使用的区块
}
if (RMnum_x > max_RMnum) {
max_RMnum = RMnum_x;
max_index = i;
}
}
return max_index;
}
/**
* 函 数:初始化函数
* 参 数:无
* 返 回 值:无
*/
void FlashInit(unsigned short *Dat_16)
{
max_index = FindMaxRMnumBlock(); //查找具有最大RMnum值的区块
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; // 无存档,装载系统默认值
}
ReadBlock(max_index, Dat_16);
RMnum++;
}
/**
* 函 数:保存数据
* 参 数:无
* 返 回 值:无
*/
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]);
}
}
/**
* 函 数:擦除存储区所有页
* 参 数:无
* 返 回 值:无
*/
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); // 擦除存储区所有页
}
}
#include <stdbool.h>
bool FlashInit_Dat(unsigned short *Dat_16)
{
uint16_t max_index = FindMaxRMnumBlock(); // 查找具有最大RMnum值的区块
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; // 无存档,装载系统默认值
}
ReadBlock(max_index, Dat_16); //读取存档
RMnum++;
return false; // ,有存档返回 false
}
/*******************main.c************************************************
unsigned short Dat16[200]; // 数据数组,参数表,485通讯用于存储与PLC/触摸屏/通信的数据
int main(void)
{
if(FlashInit_Dat(Dat16)) //有存档Dat16装载存档
{ // 无存档,装载系统默认值
DATAstart();
load_Sysczb(); //载入初始默认校正表
}
load_XZ_voltage(); //载入修正表存档
while (1)
{
if (KeyNum == 3) //按键2按下
{ SaveData(Dat16); KeyNum=0; } //保存参数
if (KeyNum == 4) //按键2按下
{ FlashInit(Dat16); KeyNum=0;} //读取参数存档
if (KeyNum == 5) //按键2按下
{ Store_Clear(); KeyNum=0; } // 擦除存储区所有页
}
***********************参数表定义**************************************/
// PLCCommunication.h
#ifndef PLC_COMMUNICATION_H
#define PLC_COMMUNICATION_H
#include <stdint.h>
// 数据数组,用于存储与PLC/触摸屏/通信的数据
extern unsigned short Dat16[200];
// 使用宏定义来简化对数组元素的引用
#define on_off Dat16[0] //运行开关
#define ZERO_on Dat16[1] //校零开关
#define GVB Dat16[2] //高压保护开关
#define DVB Dat16[3] //低压保护开关
#define FSB Dat16[4] //温控限流开关
#define TVB Dat16[5] //超温停机保护开关
#define GLB Dat16[6] //功率保护开关 暂做看门狗启用开关
#define PWMB Dat16[8] //PWM放电开关
#define LCD_ONF Dat16[9] //屏显开关
#define SED_V (*(float*)&Dat16[10]) //设定输出电压
#define SED_I (*(float*)&Dat16[12]) //设定输出电流
#define T_BH (*(float*)&Dat16[14]) //设定停机温度
#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] //息屏时间单位秒
|