在这边就不仔细介绍FALSH这个东西了,简而言之就是个存储数据的地方,比如每次完成一个任务,或者计数,就存储一下。但是在突然断电的时候可能会存不了(个人感觉只要每隔多久存一下,突然掉电也问题不大)
话不多说,直接上代码
flash.h
#ifndef _FLASH_H_
#define _FLASH_H_
#define FLASH_KEY1 0X45670123 //在"stm32f10x_flash.c"中有定义
#define FLASH_KEY2 0XCDEF89AB //在"stm32f10x_flash.c"中有定义
#include "sys.h"
#define STM32_FLASH_SIZE 512 // 所选STM32的FLASH容量大小(单位为K) STM32的手册最后几页有一个:
// Ordering information scheme,中可以看到芯片的FLSAH大小
#if STM32_FLASH_SIZE < 256
#define STM_SECTOR_SIZE 1024 //字节
#else
#define STM_SECTOR_SIZE 2048
#endif
//FLASH起始地址
#define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址
#define STM32_FLASH_ADDR 0X08070000 //STM32 要开始写的地址必须为2的倍数,并且在flash地址范围内。
u16 STMFLASH_ReadHalfWord(u32 faddr); //读出半字
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); //从指定地址开始读取指定长度的数据
#endif
flash.c
#include "stm32f10x.h"
#include "delay.h"
#include "flash.h"
//读出指定地址的半字(16位数据)
//faddr:读地址(此地址必须为2的倍数!!)
//返回值:对应数据.
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
return *(vu16*)faddr;
}
//不检查的写入: //WriteAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数
void STMFLASH_Write_NoCheck (u32 WriteAddr, u16 *pBuffer, u16 NumToWrite)
{
u16 i;
for(i=0; i<NumToWrite; i++)
{
FLASH_ProgramHalfWord(WriteAddr, pBuffer[i]);// 写入半个字=2个字节
WriteAddr += 2;// 地址增加2.
}
}
//从指定地址开始写入指定长度的数据: //WriteAddr: 起始地址(此地址必须为2的倍数!!)
//pBuffer: 数据指针
//NumToWrite: 半字(16位)数(就是要写入的16位数据的个数.)
u16 STMFLASH_BUF [STM_SECTOR_SIZE / 2]; //定义一个数组 最多是2K字节,如果是小容量的则最多是1K
void STMFLASH_Write(u32 WriteAddr, u16 *pBuffer, u16 NumToWrite)
{
u32 secpos; //扇区地址
u16 secoff; //扇区内偏移地址(16位字计算)
u16 secremain; //扇区内剩余地址(16位字计算)
u16 i; //计数 i
u32 offaddr; //去掉0X08000000后的地址
if((WriteAddr < STM32_FLASH_BASE) || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
//如果写的地址大于或小于这个flash地址,就直接跳出。
{
return;//非法地址
}
FLASH_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) //如果数据不是0xFFFF,则需要擦除
{
break;
}
}
if(i < secremain) //需要擦除
{
FLASH_ErasePage(secpos * STM_SECTOR_SIZE + STM32_FLASH_BASE);//擦除这个扇区
for(i=0; i < secremain; i++)
{
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; //写地址偏移
NumToWrite -= secremain; //字节(16位)数递减
if(NumToWrite > (STM_SECTOR_SIZE / 2)) //下一个扇区还是写不完
{
secremain = STM_SECTOR_SIZE / 2; //进入下一个扇区
}
else
secremain=NumToWrite; //下一个扇区可以写完了
}
};
FLASH_Lock(); //上锁
}
//从指定地址开始读取指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
{
u16 i;
for(i=0; i<NumToRead; i++)
{
pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr); //读取2个字节.
ReadAddr+=2; //偏移2个字节
}
}
实战也很简单,定义一个16位的数组,将想要的数据存在数组里。举个例子:
#define STM32_FLASH_ADDR 0x08060000 //定义写入地址
u16 temp[2] = {12,1234} //定义一个数组
STMFLASH_Write(STM32_FLASH_ADDR,temp,2); // 将temp的第0个和第1个的数据存到FLASH中
STMFLASH_Read(STM32_FLASH_ADDR,time,2); // 将temp的第0个和第1个的数据从FLASH中读出来