打印
[STM32F1]

使用内部flash替代eeprom

[复制链接]
95|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
jonas222|  楼主 | 2025-3-22 16:54 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
一般代码不可能把程序空间用完总有富裕,就利用富裕剩余空间,再划分为多个区块,依次存取,循环使用实现增加使用寿命。例如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]   //息屏时间单位秒      

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

40

主题

1494

帖子

0

粉丝