第50章 读写内部FLASH—零死角玩转STM32-F429系列

STM32F429内部FLASH读写擦除操作流程和寄存器配置要点 一、STM32F429 FLASH总体介绍 双bank架构,容量高达2 Mbytes,支持read-while-write边读边写(RWW) 支持128位宽的数据读取 Byte、half-word、word 、 double word 写入 sector, bank, and mass erase (两个bank) Dual bank memory organization 对于2M产品,一个bank(1 MB)分为4个16 KB的扇区,1个64 KB的扇区和7个128 KB的扇区。 2Mbytes是两个 阅读详情
 

第50章     读写内部FLASH

全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn

野火视频教程优酷观看网址:http://i.youku.com/firege

 

 

 

本章参考资料:《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的构成

区域

块

名称

块地址

大小

主存储器

块1

扇区0

0x0800 0000 - 0x0800 3FFF

16 Kbytes

扇区1

0x0800 4000 - 0x0800 7FFF

16 Kbytes

扇区2

0x0800 8000 - 0x0800 BFFF

16 Kbytes

扇区3

0x0800 C000 - 0x0800 FFFF

16 Kbyte

扇区4

0x0801 0000 - 0x0801 FFFF

64 Kbytes

扇区5

0x0802 0000 - 0x0803 FFFF

128 Kbytes

扇区6

0x0804 0000 - 0x0805 FFFF

128 Kbytes

扇区7

0x0806 0000 - 0x0807 FFFF

128 Kbytes

扇区8

0x0808 0000 - 0x0809 FFFF

128 Kbytes

扇区9

0x080A 0000 - 0x080B FFFF

128 Kbytes

扇区10

0x080C 0000 - 0x080D FFFF

128 Kbytes

扇区11

0x080E 0000 - 0x080F FFFF

128 Kbytes

块2

扇区12

0x0810 0000 - 0x0810 3FFF

16 Kbytes

扇区13

0x0810 4000 - 0x0810 7FFF

16 Kbytes

扇区14

0x0810 8000 - 0x0810 BFFF

16 Kbytes

扇区15

0x0810 C000 - 0x0810 FFFF

16 Kbyte

扇区16

0x0811 0000 - 0x0811 FFFF

64 Kbytes

扇区17

0x0812 0000 - 0x0813 FFFF

128 Kbytes

扇区18

0x0814 0000 - 0x0815 FFFF

128 Kbytes

扇区19

0x0816 0000 - 0x0817 FFFF

128 Kbytes

扇区20

0x0818 0000 - 0x0819 FFFF

128 Kbytes

扇区21

0x081A 0000 - 0x081B FFFF

128 Kbytes

扇区22

0x081C 0000 - 0x081D FFFF

128 Kbytes

扇区23

0x081E 0000 - 0x081F FFFF

128 Kbytes

系统存储区

0x1FFF 0000 - 0x1FFF 77FF

30 Kbytes

OTP区域

0x1FFF 7800 - 0x1FFF 7A0F

528 bytes

选项字节

块1 

0x1FFF C000 - 0x1FFF C00F

16 bytes

块2 

0x1FFE C000 - 0x1FFE C00F

16 bytes

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

    主存储器

一般我们说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产品的双块存储格式

1M字节单块存储器的扇区分配(默认)

1M字节双块存储器的扇区分配

DB1M=0

DB1M=1

主存储器

扇区号

扇区大小

主存储器

扇区号

扇区大小

1MB

扇区0

16 Kbytes

Bank 1

512KB

扇区0

16 Kbytes

扇区1

16 Kbytes

扇区1

16 Kbytes

扇区2

16 Kbytes

扇区2

16 Kbytes

扇区3

16 Kbytes

扇区3

16 Kbytes

扇区4

64 Kbytes

扇区4

64 Kbytes

扇区5

128 Kbytes

扇区5

128 Kbytes

扇区6

128 Kbytes

扇区6

