版权声明:本文为小生原创,转载请注明出处,好吗好的,善哉善哉!!! 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;
}
}