// File: w25q128.c #include "w25q128.h" #include "wk_system.h" /* W25Q128 挂在 SPI1 上(PA5=SCK, PA6=MISO, PA7=MOSI),CS 用 PA4 GPIO 控制 */ #define W25Q128_SPI SPI1 #define W25Q128_CS_PORT GPIOA #define W25Q128_CS_PIN GPIO_PINS_4 #define SPI_CS_LOW() gpio_bits_reset(W25Q128_CS_PORT, W25Q128_CS_PIN) #define SPI_CS_HIGH() gpio_bits_set(W25Q128_CS_PORT, W25Q128_CS_PIN) // 内部私有函数声明 static uint8_t W25Q128_ReadWriteByte(uint8_t TxData); static void W25Q128_WriteEnable(void); static void W25Q128_WaitBusy(void); /** * @brief SPI1 读写一个字节 (AT32 阻塞式全双工) * 发一个字节同时收一个字节 * @param TxData: 要发送的数据 * @retval 接收到的数据 */ uint8_t SPI1_ReadWriteByte(uint8_t TxData) { /* 等发送缓冲空(可写数据) */ while (spi_i2s_flag_get(W25Q128_SPI, SPI_I2S_TDBE_FLAG) == RESET); /* 写发送数据寄存器 */ spi_i2s_data_transmit(W25Q128_SPI, TxData); /* 等接收缓冲满(收到数据) */ while (spi_i2s_flag_get(W25Q128_SPI, SPI_I2S_RDBF_FLAG) == RESET); /* 读接收数据寄存器(自动清 RDBF) */ return (uint8_t)spi_i2s_data_receive(W25Q128_SPI); } /** * @brief 初始化W25Q128 (实质是配置 CS 引脚;SPI1 由 wk_spi1_init 初始化) */ void W25Q128_Init(void) { gpio_init_type g; /* CS 引脚 PA4:推挽输出,初始拉高(释放) */ gpio_default_para_init(&g); g.gpio_mode = GPIO_MODE_OUTPUT; g.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; g.gpio_pull = GPIO_PULL_NONE; g.gpio_pins = W25Q128_CS_PIN; gpio_init(W25Q128_CS_PORT, &g); SPI_CS_HIGH(); } /** * @brief 包装SPI读写函数,方便驱动内部调用 */ static uint8_t W25Q128_ReadWriteByte(uint8_t TxData) { return SPI1_ReadWriteByte(TxData); } /** * @brief 发送写使能命令 (任何写/擦除操作前必须调用) */ static void W25Q128_WriteEnable(void) { SPI_CS_LOW(); // 片选选中 W25Q128_ReadWriteByte(W25X_WriteEnable); // 发送写使能命令 SPI_CS_HIGH(); // 释放片选 } /** * @brief 等待芯片空闲 (忙时BUSY位为1) */ static void W25Q128_WaitBusy(void) { uint8_t status; SPI_CS_LOW(); W25Q128_ReadWriteByte(W25X_ReadStatusReg1); // 发送读状态寄存器命令 do { status = W25Q128_ReadWriteByte(0xFF); // 循环读取状态字 } while (status & 0x01); // 等待BUSY位清零 SPI_CS_HIGH(); } /** * @brief 读取芯片ID * @retval 例如 W25Q128 返回 0xEF17 */ uint16_t W25Q128_ReadID(void) { uint16_t id = 0; SPI_CS_LOW(); W25Q128_ReadWriteByte(W25X_ManufactDeviceID); // 发送读取ID命令 W25Q128_ReadWriteByte(0x00); // 3个空字节 W25Q128_ReadWriteByte(0x00); W25Q128_ReadWriteByte(0x00); id |= W25Q128_ReadWriteByte(0xFF) << 8; // Manufacturer ID id |= W25Q128_ReadWriteByte(0xFF); // Device ID SPI_CS_HIGH(); return id; } /** * @brief 读取数据 * @param pBuf: 数据存储区 * @param ReadAddr: 读取地址 (0~16MB-1) * @param NumByteToRead: 读取字节数 */ void W25Q128_Read(uint8_t* pBuf, uint32_t ReadAddr, uint16_t NumByteToRead) { uint16_t i; SPI_CS_LOW(); W25Q128_ReadWriteByte(W25X_ReadData); // 发送读数据命令 W25Q128_ReadWriteByte((ReadAddr >> 16) & 0xFF); // 发送24位地址 W25Q128_ReadWriteByte((ReadAddr >> 8) & 0xFF); W25Q128_ReadWriteByte(ReadAddr & 0xFF); for (i = 0; i < NumByteToRead; i++) { pBuf[i] = W25Q128_ReadWriteByte(0xFF); // 循环读取 } SPI_CS_HIGH(); } /** * @brief 写入数据 (自动处理跨页写,但调用前需要确保目标扇区已被擦除) * @param pBuf: 源数据 * @param WriteAddr: 写入地址 * @param NumByteToWrite: 写入字节数 (不要超过扇区大小) */ void W25Q128_Write(uint8_t* pBuf, uint32_t WriteAddr, uint16_t NumByteToWrite) { uint16_t i; uint16_t pageSize = 256; // W25Q128 页大小 uint16_t toWrite; while (NumByteToWrite > 0) { // 计算当前页可以写入的字节数 toWrite = pageSize - (WriteAddr % pageSize); if (toWrite > NumByteToWrite) toWrite = NumByteToWrite; W25Q128_WriteEnable(); // 写使能 SPI_CS_LOW(); W25Q128_ReadWriteByte(W25X_PageProgram); // 页编程命令 W25Q128_ReadWriteByte((WriteAddr >> 16) & 0xFF); W25Q128_ReadWriteByte((WriteAddr >> 8) & 0xFF); W25Q128_ReadWriteByte(WriteAddr & 0xFF); for (i = 0; i < toWrite; i++) { W25Q128_ReadWriteByte(pBuf[i]); } SPI_CS_HIGH(); W25Q128_WaitBusy(); // 等待本次页编程完成 // 更新指针和剩余字节数 NumByteToWrite -= toWrite; WriteAddr += toWrite; pBuf += toWrite; } } /** * @brief 擦除一个扇区 (4KB) * @param SectorAddr: 扇区内任意地址 */ void W25Q128_Erase_Sector(uint32_t SectorAddr) { W25Q128_WriteEnable(); // 写使能 SPI_CS_LOW(); W25Q128_ReadWriteByte(W25X_SectorErase); // 扇区擦除命令 W25Q128_ReadWriteByte((SectorAddr >> 16) & 0xFF); W25Q128_ReadWriteByte((SectorAddr >> 8) & 0xFF); W25Q128_ReadWriteByte(SectorAddr & 0xFF); SPI_CS_HIGH(); W25Q128_WaitBusy(); // 擦除需要时间,等待完成 } /** * @brief 擦除整个芯片 * @note 耗时较长,通常数秒至十几秒,务必确保等待完成 */ void W25Q128_Erase_Chip(void) { W25Q128_WriteEnable(); // 写使能 SPI_CS_LOW(); W25Q128_ReadWriteByte(W25X_ChipErase); // 全片擦除命令 SPI_CS_HIGH(); W25Q128_WaitBusy(); // 等待全片擦除完成 } void W25Q128_WriteSector_4K(uint8_t* buf, uint32_t addr,uint32_t len) { // 1. 必须 4K 对齐 if(addr % 4096 != 0) return; // 2. 擦除 1 个扇区(4KB) W25Q128_Erase_Sector(addr); // 3. 写入 4096 字节(你的函数自动分页写,完全支持) W25Q128_Write(buf, addr, len); }