历史上的今天

今天是:2024年09月19日(星期四)

正在发生

2019年09月19日 | 第50章 读写内部FLASH—零死角玩转STM32-F429系列

发布者:幸福微风 来源: eefocus关键字:读写  内部FLASH  STM32-F429系列 手机看文章 扫描二维码
随时随地手机看文章

本章参考资料:《STM32F4xx 中文参考手册》、《STM32F4xx规格书》、库说明文档《stm32f4xx_dsp_stdperiph_lib_um.chm》。


50.1 STM32的内部FLASH简介

在STM32芯片内部有一个FLASH存储器,它主要用于存储代码,我们在电脑上编写好应用程序后,使用下载器把编译后的代码文件烧录到该内部FLASH中,由于FLASH存储器的内容在掉电后不会丢失,芯片重新上电复位后,内核可从内部FLASH中加载代码并运行,见图 501。

图 501 STM32的内部框架图


除了使用外部的工具(如下载器)读写内部FLASH外,STM32芯片在运行的时候,也能对自身的内部FLASH进行读写,因此,若内部FLASH存储了应用程序后还有剩余的空间,我们可以把它像外部SPI-FLASH那样利用起来,存储一些程序运行时产生的需要掉电保存的数据。


由于访问内部FLASH的速度要比外部的SPI-FLASH快得多,所以在紧急状态下常常会使用内部FLASH存储关键记录;为了防止应用程序被抄袭,有的应用会禁止读写内部FLASH中的内容,或者在第一次运行时计算加密信息并记录到某些区域,然后删除自身的部分加密代码,这些应用都涉及到内部FLASH的操作。


1.    内部FLASH的构成

STM32的内部FLASH包含主存储器、系统存储器、OTP区域以及选项字节区域,它们的地址分布及大小见表 501。


表 501 STM32内部FLASH的构成

image.png

image.png

各个存储区域的说明如下:


    主存储器


一般我们说STM32内部FLASH的时候,都是指这个主存储器区域,它是存储用户应用程序的空间,芯片型号说明中的1M FLASH、2M FLASH都是指这个区域的大小。主存储器分为两块,共2MB,每块内分12个扇区,其中包含4个16KB扇区、1个64KB扇区和7个128KB的扇区。如我们实验板中使用的STM32F429IGT6型号芯片,它的主存储区域大小为1MB,所以它只包含有表中的扇区0-扇区11。


与其它FLASH一样,在写入数据前,要先按扇区擦除,而有的时候我们希望能以小规格操纵存储单元,所以STM32针对1MB FLASH的产品还提供了一种双块的存储格式,见表 502。(2M的产品按表 501的格式)


表 502 1MB产品的双块存储格式

image.png


通过配置FLASH选项控制寄存器FLASH_OPTCR的DB1M位,可以切换这两种格式,切换成双块模式后,扇区8-11的空间被转移到扇区12-19中,扇区细分了,总容量不变。


注意如果您使用的是STM32F40x系列的芯片,它没有双块存储格式,也不存在扇区12-23,仅STM32F42x/43x系列产品才支持扇区12-23。


    系统存储区


系统存储区是用户不能访问的区域,它在芯片出厂时已经固化了启动代码,它负责实现串口、USB以及CAN等ISP烧录功能。


    OTP区域


OTP(One Time Program),指的是只能写入一次的存储区域,容量为512字节,写入后数据就无法再更改,OTP常用于存储应用程序的加密密钥。


    选项字节


选项字节用于配置FLASH的读写保护、电源管理中的BOR级别、软件/硬件看门狗等功能,这部分共32字节。可以通过修改FLASH的选项控制寄存器修改。


50.2 对内部FLASH的写入过程

1.    解锁

由于内部FLASH空间主要存储的是应用程序,是非常关键的数据,为了防止误操作修改了这些内容,芯片复位后默认会结FLASH上锁,这个时候不允许设置FLASH的控制寄存器,并且不能对修改FLASH中的内容。


所以对FLASH写入数据前,需要先给它解锁。解锁的操作步骤如下:


(1)    往Flash 密钥寄存器 FLASH_KEYR中写入 KEY1 = 0x45670123


(2)    再往Flash 密钥寄存器 FLASH_KEYR中写入 KEY2 = 0xCDEF89AB


2.    数据操作位数

在内部FLASH进行擦除及写入操作时,电源电压会影响数据的最大操作位数,该电源电压可通过配置FLASH_CR 寄存器中的 PSIZE位改变,见表 503。


