STM32的FLASH模拟EEPROM

版权声明:本文为小生原创,转载请注明出处,好吗好的,善哉善哉!!! https://blog.csdn.net/u010650845/article/details/80255086

1. Flash.h

/*
*******************************************************************
** 
** @Brief : STM32 FLASH 模拟EEPROM 
** @Cpu   : STM32F103ZET6  
** @Flash : 512K 字节
** @Note  : 源码来自正点原子stm32 教程
*******************************************************************
*/


#ifndef __STMFLASH_H__
#define __STMFLASH_H__
#include "sys.h"  

#define STM32_FLASH_SIZE    512 /* 所选STM32的FLASH 容量大小(单位为K) */
#define STM32_FLASH_WREN    1   /* 使能FLASH写入 */             


#define STM32_FLASH_BASE 0x08000000 /* FLASH的起始地址 */

#define FLASH_KEY1               0X45670123
#define FLASH_KEY2               0XCDEF89AB

void STMFLASH_Unlock(void);                   /* FLASH解锁*/
void STMFLASH_Lock(void);                     /* FLASH上锁*/
u8 STMFLASH_GetStatus(void);                  /* 获得状态*/
u8 STMFLASH_WaitDone(u16 time);               /* 等待操作结束*/
u8 STMFLASH_ErasePage(u32 paddr);             /* 擦除页*/
u8 STMFLASH_WriteHalfWord(u32 faddr, u16 dat);/* 写入半字*/
u16 STMFLASH_ReadHalfWord(u32 faddr);         /* 读出半字  */
void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len);/* 指定地址开始写入指定长度的数据*/
u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len);                   /* 指定地址开始读取指定长度数据*/
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite);   /* 从指定地址开始写入指定长度的数据*/
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead);      /* 从指定地址开始读出指定长度的数据*/

#endif

2. Flash.c

#include "stmflash.h"
#include "delay.h"
#include "usart.h"
/*
************************************
** @Brief : 解除Flash 写保护
************************************
*/
void STMFLASH_Unlock(void){
  FLASH->KEYR=FLASH_KEY1;
  FLASH->KEYR=FLASH_KEY2;
}
/*
************************************
** @Brief : 开启Flash 写保护
************************************
*/
void STMFLASH_Lock(void){
  FLASH->CR|=1<<7;
}
/*
****************************************************************************
** @Breif : 获取Flash 状态
** @Ret   : 0 - 操作完成,可以进行其他读写操作
**             1 - 忙,指示闪存操作正在进行,   
**             2 - 编程错误,试图对内容不是0xFFFF的地址进行写入
**             3 -写保护错误,试图对写保护的闪存地址编程
****************************************************************************
*/
u8 STMFLASH_GetStatus(void){    
    u32 res;        
    res=FLASH->SR; 
    if(res&(1<<0))
        return 1;           
    else if(res&(1<<2))
        return 2;   
    else if(res&(1<<4))
        return 3;   
    return 0;                       
}
/*
*****************************************************************************
** @Brief : 等待操作完成
** @Time : 等待的最长时间
** @Ret   : 0 - 操作完成,可以进行其他读写操作
**             2 - 编程错误,试图对内容不是0xFFFF的地址进行写入
**             3 -写保护错误,试图对写保护的闪存地址编程
**        0xFF - 等待超时
*****************************************************************************
*/
u8 STMFLASH_WaitDone(u16 time){
    u8 res;
    do {
        res=STMFLASH_GetStatus();
        if(res!=1)
            break;
        delay_us(1);
        time--;
     }while(time);

     if(time==0)
        res=0xff;
     return res;
}
/*
***************************************************************
** @Brief  : 页擦除,擦除后其值为0xFFFF
** @Paddr : 页地址
***************************************************************
*/

u8 STMFLASH_ErasePage(u32 paddr){
    u8 res=0;
    res=STMFLASH_WaitDone(0X5FFF);/* 等待上一次的操作结束>20ms */ 
    if(res==0){ 
        FLASH->CR|=1<<1;/* 页擦除*/
        FLASH->AR=paddr;/* 选择擦除的页*/
        FLASH->CR|=1<<6;/* 开始擦除*/     
        res=STMFLASH_WaitDone(0X5FFF);/* 等待操作结束>20ms */
        if(res!=1){/* 非忙状态*/
            FLASH->CR&=~(1<<1);/* 清除页擦除标志*/
        }
    }
    return res;
}
/*
***************************************************************
** @Brief  : 在指定地址处写入16位数据
** @faddr : 地址, 必须是2 的整数倍
** @dat    : 待写入的数据
** @Note  : STM32 闪存的编程每次必须写入16位(半字)
***************************************************************
*/
u8 STMFLASH_WriteHalfWord(u32 faddr, u16 dat){
    u8 res;         
    res=STMFLASH_WaitDone(0XFF);/* 等待上一次操作完成*/   
    if(res==0)
    {
        FLASH->CR|=1<<0;/* 选择编程操作*/
        *(vu16*)faddr=dat;/* 写入数据*/
        res=STMFLASH_WaitDone(0XFF);/* 等待操作完成*/
        if(res!=1){/* 非忙状态*/
            FLASH->CR&=~(1<<0);/* 清除编程标志*/
        }
    } 
    return res;
} 
/*
***************************************************************
** @Brief  : 在指定地址读16位数据
** @faddr : 地址, 必须是2 的整数倍
***************************************************************
*/
u16 STMFLASH_ReadHalfWord(u32 faddr){
    return *(vu16*)faddr; 
}

