W25Qxx.c 源代码
c
/**
******************************************************************************
* @file w25qxx.c
* @brief W25Q128 Flash驱动源文件
******************************************************************************
*/
/* 包含头文件 */
#include "w25qxx.h"
#include "stm32f1xx_hal_spi.h"
#include <stdio.h>
#include <stdlib.h>
/* 外部变量声明 */
extern SPI_HandleTypeDef hspi1;
static uint8_t W25QXX_BUF[W25QXX_SECTOR_SIZE];
/**
* @brief 初始化W25QXX
* @param 无
* @retval 无
*/
void W25QXX_Init(void) {
uint8_t retry = 0;
// 配置SPI_CS引脚为输出模式
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = W25QXX_CS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(W25QXX_CS_PORT, &GPIO_InitStruct);
W25QXX_CS(1); // 取消片选
W25QXX_WAKEUP(); // 唤醒
for (retry = 0; retry < 3; retry++) // 尝试3次
{
uint16_t flashID = W25QXX_ReadID();
if (flashID != 0) // W25Q128的ID
{
// printf("Flash ID: 0x%02X \r\n", flashID);
// printf("W25QXX INIT SUCCESS! \r\n");
break;
}
}
if (retry == 3) // 检测失败
{
// 可以在这里添加错误处理代码
printf("试了3次,还是找不到设备");
}
}
/**
* @brief W25QXX片选控制
* @param level: 0-选中, 1-取消选中
* @retval 无
*/
void W25QXX_CS(uint8_t level) {
if (level) {
HAL_GPIO_WritePin(W25QXX_CS_PORT, W25QXX_CS_PIN, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(W25QXX_CS_PORT, W25QXX_CS_PIN, GPIO_PIN_RESET);
}
}
/**
* @brief 读取一个字节
* @param 无
* @retval 读取到的数据
*/
uint8_t W25QXX_ReadByte(void) {
uint8_t rxData = 0;
HAL_SPI_TransmitReceive(&hspi1, (uint8_t *)0xFF, &rxData, 1, 1000);
return rxData;
}
/**
* @brief 发送一个字节
* @param txData: 要发送的数据
* @retval 无
*/
void W25QXX_WriteByte(uint8_t txData) {
HAL_SPI_Transmit(&hspi1, &txData, 1, 1000);
}
/**
* @brief 读取W25QXX的ID
* @param 无
* @retval 0XEF4018(W25Q128)
*/
uint16_t W25QXX_ReadID(void) {
uint16_t flashID = 0;
// uint8_t data[3];
W25QXX_CS(0); // 使能器件
// W25QXX_WriteByte(W25X_ManufactDeviceID); // 发送读取ID命令
// W25QXX_WriteByte(0x00); // 发送24位地址
// W25QXX_WriteByte(0x00);
// W25QXX_WriteByte(0x00);
W25QXX_WriteByte(W25X_JedecDeviceID); // 发送读取ID命令
W25QXX_ReadByte(); // 制造商ID
W25QXX_ReadByte(); // 设备ID高字节
flashID = W25QXX_ReadByte(); // 设备ID低字节
W25QXX_CS(1); // 取消片选
// printf("Flash ID: 0x%02X 0x%02X 0x%02X\r\n", data[0], data[1], data[2]);
return flashID;
}
/**
* @brief 读取SPI FLASH
* @param pBuffer: 存储读出数据的缓冲区
* @param ReadAddr: 读取地址
* @param NumByteToRead: 读取字节数
* @retval 无
*/
void W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead) {
uint16_t i;
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_ReadData); // 发送读取命令
W25QXX_WriteByte((uint8_t)((ReadAddr) >> 16)); // 发送24bit地址
W25QXX_WriteByte((uint8_t)((ReadAddr) >> 8));
W25QXX_WriteByte((uint8_t)ReadAddr);
for (i = 0; i < NumByteToRead; i++) {
pBuffer[i] = W25QXX_ReadByte(); // 循环读数
}
W25QXX_CS(1); // 取消片选
}
/**
* @brief 写入W25QXX在指定地址开始写入最大256字节的数据
* @param pBuffer: 数据存储区
* @param WriteAddr: 开始写入的地址(最大32bit)
* @param NumByteToWrite: 要写入的字节数(最大256),该数不应该超过W25QXX的页大小
* @retval 无
*/
void W25QXX_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr,
uint16_t NumByteToWrite) {
uint16_t i;
W25QXX_WriteEnable(); // 使能写
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_PageProgram); // 发送写页命令
W25QXX_WriteByte((uint8_t)((WriteAddr) >> 16)); // 发送24bit地址
W25QXX_WriteByte((uint8_t)((WriteAddr) >> 8));
W25QXX_WriteByte((uint8_t)WriteAddr);
for (i = 0; i < NumByteToWrite; i++) {
W25QXX_WriteByte(pBuffer[i]); // 循环写数
}
W25QXX_CS(1); // 取消片选
W25QXX_Wait_Busy(); // 等待写入结束
}
/**
* @brief 无检验地写SPI FLASH
* 必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
* 具有自动换页功能
* @param pBuffer: 数据存储区
* @param WriteAddr: 开始写入的地址(最大32bit)
* @param NumByteToWrite: 要写入的字节数(最大65535)
* @retval 无
*/
void 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 // NumByteToWrite>pageremain
{
pBuffer += pageremain;
WriteAddr += pageremain;
NumByteToWrite -= pageremain; // 减去已经写入了的字节数
if (NumByteToWrite > 256) {
pageremain = 256; // 一次可以写入256字节
} else {
pageremain = NumByteToWrite; // 不够256字节了
}
}
}
}
/**
* @brief 写SPI FLASH
* 具体函数功能请参考W25QXX_Write_NoCheck函数
* @param pBuffer: 数据存储区
* @param WriteAddr: 开始写入的地址(最大32bit)
* @param NumByteToWrite: 要写入的字节数(最大65535)
* @note 该函数带有擦除操作!
* @retval 无
*/
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 = (uint8_t *)malloc(W25QXX_SECTOR_SIZE); // 申请内存
uint8_t *W25QXX_BUF_PTR = W25QXX_BUF; // 使用静态缓冲区
secpos = WriteAddr / W25QXX_SECTOR_SIZE; // 扇区地址
secoff = WriteAddr % W25QXX_SECTOR_SIZE; // 在扇区内的偏移
secremain = W25QXX_SECTOR_SIZE - secoff; // 扇区剩余空间大小
if (NumByteToWrite <= secremain) // 不跨扇区
{
secremain = NumByteToWrite;
}
while (1) {
W25QXX_Read(W25QXX_BUF_PTR, secpos * W25QXX_SECTOR_SIZE,
W25QXX_SECTOR_SIZE); // 读出整个扇区的内容
for (i = 0; i < secremain; i++) // 校验数据
{
if (W25QXX_BUF_PTR[secoff + i] != 0XFF) {
break; // 需要擦除
}
}
if (i < secremain) // 需要擦除
{
W25QXX_Erase_Sector(secpos); // 擦除这个扇区
for (i = 0; i < secremain; i++) // 复制
{
W25QXX_BUF_PTR[secoff + i] = pBuffer[i];
}
W25QXX_Write_NoCheck(W25QXX_BUF_PTR, secpos * W25QXX_SECTOR_SIZE,
W25QXX_SECTOR_SIZE); // 写入整个扇区
} else // 写已经擦除了的,直接写入扇区剩余区间
{
W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain);
}
if (NumByteToWrite == secremain) // 写入结束了
{
break;
} else // 写入未结束
{
secpos++; // 扇区地址增1
secoff = 0; // 偏移位置为0
pBuffer += secremain; // 指针偏移
WriteAddr += secremain; // 写地址偏移
NumByteToWrite -= secremain; // 字节数递减
if (NumByteToWrite > W25QXX_SECTOR_SIZE) {
secremain = W25QXX_SECTOR_SIZE; // 下一个扇区还是写不完
} else {
secremain = NumByteToWrite; // 下一个扇区可以写完
}
}
}
// free(W25QXX_BUF_PTR); // 释放内存
}
/**
* @brief 擦除一个扇区
* @param SectorAddr: 扇区地址 根据实际容量设置
* @retval 无
*/
void W25QXX_Erase_Sector(uint32_t SectorAddr) {
SectorAddr *= W25QXX_SECTOR_SIZE; // 地址对齐
W25QXX_WriteEnable(); // 使能写
W25QXX_Wait_Busy(); // 等待空闲
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_SectorErase4KB); // 发送扇区擦除指令
W25QXX_WriteByte((uint8_t)((SectorAddr) >> 16)); // 发送24bit地址
W25QXX_WriteByte((uint8_t)((SectorAddr) >> 8));
W25QXX_WriteByte((uint8_t)SectorAddr);
W25QXX_CS(1); // 取消片选
W25QXX_Wait_Busy(); // 等待擦除完成
}
/**
* @brief 擦除32KB块
* @param BlockAddr: 块地址
* @retval 无
*/
void W25QXX_Erase_Block32K(uint32_t BlockAddr) {
BlockAddr *= W25QXX_BLOCK_SIZE / 2; // 地址对齐
W25QXX_WriteEnable(); // 使能写
W25QXX_Wait_Busy(); // 等待空闲
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_BlockErase32KB); // 发送32KB块擦除指令
W25QXX_WriteByte((uint8_t)((BlockAddr) >> 16)); // 发送24bit地址
W25QXX_WriteByte((uint8_t)((BlockAddr) >> 8));
W25QXX_WriteByte((uint8_t)BlockAddr);
W25QXX_CS(1); // 取消片选
W25QXX_Wait_Busy(); // 等待擦除完成
}
/**
* @brief 擦除64KB块
* @param BlockAddr: 块地址
* @retval 无
*/
void W25QXX_Erase_Block64K(uint32_t BlockAddr) {
BlockAddr *= W25QXX_BLOCK_SIZE; // 地址对齐
W25QXX_WriteEnable(); // 使能写
W25QXX_Wait_Busy(); // 等待空闲
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_BlockErase64KB); // 发送64KB块擦除指令
W25QXX_WriteByte((uint8_t)((BlockAddr) >> 16)); // 发送24bit地址
W25QXX_WriteByte((uint8_t)((BlockAddr) >> 8));
W25QXX_WriteByte((uint8_t)BlockAddr);
W25QXX_CS(1); // 取消片选
W25QXX_Wait_Busy(); // 等待擦除完成
}
/**
* @brief 擦除整个芯片
* @param 无
* @retval 无
*/
void W25QXX_Erase_Chip(void) {
W25QXX_WriteEnable(); // 使能写
W25QXX_Wait_Busy(); // 等待空闲
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_ChipErase); // 发送片擦除命令
W25QXX_CS(1); // 取消片选
W25QXX_Wait_Busy(); // 等待芯片擦除结束
}
/**
* @brief 等待空闲
* @param 无
* @retval 无
*/
void W25QXX_Wait_Busy(void) {
while ((W25QXX_ReadStatusReg1() & 0x01) == 0x01)
; // 等待BUSY位清空
}
/**
* @brief 使能写
* @param 无
* @retval 无
*/
void W25QXX_WriteEnable(void) {
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_WriteEnable); // 发送写使能
W25QXX_CS(1); // 取消片选
}
/**
* @brief 禁止写
* @param 无
* @retval 无
*/
void W25QXX_WriteDisable(void) {
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_WriteDisable); // 发送写禁止指令
W25QXX_CS(1); // 取消片选
}
/**
* @brief 读取状态寄存器1
* @param 无
* @retval 状态寄存器1的值
*/
uint8_t W25QXX_ReadStatusReg1(void) {
uint8_t status = 0;
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_ReadStatusReg1); // 发送读取状态寄存器1命令
status = W25QXX_ReadByte(); // 读取一个字节
W25QXX_CS(1); // 取消片选
return status;
}
/**
* @brief 读取状态寄存器2
* @param 无
* @retval 状态寄存器2的值
*/
uint8_t W25QXX_ReadStatusReg2(void) {
uint8_t status = 0;
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_ReadStatusReg2); // 发送读取状态寄存器2命令
status = W25QXX_ReadByte(); // 读取一个字节
W25QXX_CS(1); // 取消片选
return status;
}
/**
* @brief 读取状态寄存器3
* @param 无
* @retval 状态寄存器3的值
*/
uint8_t W25QXX_ReadStatusReg3(void) {
uint8_t status = 0;
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_ReadStatusReg3); // 发送读取状态寄存器3命令
status = W25QXX_ReadByte(); // 读取一个字节
W25QXX_CS(1); // 取消片选
return status;
}
/**
* @brief 写入状态寄存器
* @param status: 状态寄存器值
* @retval 无
*/
void W25QXX_WriteStatusReg(uint8_t status) {
W25QXX_WriteEnable(); // 使能写
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_WriteStatusReg1); // 发送写状态寄存器命令
W25QXX_WriteByte(status); // 写入一个字节
W25QXX_CS(1); // 取消片选
}
/**
* @brief W25QXX进入低功耗模式
* @param 无
* @retval 无
*/
void W25QXX_PowerDown(void) {
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_PowerDown); // 发送低功耗命令
W25QXX_CS(1); // 取消片选
HAL_Delay(1); // 等待TPD
}
/**
* @brief 唤醒W25QXX
* @param 无
* @retval 无
*/
void W25QXX_WAKEUP(void) {
W25QXX_CS(0); // 使能器件
W25QXX_WriteByte(W25X_ReleasePowerDown); // 发送唤醒命令
W25QXX_CS(1); // 取消片选
HAL_Delay(1); // 等待TRES1
}