128 Kbytes

扇区7

128 Kbytes

扇区7

128 Kbytes

扇区8

128 Kbytes

Bank 2

512KB

扇区12

16 Kbytes

扇区9

128 Kbytes

扇区13

16 Kbytes

扇区10

128 Kbytes

扇区14

16 Kbytes

扇区11

128 Kbytes

扇区15

16 Kbytes

-

-

扇区16

64 Kbytes

-

-

扇区17

128 Kbytes

-

-

扇区18

128 Kbytes

-

-

扇区19

128 Kbytes

通过配置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 数据操作位数

电压范围

2.7 - 3.6 V

(使用外部Vpp)

2.7 - 3.6 V

2.1 – 2.7 V

1.8 – 2.1 V

位数

64

32

16

8

PSIZE(1:0)配置

11b

10b

01b

00b

最大操作位数会影响擦除和写入的速度,其中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$$Table anon$$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.

15 */

16 FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data)

17 {

18 FLASH_Status status = FLASH_COMPLETE;

19

20 /* Check the parameters */

21 assert_param(IS_FLASH_ADDRESS(Address));

22

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

24 status = FLASH_WaitForLastOperation();

25

26 if (status == FLASH_COMPLETE) {

27/* if the previous operation is completed, proceed to program the new data */

28 FLASH->CR &= CR_PSIZE_MASK;

29 FLASH->CR |= FLASH_PSIZE_WORD;

30 FLASH->CR |= FLASH_CR_PG;

31

32 *(__IO uint32_t*)Address = Data;

33

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

35 status = FLASH_WaitForLastOperation();

36

37 /* if the program operation is completed, disable the PG Bit */

38 FLASH->CR &= (~FLASH_CR_PG);

39 }

40 /* Return the Program Status */

41 return status;

42 }

看函数代码可了解到,使用指针进行赋值操作前设置了数据操作宽度,并设置了PG寄存器位,在赋值操作后,调用了FLASH_WaitForLastOperation函数等待写操作完毕。HalfWord和Byte操作宽度的函数执行过程类似。

50.5 实验:读写内部FLASH

在本小节中我们以实例讲解如何使用内部FLASH存储数据。

50.5.1 硬件设计

本实验仅操作了STM32芯片内部的FLASH空间,无需额外的硬件。

50.5.2 软件设计

本小节讲解的是"内部FLASH编程"实验,请打开配套的代码工程阅读理解。为了方便展示及移植,我们把操作内部FLASH相关的代码都编写到"bsp_internalFlash.c"及"bsp_internalFlash.h"文件中,这些文件是我们自己编写的,不属于标准库的内容,可根据您的喜好命名文件。

1.    程序设计要点

(7)    对内部FLASH解锁;

(8)    找出空闲扇区,擦除目标扇区;

(9)    进行读写测试。

2.    代码分析
硬件定义

读写内部FLASH不需要用到任何外部硬件,不过在擦写时常常需要知道各个扇区的基地址,我们把这些基地址定义到bsp_internalFlash.h文件中,见代码清单 441。

代码清单 505 各个扇区的基地址(bsp_internalFlash.h文件)

1

2 /* 各个扇区的基地址 */

3 #define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000)

4 #define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000)

5 #define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000)

6 #define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000)

7 #define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000)

8 #define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000)

9 #define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000)

10 #define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000)

11 #define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000)

12 #define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000)

13 #define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000)

14 #define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000)

15

16 #define ADDR_FLASH_SECTOR_12 ((uint32_t)0x08100000)

17 #define ADDR_FLASH_SECTOR_13 ((uint32_t)0x08104000)

18 #define ADDR_FLASH_SECTOR_14 ((uint32_t)0x08108000)

19 #define ADDR_FLASH_SECTOR_15 ((uint32_t)0x0810C000)

20 #define ADDR_FLASH_SECTOR_16 ((uint32_t)0x08110000)