#if STM32_FLASH_WREN       
/*
***************************************************************
** @Brief  : 不带检查的写入,即写入前不检查待写入
                  地址的内容是否为0xFFFF
** @WriteAddr : 待写入的起始地址
** @PBuffer : 数据指针
** @NumToWrite : 待写入的半字数(非字节数)
***************************************************************
*/

void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
{                    
    u16 i;
    for(i=0;i<NumToWrite;i++){
        STMFLASH_WriteHalfWord(WriteAddr,pBuffer[i]);
        WriteAddr+=2;
    }  
} 
/*
***************************************************************
** @Brief  : 带检查的写入,即写入前检查待写入
                  地址的内容是否为0xFFFF
** @WriteAddr : 待写入的起始地址
** @pBuffer : 数据指针
** @NumToWrite : 待写入的半字数(非字节数)
***************************************************************
*/
#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024
#else 
#define STM_SECTOR_SIZE 2048
#endif       
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];/* 2K 字节*/
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite){
    u32 secpos;    /* 扇区地址*/
    u16 secoff;    /* 扇区内偏移地址(16位字计算) */
    u16 secremain; /* 扇区内剩余地址(16位字计算)    */ 
    u16 i;    
    u32 offaddr;   /* 去掉0X08000000 后的地址*/
    if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))
        return;/* 非法地址*/
    STMFLASH_Unlock();                   /* 解锁*/
    offaddr=WriteAddr-STM32_FLASH_BASE;  /* 实际偏移地址*/
    secpos=offaddr/STM_SECTOR_SIZE;      /* 扇区地址*/
    secoff=(offaddr%STM_SECTOR_SIZE)/2;  /* 在扇区内的偏移(2个字节为基本单位) */
    secremain=STM_SECTOR_SIZE/2-secoff;  /* 扇区剩余空间大小*/
    if(NumToWrite<=secremain)
        secremain=NumToWrite;/* 待写入的数据总数不大于该扇区范围*/
    while(1) {  
        /* 读出整个扇区的内容*/
        STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
        for(i=0;i<secremain;i++){/* 校验数据*/
            if(STMFLASH_BUF[secoff+i]!=0XFFFF)
                break;  
        }
        if(i<secremain){/* 需要擦除*/
            /* 擦除整个扇区,扇区本来的数据已保存在STMFLASH_BUF */
            STMFLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);
            for(i=0;i<secremain;i++){/* 将待写入的数据复制到STMFLASH_BUF */ 
                STMFLASH_BUF[i+secoff]=pBuffer[i];    
            }
            /* 写入整个扇区  */
            STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
        }else 
            /* 不需要擦除,直接写入扇区剩余区间*/
            STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);           
        if(NumToWrite==secremain)/* 写入结束了*/
            break;
        else{/* 写入未结束*/
            secpos++;               /* 扇区地址增1*/
            secoff=0;               /* 偏移位置为0   */ 
            pBuffer+=secremain;     /* 指针偏移*/
            WriteAddr+=secremain*2; /* 写地址偏移(16位数据地址,需要*2)     */
            NumToWrite-=secremain;  /* 字节(16位)数递减*/
            if(NumToWrite>(STM_SECTOR_SIZE/2))/* 下一个扇区还是写不完*/
                secremain=STM_SECTOR_SIZE/2;
            else /*下一个扇区可以写完了*/
                secremain=NumToWrite;
        }    
    };  
    STMFLASH_Lock();/* 上锁*/
}
#endif

/*
***************************************************************
** @Brief  : 从指定地址开始读出指定长度的数据
** @ReadAddr : 待读取的起始地址
** @pBuffer : 数据指针
** @NumToRead : 待读取的半字数(非字节数)
***************************************************************
*/
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead){
    u16 i;
    for(i=0;i<NumToRead;i++)
    {
        pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);
        ReadAddr+=2;
    }
}

猜你喜欢

转载自blog.csdn.net/u010650845/article/details/80255086