表 503 数据操作位数

image.png


最大操作位数会影响擦除和写入的速度,其中64位宽度的操作除了配置寄存器位外,还需要在Vpp引脚外加一个8-9V的电压源,且其供电时间不得超过一小时,否则FLASH可能损坏,所以64位宽度的操作一般是在量产时对FLASH写入应用程序时才使用,大部分应用场合都是用32位的宽度。


3.    擦除扇区

在写入新的数据前,需要先擦除存储区域,STM32提供了扇区擦除指令和整个FLASH擦除(批量擦除)的指令,批量擦除指令仅针对主存储区。


扇区擦除的过程如下:


(1)    检查 FLASH_SR 寄存器中的"忙碌寄存器位 BSY",以确认当前未执行任何 Flash 操作;


(2)    在 FLASH_CR 寄存器中,将"激活扇区擦除寄存器位SER "置 1,并设置"扇区编号寄存器位SNB",选择要擦除的扇区;


(3)    将 FLASH_CR 寄存器中的"开始擦除寄存器位 STRT "置 1,开始擦除;


(4)    等待 BSY 位被清零时,表示擦除完成。


4.    写入数据

擦除完毕后即可写入数据,写入数据的过程并不是仅仅使用指针向地址赋值,赋值前还还需要配置一系列的寄存器,步骤如下:


(1)    检查 FLASH_SR 中的 BSY 位,以确认当前未执行任何其它的内部 Flash 操作;


(2)    将 FLASH_CR 寄存器中的 "激活编程寄存器位PG" 置 1;


(3)    针对所需存储器地址(主存储器块或 OTP 区域内)执行数据写入操作;


(4)    等待 BSY 位被清零时,表示写入完成。


50.3 查看工程的空间分布

由于内部FLASH本身存储有程序数据,若不是有意删除某段程序代码,一般不应修改程序空间的内容,所以在使用内部FLASH存储其它数据前需要了解哪一些空间已经写入了程序代码,存储了程序代码的扇区都不应作任何修改。通过查询应用程序编译时产生的"*.map"后缀文件,可以了解程序存储到了哪些区域,它在工程中的打开方式见图 502,也可以到工程目录中的"Listing"文件夹中找到。



图 502 打开工程的.map文件


打开map文件后,查看文件最后部分的区域,可以看到一段以"Memory Map of the image"开头的记录(若找不到可用查找功能定位),见代码清单 501。


代码清单 501 map文件中的存储映像分布说明


1 =======================================================================


2 Memory Map of the image //存储分布映像


3


4 Image Entry point : 0x080001ad


5


6 /*程序ROM加载空间*/


7 Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00000b50, Max: 0x00100000, ABSOLUTE)


8


9 /*程序ROM执行空间*/


10 Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x00000b3c, Max: 0x00100000, ABSOLUTE)


11


12 /*地址分布列表*/


13 Base Addr Size Type Attr Idx E Section Name Object


14


15 0x08000000 0x000001ac Data RO 3 RESET startup_stm32f429_439xx.o


16 0x080001ac 0x00000000 Code RO 5359 * .ARM.Collect

00000000 mc_w.l(entry.o)


17 0x080001ac 0x00000004 Code RO 5622 .ARM.Collect

00000001 mc_w.l(entry2.o)


18 0x080001b0 0x00000004 Code RO 5625 .ARM.Collect

00000004 mc_w.l(entry5.o)


19 0x080001b4 0x00000000 Code RO 5627 .ARM.Collect

00000008 mc_w.l(entry7b.o)


20 0x080001b4 0x00000000 Code RO 5629 .ARM.Collect

0000000A mc_w.l(entry8b.o)


21 /*...此处省略大部分内容*/


22 0x08000948 0x0000000e Code RO 4910 i.USART_GetFlagStatus stm32f4xx_usart.o


23 0x08000956 0x00000002 PAD


24 0x08000958 0x000000bc Code RO 4914 i.USART_Init stm32f4xx_usart.o


25 0x08000a14 0x00000008 Code RO 4924 i.USART_SendData stm32f4xx_usart.o


26 0x08000a1c 0x00000002 Code RO 5206 i.UsageFault_Handler stm32f4xx_it.o


27 0x08000a1e 0x00000002 PAD


28 0x08000a20 0x00000010 Code RO 5363 i.__0printf$bare mc_w.l(printfb.o)


29 0x08000a30 0x0000000e Code RO 5664 i.__scatterload_copy mc_w.l(handlers.o)


