不同型号的STM芯片,FLASH 大小不一样,最小只有16k字节,最大的1024k字节,它由一些信息快与自身操作相关的寄存器以及最重要的主存储器组成,而主存储器就是我们存放代码和常量(比如const类型)的地方,比如上一篇博文讲的4寸SPI_TFTLCD的屏幕例子,那张图片以及单片机内部的执行代码都是在flash里面存储的,而flash特点在于,他在掉电之后不会被擦除,仍然保留原来的数据,所以这里我们可以利用它存一些我们需要记录下来的数据。
flash以页为单位,以256k flash为分界线,flash大于等于256k的为大容量产品,2k字节一页 ,小于256k的为中小型容量产品,每页为1k字节
flash写入操作顺序:1.flash解锁 2.查看flash是否能被操作(如果有别的进程占用了flash显然就不行了) 3.擦除这一页 4.写入新的数据 5.flash上锁 只说了关键步骤,其实细分还有很多。
每个人手上的单片机不一样,flash大小也不一样,如果自己选地址存放,肯定是要选flash靠后位置的区域来存储,因为自己后来私存的数据决不能将原来代码存储的部分擦除了,否则单片机程序会直接崩溃,但这里还要教大家如何查看自己烧录到单片机里面的代码到底占用了多大呢,这样大家用起来心里也更有底。
这里给大家另一篇博文,这里讲的很清楚了,如果不是很想了解原理的可以跳过https://www.cnblogs.com/zhangbing12304/p/11456844.html
最重要的:bin文件,很多人以为hex文件就是最后烧录到单片机内的程序占用的大小,并不是哈,因为hex文件里面还带有一些ascll码值等一些别的东西,而我说的bin文件是啥呢?Bin文件是最纯粹的二进制机器代码, 或者说是"顺序格式"。按照assembly code顺序翻译成binary machine code,内部没有地址标记。Bin是直接的内存映象表示,二进制文件大小即为文件所包含的数据的实际大小。 BIN文件就是直接的二进制文件,一般用编程器烧写时从00开始,而如果下载运行,则下载到编译时的地址即可。可以直接在裸机上运行。我们需要把.afx文件转换成bin文件,而bin文件就是最后烧录到单片机内flash占用的大小了,而如何转化呢?
以上就是bin文件得到的方法,我们要写数据就绝不能占用到这一部分
flash起始地址是0x08000000,后面开始就是我们片内的东西了。比如给大家看看资料f103zet6的flash地址手册:
不要觉得非要看手册才会用哈,其实起始地址大家都一样,大容量就是2k一页,中小容量就是1k一页,地址往后面推,flash有多大就推多少。
uint16_t pBuffer_send[8] = {1314 ,520 ,1999 ,625 ,9527 ,7812 ,12121 ,15555};
uint16_t pBuffer[8];
STMFLASH_Write ( 0x08077800, &pBuffer_send[0], 8 ) ; //从这个地址写入8个16位数据,也就是把我们上面这个数组存入,&pBuffer_send[0]就代表这个数组的起始地址
//以下是分别读出数据(每次读16位为单位的数据)存到我们准备好的数组里面
STMFLASH_Read ( 0x08077800, &pBuffer[0], 1 );HAL_Delay(2);
STMFLASH_Read ( 0x08077802, &pBuffer[1], 1 );HAL_Delay(2);
STMFLASH_Read ( 0x08077804, &pBuffer[2], 1 );HAL_Delay(2);
STMFLASH_Read ( 0x08077806, &pBuffer[3], 1 );HAL_Delay(2);
STMFLASH_Read ( 0x08077808, &pBuffer[4], 1 );HAL_Delay(2);
STMFLASH_Read ( 0x0807780A, &pBuffer[5], 1 );HAL_Delay(2);
STMFLASH_Read ( 0x0807780C, &pBuffer[6], 1 );HAL_Delay(2);
STMFLASH_Read ( 0x0807780E, &pBuffer[7], 1 );HAL_Delay(2);
printf("data_read:%d\r\n", pBuffer[0]);
printf("data_read:%d\r\n", pBuffer[1]);
printf("data_read:%d\r\n", pBuffer[2]);
printf("data_read:%d\r\n", pBuffer[3]);
printf("data_read:%d\r\n", pBuffer[4]);
printf("data_read:%d\r\n", pBuffer[5]);
printf("data_read:%d\r\n", pBuffer[6]);
printf("data_read:%d\r\n", pBuffer[7]);
以下是flash.h的内容
#ifndef __FLASH_H__
#define __FLASH_H__
#include "main.h"
#define STM32_FLASH_SIZE 512 // 所选STM32的FLASH容量大小(单位为K)
#define STM32_FLASH_WREN 1 // stm32芯片内容FLASH 写入使能(0,禁用;1,使能)
uint16_t STMFLASH_ReadHalfWord(uint32_t faddr); //读出半字
void STMFLASH_WriteLenByte(uint32_t WriteAddr, uint32_t DataToWrite, uint16_t Len ); //指定地址开始写入指定长度的数据
uint32_t STMFLASH_ReadLenByte(uint32_t ReadAddr, uint16_t Len ); //指定地址开始读取指定长度数据
void STMFLASH_Write( uint32_t WriteAddr, uint16_t * pBuffer, uint16_t NumToWrite ); //从指定地址开始写入指定长度的数据
void STMFLASH_Read( uint32_t ReadAddr, uint16_t * pBuffer, uint16_t NumToRead ); //从指定地址开始读出指定长度的数据
void Test_Write( uint32_t WriteAddr, uint16_t WriteData ); //测试写入
#endif /* __STMFLASH_H__ */
以下是flash.c的内容
#include "flash.h"
#if STM32_FLASH_SIZE < 256
#define STM_SECTOR_SIZE 1024 //字节
#else
#define STM_SECTOR_SIZE 2048
#endif
#if STM32_FLASH_WREN //如果使能了写
static uint16_t STMFLASH_BUF [ STM_SECTOR_SIZE / 2 ];//最多是2K字节
#endif
/**
* 函数功能: 读取指定地址的半字(16位数据)
* 输入参数: faddr:读地址(此地址必须为2的倍数!!)
* 返 回 值: 返回值:对应数据.
* 说 明:无
*/
uint16_t STMFLASH_ReadHalfWord ( uint32_t faddr )
{
return *(__IO uint16_t*)faddr;
}
#if STM32_FLASH_WREN //如果使能了写
/**
* 函数功能: 不检查的写入
* 输入参数: WriteAddr:起始地址
* pBuffer:数据指针
* NumToWrite:半字(16位)数
* 返 回 值: 无
* 说 明:无
*/
void STMFLASH_Write_NoCheck ( uint32_t WriteAddr, uint16_t * pBuffer, uint16_t NumToWrite )
{
uint16_t i;
for(i=0;i<NumToWrite;i++)
{
HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,pBuffer[i]);
WriteAddr+=2; //地址增加2.
}
}
void FLASH_PageErase(unsigned int addr)
{
//传入的就是写入的那个扇区的地址,这里的目的就是清除这个扇区
uint32_t SectorError;
FLASH_EraseInitTypeDef EraseInitStruct;
EraseInitStruct.TypeErase = TYPEERASE_SECTORS;
EraseInitStruct .VoltageRange =VOLTAGE_RANGE_3 ; //提供的电压是3.3v的所以选VOLTAGE_RANGE_3
//EraseInitStruct.Sector = FLASH_SECTOR_7 ; //从FLASH_SECTOR_7也就是第七块开始删除,删除的数量是2个,不同的芯片块数量不一样,具体参照自己的芯片
EraseInitStruct.NbSectors = addr;
HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError); //清除函数,如果出错则错误码存储到SectorError这个里面
}
/**
* 函数功能: 从指定地址开始写入指定长度的数据
* 输入参数: WriteAddr:起始地址(此地址必须为2的倍数!!)
* pBuffer:数据指针
* NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
* 返 回 值: 无
* 说 明:无
*/
void STMFLASH_Write ( uint32_t WriteAddr, uint16_t * pBuffer, uint16_t NumToWrite )
{
uint16_t secoff; //扇区内偏移地址(16位字计算)
uint16_t secremain; //扇区内剩余地址(16位字计算)
uint16_t i;
uint32_t secpos; //扇区地址
uint32_t offaddr; //去掉0X08000000后的地址
if(WriteAddr<FLASH_BASE||(WriteAddr>=(FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
HAL_FLASH_Unlock(); //解锁
offaddr=WriteAddr-FLASH_BASE; //实际偏移地址.
secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6
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+FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
for(i=0;i<secremain;i++)//校验数据
{
if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除
}
if(i<secremain)//需要擦除
{
//FLASH_PageErase(secpos*STM_SECTOR_SIZE+FLASH_BASE);//擦除这个扇区
FLASH_PageErase(secpos);//擦除这个扇区
for(i=0;i<secremain;i++)//复制
{
STMFLASH_BUF[i+secoff]=pBuffer[i];
}
STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+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;//下一个扇区可以写完了
}
};
HAL_FLASH_Lock();//上锁
}
#endif
/**
* 函数功能: 从指定地址开始读出指定长度的数据
* 输入参数: ReadAddr:起始地址
* pBuffer:数据指针
* NumToRead:半字(16位)数
* 返 回 值: 无
* 说 明:无
*/
void STMFLASH_Read ( uint32_t ReadAddr, uint16_t *pBuffer, uint16_t NumToRead )
{
uint16_t i;
for(i=0;i<NumToRead;i++)
{
pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
ReadAddr+=2;//偏移2个字节.
}
}
/**
* 函数功能: 向内部flash写入数据测试
* 输入参数: WriteAddr:起始地址
* WriteData:要写入的数据
* 返 回 值: 无
* 说 明:无
*/
void Test_Write( uint32_t WriteAddr, uint16_t WriteData )
{
STMFLASH_Write(WriteAddr,&WriteData,1);//写入一个字
}
/******************* (C) COPYRIGHT 2015-2020 硬石嵌入式开发团队 *****END OF FILE****/