STM32F1实战:用CubeIDE HAL库搞定W25Q128跨页跨扇区写入(附完整代码)
当你在STM32项目中需要存储大量数据时,外部Flash芯片W25Q128是个不错的选择。但真正用起来,你会发现它有个让人头疼的特性:写入不能跨页,擦除最小单位是扇区。这就意味着,如果你的数据刚好卡在页或扇区边界,常规的写入方法就会失效。
我最近在一个物联网项目中就遇到了这个问题。设备需要定期记录传感器数据,每条记录大小不固定,经常出现跨页甚至跨扇区的情况。经过几次失败的尝试和调试,终于总结出一套可靠的解决方案。下面就把我的实战经验分享给大家,特别是那些已经掌握W25Q128基础操作,但在处理复杂写入场景时遇到瓶颈的中级开发者。
1. 理解W25Q128的存储架构
W25Q128的16MB存储空间被组织成一个层级结构:
- 块(Block):256个,每个64KB
- 扇区(Sector):每个块包含16个扇区,共4096个扇区,每个4KB
- 页(Page):每个扇区包含16页,每页256字节
关键限制:
- 写入操作:单次最多写入一页(256字节),且不能跨页
- 擦除操作:最小擦除单位是扇区(4KB),擦除后所有位变为1
- 编程特性:只能将1改为0,不能将0改为1,必须先擦除才能重新写入
c复制// W25Q128存储结构定义
#define PAGE_SIZE 256 // 页大小(字节)
#define SECTOR_SIZE 4096 // 扇区大小(字节)
#define BLOCK_SIZE 65536 // 块大小(字节)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 跨页写入的解决方案
当数据长度超过当前页剩余空间时,需要将数据拆分到多个页写入。下面是处理跨页写入的关键函数:
2.1 页写入基础函数
首先实现单页写入的基础函数,这是所有写入操作的基础:
c复制/**
* @brief 写入单页数据
* @param pData: 要写入的数据指针
* @param addr: 写入起始地址
* @param size: 要写入的数据大小(不超过256字节)
* @retval HAL状态
*/
HAL_StatusTypeDef W25Q128_WritePage(uint8_t *pData, uint32_t addr, uint16_t size) {
// 检查写入是否跨页
if((addr % PAGE_SIZE) + size > PAGE_SIZE) {
return HAL_ERROR;
}
W25Q128_WriteEnable();
W25Q128_Enable();
// 发送页编程指令
spi2_Transmit_one_byte(0x02);
// 发送24位地址
spi2_Transmit_one_byte((addr >> 16) & 0xFF);
spi2_Transmit_one_byte((addr >> 8) & 0xFF);
spi2_Transmit_one_byte(addr & 0xFF);
// 写入数据
for(uint16_t i = 0; i < size; i++) {
spi2_Transmit_one_byte(pData[i]);
}
W25Q128_Disable();
W25Q128_WaitBusy();
return HAL_OK;
}
2.2 跨页写入函数
基于单页写入,实现自动处理跨页情况的通用写入函数:
c复制/**
* @brief 跨页写入数据
* @param pData: 要写入的数据指针
* @param addr: 写入起始地址
* @param size: 要写入的数据总大小
* @retval HAL状态
*/
HAL_StatusTypeDef W25Q128_WriteMultiPage(uint8_t *pData, uint32_t addr, uint32_t size) {
uint32_t remaining = size;
uint32_t currentAddr = addr;
uint8_t *currentPtr = pData;