30 0x08000a3e 0x00000002 Code RO 5665 i.__scatterload_null mc_w.l(handlers.o)


31 0x08000a40 0x0000000e Code RO 5666 i.__scatterload_zeroinit mc_w.l(handlers.o)


32 0x08000a4e 0x00000022 Code RO 5370 i._printf_core mc_w.l(printfb.o)


33 0x08000a70 0x00000024 Code RO 5275 i.fputc bsp_debug_usart.o


34 0x08000a94 0x00000088 Code RO 5161 i.main main.o


35 0x08000b1c 0x00000020 Data RO 5662 Region

Tableanon

Tableanon

obj.o


36


这一段是某工程的ROM存储器分布映像,在STM32芯片中,ROM区域的内容就是指存储到内部FLASH的代码。


1.    程序ROM的加载与执行空间

上述说明中有两段分别以"Load Region LR_ROM1"及"Execution Region ER_IROM1"开头的内容,它们分别描述程序的加载及执行空间。在芯片刚上电运行时,会加载程序及数据,例如它会从程序的存储区域加载到程序的执行区域,还把一些已初始化的全局变量从ROM复制到RAM空间,以便程序运行时可以修改变量的内容。加载完成后,程序开始从执行区域开始执行。


在上面map文件的描述中,我们了解到加载及执行空间的基地址(Base)都是0x08000000,它正好是STM32内部FLASH的首地址,即STM32的程序存储空间就直接是执行空间;它们的大小(Size)分别为0x00000b50及0x00000b3c,执行空间的ROM比较小的原因就是因为部分RW-data类型的变量被拷贝到RAM空间了;它们的最大空间(Max)均为0x00100000,即1M字节,它指的是内部FLASH的最大空间。


计算程序占用的空间时,需要使用加载区域的大小进行计算,本例子中应用程序使用的内部FLASH是从0x08000000至(0x08000000+0x00000b50)地址的空间区域。


2.    ROM空间分布表

在加载及执行空间总体描述之后,紧接着一个ROM详细地址分布表,它列出了工程中的各个段(如函数、常量数据)所在的地址Base Addr及占用的空间Size,列表中的Type说明了该段的类型,CODE表示代码,DATA表示数据,而PAD表示段之间的填充区域,它是无效的内容,PAD区域往往是为了解决地址对齐的问题。


观察表中的最后一项,它的基地址是0x08000b1c,大小为0x00000020,可知它占用的最高的地址空间为0x08000b3c,跟执行区域的最高地址0x00000b3c一样,但它们比加载区域说明中的最高地址0x8000b50要小,所以我们以加载区域的大小为准。对比表 501的内部FLASH扇区地址分布表,可知仅使用扇区0就可以完全存储本应用程序,所以从扇区1(地址0x08004000)后的存储空间都可以作其它用途,使用这些存储空间时不会篡改应用程序空间的数据。


50.4 操作内部FLASH的库函数

为简化编程,STM32标准库提供了一些库函数,它们封装了对内部FLASH写入数据操作寄存器的过程。


1.    FLASH解锁、上锁函数

对内部FLASH解锁、上锁的函数见代码清单 502。


代码清单 502 FLASH解锁、上锁


1


2 #define FLASH_KEY1 ((uint32_t)0x45670123)


3 #define FLASH_KEY2 ((uint32_t)0xCDEF89AB)


4 /**


5 * @brief Unlocks the FLASH control register access


6 * @param None


7 * @retval None


8 */


9 void FLASH_Unlock(void)


10 {


11 if ((FLASH->CR & FLASH_CR_LOCK) != RESET) {


12 /* Authorize the FLASH Registers access */


13 FLASH->KEYR = FLASH_KEY1;


14 FLASH->KEYR = FLASH_KEY2;


15 }


16 }


17


18 /**


19 * @brief Locks the FLASH control register access


20 * @param None


21 * @retval None


22 */


23 void FLASH_Lock(void)


24 {


25 /* Set the LOCK Bit to lock the FLASH Registers access */


26 FLASH->CR |= FLASH_CR_LOCK;


27 }


解锁的时候,它对FLASH_KEYR寄存器写入两个解锁参数,上锁的时候,对FLASH_CR寄存器的FLASH_CR_LOCK位置1。


2.    设置操作位数及擦除扇区

解锁后擦除扇区时可调用FLASH_EraseSector完成,见代码清单 503。


代码清单 503 擦除扇区


