/** ****************************************************************************** * @file w25qxx.c * @brief W25Q 系列 SPI NOR Flash 驱动(H7 QUADSPI / F4 SPI 双传输层) * * 本文件通过宏 W25QXX_USE_QSPI / W25QXX_USE_SPI 在同一套源码中同时支持 * 两种传输层: * * - W25QXX_USE_QSPI(STM32H7): * 基于参考工程 robot/project_old/paint_robot_old/BASE/Src/BSP/ * bsp_qspi_w25q128.c(在 H7 下工作正常)移植而来,使用 CubeMX 生成 * 的 QUADSPI 外设句柄 hqspi,指令/地址/数据均采用 1-Line(SPI 兼容 * 时序),不依赖 QE 位,读写擦稳定可靠。 * - W25QXX_USE_SPI(STM32F4 等): * 原正点原子经典 SPI 驱动,依赖 bsp 中定义的 W25QXXSPI 与 W25QXX_CS_x。 * * 对外 API 两种传输层完全一致,可与 robot/library/littlefs/lfs_flashbd.c * 无缝接驳: * W25QXX_Init / W25QXX_Read / W25QXX_Write_NoCheck * W25QXX_Erase_Sector(入参为扇区号,内部 ×4096) / W25QXX_Wait_Busy ****************************************************************************** */ #include "w25qxx.h" /* 当前芯片型号,默认 W25Q80(两种传输层共用) */ uint16_t W25QXX_TYPE = W25Q80; /* =========================================================================== * STM32H7 QUADSPI 传输层 * =========================================================================*/ #if defined(W25QXX_USE_QSPI) #include "quadspi.h" /* 是否启用 4 字节地址模式(仅 W25Q256 需要) */ static uint8_t s_w25qxx_4byte = 0; /* 带擦除写入用的扇区缓冲 */ static uint8_t W25QXX_BUFFER[W25QXX_SECTOR_SIZE]; /** 填充 QSPI 命令帧的公共字段 */ static void W25QXX_Command_Base(QSPI_CommandTypeDef *cmd) { cmd->InstructionMode = QSPI_INSTRUCTION_1_LINE; cmd->AddressSize = s_w25qxx_4byte ? QSPI_ADDRESS_32_BITS : QSPI_ADDRESS_24_BITS; cmd->AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; cmd->DdrMode = QSPI_DDR_MODE_DISABLE; cmd->DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; cmd->SIOOMode = QSPI_SIOO_INST_EVERY_CMD; cmd->DummyCycles = 0; cmd->NbData = 0; } /** 仅发送一条指令(无地址、无数据),如写使能/写禁止/片擦除/复位 */ static void W25QXX_CommandOnly(uint8_t instruction) { QSPI_CommandTypeDef cmd = {0}; W25QXX_Command_Base(&cmd); cmd.Instruction = instruction; cmd.AddressMode = QSPI_ADDRESS_NONE; cmd.DataMode = QSPI_DATA_NONE; HAL_QSPI_Command(&hqspi, &cmd, HAL_QSPI_TIMEOUT_DEFAULT_VALUE); } /** 内部:带超时的等待 Flash 空闲(自动轮询状态寄存器 bit0(BUSY) 直至为 0) */ static void W25QXX_WaitBusy_Timeout(uint32_t timeout_ms) { QSPI_CommandTypeDef cmd = {0}; QSPI_AutoPollingTypeDef cfg = {0}; W25QXX_Command_Base(&cmd); cmd.Instruction = W25X_ReadStatusReg1; /* 0x05 */ cmd.AddressMode = QSPI_ADDRESS_NONE; cmd.DataMode = QSPI_DATA_1_LINE; cmd.DummyCycles = 0; cfg.Match = 0x00; /* 等待 BUSY=0 */ cfg.Mask = 0x01; /* 只关心 bit0 */ cfg.MatchMode = QSPI_MATCH_MODE_AND; cfg.StatusBytesSize = 1; cfg.Interval = 0x10; cfg.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; HAL_QSPI_AutoPolling(&hqspi, &cmd, &cfg, timeout_ms); } /** 初始化:确保 QUADSPI 外设已初始化,软件复位 Flash 到 SPI 模式,读取型号 */ void W25QXX_Init(void) { /* 若 QUADSPI 尚未初始化(例如未走 main 的 MX_QUADSPI_Init),则补初始化 */ if (hqspi.State == HAL_QSPI_STATE_RESET) { MX_QUADSPI_Init(); } /* 软件复位(0x66 + 0x99),确保芯片回到已知的 SPI 模式, 避免之前被置入 QPI 模式导致后续 1-Line 指令失效 */ W25QXX_CommandOnly(W25X_ResetEnable); W25QXX_CommandOnly(W25X_ResetMemory); W25QXX_Wait_Busy(); /* 读取芯片型号 */ W25QXX_TYPE = W25QXX_ReadID(); /* 仅 W25Q256 需要进入 4 字节地址模式 */ s_w25qxx_4byte = 0; if (W25QXX_TYPE == W25Q256) { W25QXX_Write_Enable(); W25QXX_CommandOnly(W25X_Enable4ByteAddr); s_w25qxx_4byte = 1; } } /** 使能 4 字节地址模式 */ void W25QXX_4ByteAddr_Enable(void) { W25QXX_Write_Enable(); W25QXX_CommandOnly(W25X_Enable4ByteAddr); s_w25qxx_4byte = 1; } /** 读取芯片型号 ID(Manufact/Device ID 0x90 读法,返回 0xEF17 等) */ uint16_t W25QXX_ReadID(void) { uint16_t temp = 0; uint8_t buf[2] = {0, 0}; QSPI_CommandTypeDef cmd = {0}; W25QXX_Command_Base(&cmd); cmd.Instruction = W25X_ManufactDeviceID; /* 0x90 */ cmd.AddressMode = QSPI_ADDRESS_1_LINE; cmd.AddressSize = QSPI_ADDRESS_24_BITS; cmd.Address = 0x000000; cmd.DataMode = QSPI_DATA_1_LINE; cmd.NbData = 2; if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return 0; } if (HAL_QSPI_Receive(&hqspi, buf, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return 0; } temp = (uint16_t)((buf[0] << 8) | buf[1]); return temp; } /** 读取状态寄存器(regno: 1~3) */ uint8_t W25QXX_ReadSR(uint8_t regno) { uint8_t command; uint8_t data = 0xFF; QSPI_CommandTypeDef cmd = {0}; switch (regno) { case 2: command = W25X_ReadStatusReg2; break; case 3: command = W25X_ReadStatusReg3; break; default: command = W25X_ReadStatusReg1; break; } W25QXX_Command_Base(&cmd); cmd.Instruction = command; cmd.AddressMode = QSPI_ADDRESS_NONE; cmd.DataMode = QSPI_DATA_1_LINE; cmd.NbData = 1; if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return 0xFF; } if (HAL_QSPI_Receive(&hqspi, &data, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return 0xFF; } return data; } /** 写状态寄存器 */ void W25QXX_Write_SR(uint8_t regno, uint8_t sr) { uint8_t command; QSPI_CommandTypeDef cmd = {0}; switch (regno) { case 2: command = W25X_WriteStatusReg2; break; case 3: command = W25X_WriteStatusReg3; break; default: command = W25X_WriteStatusReg1; break; } W25QXX_Write_Enable(); W25QXX_Command_Base(&cmd); cmd.Instruction = command; cmd.AddressMode = QSPI_ADDRESS_NONE; cmd.DataMode = QSPI_DATA_1_LINE; cmd.NbData = 1; if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) == HAL_OK) { HAL_QSPI_Transmit(&hqspi, &sr, HAL_QSPI_TIMEOUT_DEFAULT_VALUE); } W25QXX_Wait_Busy(); } /** 写使能 */ void W25QXX_Write_Enable(void) { W25QXX_CommandOnly(W25X_WriteEnable); } /** 写禁止 */ void W25QXX_Write_Disable(void) { W25QXX_CommandOnly(W25X_WriteDisable); } /** 读取指定地址、指定长度的数据(1-Line 标准读 0x03),返回 0 成功 / -1 失败 */ int W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead) { QSPI_CommandTypeDef cmd = {0}; if (NumByteToRead == 0) { return 0; } W25QXX_Command_Base(&cmd); cmd.Instruction = W25X_ReadData; /* 0x03 */ cmd.AddressMode = QSPI_ADDRESS_1_LINE; cmd.Address = ReadAddr; cmd.DataMode = QSPI_DATA_1_LINE; cmd.DummyCycles = 0; cmd.NbData = NumByteToRead; if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return -1; } if (HAL_QSPI_Receive(&hqspi, pBuffer, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return -1; } return 0; } /** 页编程(≤256 字节,不跨页),返回 0 成功 / -1 失败 */ int W25QXX_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { QSPI_CommandTypeDef cmd = {0}; if (NumByteToWrite == 0) { return 0; } W25QXX_Write_Enable(); W25QXX_Command_Base(&cmd); cmd.Instruction = W25X_PageProgram; /* 0x02 */ cmd.AddressMode = QSPI_ADDRESS_1_LINE; cmd.Address = WriteAddr; cmd.DataMode = QSPI_DATA_1_LINE; cmd.DummyCycles = 0; cmd.NbData = NumByteToWrite; if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return -1; } if (HAL_QSPI_Transmit(&hqspi, pBuffer, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return -1; } W25QXX_Wait_Busy(); return 0; } /** 无校验写入:自动跨页,不检查/不擦除(调用方须保证目标区已擦除),返回 0 成功 / -1 失败 */ int W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { uint16_t pageremain = W25QXX_PAGE_SIZE - (WriteAddr % W25QXX_PAGE_SIZE); /* 本页剩余 */ if (NumByteToWrite <= pageremain) { pageremain = NumByteToWrite; } while (1) { if (W25QXX_Write_Page(pBuffer, WriteAddr, pageremain) != 0) { return -1; } if (NumByteToWrite == pageremain) { break; /* 写入结束 */ } else { pBuffer += pageremain; WriteAddr += pageremain; NumByteToWrite -= pageremain; if (NumByteToWrite > W25QXX_PAGE_SIZE) { pageremain = W25QXX_PAGE_SIZE; } else { pageremain = NumByteToWrite; } } } return 0; } /** 带擦除的写入:自动跨页跨扇区,非 0xFF 区域先擦除再写 */ void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { uint32_t secpos; uint16_t secoff, secremain, i; uint8_t *buf = W25QXX_BUFFER; secpos = WriteAddr / W25QXX_SECTOR_SIZE; /* 扇区地址 */ secoff = WriteAddr % W25QXX_SECTOR_SIZE; /* 扇区内偏移 */ secremain = W25QXX_SECTOR_SIZE - secoff; /* 扇区剩余空间 */ if (NumByteToWrite <= secremain) { secremain = NumByteToWrite; } while (1) { W25QXX_Read(buf, secpos * W25QXX_SECTOR_SIZE, W25QXX_SECTOR_SIZE); /* 读出整扇区 */ /* 判断该区间是否全为 0xFF(无需擦除) */ for (i = 0; i < secremain; i++) { if (buf[secoff + i] != 0xFF) { break; } } if (i < secremain) /* 需要擦除 */ { W25QXX_Erase_Sector((uint32_t)secpos); for (i = 0; i < secremain; i++) { buf[secoff + i] = pBuffer[i]; } W25QXX_Write_NoCheck(buf, secpos * W25QXX_SECTOR_SIZE, W25QXX_SECTOR_SIZE); } else /* 已擦除,直接写剩余区间 */ { W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain); } if (NumByteToWrite == secremain) { break; /* 写入结束 */ } else { secpos++; secoff = 0; pBuffer += secremain; WriteAddr += secremain; NumByteToWrite -= secremain; if (NumByteToWrite > W25QXX_SECTOR_SIZE) { secremain = W25QXX_SECTOR_SIZE; } else { secremain = NumByteToWrite; } } } } /** 整片擦除(等待时间较长) */ void W25QXX_Erase_Chip(void) { W25QXX_Write_Enable(); W25QXX_Wait_Busy(); W25QXX_CommandOnly(W25X_ChipErase); /* 整片擦除最长可达约 250s,需用较长的轮询超时 */ W25QXX_WaitBusy_Timeout(250000U); } /** 扇区擦除,入参为扇区号(内部 ×4096 得到字节地址),返回 0 成功 / -1 失败 */ int W25QXX_Erase_Sector(uint32_t Dst_Addr) { uint32_t byte_addr = Dst_Addr * W25QXX_SECTOR_SIZE; QSPI_CommandTypeDef cmd = {0}; W25QXX_Write_Enable(); W25QXX_Wait_Busy(); W25QXX_Command_Base(&cmd); cmd.Instruction = W25X_SectorErase; /* 0x20 */ cmd.AddressMode = QSPI_ADDRESS_1_LINE; cmd.Address = byte_addr; cmd.DataMode = QSPI_DATA_NONE; cmd.NbData = 0; if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return -1; } W25QXX_Wait_Busy(); return 0; } /** 等待 Flash 空闲:自动轮询状态寄存器 bit0(BUSY) 直至为 0 */ void W25QXX_Wait_Busy(void) { /* 页编程 ≤3ms、扇区擦除 ≤400ms,默认 5s 超时足够 */ W25QXX_WaitBusy_Timeout(HAL_QSPI_TIMEOUT_DEFAULT_VALUE); } /** 进入掉电模式 */ void W25QXX_PowerDown(void) { W25QXX_CommandOnly(W25X_PowerDown); /* 0xB9 */ HAL_Delay(3); } /** 退出掉电模式 */ void W25QXX_WAKEUP(void) { W25QXX_CommandOnly(W25X_ReleasePowerDown); /* 0xAB */ HAL_Delay(3); } /* =========================================================================== * STM32F4 等 经典 SPI 传输层 * =========================================================================*/ #elif defined(W25QXX_USE_SPI) #include "spi.h" /* 带擦除写入用的扇区缓冲 */ uint8_t W25QXX_BUFFER[4096]; uint8_t SPI2_ReadWriteByte(uint8_t TxData) { uint8_t Rxdata; HAL_SPI_TransmitReceive(&W25QXXSPI, &TxData, &Rxdata, 1, 1000); return Rxdata; } /* 初始化 SPI FLASH 的 IO 口 */ void W25QXX_Init(void) { uint8_t temp; SPI2_ReadWriteByte(0xFF); /* 启动传输 */ W25QXX_CS(1); /* SPI FLASH 不选中 */ W25QXX_TYPE = W25QXX_ReadID(); /* 读取 FLASH ID */ if (W25QXX_TYPE == W25Q256) /* 4 字节地址模式 */ { temp = W25QXX_ReadSR(3); if ((temp & 0x01) == 0) { W25QXX_CS(0); SPI2_ReadWriteByte(W25X_Enable4ByteAddr); W25QXX_CS(1); } } } /** 使能 4 字节地址模式 */ void W25QXX_4ByteAddr_Enable(void) { W25QXX_CS(0); SPI2_ReadWriteByte(W25X_Enable4ByteAddr); W25QXX_CS(1); } /* 读取状态寄存器 */ uint8_t W25QXX_ReadSR(uint8_t regno) { uint8_t byte = 0, command = 0; switch (regno) { case 1: command = W25X_ReadStatusReg1; break; case 2: command = W25X_ReadStatusReg2; break; case 3: command = W25X_ReadStatusReg3; break; default: command = W25X_ReadStatusReg1; break; } W25QXX_CS(0); SPI2_ReadWriteByte(command); byte = SPI2_ReadWriteByte(0xFF); W25QXX_CS(1); return byte; } /* 写状态寄存器 */ void W25QXX_Write_SR(uint8_t regno, uint8_t sr) { uint8_t command = 0; switch (regno) { case 1: command = W25X_WriteStatusReg1; break; case 2: command = W25X_WriteStatusReg2; break; case 3: command = W25X_WriteStatusReg3; break; default: command = W25X_WriteStatusReg1; break; } W25QXX_CS(0); SPI2_ReadWriteByte(command); SPI2_ReadWriteByte(sr); W25QXX_CS(1); } void W25QXX_Write_Enable(void) { W25QXX_CS(0); SPI2_ReadWriteByte(W25X_WriteEnable); W25QXX_CS(1); } void W25QXX_Write_Disable(void) { W25QXX_CS(0); SPI2_ReadWriteByte(W25X_WriteDisable); W25QXX_CS(1); } uint16_t W25QXX_ReadID(void) { uint16_t Temp = 0; W25QXX_CS(0); SPI2_ReadWriteByte(0x90); SPI2_ReadWriteByte(0x00); SPI2_ReadWriteByte(0x00); SPI2_ReadWriteByte(0x00); Temp |= SPI2_ReadWriteByte(0xFF) << 8; Temp |= SPI2_ReadWriteByte(0xFF); W25QXX_CS(1); return Temp; } int W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead) { uint16_t i; W25QXX_CS(0); SPI2_ReadWriteByte(W25X_ReadData); if (W25QXX_TYPE == W25Q256) { SPI2_ReadWriteByte((uint8_t)((ReadAddr) >> 24)); } SPI2_ReadWriteByte((uint8_t)((ReadAddr) >> 16)); SPI2_ReadWriteByte((uint8_t)((ReadAddr) >> 8)); SPI2_ReadWriteByte((uint8_t)ReadAddr); for (i = 0; i < NumByteToRead; i++) { pBuffer[i] = SPI2_ReadWriteByte(0xFF); } W25QXX_CS(1); return 0; } int W25QXX_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { uint16_t i; W25QXX_Write_Enable(); W25QXX_CS(0); SPI2_ReadWriteByte(W25X_PageProgram); if (W25QXX_TYPE == W25Q256) { SPI2_ReadWriteByte((uint8_t)((WriteAddr) >> 24)); } SPI2_ReadWriteByte((uint8_t)((WriteAddr) >> 16)); SPI2_ReadWriteByte((uint8_t)((WriteAddr) >> 8)); SPI2_ReadWriteByte((uint8_t)WriteAddr); for (i = 0; i < NumByteToWrite; i++) { SPI2_ReadWriteByte(pBuffer[i]); } W25QXX_CS(1); W25QXX_Wait_Busy(); return 0; } int W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { uint16_t pageremain; pageremain = 256 - WriteAddr % 256; if (NumByteToWrite <= pageremain) { pageremain = NumByteToWrite; } while (1) { W25QXX_Write_Page(pBuffer, WriteAddr, pageremain); if (NumByteToWrite == pageremain) { break; } else { pBuffer += pageremain; WriteAddr += pageremain; NumByteToWrite -= pageremain; if (NumByteToWrite > 256) { pageremain = 256; } else { pageremain = NumByteToWrite; } } } return 0; } void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { uint32_t secpos; uint16_t secoff; uint16_t secremain; uint16_t i; uint8_t *W25QXX_BUF; W25QXX_BUF = W25QXX_BUFFER; secpos = WriteAddr / 4096; secoff = WriteAddr % 4096; secremain = 4096 - secoff; if (NumByteToWrite <= secremain) { secremain = NumByteToWrite; } while (1) { W25QXX_Read(W25QXX_BUF, secpos * 4096, 4096); for (i = 0; i < secremain; i++) { if (W25QXX_BUF[secoff + i] != 0xFF) { break; } } if (i < secremain) { W25QXX_Erase_Sector(secpos); for (i = 0; i < secremain; i++) { W25QXX_BUF[i + secoff] = pBuffer[i]; } W25QXX_Write_NoCheck(W25QXX_BUF, secpos * 4096, 4096); } else { W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain); } if (NumByteToWrite == secremain) { break; } else { secpos++; secoff = 0; pBuffer += secremain; WriteAddr += secremain; NumByteToWrite -= secremain; if (NumByteToWrite > 4096) { secremain = 4096; } else { secremain = NumByteToWrite; } } } } void W25QXX_Erase_Chip(void) { W25QXX_Write_Enable(); W25QXX_Wait_Busy(); W25QXX_CS(0); SPI2_ReadWriteByte(W25X_ChipErase); W25QXX_CS(1); W25QXX_Wait_Busy(); } int W25QXX_Erase_Sector(uint32_t Dst_Addr) { Dst_Addr *= 4096; W25QXX_Write_Enable(); W25QXX_Wait_Busy(); W25QXX_CS(0); SPI2_ReadWriteByte(W25X_SectorErase); if (W25QXX_TYPE == W25Q256) { SPI2_ReadWriteByte((uint8_t)((Dst_Addr) >> 24)); } SPI2_ReadWriteByte((uint8_t)((Dst_Addr) >> 16)); SPI2_ReadWriteByte((uint8_t)((Dst_Addr) >> 8)); SPI2_ReadWriteByte((uint8_t)Dst_Addr); W25QXX_CS(1); W25QXX_Wait_Busy(); return 0; } void W25QXX_Wait_Busy(void) { while ((W25QXX_ReadSR(1) & 0x01) == 0x01); } void W25QXX_PowerDown(void) { W25QXX_CS(0); SPI2_ReadWriteByte(W25X_PowerDown); W25QXX_CS(1); HAL_Delay(3); } void W25QXX_WAKEUP(void) { W25QXX_CS(0); SPI2_ReadWriteByte(W25X_ReleasePowerDown); W25QXX_CS(1); HAL_Delay(3); } #endif /* W25QXX_USE_QSPI / W25QXX_USE_SPI */