<学习记录>存储中的大小端模式以及STM32的小端模式储存

本文介绍了存储模式中的大小端概念,小端模式是指较高有效字节存储在低地址,而STM32采用的就是小端模式。大端模式则是相反。大端模式在判断符号位时更直观,小端模式则在数据转换时更便捷。此外,文章还探讨了为何会出现大小端模式,指出常见的X86架构为小端,而某些ARM处理器可以切换字节序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(转)https://blog.youkuaiyun.com/ERTFYANG/article/details/81122993
存储模式:
小端:较高的有效字节存储在较高的存储器地址,较低的有效字节存储在较低的存储器地址。
大端:较高的有效字节存储在较低的存储器地址,较低的有效字节存储在较高的存储器地址。

 

STM32 属于小端模式,简单地说:比如:temp=0X12345678;假设temp的地址为:0X4000 0000
那么,在内存里面,其存储就变成了:
|       地址                           |    HEX                       |
|0X4000 0000                    |    78 56 34 12           |

更为简单一点:
低地址---------->高地址【大端模式】:

数据高位------->数据低位
0X12|0X34|0X56|0X78|


低地址---------->高地址【小端模式】:

数据低位-------->数据高位
0X78|0X56|0X34|0X12|

大端与小端的优势
二者无所谓优势,无所谓劣势,各自优势便是对方劣势
大端模式:符号位的判定固定为第一个字节,容易判断正负。
小端模式:强制转换数据不需要调整字节内容,1、2、4字节的存储方式一样

数组在大端小端情况下的存储:
以unsigned int value =

<think>我们正在使用STM32F407ZGT6芯片,需要利用其内部FLASH存储器来存储数据。根据引用[1]和[2],我们知道FLASH存储器在嵌入式系统中常用于存储固件程序、操作系统、应用程序等,并且可以电擦写多次。在STM32中,内部FLASH通常被用来存储程序代码,但也可以用来存储用户数据,不过需要注意避免与程序存储区域冲突。 步骤: 1. 确定要使用的FLASH扇区:STM32F407ZGT6的FLASH分为多个扇区,每个扇区大小不同。我们需要选择一个未用于程序存储的扇区来存储数据。通常,程序代码从扇区0开始存放,我们可以选择最后一个扇区(例如扇区11)来存储用户数据,以避免冲突。 2. 解锁FLASH:在对FLASH进行写操作之前,需要解锁FLASH控制寄存器。 3. 擦除扇区:FLASH在写入之前必须先擦除(擦除后所有位为1,写入时只能将1改为0,不能将0改为1)。擦除操作是按扇区进行的。 4. 写入数据:将数据按16位半字或32位字写入(具体根据数据对齐要求)。注意,每次写入的数据大小必须是16位(半字)或32位(字)。 5. 锁定FLASH:完成写操作后,重新锁定FLASH以防止意外写操作。 注意: - 在擦除和写入期间,不能执行FLASH中的代码(包括中断服务程序),因此这些操作通常需要在RAM中运行(但HAL库已经处理了这个问题,只要确保中断不会在擦写过程中访问FLASH即可)。 - 擦除扇区会清除整个扇区,所以如果需要保存多个数据,应该先缓存要保存的数据,然后一次性擦除并写入。 下面我们使用HAL库函数来实现: 1. 首先,包含必要的头文件: ```c #include "stm32f4xx_hal.h" ``` 2. 定义要使用的FLASH扇区。根据STM32F407ZGT6的FLASH结构,扇区11的起始地址为0x080E0000,大小为128KB(注意:不同扇区大小不同,请参考芯片手册)。 3. 编写FLASH写入函数: ```c // 定义FLASH扇区11的起始地址和大小 #define FLASH_USER_START_ADDR ((uint32_t)0x080E0000) // 扇区11起始地址 #define FLASH_USER_END_ADDR ((uint32_t)0x080FFFFF) // 扇区11结束地址 // 写入数据到FLASH HAL_StatusTypeDef FLASH_WriteData(uint32_t address, uint64_t *data, uint32_t dataLength) { HAL_StatusTypeDef status = HAL_OK; // 解锁FLASH HAL_FLASH_Unlock(); // 擦除扇区(先检查是否需要擦除,但通常我们直接擦除整个扇区) FLASH_EraseInitTypeDef eraseInit; uint32_t sectorError; eraseInit.TypeErase = FLASH_TYPEERASE_SECTORS; eraseInit.Sector = FLASH_SECTOR_11; // 选择扇区11 eraseInit.NbSectors = 1; // 擦除1个扇区 eraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3; // 电压范围,对于3.3V系统 // 执行擦除 status = HAL_FLASHEx_Erase(&eraseInit, &sectorError); if (status != HAL_OK) { HAL_FLASH_Lock(); return status; } // 写入数据(按64位写入,因为STM32F4的FLASH写入最小为32位,但推荐64位写入以提高效率) for (uint32_t i = 0; i < dataLength; i++) { status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address + i*8, data[i]); if (status != HAL_OK) { break; } } // 锁定FLASH HAL_FLASH_Lock(); return status; } ``` 4. 读取数据:直接从指定地址读取即可。 ```c void FLASH_ReadData(uint32_t address, uint64_t *data, uint32_t dataLength) { for (uint32_t i = 0; i < dataLength; i++) { data[i] = *(__IO uint64_t*)(address + i*8); } } ``` 5. 使用示例: ```c // 要写入的数据 uint64_t dataToWrite[] = {0x0123456789ABCDEF, 0x1122334455667788}; uint32_t dataLength = sizeof(dataToWrite)/sizeof(dataToWrite[0]); // 写入 if (FLASH_WriteData(FLASH_USER_START_ADDR, dataToWrite, dataLength) == HAL_OK) { // 写入成功 } // 读取 uint64_t readData[2]; FLASH_ReadData(FLASH_USER_START_ADDR, readData, dataLength); ``` 注意事项: - 擦除操作会清除整个扇区,所以如果需要保存多个独立的数据,需要自己管理扇区内的空间(例如,使用类似文件系统的结构或记录每个数据的起始地址)。 - 写入数据时,确保地址对齐(64位对齐可以提高效率,但32位对齐也可以)。 - 注意FLASH的寿命(通常为1万到10万次擦写),所以不要频繁擦写同一区域。 另外,根据引用[3],RTC在断电后需要电池维持,而FLASH是非易失性的,断电后数据不会丢失,所以不需要电池。 以上代码基于HAL库,请确保在工程中正确配置了FLASH模块(通常不需要在CubeMX中额外配置,但需要包含HAL_FLASH_MODULE_ENABLED)。 如果存储的数据量不大,也可以考虑使用EEPROM(但STM32F407内部没有EEPROM,需要外接,如AT24C02),或者使用备份寄存器(BKP,但容量很小)[^2]。 [^1]: 存储器——基于STM32F407ZGT6 [^2]: eeprom使用 cubemx STM32F407ZGT6【IIC驱动AT24C02】 [^3]: STM32F407ZGT6-RTC §§相关问题§§ 1. STM32F407ZGT6的FLASH扇区划分是怎样的? 2. 如何避免在FLASH存储数据时与程序代码冲突? 3. FLASH存储的寿命是多少?如何延长其使用寿命? 4. 除了内部FLASH,还有哪些方法可以在STM32F407上实现非易失性数据存储?</think>### STM32F407ZGT6 FLASH数据存储实现指南 #### 一、核心步骤 1. **确定存储区域** STM32F407ZGT6的FLASH容量为1MB,分为12个扇区(Sector 0-11)[^1]: ```c /* 扇区分布(参考芯片手册) */ #define FLASH_USER_START_ADDR 0x080E0000 // 扇区11起始地址(128KB) #define FLASH_USER_END_ADDR 0x080FFFFF // 扇区11结束地址 ``` 2. **FLASH擦除**(必须先擦除后写入) ```c #include "stm32f4xx_hal.h" HAL_StatusTypeDef Flash_EraseSector(uint32_t sector) { HAL_FLASH_Unlock(); // 解锁FLASH控制寄存器 FLASH_EraseInitTypeDef eraseConfig = { .TypeErase = FLASH_TYPEERASE_SECTORS, .Sector = sector, .NbSectors = 1, // 擦除1个扇区 .VoltageRange = FLASH_VOLTAGE_RANGE_3 // 3.3V系统 }; uint32_t sectorError; HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&eraseConfig, &sectorError); HAL_FLASH_Lock(); // 重新上锁 return status; } ``` 3. **数据写入**(以64位为单位) ```c HAL_StatusTypeDef Flash_WriteData(uint32_t address, uint64_t *data, uint32_t len) { HAL_FLASH_Unlock(); HAL_StatusTypeDef status = HAL_OK; for(uint32_t i=0; i<len; i++) { status = HAL_FLASH_Program( FLASH_TYPEPROGRAM_DOUBLEWORD, // 64位写入模式 address + (i*8), // 地址偏移 data[i] // 数据值 ); if(status != HAL_OK) break; } HAL_FLASH_Lock(); return status; } ``` 4. **数据读取** ```c void Flash_ReadData(uint32_t address, uint64_t *buffer, uint32_t len) { for(uint32_t i=0; i<len; i++) { buffer[i] = *(__IO uint64_t*)(address + (i*8)); } } ``` #### 二、关键注意事项 1. **地址对齐要求** - 写入地址必须64位对齐(地址 % 8 == 0) - 擦除最小单位是扇区(扇区11为128KB) 2. **中断处理** 在擦除/写入期间需禁用中断: ```c __disable_irq(); // 开始操作前禁用 // ... FLASH操作 ... __enable_irq(); // 操作完成后启用 ``` 3. **寿命管理** FLASH擦写次数约1万次[^1]: - 使用磨损均衡算法(如循环缓冲区) - 避免频繁写入(建议每次写入≥512字节) 4. **数据校验** 建议添加CRC校验: ```c #include "crc.h" uint32_t crc = HAL_CRC_Calculate(&hcrc, (uint32_t*)data, len); ``` #### 三、完整使用示例 ```c // 存储数据结构体 typedef struct { uint32_t timestamp; float sensor_data[4]; uint32_t crc_check; } LogEntry; void Save_Data(void) { LogEntry newEntry = {...}; // 填充数据 // 1. 计算CRC newEntry.crc_check = HAL_CRC_Calculate(&hcrc, (uint32_t*)&newEntry, sizeof(LogEntry)-4); // 2. 擦除扇区(首次使用或空间不足时) if(need_erase) Flash_EraseSector(FLASH_SECTOR_11); // 3. 写入数据 Flash_WriteData(current_addr, (uint64_t*)&newEntry, sizeof(LogEntry)/8); } void Load_Data(void) { LogEntry savedEntry; Flash_ReadData(SAVE_ADDRESS, (uint64_t*)&savedEntry, sizeof(LogEntry)/8); // 验证CRC uint32_t crc_calc = HAL_CRC_Calculate(&hcrc, (uint32_t*)&savedEntry, sizeof(LogEntry)-4); if(crc_calc == savedEntry.crc_check) { // 数据有效 } } ``` #### 四、替代方案对比 | 存储类型 | 容量 | 擦写次数 | 优点 | 缺点 | |---------|------|---------|------|------| | **内部FLASH** | 1MB | ~10K | 无需外设,零成本 | 需擦除整个扇区 | | **外接EEPROM** | KB级 | 100万+ | 字节级擦写 | 需I²C接口,速度慢[^2] | | **外部FLASH** | MB级 | 10万+ | 大容量存储 | 需SPI接口,增加BOM成本 | > **提示**:对于频繁修改的小数据(如系统配置),建议使用备份寄存器(BKP)或外接EEPROM[^2][^3]。 [^1]: 存储器——基于STM32F407ZGT6 [^2]: eeprom使用 cubemx STM32F407ZGT6【IIC驱动AT24C02】 [^3]: STM32F407ZGT6-RTC
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值