内部Flash
STM32芯片内部有一个FLASH存储器,主要用于存储代码。我们在电脑上编写好应用程序后,使用下载器把编译后的代码文件烧录到该内部FLASH中,由于FLASH存储器的内容在掉电后不会丢失,芯片重新上电复位后,内核可从内部FLASH中加载代码并运行。
除了使用外部的工具(如下载器)读写内部FLASH外,STM32芯片在运行的时候,也能对自身的内部FLASH进行读写,因此,若内部FLASH存储了应用程序后还有剩余的空间,我们可以把它像外部SPI-FLASH那样利用起来,存储一些程序运行时产生的需要掉电保存的数据。
由于访问内部FLASH的速度要比外部的SPI-FLASH快得多,所以在紧急状态下常常会使用内部FLASH存储关键记录;为了防止应用程序被抄袭,有的应用会禁止读写内部FLASH中的内容,或者在第一次运行时计算加密信息并记录到某些区域,然后删除自身的部分加密代码,这些应用都涉及到内部FLASH的操作。
如图所示为内部Flash结构(大容量):
主要功能描述:
- 主存储器中保存了我们烧写到Flash中的程序;
- 启动程序代码(系统存储区):是用户不能访问的区域,它在芯片出厂时已经固化了启动代码,它负责实现串口、USB以及CAN等ISP烧录功能;
- 用户选择字节(选项字节):选项字节用于配置FLASH的读写保护、待机/停机复位、软件/硬件看门狗等功能;
主存储器:
通常我们说STM32内部FLASH的时候,都是指这个主存储器区域,它是存储用户应用程序的空间,芯片型号说明中的256K FLASH、512K FLASH都是指这个区域的大小。
主存储器分为256页,每页大小为2KB,共512KB。这个分页的概念,实质就是FLASH存储器的扇区,与其它FLASH一样,在写入数据前,要先按页(扇区)擦除。
对内部Flash的写入过程:
1 解锁:
闪存编程手册说明:
操作过程:
操作的寄存器:
2 页擦除:
闪存编程手册说明:
执行流程:
具体的实现过程:
涉及到的寄存器:
还有不常使用的全片擦除(防止破解):
3 写入数据:
擦除完毕后即可写入数据,写入数据的过程并不是仅仅使用指针向地址赋值,赋值前还还需要配置一系列的寄存器,步骤如下:
- 检查 FLASH_SR 中的 BSY 位,以确认当前未执行任何其它的内部 Flash 操作;
- 将 FLASH_CR 寄存器中的 “激活编程寄存器位 PG” 置 1;
- 向指定的 FLASH 存储器地址执行数据写入操作,每次只能以 16位的方式写入;
- 等待 BSY 位被清零时,表示写入完成;
闪存编程手册中的描述:
操作内部Flash的库函数
解锁 + 上锁
void FLASH_Unlock(void)
{
/* Authorize the FPEC of Bank1 Access */
FLASH->KEYR = FLASH_KEY1;
FLASH->KEYR = FLASH_KEY2;
}
void FLASH_Lock(void)
{
/* Set the Lock Bit to lock the FPEC and the CR of Bank1 */
FLASH->CR |= CR_LOCK_Set;
}
擦除扇区
/**
* @brief Erases a specified FLASH page.
* @note This function can be used for all STM32F10x devices.
* @param Page_Address: The page address to be erased.
* @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG,
* FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
*/
FLASH_Status FLASH_ErasePage(uint32_t Page_Address)
{
FLASH_Status status = FLASH_COMPLETE;
/* 等待上一次操作完成 */
status = FLASH_WaitForLastOperation(EraseTimeout);
if(status == FLASH_COMPLETE)
{
/* 准备擦除页 */
FLASH->CR|= CR_PER_Set;
FLASH->AR = Page_Address;
FLASH->CR|= CR_STRT_Set;
/* 等待操作完成 */
status = FLASH_WaitForLastOperation(EraseTimeout);
/* 禁用页擦除 */
FLASH->CR &= CR_PER_Reset;
}
return status;
}
闪存编程(一次必须写入16位)
/**
* @brief Programs a half word at a specified address.
* @note This function can be used for all STM32F10x devices.
* @param Address: specifies the address to be programmed.
* @param Data: specifies the data to be programmed.
* @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
* FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
*/
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
FLASH_Status status = FLASH_COMPLETE;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(ProgramTimeout);
if(status == FLASH_COMPLETE)
{
/* if the previous operation is completed, proceed to program the new data */
FLASH->CR |= CR_PG_Set;
/* 把地址参数强制转换为指针,再通过指针操作写入数据 */
*(__IO uint16_t*)Address = Data;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(ProgramTimeout);
/* Disable the PG Bit */
FLASH->CR &= CR_PG_Reset;
}
/* Return the Program Status */
return status;
}
实验:读写内部Flash
/**
* @brief InternalFlash_Test,对内部FLASH进行读写测试
* @param None
* @retval None
*/
int InternalFlash_Test(void)
{
uint32_t EraseCounter = 0x00; //记录要擦除多少页
uint32_t Address = 0x00; //记录写入的地址
uint32_t Data = 0x3210ABCD; //记录写入的数据
uint32_t NbrOfPage = 0x00; //记录写入多少页
FLASH_Status FLASHStatus = FLASH_COMPLETE; //记录每次擦除的结果
TestStatus MemoryProgramStatus = PASSED;//记录整个测试结果
/* 解锁 */
FLASH_Unlock();
/* 计算要擦除多少页 */
NbrOfPage = (WRITE_END_ADDR - WRITE_START_ADDR) / FLASH_PAGE_SIZE;
/* 清空所有标志位 */
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
/* 按页擦除*/
for(EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
{
FLASHStatus = FLASH_ErasePage(WRITE_START_ADDR + (FLASH_PAGE_SIZE * EraseCounter));
}
/* 向内部FLASH写入数据 */
Address = WRITE_START_ADDR;
while((Address < WRITE_END_ADDR) && (FLASHStatus == FLASH_COMPLETE))
{
FLASHStatus = FLASH_ProgramWord(Address, Data);
Address = Address + 4;
}
FLASH_Lock();
/* 检查写入的数据是否正确 */
Address = WRITE_START_ADDR;
while((Address < WRITE_END_ADDR) && (MemoryProgramStatus != FAILED))
{
if((*(__IO uint32_t*) Address) != Data)
{
MemoryProgramStatus = FAILED;
}
Address += 4;
}
return MemoryProgramStatus;
}
主要流程:
实验结果: