flash的读写和擦除

本文介绍了NorFlash与SD卡的读写特性的区别,重点探讨了NorFlash的写入限制及其解决方法,并通过具体实验记录展示了如何正确地在NorFlash上写入U-Boot。

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

这两天在做norflash,sd的读写操作,记录一下。

flash的特性是,写数据只能将1写为0,0不能写为1.擦除数据是将所有数据都写为1.因此如果想在已经数据的flash上写入新的数据,则必须先擦除。sdcard则没有这个特性,就直接可以写入新的数据。
例:
只列出每个文件的前4个字节,file A是flash的原始数据,接着不擦除数据,直接写入file B, result是写完后norflash上的数据,转换成2进制,一看就明白。
file A: 12 00 00 EA
file B: 7F 45 4C 46
result:12 00 00 42

还有一点,在擦除flash的时候,必须是整块的擦除。

昨天写了一边,一些东西没有弄明白,今天终于有了个结果,补上。
主要任务是往norflash的mtd0/mtdblock0写入uboot,但当初不知道为什么,认为就是用/dev/mtd0,
而用/dev/mtdblock0是不对,今天回去过去再分析下,下面是操作过程;
~# cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00060000 00020000 "bootloader"
mtd1: 00020000 00020000 "env"
mtd2: 00020000 00020000 "target_info"
mtd3: 00020000 00020000 "factory_tuning"
mtd4: 00020000 00020000 "versionup"
mtd5: 00400000 00020000 "opening_image"
mtd6: 00600000 00020000 "m3-1"
mtd7: 00200000 00020000 "reserved1"
mtd8: 00060000 00020000 "apli_private"
mtd9: 00020000 00020000 "DSP_param"
mtd10: 00020000 00020000 "E.C_param"
mtd11: 00080000 00020000 "exception_info"
mtd12: 00100000 00020000 "lastmemory"
mtd13: 00080000 00020000 "reserved2"
mtd14: 00400000 00020000 "kernel"

1. cp u-boot.bin /dev/mtd0                                     kernel提示:没有任何提示
2. cp u-boot.bin /dev/mtdblock0                             kernel提示:cp: write error: Operation not permitted
3. flash_erase /dev/mtd0 0 0                                  kernel提示:libmtd: error!: "/dev/mtd0" is not a character device
                                                                                                    flash_erase: error!: mtd_get_dev_info failed
4. flash_erase /dev/mtdblock0 0 0                          kernel提示:  和操作3一样
5. flash_erase /dev/mtd14 0 0                                kernel提示:Erasing 128 Kibyte @ 3e0000 -- 100 % complete
6. flash_erase /dev/mtdblock14 0 0                        kernel提示:和操作3一样

以下两个今天补作了下
7. dd if=u.boot.bin of=/dev/mtdblock0                    kernel提示: 287+1 records in
                                                                                                    287+1 records out
8. dd if=u.boot.bin of=/dev/mtd0                            kernel提示: dd: writing '/dev/mtdblock0': Operation not permitted
                                                                                                    1+0 records in
                                                                                                    0+0 records out
9. dd if=u.boot.bin of=/dev/mtdblock0                    kernel提示: 287+1 records in
                                                                                                    287+1 records out
10. dd if=u.boot.bin of=/dev/mtd0                            kernel提示: 287+1 records in
                                                                                                      287+1 records in

mtd0和mtd14的区别是kernel的mtd_patitions中mask_flag不同,mtd0屏蔽的writable,而mtd14则没有。
在3,4的操作中,都是提示了不是字符设备,5,6的操作则反应了擦除是由mask_flag决定,而且说明我能操作的
应该是/dev/mtd*,而如何解释3的报错【mtd0是字符设备,mtdblock0是块设备】,只能认为是没有权限导致
报这样的错,当初是这么说服自己的。
1和2的操作,说明cp不能用在块设备上,而cp到/dev/mtd0没报道,经过查看还是没有成功,也更加确定没有权限
造成的。
综合以上有3点让我确定是用/dev/mtd0
1. 提示不是字符设备
2. cp /dev/mtd0没有提示,而/dev/mtdblock0却提示操作不允许
3. flash_erase /dev/mtd14能成功,而flash_erase /dev/mtdblock14不能成功。
其实以上都是错误的,说下我今天得出的结论。

