一、Flash概述
闪存(Flash Memory)是一种长寿命的非易失性(在断电情况下仍能保持所存储的数据信息)的存储器。
用途:SD卡、固态硬盘、芯片内存存储单元存储代码。
二、内部FLASH特征
1、以分区形式进行规划,配置数据最好从最后扇区进行操作,防止覆盖扇区0的代码。
2、写入数据之前得先擦除数据,类似与读书时的黑板原理。
思考题1:擦除完之后,扇区里面所有的数据是什么?
答:所有的数据都是为0xFF,所有bit位都是1.
思考题2:假如说现在已经擦除完扇区,先写入了1个字节,然后在下一个偏移地址再次写入新的字节是否在需要擦除扇区?
答案:不需要的。
思考题3:假如说现在已经擦除完扇区,先写了1个字节,然后在同一个地址再次写入新的字节是否在需要擦除扇区?
答案:需要进行擦除!
例子,已经写入数据为0x12345678,然后再写入新的数据为0x1111111,最后得到的数据居然是0x101010.
#define FLASH_USER_START_ADDR ADDR_FLASH_SECTOR_6 /* Start address of user Flash area */
#define FLASH_USER_END_ADDR ADDR_FLASH_SECTOR_7 /* End address of user Flash area */
/* Base address of the Flash sectors */
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base address of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base address of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base address of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base address of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base address of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base address of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base address of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base address of Sector 7, 128 Kbytes */
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) /* Base address of Sector 8, 128 Kbytes */
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) /* Base address of Sector 9, 128 Kbytes */
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) /* Base address of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) /* Base address of Sector 11, 128 Kbytes */
uint32_t uwStartSector = 0;
uint32_t uwEndSector = 0;
uint32_t uwAddress = 0;
uint32_t uwSectorCounter = 0;
__IO uint32_t uwData32 = 0;
//获取所在扇区
static uint32_t GetSector(uint32_t Address)
{
uint32_t sector = 0;
if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0))
{
sector = FLASH_Sector_0;
}
else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1))
{
sector = FLASH_Sector_1;
}
else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2))
{
sector = FLASH_Sector_2;
}
else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3))
{
sector = FLASH_Sector_3;
}
else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4))
{
sector = FLASH_Sector_4;
}
else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5))
{
sector = FLASH_Sector_5;
}
else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6))
{
sector = FLASH_Sector_6;
}
else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7))
{
sector = FLASH_Sector_7;
}
else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8))
{
sector = FLASH_Sector_8;
}
else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9))
{
sector = FLASH_Sector_9;
}
else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10))
{
sector = FLASH_Sector_10;
}
else/*(Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_11))*/
{
sector = FLASH_Sector_11;
}
return sector;
}
//擦除扇区
void FLASH_Eraze(void)
{
//解锁闪存
/* Enable the flash control register access ,使能闪存控制寄存器的访问*/
FLASH_Unlock();
/* Clear pending flags (if any) ,清空所有相关的标志位*/
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
/* Get the number of the start and end sectors,将扇区地址转换为扇区号 */
uwStartSector = GetSector(FLASH_USER_START_ADDR); //起始扇区为扇区6
uwEndSector = GetSector(FLASH_USER_END_ADDR); //结束扇区为扇区7
/* start the erase operation */
uwSectorCounter = uwStartSector;
//判断擦除是否已经到达结束扇区
while (uwSectorCounter <= uwEndSector)
{
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
be done by word
像手机升级的一样,必须保证电压稳定,同时看到如果擦除扇区,供电的电压必须为2.7V~3.6V
*/
if (FLASH_EraseSector(uwSectorCounter, VoltageRange_3) != FLASH_COMPLETE)
{
//擦除失败
printf("FLASH_EraseSector errorrn");
while (1);
}
/* jump to the next sector ,切换到下一个扇区*/
if (uwSectorCounter == FLASH_Sector_11)
{
uwSectorCounter += 40;
}
else
{
uwSectorCounter += 8;
}
}
}
void FLASH_Wirte_Data(uint32_t data)
{
//对对应的地址写入数据,从扇区6开始写入数据
uwAddress = FLASH_USER_START_ADDR; //扇区6的起始地址
//判断当前的写入地址是否已经到达结束扇区地址
while (uwAddress < FLASH_USER_END_ADDR)
{
//对对应的地址写入4字节数据,全部数据位0x12345678
if (FLASH_ProgramWord(uwAddress, data) == FLASH_COMPLETE)
{
//写入成功后,地址偏移4个字节
uwAddress = uwAddress + 4;
}
else
{
//写入数据出问题,则将错误的地址进行打印
printf("FLASH_ProgramWord at %x errorrn",uwAddress);
while (1);
}
}
//写入完毕后,则进行锁定闪存,不允许任何的修改
FLASH_Lock();
}
void FLASH_Read_Data(void)
{
//从起始扇区开始读取
uwAddress = FLASH_USER_START_ADDR;
//判断读取数据的地址是否已经到达结束扇区
while (uwAddress < FLASH_USER_END_ADDR)
{
//读取数据
uwData32 = *(__IO uint32_t*)uwAddress;
if (uwData32 != 0x12345678)
{
printf("flash read at %X,val=%Xrn",uwAddress,uwData32);
}
//地址偏移4个字节
uwAddress = uwAddress + 4;
}
}
//在6扇区记录
void FLASH_Record(uint32_t data, uint32_t record_count)
{
//对对应的地址写入数据,从扇区6开始写入数据
//uwAddress = FLASH_USER_START_ADDR; //扇区6的起始地址
//一系列的字节数40
uwAddress = FLASH_USER_START_ADDR + uwRecordCounter + record_count*40;
//判断当前的写入地址是否已经到达结束扇区地址
if(uwAddress < FLASH_USER_END_ADDR)
{
uwRecordCounter = uwRecordCounter + 4;
//对对应的地址写入4字节数据
if (FLASH_ProgramWord(uwAddress, data) != FLASH_COMPLETE)
{
//写入数据出问题,则将错误的地址进行打印
printf("FLASH_ProgramWord at %x errorrn",uwAddress);
while (1);
}
while(uwRecordCounter == 40)
{
uwRecordCounter = 0;
}
}
//写入完毕后,则进行锁定闪存,不允许任何的修改
//FLASH_Lock();
}
//读取出记录
void Record_DataRead(uint32_t record_count)
{
uint32_t Read_buf[10] = {0};
uint32_t Read_buf_count = 0;
uint32_t times = 0;
//从起始扇区开始读取
uwAddress = FLASH_USER_START_ADDR;
//判断读取数据的地址是否已经count条记录 每条记录40字节
while (uwAddress < (FLASH_USER_START_ADDR+record_count*40))
{
for(Read_buf_count = 0; Read_buf_count < 10; Read_buf_count++)
{
//读取数据
Read_buf[Read_buf_count] = *(__IO uint32_t*)uwAddress;
//地址偏移4字节
uwAddress = uwAddress + 4;
}
times++;
//格式:[001]2017/10/14 9:40:12 Temp=30.0 Humi=92.0
printf("[%03d]20%x/%x/%x %x:%x:%x Temp=%d.%d℃ Humi=%d.%drn",times,Read_buf[0],Read_buf[1],
Read_buf[2],Read_buf[3],Read_buf[4],
Read_buf[5],Read_buf[6],Read_buf[7],
Read_buf[8],Read_buf[9]);
}
printf("读取完毕rn");
}
上一篇:STM32小白入门(第15天)-------低功耗
下一篇:STM32小白入门(第13天)-------RTC实时时钟和闹钟事件
推荐阅读最新更新时间:2024-11-05 06:44
设计资源 培训 开发板 精华推荐
- GD32E230C8T6自用开发板
- 使用 ROHM Semiconductor 的 BD9A600MUV 的参考设计
- NCV2002SN2T1G高顺从电流吸收器的典型应用电路
- 在主/从中使用 LTC3612IUDC 降压稳压器以使用 1MHz 外部时钟实现一致跟踪输出的典型应用
- AM1G-1205SZ 5V 1 瓦 DC-DC 转换器的典型应用
- STM32MP157C-EV1,带有 STM32MP157C MPU 的评估板
- 使用带有特殊驱动器的 IGBT 模块降低过冲电压
- 【训练营_基础班】基于LM2577的升压型可调电源设计
- AM2LS-2415S-NZ 15V 2W DC-DC 转换器的典型应用
- LT1037 的典型应用 - 低噪声、高速精密运算放大器