1 /**


2 * @brief Erases a specified FLASH Sector.


3 *


4 * @note If an erase and a program operations are requested simultaneously,


5 * the erase operation is performed before the program one.


6 *


7 * @param FLASH_Sector: The Sector number to be erased.


8 *


9 * @note For STM32F42xxx/43xxx devices this parameter can be a value between


10 * FLASH_Sector_0 and FLASH_Sector_23.


11 *


12 * @param VoltageRange: The device voltage range which defines the erase parallelism.


13 * This parameter can be one of the following values:


14 * @arg VoltageRange_1: when the device voltage range is 1.8V to 2.1V,


15 * the operation will be done by byte (8-bit)


16 * @arg VoltageRange_2: when the device voltage range is 2.1V to 2.7V,


17 * the operation will be done by half word (16-bit)


18 * @arg VoltageRange_3: when the device voltage range is 2.7V to 3.6V,


19 * the operation will be done by word (32-bit)


20 * @arg VoltageRange_4: when the device voltage range is 2.7V to 3.6V + External Vpp,


21 * the operation will be done by double word (64-bit)


22 *


23 * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PROGRAM,


24 * FLASH_ERROR_WRP, FLASH_ERROR_OPERATION or FLASH_COMPLETE.


25 */


26 FLASH_Status FLASH_EraseSector(uint32_t FLASH_Sector, uint8_t VoltageRange)


27 {


28 uint32_t tmp_psize = 0x0;


29 FLASH_Status status = FLASH_COMPLETE;


30


31 /* Check the parameters */


32 assert_param(IS_FLASH_SECTOR(FLASH_Sector));


33 assert_param(IS_VOLTAGERANGE(VoltageRange));


34


35 if (VoltageRange == VoltageRange_1) {


36 tmp_psize = FLASH_PSIZE_BYTE;


37 } else if (VoltageRange == VoltageRange_2) {


38 tmp_psize = FLASH_PSIZE_HALF_WORD;


39 } else if (VoltageRange == VoltageRange_3) {


40 tmp_psize = FLASH_PSIZE_WORD;


41 } else {


42 tmp_psize = FLASH_PSIZE_DOUBLE_WORD;


43 }


44 /* Wait for last operation to be completed */


45 status = FLASH_WaitForLastOperation();


46


47 if (status == FLASH_COMPLETE) {


48 /* if the previous operation is completed, proceed to erase the sector */


49 FLASH->CR &= CR_PSIZE_MASK;


50 FLASH->CR |= tmp_psize;


51 FLASH->CR &= SECTOR_MASK;


52 FLASH->CR |= FLASH_CR_SER | FLASH_Sector;


53 FLASH->CR |= FLASH_CR_STRT;


54


55 /* Wait for last operation to be completed */


56 status = FLASH_WaitForLastOperation();


57


58 /* if the erase operation is completed, disable the SER Bit */


59 FLASH->CR &= (~FLASH_CR_SER);


60 FLASH->CR &= SECTOR_MASK;


61 }


62 /* Return the Erase Status */


63 return status;


64 }


本函数包含两个输入参数,分别是要擦除的扇区号和工作电压范围,选择不同电压时实质是选择不同的数据操作位数,参数中可输入的宏在注释里已经给出。函数根据输入参数配置PSIZE位,然后擦除扇区,擦除扇区的时候需要等待一段时间,它使用FLASH_WaitForLastOperation等待,擦除完成的时候才会退出FLASH_EraseSector函数。


3.    写入数据

对内部FLASH写入数据不像对SDRAM操作那样直接指针操作就完成了,还要设置一系列的寄存器,利用FLASH_ProgramWord、FLASH_ProgramHalfWord和FLASH_ProgramByte函数可按字、半字及字节单位写入数据,见代码清单 504。


代码清单 504 写入数据


1