/dev/mtd0和/dev/mtdblock0其实都可以写入数据,关键是方法不一样,而且/dev/mtd0和/dev/mtdblock0就是对应
统一个地址,读出数据是一样。
/dev/mtdblock0:将mask_flag设置为可以,就能用cp和dd进行数据的操作。
/dev/mtd0: 直接用cp和dd是不行的,符合昨天提到的,往flash写入数据,只能将1写成0,而不能将0写成1.因此
在使用之前先要使用flash_erase将分区擦除【全为1】,但是使用flash_erase的前提是将mask_flag设置为可以。

今天的实验操作记录都放在我的115邮箱,这里记录下,以防忘记
<think>首先,用户的问题是:“我想使用HAL库在STM32F427上实现FLASH读写擦除操作 请问STM32F427 HAL库实现FLASH读写擦除”。我需要基于提供的引用系统指令来回答。 系统指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题,保证回答真实可靠。 参考的引用: - [^1]: STM32G0xx HALLL库Flash读写擦除操作。 - [^2]: stm32 hal库 flash读写擦除(基于江科大代码)。 - [^3]: STM32 使用HAL库实现flash读写。 - [^4]: STM32F030 HAL库内部 FLASH读写。 这些引用是关于STM32不同型号的HAL库FLASH操作,但用户使用的是STM32F427。所以,我需要将通用原则应用到STM32F427上,并参考这些引用中的方法。 关键点从引用中提取: - 解锁加锁FLASH:操作前解锁,操作后加锁以防止意外写入。[^1][^2] - 擦除FLASH必须先擦除才能写入,因为写入只能将1变为0,不能将0变为1。擦除将整个块置1。[^3] - 擦除配置:使用结构体如FLASH_EraseInitTypeDef来配置擦除参数,如页地址、页数等。[^2] - 写入:HAL库提供函数如HAL_FLASH_Program()来写入数据。引用中提到双字写入。[^1][^3] - 忙等待:在操作后检查FLASH是否忙。[^1] - 对于STM32F427,需要查阅其特定FLASH大小页大小,但回答中应保持通用。 用户设备:STM32F427。我需要回忆或基于知识:STM32F427有内部FLASH,通常以页或扇区为单位操作。HAL库函数是通用的,但参数可能因型号而异。 回答结构: 1. **介绍FLASH操作基本概念**:解释为什么需要擦除写入的机制。 2. **步骤分解**: - 解锁FLASH。 - 擦除FLASH页。 - 写入数据到FLASH。 - 读取数据。 - 加锁FLASH。 3. **代码示例**:提供HAL库的代码片段,基于引用STM32F427。 4. **注意事项**:如忙等待、中断处理、选项字节等。 5. **生成相关问题**:在末尾添加相关问题。 使用中文回答。 行内数学表达式:如果有数学部分,比如地址计算,使用$...$。例如,地址偏移:$0x08000000 + offset$。 独立公式:如果有公式,单独成段用$$...$$。但在这个上下文中,可能不需要复杂公式。 引用标识:在回答中,当引用特定段落时添加[^n]。 现在,构建回答: - **开头**:简要介绍FLASH操作。 - **步骤**: 1. **解锁FLASH**:使用HAL_FLASH_Unlock()。 2. **擦除操作**:配置FLASH_EraseInitTypeDef,调用HAL_FLASHEx_Erase()。 - 对于STM32F427,FLASH被分成扇区(sectors),不是页(pages)。STM32F4系列使用扇区。每个扇区大小不同。 - 引用[^2]提到页操作,但F4是扇区。需调整。 - 擦除类型:FLASH_TYPEERASE_SECTORS。 3. **写入操作**:使用HAL_FLASH_Program()。写入类型可以是FLASH_TYPEPROGRAM_BYTE, WORD, DOUBLEWORD等。STM32F4支持多种编程大小。 4. **读取操作**:直接读取内存地址。 5. **加锁FLASH**:HAL_FLASH_Lock()。 - **代码示例**:提供C代码片段。 - **注意事项**:擦除前备份数据,因为擦除影响整个扇区;操作后检查状态;考虑中断安全。 - **相关问题**:生成3个相关问题。 生成相关问题: 1. 如何管理FLASH磨损均衡? 2. STM32F427 FLASH的扇区大小是多少? 3. HAL库中FLASH操作如何防止中断干扰? 最后,确保回答基于引用,但针对STM32F427。</think>### STM32F427 HAL库实现FLASH读写擦除操作指南 在STM32F427微控制器上,使用HAL库进行内部FLASH读写擦除操作是常见的需求,例如存储配置参数或实现固件自更新。FLASH存储器必须先擦除后写入,因为写入操作只能将数据位从“1”变为“0”,而无法将“0”变为“1”;擦除操作则将整个扇区置为全“1”状态[^3]。STM32F427的FLASH被划分为多个扇区(sectors),每个扇区大小不同(例如,扇区0大小为16KB,扇区1-3为64KB等),操作时需注意扇区边界。以下是基于HAL库的详细实现步骤代码示例,参考了通用STM32 HAL库操作原则[^1][^2][^3][^4]。 --- ### 步骤分解(以读写一个双字数据为例) 以下步骤假设使用STM32CubeIDE开发环境,并已正确初始化HAL库。操作前需包含头文件`stm32f4xx_hal_flash.h`。 1. **解锁FLASH** 操作前必须解锁FLASH选项字节(Option Bytes),以防止意外写入。使用`HAL_FLASH_Unlock()`解锁FLASH控制寄存器,并调用`HAL_FLASH_OB_Unlock()`解锁选项字节(如果需要修改配置)。 ```c if (HAL_FLASH_Unlock() != HAL_OK) { // 解锁失败处理 Error_Handler(); } if (HAL_FLASH_OB_Unlock() != HAL_OK) { // 解锁选项字节失败处理 Error_Handler(); } ``` 2. **擦除FLASH扇区** STM32F427使用扇区擦除(而非页擦除)。配置`FLASH_EraseInitTypeDef`结构体,指定扇区编号、擦除类型扇区数量。调用`HAL_FLASHEx_Erase()`执行擦除,并使用忙等待检查操作完成。 - **关键参数**: - `TypeErase`: 设为`FLASH_TYPEERASE_SECTORS`(扇区擦除)。 - `Sector`: 起始扇区号(例如,扇区5)。 - `NbSectors`: 擦除的扇区数量(例如,1)。 - `VoltageRange`: 电压范围,通常设为`FLASH_VOLTAGE_RANGE_3`(适用于3.3V系统)。 ```c FLASH_EraseInitTypeDef EraseInitStruct; uint32_t SectorError = 0; EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; EraseInitStruct.Sector = FLASH_SECTOR_5; // 示例:擦除扇区5 EraseInitStruct.NbSectors = 1; // 擦除1个扇区 EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; EraseInitStruct.Banks = FLASH_BANK_1; // STM32F427只有Bank 1 if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) { // 擦除失败处理 Error_Handler(); } // 忙等待,确保擦除完成 while (__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)); ``` **注意**:擦除会影响整个扇区,擦除前需备份重要数据。扇区大小参考STM32F427参考手册(例如,扇区5大小为128KB)。擦除时间较长,需避免中断干扰[^1][^2]。 3. **写入数据到FLASH** 使用`HAL_FLASH_Program()`函数写入数据。STM32F427支持多种编程大小(字节、半字、字、双字)。推荐双字写入(64位)以提高效率。写入地址必须在擦除后的扇区内,且对齐到双字边界(地址能被8整除)。 - **写入类型**:设为`FLASH_TYPEPROGRAM_DOUBLEWORD`(双字写入)。 - **地址计算**:FLASH起始地址为$0x08000000$,目标地址可计算为$ \text{base} + \text{offset} $,例如$0x08020000$(扇区5起始地址)。 ```c uint64_t data = 0x123456789ABCDEF0; // 示例数据(64位) uint32_t address = 0x08020000; // 扇区5起始地址 if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data) != HAL_OK) { // 写入失败处理 Error_Handler(); } // 忙等待,确保写入完成 while (__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)); ``` **注意**:写入操作只能将“1”变“0”,因此必须在擦除后执行。连续写入时,地址需递增8字节(双字大小)[^1][^3]。 4. **读取数据** 直接从FLASH内存地址读取数据,无需特殊函数。确保读取地址在有效范围内。 ```c uint64_t read_data = *(__IO uint64_t *)address; // 读取双字数据 // 示例:打印读取值 printf("Read data: 0x%llx\n", read_data); ``` 5. **加锁FLASH** 操作完成后加锁FLASH,防止意外修改。 ```c if (HAL_FLASH_Lock() != HAL_OK) { // 加锁失败处理 Error_Handler(); } if (HAL_FLASH_OB_Lock() != HAL_OK) { // 加锁选项字节失败处理 Error_Handler(); } ``` --- ### 完整代码示例 以下是一个简单的读写擦除测试函数,集成上述步骤: ```c #include "stm32f4xx_hal.h" void FLASH_ReadWriteTest(void) { // 步骤1: 解锁FLASH if (HAL_FLASH_Unlock() != HAL_OK) Error_Handler(); if (HAL_FLASH_OB_Unlock() != HAL_OK) Error_Handler(); // 步骤2: 擦除扇区5 (地址: 0x08020000) FLASH_EraseInitTypeDef EraseInitStruct; uint32_t SectorError = 0; EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; EraseInitStruct.Sector = FLASH_SECTOR_5; EraseInitStruct.NbSectors = 1; EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; EraseInitStruct.Banks = FLASH_BANK_1; if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) Error_Handler(); while (__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)); // 步骤3: 写入双字数据 uint32_t address = 0x08020000; uint64_t data = 0x1122334455667788; if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data) != HAL_OK) Error_Handler(); while (__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)); // 步骤4: 读取数据 uint64_t read_data = *(__IO uint64_t *)address; // 可添加校验逻辑,例如: if (read_data != data) { /* 错误处理 */ } // 步骤5: 加锁FLASH if (HAL_FLASH_Lock() != HAL_OK) Error_Handler(); if (HAL_FLASH_OB_Lock() != HAL_OK) Error_Handler(); } ``` --- ### 注意事项 1. **扇区大小与地址**:STM32F427的FLASH扇区大小不统一(扇区0: 16KB, 扇区1-3: 64KB, 扇区4: 128KB, 等)。操作前查阅参考手册确认目标扇区的起始地址大小。例如,扇区5起始地址为$0x08020000$[^1]。 2. **中断安全**:FLASH操作期间需禁用中断(使用`__disable_irq()``__enable_irq()`),避免因中断导致操作失败。 3. **擦除限制**:每个扇区有擦除寿命(约10,000次)。频繁擦写时需实现磨损均衡算法[^3]。 4. **错误处理**:检查HAL函数返回值(`HAL_OK`或`HAL_ERROR`),并使用`__HAL_FLASH_GET_FLAG`检测状态标志(如`FLASH_FLAG_PGERR`)。 5. **选项字节**:修改选项字节(如写保护)需额外调用`HAL_FLASHEx_OBProgram()`函数,但一般读写操作无需修改[^2]。 通过以上步骤,您可以在STM32F427上可靠地实现FLASH读写擦除。测试时建议使用调试器监视FLASH内容,并验证数据一致性[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值