21 #define ADDR_FLASH_SECTOR_17 ((uint32_t)0x08120000)

22 #define ADDR_FLASH_SECTOR_18 ((uint32_t)0x08140000)

23 #define ADDR_FLASH_SECTOR_19 ((uint32_t)0x08160000)

24 #define ADDR_FLASH_SECTOR_20 ((uint32_t)0x08180000)

25 #define ADDR_FLASH_SECTOR_21 ((uint32_t)0x081A0000)

26 #define ADDR_FLASH_SECTOR_22 ((uint32_t)0x081C0000)

27 #define ADDR_FLASH_SECTOR_23 ((uint32_t)0x081E0000)

这些宏跟表 501中的地址说明一致。

根据扇区地址计算SNB寄存器的值

在擦除操作时,需要向FLASH控制寄存器FLASH_CR的SNB位写入要擦除的扇区号,固件库把各个扇区对应的寄存器值使用宏定义到了stm32f4xx_flash.h文件。为了便于使用,我们自定义了一个GetSector函数,根据输入的内部FLASH地址,找出其所在的扇区,并返回该扇区对应的SNB位寄存器值,见代码清单 442。

代码清单 506 写入到SNB寄存器位的值(stm32f4xx_flash.h及bsp_internalFlash.c文件)

1 /*固件库定义的用于扇区写入到SNB寄存器位的宏(stm32f4xx_flash.h文件)*/
				
 2 #define FLASH_Sector_0     ((uint16_t)0x0000)
				
 3 #define FLASH_Sector_1     ((uint16_t)0x0008)
				
 4 #define FLASH_Sector_2     ((uint16_t)0x0010)
				
 5 #define FLASH_Sector_3     ((uint16_t)0x0018)
				
 6 #define FLASH_Sector_4     ((uint16_t)0x0020)
				
 7 #define FLASH_Sector_5     ((uint16_t)0x0028)
				
 8 #define FLASH_Sector_6     ((uint16_t)0x0030)
				
 9 #define FLASH_Sector_7     ((uint16_t)0x0038)
				
10 #define FLASH_Sector_8     ((uint16_t)0x0040)
				
11 #define FLASH_Sector_9     ((uint16_t)0x0048)
				
12 #define FLASH_Sector_10    ((uint16_t)0x0050)
				
13 #define FLASH_Sector_11    ((uint16_t)0x0058)
				
14 #define FLASH_Sector_12    ((uint16_t)0x0080)
				
15 #define FLASH_Sector_13    ((uint16_t)0x0088)
				
16 #define FLASH_Sector_14    ((uint16_t)0x0090)
				
17 #define FLASH_Sector_15    ((uint16_t)0x0098)
				
18 #define FLASH_Sector_16    ((uint16_t)0x00A0)
				
19 #define FLASH_Sector_17    ((uint16_t)0x00A8)
				
20 #define FLASH_Sector_18    ((uint16_t)0x00B0)
				
21 #define FLASH_Sector_19    ((uint16_t)0x00B8)
				
22 #define FLASH_Sector_20    ((uint16_t)0x00C0)
				
23 #define FLASH_Sector_21    ((uint16_t)0x00C8)
				
24 #define FLASH_Sector_22    ((uint16_t)0x00D0)
				
25 #define FLASH_Sector_23    ((uint16_t)0x00D8)
				
26 
			
27 /*定义在bsp_internalFlash.c文件中的函数*/
				
28 /**
				
29   * @brief  根据输入的地址给出它所在的sector
				
30   *         例如:
			
31             uwStartSector = GetSector(FLASH_USER_START_ADDR);
				
32             uwEndSector = GetSector(FLASH_USER_END_ADDR);
				
33   * @param  Address:地址
			
34   * @retval 地址所在的sector
				
35   */
				
36 static uint32_t GetSector(uint32_t Address)
				
37 {
				
38 
							uint32_t sector = 0;
				
39 
			
40 
							if ((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0)) {
				
41         sector = FLASH_Sector_0;
				
42     } else if ((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1)) {
43         sector = FLASH_Sector_1;
				
44     }
				
45 
			
46 
								/*此处省略扇区2-扇区21的内容*/
				
47 
			
48 else if ((Address < ADDR_FLASH_SECTOR_23) && (Address >= ADDR_FLASH_SECTOR_22)) {
49         sector = FLASH_Sector_22;
				
50     } else { /*(Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_23))*/
51         sector = FLASH_Sector_23;
				
52     }
				
53 
							return sector;
				
54 }

代码中固件库定义的宏FLASH_Sector_0-23对应的值是跟寄存器说明一致的,见图 503。

图 503 FLASH_CR寄存器的SNB位的值

GetSector函数根据输入的地址与各个扇区的基地址进行比较,找出它所在的扇区,并使用固件库中的宏,返回扇区对应的SNB值。

读写内部FLASH

一切准备就绪,可以开始对内部FLASH进行擦写,这个过程不需要初始化任何外设,只要按解锁、擦除及写入的流程走就可以了,见代码清单 443。

代码清单 507 对内部地FLASH进行读写测试(bsp_internalFlash.c文件)

1

2 /*准备写入的测试数据*/

3 #define DATA_32 ((uint32_t)0x00000000)

4 /* 要擦除内部FLASH的起始地址 */

5 #define FLASH_USER_START_ADDR ADDR_FLASH_SECTOR_8

6 /* 要擦除内部FLASH的结束地址 */

7 #define FLASH_USER_END_ADDR ADDR_FLASH_SECTOR_12

8

9 /**

10 * @brief InternalFlash_Test,对内部FLASH进行读写测试

11 * @param None

12 * @retval None

13 */

14 int InternalFlash_Test(void)

15 {

16 /*要擦除的起始扇区(包含)及结束扇区(不包含),如8-12,表示擦除8、9、10、11扇区*/

17 uint32_t uwStartSector = 0;

18 uint32_t uwEndSector = 0;

19

20 uint32_t uwAddress = 0;

21 uint32_t uwSectorCounter = 0;

22

23 __IO uint32_t uwData32 = 0;

24 __IO uint32_t uwMemoryProgramStatus = 0;

25

26 /* FLASH 解锁 ********************************/

27 /* 使能访问FLASH控制寄存器 */

28 FLASH_Unlock();

29

30 /* 擦除用户区域 (用户区域指程序本身没有使用的空间,可以自定义)**/

31 /* 清除各种FLASH的标志位 */

32 FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |

33 FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);

34

35

36 uwStartSector = GetSector(FLASH_USER_START_ADDR);

37 uwEndSector = GetSector(FLASH_USER_END_ADDR);

38

39 /* 开始擦除操作 */

40 uwSectorCounter = uwStartSector;

41 while (uwSectorCounter <= uwEndSector) {

42 /* VoltageRange_3 以"字"的大小进行操作 */

43 if (FLASH_EraseSector(uwSectorCounter, VoltageRange_3) != FLASH_COMPLETE) {

44 /*擦除出错,返回,实际应用中可加入处理 */

45 return -1;

46 }

47 /* 计数器指向下一个扇区 */

48 if (uwSectorCounter == FLASH_Sector_11) {

49 uwSectorCounter += 40;

50 } else {

51 uwSectorCounter += 8;

52 }

53 }

54

55 /* 以"字"的大小为单位写入数据 ********************************/

56 uwAddress = FLASH_USER_START_ADDR;

57

58 while (uwAddress < FLASH_USER_END_ADDR) {

59 if (FLASH_ProgramWord(uwAddress, DATA_32) == FLASH_COMPLETE) {

60 uwAddress = uwAddress + 4;

61 } else {

62 /*写入出错,返回,实际应用中可加入处理 */

63 return -1;

64 }

65 }

66

67

68 /* 给FLASH上锁,防止内容被篡改*/

69 FLASH_Lock();

70

71

72 /* 从FLASH中读取出数据进行校验***************************************/

73 /* MemoryProgramStatus = 0: 写入的数据正确

74 MemoryProgramStatus != 0: 写入的数据错误,其值为错误的个数 */

75 uwAddress = FLASH_USER_START_ADDR;

76 uwMemoryProgramStatus = 0;

77

78 while (uwAddress < FLASH_USER_END_ADDR) {

79 uwData32 = *(__IO uint32_t*)uwAddress;

80

81 if (uwData32 != DATA_32) {

82 uwMemoryProgramStatus++;

83 }

84

85 uwAddress = uwAddress + 4;

86 }

87 /* 数据校验不正确 */

88 if (uwMemoryProgramStatus) {

89 return -1;

90 } else { /*数据校验正确*/

91 return 0;

92 }

93 }

94

该函数的执行过程如下:

(1)    调用FLASH_Unlock解锁;

(2)    调用FLASH_ClearFlag清除各种标志位;

(3)    调用GetSector根据起始地址及结束地址计算要擦除的扇区;

(4)    调用FLASH_EraseSector擦除扇区,擦除时按字为单位进行操作;

(5)    调用FLASH_ProgramWord函数向起始地址至结束地址的存储区域都写入数值"DATA_32";

(6)    调用FLASH_Lock上锁;

(7)    使用指针读取数据内容并校验。

main函数

最后我们来看看main函数的执行流程,见代码清单 444。

代码清单 508 main函数(main.c文件)

1 /**
				
 2   * @brief  主函数
			
 3   * @param  无
			
 4   * @retval 无
			
 5   */
				
 6 int main(void)
				
 7 {
				
 8 
								/*初始化USART,配置模式为 115200 8-N-1*/
				
 9     Debug_USART_Config();
				
10     LED_GPIO_Config();
				
11 
			
12     LED_BLUE;
				
13 
								/*调用printf函数,因为重定向了fputc,printf的内容会输出到串口*/
				
14     printf("this is a usart printf demo. \r\n");
				
15     printf("\r\n欢迎使用秉火  STM32 F429 开发板。\r\n");
					
16     printf("正在进行读写内部FLASH实验,请耐心等待\r\n");
					
17 
			
18 
							if (InternalFlash_Test()==0) {
				
19         LED_GREEN;
				
20         printf("读写内部FLASH测试成功\r\n");
					
21 
			
22     } else {
				
23         printf("读写内部FLASH测试失败\r\n");
					
24         LED_RED;
				
25     }
				
26 }

main函数中初始化了用于指示调试信息的LED及串口后,直接调用了InternalFlash_Test函数,进行读写测试并根据测试结果输出调试信息。

50.5.3 下载验证

用USB线连接开发板"USB TO UART"接口跟电脑,在电脑端打开串口调试助手,把编译好的程序下载到开发板。在串口调试助手可看到擦写内部FLASH的调试信息。

 

50.6 每课一问

5.    尝试擦除应用程序所在的内部FLASH扇区,观察实验现象。

6.    使用C语言的"const uint8_t value;"和"volatile const uint8_t value"语句定义的变量value有什么区别?若定义后使用内部FLASH操作擦除value的存储空间,再读取value的值,哪种定义能正常读取?

 

第51章 设置FLASH的读写保护及解除—零死角玩转STM32-F429系列 第51章     设置FLASH的读写保护及解除 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege       本章参考资料:《STM32F4xx 中文参考手册》、《STM32F4xx规格书》、库说明文档《stm32f4xx_dsp_stdperiph_lib_um 阅读详情

相关推荐

第24章 SPI—读写串行FLASH—零死角玩转STM32-F429系列

第24章     SPI—读写串行FLASH 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege     本章参考资料:《STM32F4xx 中文参考手册》、《STM32F4xx规格书》、库帮助文档《stm32f4xx_dsp_stdperiph_lib_um.chm》

flyleaf91的专栏 9739

STM32F429 FLASH读写

STM32F429VET6的FLASH空间: 基地址=0X8000000 大小=0X80000。读写函数,字读写(4字节读写)MAIN函数(字读写示例)擦除时要按扇区擦除,

qq_36272499的博客 1725

第14章 启动文件详解—零死角玩转STM32-F429系列

第14章     启动文件详解 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege   本章参考资料《STM32F4xx 中文参考手册》第十章-中断和事件:表 46. STM32F42xxx 和 STM32F43xxx 的向量表;MDK中的帮助手册—ARM Develop

flyleaf91的专栏 4067

第26章 FMC—扩展外部SDRAM—零死角玩转STM32-F429系列

第26章     FMC—扩展外部SDRAM 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege     本章参考资料:《STM32F4xx 中文参考手册2》、《STM32F4xx规格书》、库帮助文档《stm32f4xx_dsp_stdperiph_lib_um.chm

flyleaf91的专栏 2万+

STM32F429内部FLASH读写程序

基于STM32F429内部FLASH的读写,操作,有KEIL和IAR工程,直接可用

STM32F429内部FLASH读写保护测试程序

基于STM32F429的内部Flash读写保护,主要功能为写保护的设置与解除,读保护的设置与解除.注意千万不要设为二级读保护,否则将变为废片!

第25章 串行FLASH文件系统FatFs—零死角玩转STM32-F429系列

第25章     串行FLASH文件系统FatFs 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege     本章参考资料:《00index_e.html》,这是FatFs官方的编译好的HTML文档,里面有FatFs所有函数的介绍和函数的应用示例,学习FatFs看这个

flyleaf91的专栏 1万+

别再只调API了!深度拆解Dify文件上传的两种传参方式(sys.file vs. inputs.upload_file)

本文深度解析Dify平台文件上传的两种传参方式——sys.file与inputs.upload_file,详细对比其设计原理、技术实现及适用场景。通过代码示例和性能分析,帮助开发者在本地大模型应用中做出精准技术选型,提升文件处理效率与系统稳定性。

weixin_29063979的博客 268

设置FLASH的读写保护及解除—零死角玩转STM32-F429系列

第51章 设置FLASH的读写保护及解除 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料:《STM32F4xx 中文参考手册》、《STM32F4xx规格书》、库说明文档《stm32f4xx_dsp_stdperiph_lib_um.c...

oshan2012的博客 4188

美团外卖mtgsig 1.2逆向工程:从补环境到签名生成全解析

本文深入解析了美团外卖mtgsig 1.2签名算法的逆向工程全流程。从环境检测原理与补环境实战入手,详细拆解了签名JSON中每个关键字段的生成逻辑,并重点剖析了核心签名a2的生成步骤,涉及设备指纹、MD5、AES加密等关键技术。最后提供了构建签名生成器的实践思路与常见坑点,为理解复杂风控体系提供了完整的技术视角。

weixin_29324401的博客 1757

GPU基准测试全攻略:从驱动验证到深度学习环境部署

GPU基准测试是评估显卡性能与稳定性的核心方法,其原理在于通过标准化负载程序,对GPU的计算单元、显存和散热系统进行压力测试。这项技术的价值在于,它能科学量化硬件性能,确保软件环境配置正确,为深度学习、科学计算等重负载任务提供稳定基础。在实际应用场景中,无论是新卡验机、排查深度学习环境问题(如“warning: no usable gpu found”报错),还是验证“PyTorch GPU版本安装”后的实际算力,系统的基准测试都是不可或缺的工程实践环节。通过结合通用计算测试与深度学习框架验证,用户可以提前

weixin_30080745的博客 343

第4章 初识STM32—零死角玩转STM32-F429系列

第4章     初识STM32 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege   本章参考资料:1、《STM8和STM32产品选型手册》2、SetupSTM32CubeMX-4.11.0.exe 4.1 什么是STM32 STM32,从字面上来理解,ST是意法半导