2 /**


3 * @brief Programs a word (32-bit) at a specified address.


4 *


5 * @note This function must be used when the device voltage range is from 2.7V to 3.6V.


6 *


7 * @note If an erase and a program operations are requested simultaneously,


8 * the erase operation is performed before the program one.


9 *


10 * @param Address: specifies the address to be programmed.


11 * This parameter can be any address in Program memory zone or in OTP zone.


12 * @param Data: specifies the data to be programmed.


13 * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PROGRAM,


14 * FLASH_ERROR_WRP, FLASH_ERROR_OPERATION or FLASH_COMPLETE.

[1] [2] [3]
关键字:读写  内部FLASH  STM32-F429系列 引用地址:第50章 读写内部FLASH—零死角玩转STM32-F429系列

上一篇:第49章 在SRAM中调试代码—零死角玩转STM32-F429系列
下一篇:第38章 I2S—音频播放与录音输入—零死角玩转STM32-F429系列

推荐阅读

   早前我们报道了华为Mate20系列成功入网的消息,这个系列总共有三款,它们的型号分别为HMA-AL00/ HMA-TL00、LYA-ALOO/ LYA-TL00、EVE-AL00/EVE-TL00,它们的对外宣传名字依次可能是Mate20、Mate20 Pro、Mate20 Pro保时捷版。 现在这个系列通过了3C认证,关于它的信息得到了进一步的确认,页面显示这三款手机均配备型号为HW-050450C00的充电头,...
本章参考资料:MDK的帮助手册《ARM Development Tools》,点击MDK界面的"help->uVision Help"菜单可打开该文件。关于ELF文件格式,参考配套资料里的《ELF文件格式》文件。在本章中讲解了非常多的文件类型,学习时请跟着教程的节奏,打开实际工程中的文件来了解。相信您已经非常熟练地使用MDK创建应用程序了,平时使用MDK编写源代码,然后编译生成机器码...
继释放了关于‘超级闪充’、‘小光芒设计’的消息后,近日OPPO再次为新机Reno4 SE预热,多形式曝光该机的核心信息。据了解,这款机型将于9月21日9点21分以线上视频形式发布,届时将由TFBOYS-王俊凯 担任主讲人,这场别开生面的新品发布会还真令人期待不已。目前关于新品OPPO Reno4 SE的爆料消息已呈现不少,唯独在价格方面引起了网上的热议,许多人在...

史海拾趣

问答坊 | AI 解惑

线性调频脉冲串制式合成孔径雷达实时成像

摘要介绍了线性调频( L,FM)脉冲串制式合成孔径雷达(SAR)提高距离向分辨率原理,并且提出线性调频SAR的信号处理算法和步骤,避免了为提高距离向分辨率而加大系统带宽的问题。通过模拟验证了算法的正确性,并分析了计算量。设计了相应的SAR实时成像处 ...…

查看全部问答∨

基于单片机的智能充电器设计,求助!

谁有关于这个的资料给我一份吧,还有我不知道用51做还是用ACR做,还是用什么ICF做!…

查看全部问答∨

现在还有带COM口的笔记本吗?

本信息来自合作QQ群:AVR单片机学习与交流群(17727270) 群管理员在坛子里的ID:铜河 要有COM口,有并口。有网口…

查看全部问答∨

在DDK中如何区分U盘和移动硬盘?

我在过滤驱动中利用IOCTL_STORAGE_QUERY_PROPERTY获取设备的总线信息, U盘和移动硬盘的总线类型都是7,本地硬盘是3. 我现在想知道如何区分U盘和移动硬盘? …

查看全部问答∨

WINCE USB摄像头驱动成功,结果实时视频速度太慢

这是一个大喜又大悲的结果,前天搞定了6410下USB摄像头驱动,可以捕获320X240 YUV420格式图像,昨天修改成了实时视频流的程序,结果大失所望,S3C6410的USB HOST是USB1.1协议的,全速12Mbps,所以我想获取30FPS的图像很难呀,实际测试结果是160MS一 ...…

查看全部问答∨

补充一点

关键是ARN9,S3C2410的功耗问题,在有一定的外围电路时,静态功耗和工作功耗?…

查看全部问答∨

智能手机成熟稳定性的关键——选对平台

关键字: 智能手机  测试  Windows Mobile  转载:电子工程专辑智能手机是一种功能丰富、第三方应用灵活的高级手持终端,但相比一般手机更容易出现死机、重启等故障问题,这极大地影响了用户的使用体验,从技术层面看,这些主要是 ...…

查看全部问答∨

BeagleBone 学习笔记03_2012_10_08

开贴说说sqlite移植 1)下载sqlite的源码,解压后进入文件夹,新建build文件夹 2)可使用../configure -help查看配置的参数说明项 3)进入build文件夹使用命令生成makefile文件:../configure --host=arm-arago-linux-gnueabi --prefix=/hom ...…

查看全部问答∨

某个处理器会一统天下?

本帖最后由 jameswangsynnex 于 2015-3-3 19:54 编辑 · ARM 32位架构现在是淘汰8位架构的最强大候选人。 · 由于32位处理器依赖于更小的工艺结点,因此增加了获得相同价格与能效的机会。 · 每种处理器大小与类型都能最好地服务于一个特定的问 ...…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved