STM32的FLASH结构
-
主存储器(Main Memory):这是STM32中最大的存储区域,用于存储用户的程序代码、常量数据以及程序运行时不变的数据。STM32的主存储器通常被组织为多个扇区(sector),每个扇区的大小不同,可以是几个KB到几十KB不等。这些扇区可以被独立擦除,但不能独立编程。
-
信息块(Information Block):这个区域通常包含了STM32微控制器的唯一标识码(UID)、产品型号、生产批次等信息。这些信息对于产品的追踪和生产管理是非常有用的,但是在正常编程时不会修改这部分数据。
-
系统存储器(System Memory):这部分存储区域通常用于存储ST公司提供的bootloader。系统存储器的内容是在生产时被固化在芯片中的,用户一般不能修改。
-
选项字节(Option Bytes):这些字节用于配置微控制器的某些选项,例如读保护、写保护、BOR级别(Brown-out Reset Level)、软件或硬件看门狗等。选项字节的内容可以通过软件编程来修改,但需要遵循特定的编程流程。
-
-
OTP区域(One-Time Programmable Area):这是一些只能编程一次的区域,用于存储一些关键的安全信息或者配置参数。一旦编程之后,这些信息就无法更改。
型号范例 | STM32 F 103 Z E T 6 |
家族 | “STM32 “表示32bit的MCU |
产品类型 | “F”表示基础型 |
具体特性 | “103”基础型 |
引脚数目 | “Z”表示144个引脚, 其他常用的为: C表示48引脚, R表示64引脚, V表示100引脚, “Z”表示144个引脚, B表示208引脚, N表示216引脚 |
FLASH大小 | E表示512KB, 其他常用的为: 4表示16KB(小容量ld), 6表示32KB(小容量ld), 8表示64KB(中容量md), B表示128KB(中容量md), C表示256 KB(大容量hd), E表示512 KB(大容量hd), F表示768KB(超大容量xl), G表示1024KB(超大容量xl), |
封装 | “T”表示QFP封装,这个是最常用的封装 |
温度 | “6”表示温度等级为A :-40~85° |
STM32 Flash编程的注意事项:
-
扇区和页的结构:STM32的Flash扇区和页的大小根据不同的型号会有所不同。例如,一些STM32型号的页大小可能是128字节或256字节,而扇区大小可能是2KB、4KB、8KB等。
-
编程(写)操作:编程操作只能在已经擦除的页上进行,且一次只能编程一页。你不能在未擦除的页上直接编程,因为这可能会导致数据损坏。
-
擦除操作:在编程之前,目标页所在的扇区必须先被擦除。擦除操作会清除扇区内的所有数据,将所有位设置为1。
-
写保护:STM32的Flash通常具有写保护功能,以防止意外写入或擦除。在编程之前,可能需要确保相应的写保护功能被禁用。
-
地址对齐:在编程时,写入的数据必须与页的起始地址对齐。如果写入的数据跨越了页的边界,就需要分成多次编程操作。
-
固件升级:在进行固件升级时,通常会选择一个特定的扇区来存储新的固件映像,这个扇区通常被称为“引导扇区”或“更新扇区”。
-
特殊扇区:有些扇区可能被保留用于存储Bootloader或其他重要的系统代码,因此在编程时应该避免对这些扇区进行操作。
stm32的FPEC与FSMC的区别
在STM32中,FPEC和FSMC是两个不同的功能模块,它们服务于不同的目的。
-
FPEC (Flash Program and Erase Controller):
- FPEC是STM32的一个模块,负责控制内置的Flash存储器的编程(烧写)和擦除操作。
- 它允许微控制器在运行时重新编程自己的Flash,这意味着可以不通过外部编程器而直接在微控制器上更新固件。
- FPEC还支持扇区擦除和整个Flash的擦除操作,确保了固件更新的灵活性。
-
FSMC (Flexible Static Memory Controller):
- FSMC是STM32的一个接口,它允许微控制器与外部静态存储器(如SRAM、ROM、NOR闪存和NAND闪存)进行通信。
- FSMC支持多种访问模式,包括8位、16位和32位,以及不同的时序控制,以适应各种外部存储设备的操作需求。
- FSMC的灵活性使其可以用于多种应用,如扩展微控制器的存储空间,或者连接图形液晶显示器(LCD)等设备。
在使用STM32进行嵌入式系统设计时,开发者可以根据需要选择是否使用FPEC和FSMC。例如,当需要频繁更新固件或者使用外部存储器扩展存储空间时,这两个模块就特别有用。
FPEC
STM32微控制器中的FPEC(Flash Program and Erase Controller)是负责管理闪存编程和擦除的硬件模块。FPEC允许微控制器对自己的内置闪存进行读写操作,这是固件更新、数据存储和其他类似应用的基础。
FPEC的主要功能包括:
-
编程(Programming):将数据写入闪存。STM32的闪存通常以半字(16位)或字(32位)为单位进行编程。
-
擦除(Erasing):清除闪存中的数据,通常是以扇区(sector)为单位的。STM32的闪存扇区大小不一,从几个KB到几十KB不等。
-
读取(Reading):从闪存中读取数据,这是最简单的操作,不需要特殊的硬件支持。
-
选项字节编程(Option Byte Programming):配置微控制器的某些选项,如读保护、写保护、BOR级别等。
步骤
用STM32的标准库或HAL库:
-
配置闪存接口:
- 确保时钟已经使能到闪存接口。
- 如果需要,配置闪存访问时间(LATENCY),这通常在高速时钟下是必需的。
-
解锁闪存编程控制寄存器:
- 使用
HAL_FLASH_Unlock()
或FLASH_Unlock()
函数来解锁闪存编程和擦除控制。
- 使用
-
擦除扇区:
- 使用
HAL_FLASHEx_Erase()
或FLASH_ErasePage()
函数来选择并擦除要写入的扇区。STM32的闪存被分为多个扇区,每个扇区可以独立擦除。 - 等待擦除完成,可以通过检查闪存状态寄存器来判断。
- 使用
-
编程闪存:
- 使用
HAL_FLASH_Program()
或FLASH_ProgramWord()
函数将数据编程到闪存中。这个操作通常是以半字或字为单位的。 - 确保在编程之前扇区已经被擦除。
- 等待编程完成,并检查闪存状态寄存器是否有错误发生。
- 使用
-
读取闪存:
- 直接通过指针访问闪存地址来读取数据。闪存的读取操作是最简单的,不需要特殊的硬件支持。
-
配置选项字节(如果需要):
- 使用
HAL_FLASH_OB_Program()
或FLASH_OB_Program()
函数来配置选项字节。 - 选项字节的编程可能需要特定的序列和条件,比如在执行选项字节编程之前可能需要禁用全局中断。
- 使用
-
锁定闪存编程控制寄存器:
- 使用
HAL_FLASH_Lock()
或FLASH_Lock()
函数来锁定闪存编程和擦存控制,防止意外的写入。
- 使用
-
错误处理:
- 在编程和擦除过程中,如果发生错误,通常需要通过读取闪存状态寄存器来确定错误类型,并采取相应的措施。
框图
HAL配置
STM32CubeMX可以帮助用户初始化FPEC(Flash Programming and Erase Controller),但这是在生成的代码中自动完成的,而不是通过STM32CubeMX的用户界面直接配置
1打开GPIO中断
2配置时钟
3打开UART以显示数据
即可生成代码
代码编写
由于内部FLASH本身存储有程序数据,若不是有意删除某段程序代码,一般不应修改程序空间的内容,所以在使用 内部FLASH存储其它数据前需要了解哪一些空间已经写入了程序代码,存储了程序代码的扇区都不应作任何修改。 通过查询应用程序编译时产生的“*.map”后缀文件,可以了解程序存储到了哪些区域,
找.map文件(可以确定变量的地址)
步骤操作:
-
编译项目:首先确保你已经编译了你的STM32项目。在Keil 5中,这通常是通过点击“Build”菜单下的“Rebuild all”来完成的。
-
查看项目设置:在Keil 5中,默认情况下
.map
文件是在编译过程中自动生成的。你可以在项目的选项设置中确认这一点。点击“Project”菜单下的“Options for Target”打开目标选项窗口。 -
输出目录:在目标选项窗口中,选择“Output”标签页。在这里,你可以看到“Select folder for object files”和“Select folder for listing files”。这些选项指定了对象文件和列表文件(包括
.map
文件)的输出目录。 -
查找.map文件:在指定的输出目录中,查找与你项目同名的
.map
文件。例如,如果你的项目名为MyProject.uvprojx
,那么.map
文件可能被命名为MyProject.map
。 -
打开.map文件:找到
.map
文件后,你可以使用文本编辑器(如Notepad++或Visual Studio Code)打开它,以便查看和解析其中的内容。
截取重要的一段解释
Execution Region ER_IROM1
确实指的是用户编写程序所要的空间大小,即用户编写的程序代码在Flash存储器中实际占用的空间。这个区域的大小取决于用户编写的代码、链接器分配的其他段(如数据段、未初始化数据段等)的大小,以及链接器在编译和链接过程中所做的优化和调整。
==============================================================================
Memory Map of the image
/*程序的入口点,即程序开始执行的地址。在这个特定的例子中,Image Entry point的值为0x08000131。
这意味着在STM32微控制器启动时,它将从这个地址开始执行程序。这个地址是在Flash存储器中的,对于STM32系列微控制器,通常起始地址是0x08000000,但由于Flash存储器是分段的,不同的段可能会有不同的起始地址。*/
Image Entry point : 0x08000131
/*程序ROM加载空间*/
Load Region LR_IROM1 (Base: 0x08000000, Size: 0x0000137c, Max: 0x00080000, ABSOLUTE)
//Load Region LR_IROM1:
Base: 基地址,即Flash存储器中这个区域的起始地址,这里是0x08000000。
Size: 区域的大小,这里是0x0000137c字节。
Max: 区域的最大值,即Flash存储器中这个区域可以使用的最大地址,这里是0x00080000。
ABSOLUTE: 表示这个区域的地址是绝对的,即在整个Flash存储器中是固定不变的。
/*程序ROM执行空间*/
Execution Region ER_IROM1 (Exec base: 0x08000000, Load base: 0x08000000, Size: 0x0000136c, Max: 0x00080000, ABSOLUTE)
//Execution Region ER_IROM1:
Exec base: 执行基地址,与加载基地址相同,这里是0x08000000。
Load base: 加载基地址,与加载基地址相同,这里是0x08000000。
Size: 执行区域的大小,这里是0x0000136c字节。
Max: 执行区域的最大值,与加载区域的最大值相同,这里是0x00080000。
ABSOLUTE: 表示这个区域的地址是绝对的。
//总结来说,Load Region LR_IROM1和Execution Region ER_IROM1描述的是同一个Flash区域,这个区域用于存储程序代码。Load Region是指程序被加载到Flash中的地址,而Execution Region是指程序实际执行时的地址。由于STM32微控制器在启动时会从Flash中加载程序代码并执行,所以这两个地址是相同的。
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x08000000 0x08000000 0x00000130 Data RO 3 RESET startup_stm32f103xe.o
0x08000130 0x08000130 0x00000008 Code RO 2341 * !!!main c_w.l(__main.o)
0x08000138 0x08000138 0x00000034 Code RO 2500 !!!scatter c_w.l(__scatter.o)
0x0800016c 0x0800016c 0x0000001a Code RO 2502 !!handler_copy c_w.l(__scatter_copy.o)
0x08000186 0x08000186 0x00000002 PAD
0x08000188 0x08000188 0x0000001c Code RO 2504 !!handler_zi c_w.l(__scatter_zi.o)
0x080001a4 0x080001a4 0x00000002 Code RO 2368 .ARM.Collect$$libinit$$00000000 c_w.l(libinit.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2375 .ARM.Collect$$libinit$$00000002 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2377 .ARM.Collect$$libinit$$00000004 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2380 .ARM.Collect$$libinit$$0000000A c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2382 .ARM.Collect$$libinit$$0000000C c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2384 .ARM.Collect$$libinit$$0000000E c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2387 .ARM.Collect$$libinit$$00000011 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2389 .ARM.Collect$$libinit$$00000013 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2391 .ARM.Collect$$libinit$$00000015 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2393 .ARM.Collect$$libinit$$00000017 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2395 .ARM.Collect$$libinit$$00000019 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2397 .ARM.Collect$$libinit$$0000001B c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2399 .ARM.Collect$$libinit$$0000001D c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2401 .ARM.Collect$$libinit$$0000001F c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2403 .ARM.Collect$$libinit$$00000021 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2405 .ARM.Collect$$libinit$$00000023 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2407 .ARM.Collect$$libinit$$00000025 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2411 .ARM.Collect$$libinit$$0000002C c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2413 .ARM.Collect$$libinit$$0000002E c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2415 .ARM.Collect$$libinit$$00000030 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000000 Code RO 2417 .ARM.Collect$$libinit$$00000032 c_w.l(libinit2.o)
0x080001a6 0x080001a6 0x00000002 Code RO 2418 .ARM.Collect$$libinit$$00000033 c_w.l(libinit2.o)
0x080001a8 0x080001a8 0x00000002 Code RO 2438 .ARM.Collect$$libshutdown$$00000000 c_w.l(libshutdown.o)
0x080001aa 0x080001aa 0x00000000 Code RO 2451 .ARM.Collect$$libshutdown$$00000002 c_w.l(libshutdown2.o)
0x080001aa 0x080001aa 0x00000000 Code RO 2453 .ARM.Collect$$libshutdown$$00000004 c_w.l(libshutdown2.o)
0x080001aa 0x080001aa 0x00000000 Code RO 2455 .ARM.Collect$$libshutdown$$00000006 c_w.l(libshutdown2.o)
0x080001aa 0x080001aa 0x00000000 Code RO 2458 .ARM.Collect$$libshutdown$$00000009 c_w.l(libshutdown2.o)
0x080001aa 0x080001aa 0x00000000 Code RO 2461 .ARM.Collect$$libshutdown$$0000000C c_w.l(libshutdown2.o)
0x080001aa 0x080001aa 0x00000000 Code RO 2463 .ARM.Collect$$libshutdown$$0000000E c_w.l(libshutdown2.o)
0x080001aa 0x080001aa 0x00000000 Code RO 2466 .ARM.Collect$$libshutdown$$00000011 c_w.l(libshutdown2.o)
0x080001aa 0x080001aa 0x00000002 Code RO 2467 .ARM.Collect$$libshutdown$$00000012 c_w.l(libshutdown2.o)
0x080001ac 0x080001ac 0x00000000 Code RO 2343 .ARM.Collect$$rtentry$$00000000 c_w.l(__rtentry.o)
0x080001ac 0x080001ac 0x00000000 Code RO 2345 .ARM.Collect$$rtentry$$00000002 c_w.l(__rtentry2.o)
0x080001ac 0x080001ac 0x00000006 Code RO 2357 .ARM.Collect$$rtentry$$00000004 c_w.l(__rtentry4.o)
0x080001b2 0x080001b2 0x00000000 Code RO 2347 .ARM.Collect$$rtentry$$00000009 c_w.l(__rtentry2.o)
0x080001b2 0x080001b2 0x00000004 Code RO 2348 .ARM.Collect$$rtentry$$0000000A c_w.l(__rtentry2.o)
0x080001b6 0x080001b6 0x00000000 Code RO 2350 .ARM.Collect$$rtentry$$0000000C c_w.l(__rtentry2.o)
0x080001b6 0x080001b6 0x00000008 Code RO 2351 .ARM.Collect$$rtentry$$0000000D c_w.l(__rtentry2.o)
0x080001be 0x080001be 0x00000002 Code RO 2372 .ARM.Collect$$rtexit$$00000000 c_w.l(rtexit.o)
0x080001c0 0x080001c0 0x00000000 Code RO 2420 .ARM.Collect$$rtexit$$00000002 c_w.l(rtexit2.o)
0x080001c0 0x080001c0 0x00000004 Code RO 2421 .ARM.Collect$$rtexit$$00000003 c_w.l(rtexit2.o)
0x080001c4 0x080001c4 0x00000006 Code RO 2422 .ARM.Collect$$rtexit$$00000004 c_w.l(rtexit2.o)
0x080001ca 0x080001ca 0x00000002 PAD
0x080001cc 0x080001cc 0x00000040 Code RO 4 .text startup_stm32f103xe.o
0x0800020c 0x0800020c 0x0000004e Code RO 2337 .text c_w.l(rt_memclr_w.o)
0x0800025a 0x0800025a 0x00000006 Code RO 2339 .text c_w.l(heapauxi.o)
0x08000260 0x08000260 0x0000004a Code RO 2359 .text c_w.l(sys_stackheap_outer.o)
0x080002aa 0x080002aa 0x00000012 Code RO 2361 .text c_w.l(exit.o)
0x080002bc 0x080002bc 0x00000008 Code RO 2369 .text c_w.l(libspace.o)
0x080002c4 0x080002c4 0x0000000c Code RO 2430 .text c_w.l(sys_exit.o)
0x080002d0 0x080002d0 0x00000002 Code RO 2441 .text c_w.l(use_no_semi.o)
0x080002d2 0x080002d2 0x00000000 Code RO 2443 .text c_w.l(indicate_semi.o)
0x080002d2 0x080002d2 0x00000002 Code RO 213 i.BusFault_Handler stm32f1xx_it.o
0x080002d4 0x080002d4 0x00000002 Code RO 214 i.DebugMon_Handler stm32f1xx_it.o
0x080002d6 0x080002d6 0x00000004 Code RO 13 i.Error_Handler main.o
0x080002da 0x080002da 0x00000002 PAD
0x080002dc 0x080002dc 0x00000024 Code RO 1328 i.HAL_Delay stm32f1xx_hal.o
0x08000300 0x08000300 0x000001f8 Code RO 1638 i.HAL_GPIO_Init stm32f1xx_hal_gpio.o
0x080004f8 0x080004f8 0x0000000c Code RO 1332 i.HAL_GetTick stm32f1xx_hal.o
0x08000504 0x08000504 0x00000010 Code RO 1338 i.HAL_IncTick stm32f1xx_hal.o
0x08000514 0x08000514 0x00000024 Code RO 1339 i.HAL_Init stm32f1xx_hal.o
0x08000538 0x08000538 0x00000040 Code RO 1340 i.HAL_InitTick stm32f1xx_hal.o
0x08000578 0x08000578 0x0000003c Code RO 288 i.HAL_MspInit stm32f1xx_hal_msp.o
0x080005b4 0x080005b4 0x00000040 Code RO 1804 i.HAL_NVIC_SetPriority stm32f1xx_hal_cortex.o
0x080005f4 0x080005f4 0x00000024 Code RO 1805 i.HAL_NVIC_SetPriorityGrouping stm32f1xx_hal_cortex.o
0x08000618 0x08000618 0x0000012c Code RO 1496 i.HAL_RCC_ClockConfig stm32f1xx_hal_rcc.o
0x08000744 0x08000744 0x0000004c Code RO 1505 i.HAL_RCC_GetSysClockFreq stm32f1xx_hal_rcc.o
0x08000790 0x08000790 0x00000320 Code RO 1508 i.HAL_RCC_OscConfig stm32f1xx_hal_rcc.o
0x08000ab0 0x08000ab0 0x00000028 Code RO 1809 i.HAL_SYSTICK_Config stm32f1xx_hal_cortex.o
0x08000ad8 0x08000ad8 0x0000007c Code RO 1073 i.HAL_TIMEx_MasterConfigSynchronization stm32f1xx_hal_tim_ex.o
0x08000b54 0x08000b54 0x0000005a Code RO 350 i.HAL_TIM_Base_Init stm32f1xx_hal_tim.o
0x08000bae 0x08000bae 0x00000002 PAD
0x08000bb0 0x08000bb0 0x00000024 Code RO 166 i.HAL_TIM_Base_MspInit tim.o
0x08000bd4 0x08000bd4 0x000000dc Code RO 359 i.HAL_TIM_ConfigClockSource stm32f1xx_hal_tim.o
0x08000cb0 0x08000cb0 0x00000048 Code RO 167 i.HAL_TIM_MspPostInit tim.o
0x08000cf8 0x08000cf8 0x000000cc Code RO 422 i.HAL_TIM_PWM_ConfigChannel stm32f1xx_hal_tim.o
0x08000dc4 0x08000dc4 0x0000005a Code RO 425 i.HAL_TIM_PWM_Init stm32f1xx_hal_tim.o
0x08000e1e 0x08000e1e 0x00000002 Code RO 427 i.HAL_TIM_PWM_MspInit stm32f1xx_hal_tim.o
0x08000e20 0x08000e20 0x000000b4 Code RO 430 i.HAL_TIM_PWM_Start stm32f1xx_hal_tim.o
0x08000ed4 0x08000ed4 0x00000002 Code RO 215 i.HardFault_Handler stm32f1xx_it.o
0x08000ed6 0x08000ed6 0x00000002 PAD
0x08000ed8 0x08000ed8 0x00000058 Code RO 141 i.MX_GPIO_Init gpio.o
0x08000f30 0x08000f30 0x0000009c Code RO 168 i.MX_TIM2_Init tim.o
0x08000fcc 0x08000fcc 0x00000002 Code RO 216 i.MemManage_Handler stm32f1xx_it.o
0x08000fce 0x08000fce 0x00000002 Code RO 217 i.NMI_Handler stm32f1xx_it.o
0x08000fd0 0x08000fd0 0x00000002 Code RO 218 i.PendSV_Handler stm32f1xx_it.o
0x08000fd2 0x08000fd2 0x00000002 Code RO 219 i.SVC_Handler stm32f1xx_it.o
0x08000fd4 0x08000fd4 0x00000004 Code RO 220 i.SysTick_Handler stm32f1xx_it.o
0x08000fd8 0x08000fd8 0x0000005e Code RO 14 i.SystemClock_Config main.o
0x08001036 0x08001036 0x00000002 Code RO 2299 i.SystemInit system_stm32f1xx.o
0x08001038 0x08001038 0x0000008c Code RO 443 i.TIM_Base_SetConfig stm32f1xx_hal_tim.o
0x080010c4 0x080010c4 0x0000001a Code RO 444 i.TIM_CCxChannelCmd stm32f1xx_hal_tim.o
0x080010de 0x080010de 0x00000014 Code RO 454 i.TIM_ETR_SetConfig stm32f1xx_hal_tim.o
0x080010f2 0x080010f2 0x00000010 Code RO 455 i.TIM_ITRx_SetConfig stm32f1xx_hal_tim.o
0x08001102 0x08001102 0x00000002 PAD
0x08001104 0x08001104 0x00000060 Code RO 456 i.TIM_OC1_SetConfig stm32f1xx_hal_tim.o
0x08001164 0x08001164 0x0000006c Code RO 457 i.TIM_OC2_SetConfig stm32f1xx_hal_tim.o
0x080011d0 0x080011d0 0x00000068 Code RO 458 i.TIM_OC3_SetConfig stm32f1xx_hal_tim.o
0x08001238 0x08001238 0x00000050 Code RO 459 i.TIM_OC4_SetConfig stm32f1xx_hal_tim.o
0x08001288 0x08001288 0x00000022 Code RO 461 i.TIM_TI1_ConfigInputStage stm32f1xx_hal_tim.o
0x080012aa 0x080012aa 0x00000024 Code RO 463 i.TIM_TI2_ConfigInputStage stm32f1xx_hal_tim.o
0x080012ce 0x080012ce 0x00000002 Code RO 221 i.UsageFault_Handler stm32f1xx_it.o
0x080012d0 0x080012d0 0x00000020 Code RO 1811 i.__NVIC_SetPriority stm32f1xx_hal_cortex.o
0x080012f0 0x080012f0 0x00000038 Code RO 15 i.main main.o
0x08001328 0x08001328 0x00000012 Data RO 1509 .constdata stm32f1xx_hal_rcc.o
0x0800133a 0x0800133a 0x00000010 Data RO 2300 .constdata system_stm32f1xx.o
0x0800134a 0x0800134a 0x00000002 PAD
0x0800134c 0x0800134c 0x00000020 Data RO 2498 Region$$Table anon$$obj.o
Execution Region RW_IRAM1 (Exec base: 0x20000000, Load base: 0x0800136c, Size: 0x000006b8, Max: 0x00010000, ABSOLUTE)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x20000000 0x0800136c 0x0000000c Data RW 1346 .data stm32f1xx_hal.o
0x2000000c 0x08001378 0x00000004 Data RW 2302 .data system_stm32f1xx.o
0x20000010 - 0x00000048 Zero RW 169 .bss tim.o
0x20000058 - 0x00000060 Zero RW 2370 .bss c_w.l(libspace.o)
0x200000b8 - 0x00000200 Zero RW 2 HEAP startup_stm32f103xe.o
0x200002b8 - 0x00000400 Zero RW 1 STACK startup_stm32f103xe.o
==============================================================================
有哪些函数
1解锁与上锁
解锁
HAL_StatusTypeDef HAL_FLASH_Unlock(void);
HAL_OK: 操作成功。
HAL_ERROR: 操作失败。
HAL_BUSY: FLASH控制器忙,操作不能执行。
HAL_TIMEOUT: 操作超时。
上锁
HAL_StatusTypeDef HAL_FLASH_Unlock(void);
HAL_OK: 操作成功。
HAL_ERROR: 操作失败。
HAL_BUSY: FLASH控制器忙,操作不能执行。
HAL_TIMEOUT: 操作超时。
2写操作
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);
TypeProgram:指定要执行的编程操作类型。这可以是FLASH_TYPEPROGRAM_WORD(写入一个字),FLASH_TYPEPROGRAM_HALFWORD(写入一个半字),或者FLASH_TYPEPROGRAM_DOUBLEWORD(写入一个双字)。
Address:要写入数据的FLASH地址。
Data:要写入的数据。即使你只写入一个字,这个参数也是一个64位的,因为HAL库支持写入双字操作。
HAL_OK: 操作成功。
HAL_ERROR: 操作失败。
HAL_BUSY: FLASH控制器忙,操作不能执行。
HAL_TIMEOUT: 操作超时。
3闪存擦除函数
HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError);
pEraseInit:指向一个FLASH_EraseInitTypeDef结构的指针,该结构包含了擦除操作的参数,如擦除类型、起始页、页数等。
PageError:指向一个uint32_t变量的指针,该变量用于存储擦除过程中出现错误的页码(如果有的话)。
-
TypeErase
:这个成员定义了擦除操作的类型。它可以是以下值之一,这些值通常在@ref FLASH_TYPE_ERASE
中定义:FLASH_TYPEERASE_MASSERASE
:执行整个Flash存储器的擦除。FLASH_TYPEERASE_PAGEERASE
:仅擦除指定的一个或多个页面。
-
Banks
:这个成员用于在大规模擦除时选择要擦除的银行。STM32微控制器可能有多个Flash存储器银行。这个参数的值通常在@ref FLASH_BANK_ERASE
中定义。对于页面擦除,这个参数不被使用。 -
Page
:这个成员指定了页面擦除操作的起始页面。页面是从0开始编号的,并且这个值必须位于0和MAX_PAGE_NUMBER - 1
之间。MAX_PAGE_NUMBER
是微控制器Flash的最大页面数。对于大规模擦除,这个参数不被使用。 -
NbPages
:这个成员指定了要擦除的页面数量。这个值必须在1和(MAX_PAGE_NUMBER - 起始页的值)
之间。对于大规模擦除,这个参数不被使用。
4获取闪存状态函数
HAL_FLASH_StateTypeDef HAL_FLASH_GetState(void);
这个函数可以用来检查FLASH是否处于忙碌状态
返回值
HAL_FLASH_STATE_BUSY: FLASH忙,一个编程或擦除操作正在进行中。
HAL_FLASH_STATE_ERROR: FLASH发生错误,可能是因为编程或擦除操作失败。
HAL_FLASH_STATE_READY: FLASH准备就绪,没有进行中的操作,并且没有错误。
5等待操作完成函数
HAL_StatusTypeDef HAL_FLASH_WaitForLastOperation(uint32_t Timeout);
当你需要对FLASH进行操作时,通常需要等待上一个操作完成。这可以通过检查FLASH的状态或者使用特定的等待函数来实现
Timeout:等待操作完成的超时时间(以毫秒为单位)。
这个函数返回一个HAL_StatusTypeDef类型的值,用于指示操作是否成功。可能的返回值包括:
HAL_OK: 操作成功完成。
HAL_ERROR: 操作失败。
HAL_TIMEOUT: 等待操作完成时发生超时。
6读取闪存特定地址的函数 //库中没有
main.h源码
使用了UART GPIO
不用初始换写入的闪存 库已经初始化了
思路
uart输出地址内的数据1秒一次
按键点击一下触发中断中断内部是写了,先将地址的值先擦除,在写入数据数据会将原来的值加一
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint32_t address =0x0807F800;//地址data_32的地址(在MOP文件中看一下哪些位置没有写入数据)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
uint32_t data_values=*(uint32_t*)address;//要写入的数据
//解锁 FLASH(闪存)
HAL_FLASH_Unlock();
//擦除页 //必须擦除要写入的页在写入不然数据不能保存或写入失败
FLASH_EraseInitTypeDef eraseInit;
//eraseInit.Banks=FLASH_BANK_1;//只有一块闪存时可以不写 //指的是那一块
eraseInit.NbPages=1;//擦除几页
eraseInit.PageAddress=address;//开始地址
eraseInit.TypeErase=FLASH_TYPEERASE_PAGES;//按页擦除
//变量存放错误 值为0xffffff 为成功
uint32_t pag=0xFFFFFFFF;
//开始擦写
HAL_FLASHEx_Erase(&eraseInit,&pag);
//写入数据
//1代表地址是多大16 32 64 2:地址3:数据
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,address,(data_values+1))==HAL_OK){
uint8_t a[]={"写入成功"};
HAL_UART_Transmit(&huart1,a,8,20);
}else{
uint8_t a[]={"写入失败"};
HAL_UART_Transmit(&huart1,a,8,20);
}
//上锁
HAL_FLASH_Lock();
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
char data_char[19];
//闪存地址的值
long data_=*(uint32_t*)address;
//将值转换为字符
sprintf(data_char,"数据是:%ld",data_);
HAL_UART_Transmit(&huart1,(uint8_t*)data_char,18,20);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
重点总结
在STM32微控制器中,Flash存储器的未编程区域通常会被初始化为0xFF(因为Flash擦除后的状态是所有位都是1)。然而,SRAM(静态随机存取存储器)的未初始化区域则可能包含随机数据,这些数据是上电后或者上次执行程序后遗留下来的。
使用前必须先解锁,之后在上所