205 lines
5.9 KiB
C
205 lines
5.9 KiB
C
// File: w25q128.c
|
||
#include "w25q128.h"
|
||
#include "wk_system.h"
|
||
#include "rtthread.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); // 循环读取状态字
|
||
if (status & 0x01) {
|
||
SPI_CS_HIGH();
|
||
rt_thread_mdelay(1);
|
||
SPI_CS_LOW();
|
||
W25Q128_ReadWriteByte(W25X_ReadStatusReg1);
|
||
}
|
||
} 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(); // 等待全片擦除完成
|
||
}
|