flyleaf91的专栏 1万+

Colab虚拟环境配置全攻略:从安装到实战应用

本文详细介绍了在Google Colab中配置Python虚拟环境的完整流程与实战技巧。通过安装Anaconda、创建独立环境、解决常见错误及结合深度学习项目应用,帮助用户有效管理依赖、避免冲突,并利用环境持久化策略提升在Colab中的开发效率与项目可复现性。

weixin_29204121的博客 341

第5章 什么是寄存器—零死角玩转STM32-F429系列

第5章     什么是寄存器 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege   本章参考资料:《STM32F4xx 中文参考手册》、《STM32F429xx数据手册》、 学习本章时,配合《STM32F4xx 中文参考手册》"存储器和总线架构"、"嵌入式FLASH接口

flyleaf91的专栏 2889

同济大学824复试全攻略:从笔试到面试的实战解析

本文详细解析同济大学824复试全流程,包括专业课笔试、外语听力与口语、专业外语笔试和专业综合面试的备考策略。重点分享数字信号处理、计算机网络等专业课的高效学习方法,以及专业外语速成和口语面试技巧,助力考生全面提升复试表现。

qq_27496129的博客 1055

NoC架构深度解析:从拓扑结构到流量控制的全面指南

本文深入解析了片上网络(NoC)架构,从总线架构的瓶颈出发,阐述了NoC作为芯片内部“交通网”的核心优势。文章详细探讨了主流拓扑结构(如Mesh、Torus、Ring)的选型权衡,并系统介绍了数据包格式、路由算法、路由器流水线、流量控制机制以及网络接口(NI)设计,为理解现代多核SoC的通信基石提供了全面指南。

gg901的博客 1042

第3章 如何用DAP仿真器下载程序—零死角玩转STM32-F429系列

第3章     如何用DAP仿真器下载程序 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege   3.1 仿真器简介 本书配套的仿真器为Fire-Debugger,遵循ARM公司的CMSIS-DAP标准,支持所有基于Cortex内核的单片机,常见的M3、M4和M7都可以

flyleaf91的专栏 1万+

STM32测测速---编码电机读取速度的计算

速度计算:轮胎每转一圈的脉冲数取决于编码器的分辨率,可由下面公式进行计算:PPR是电机的线数最后可以计算轮胎每转一圈的脉冲数为:轮胎每转一圈的脉冲数 = 9.6 × 11 × 4 = 422.4最后的速度公式简化为:速度 = (0.01 秒内产生的脉冲数 ∗ 0.055 ∗ π )/(0.01 ∗422.4​。

weixin_69414084的博客 3812

第49章 在SRAM中调试代码—零死角玩转STM32-F429系列

第49章 在SRAM中调试代码 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料:《STM32F4xx 中文参考手册》、《STM32F4xx规格书》、《Cortex-M3权威指南》、《Cortex-M4 Techni...

aicai1841的博客 672

Google Earth Engine(GEE)——MOD09A1/MOD11A2/MOD13A1不同的QA波段去云进行分析

​​​​​​​大多数光学卫星图像产品都带有一个或多个 QA 波段,允许用户评估每个像素的质量并提取满足其要求的像素。QA 波段最常见的应用是提取有关多云像素的信息并对其进行屏蔽。但是 QA 波段包含大量其他信息,可以帮助您从分析中删除低质量数据。通常,QA 频带中包含的信息存储为按位标志。在这篇文章中,我将介绍与位运算相关的基本概念,以及如何使用位掩码提取和屏蔽特定的质量指标。

此星光明博客 4576

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

上一篇: 第49章 在SRAM中调试代码—零死角玩转STM32-F429系列
下一篇: 第51章 设置FLASH的读写保护及解除—零死角玩转STM32-F429系列
0木木0
博客等级 码龄17年 458粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值