久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 1906|回復: 5
收起左側(cè)

stm32使用內(nèi)部flash替代eepram

  [復制鏈接]
ID:309610 發(fā)表于 2024-8-6 16:53 | 顯示全部樓層 |閱讀模式
     最近研究一個項目在已有電源板子上修改程序增加功能控制。硬件無法改動需要用到參數(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]   //息屏時間單位秒       


評分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎勵!

查看全部評分

回復

使用道具 舉報

ID:913410 發(fā)表于 2024-8-9 11:15 | 顯示全部樓層
本帖最后由 y0531 于 2024-8-26 08:08 編輯

真不錯。有機會用一下。
試驗是可用的。但最后還是選了一片24CXX掛在I2C總線上,來的簡單直接。
回復

使用道具 舉報

ID:326998 發(fā)表于 2024-9-25 20:59 | 顯示全部樓層
學習收藏了
回復

使用道具 舉報

ID:1051190 發(fā)表于 2025-1-8 15:50 | 顯示全部樓層
非常好的文章。
回復

使用道具 舉報

ID:1051190 發(fā)表于 2025-1-8 15:51 | 顯示全部樓層
非常好的文章,有時間試一下。
回復

使用道具 舉報

ID:66862 發(fā)表于 2025-4-8 23:34 | 顯示全部樓層
好文章,收藏學習
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 日韩精品一| 欧美αv| 欧美一区二区三区在线 | 亚洲欧美中文日韩在线v日本 | 精品视频一区二区 | 国产精品美女久久久久aⅴ国产馆 | 精品一区二区三区在线播放 | 久久精品视频免费看 | 美女爽到呻吟久久久久 | 久久综合久久自在自线精品自 | 免费观看一级毛片 | 欧美成人精品欧美一级 | 最新国产精品视频 | 福利片在线观看 | 国产福利在线播放 | 免费国产一区二区视频 | 精品国产一区二区三区久久久久久 | 国产不卡视频在线 | 久久久久成人精品亚洲国产 | 美女一区二区在线观看 | 久久精品国产一区二区电影 | 一级看片免费视频 | 国产精品久久 | 亚洲天堂一区二区 | 欧美不卡在线 | av不卡一区 | 国产在线二区 | 亚洲视频在线播放 | 久草视频在线播放 | 国产成都精品91一区二区三 | 欧美日韩在线一区二区三区 | 国产成人精品网站 | 国产精品日韩高清伦字幕搜索 | 亚洲精品国产一区 | 永久www成人看片 | 一级做a爰片久久毛片免费看 | 欧美日韩高清在线一区 | 成人三级影院 | 日韩成人中文字幕 | 特黄特黄a级毛片免费专区 av网站免费在线观看 | 国产精品一级在线观看 |