K1Guitar/device/W25Q64/W25Q128.c

205 lines
5.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// File: w25q128.c
#include "w25q128.h"
#include "wk_system.h"
#include "rtthread.h"
/* W25Q128 挂在 SPI1 上PA5=SCK, PA6=MISO, PA7=MOSICS 用 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(); // 等待全片擦除完成
}