#include "w25qxx.h"
#include "spi.h"
#include "delay.h"
u16 W25QXX_TYPE=W25Q64; //默認是W25Q128
//4Kbytes為一個Sector
//16個扇區為1個Block
//W25Q128
//容量為16M字節,共有128個Block,4096個Sector
//初始化SPI FLASH的IO口
void W25QXX_Init(void)
{
RCC->AHB1ENR|=1<<1; //使能PORTB時鐘
RCC->AHB1ENR|=1<<6; //使能PORTG時鐘
GPIOB->MODER |=(0X1<<(2*14)); //配置成復用輸出
GPIOB->OTYPER &=~(1<<14); //配置成輸出推挽
GPIOB->OSPEEDR &=~(0X3<<(2*14)); //清零
GPIOB->OSPEEDR |=(0X3<<(2*14)); //配置成輸出速率100M
GPIOB->PUPDR &=~(0X3<<28); //清零
GPIOB->PUPDR |=(0X1<<14); //管腳初始化時輸出高電平
GPIOB->MODER |=(0X1<<(2*7)); //配置成復用輸出
GPIOB->OTYPER &=~(1<<7); //配置成輸出推挽
GPIOB->OSPEEDR &=~(0X3<<(2*7)); //清零
GPIOB->OSPEEDR |=(0X3<<(2*7)); //配置成輸出速率100M
GPIOB->PUPDR &=~(0X3<<14); //清零
GPIOB->PUPDR |=(0X1<<7); //管腳初始化時輸出高電平
GPIOG->ODR|=1<<7; //PG7輸出1,防止NRF干擾SPI FLASH的通信
GPIOB->ODR |= 1<<14; //SPI FLASH不選中
SPI1_Init(); //初始化SPI
SPI1_SetSpeed(SPI_SPEED_2); //設置為42M時鐘(超頻了一點點),高速模式
W25QXX_TYPE=W25QXX_ReadID(); //讀取FLASH ID.
}
//讀取W25QXX的狀態寄存器
//BIT7 6 5 4 3 2 1 0
//SPR RV TB BP2 BP1 BP0 WEL BUSY
//SPR:默認0,狀態寄存器保護位,配合WP使用
//TB,BP2,BP1,BP0:FLASH區域寫保護設置
//WEL:寫使能鎖定
//BUSY:忙標記位(1,忙;0,空閑)
//默認:0x00
u8 W25QXX_ReadSR(void)
{
u8 byte=0;
GPIOB->ODR &= ~(1<<14); //使能器件
SPI1_ReadWriteByte(W25X_ReadStatusReg); //發送讀取狀態寄存器命令
byte=SPI1_ReadWriteByte(0Xff); //讀取一個字節
GPIOB->ODR |= 1<<14; //取消片選
return byte;
}
//寫W25QXX狀態寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以寫!!!
void W25QXX_Write_SR(u8 sr)
{
GPIOB->ODR &= ~(1<<14); //使能器件
SPI1_ReadWriteByte(W25X_WriteStatusReg); //發送寫取狀態寄存器命令
SPI1_ReadWriteByte(sr); //寫入一個字節
GPIOB->ODR |= 1<<14; //取消片選
}
//W25QXX寫使能
//將WEL置位
void W25QXX_Write_Enable(void)
{
GPIOB->ODR &= ~(1<<14); //使能器件
SPI1_ReadWriteByte(W25X_WriteEnable); //發送寫使能
GPIOB->ODR |= 1<<14; //取消片選
}
//W25QXX寫禁止
//將WEL清零
void W25QXX_Write_Disable(void)
{
GPIOB->ODR &= ~(1<<14); //使能器件
SPI1_ReadWriteByte(W25X_WriteDisable); //發送寫禁止指令
GPIOB->ODR |= 1<<14; //取消片選
}
//讀取芯片ID
//返回值如下:
//0XEF13,表示芯片型號為W25Q80
//0XEF14,表示芯片型號為W25Q16
//0XEF15,表示芯片型號為W25Q32
//0XEF16,表示芯片型號為W25Q64
//0XEF17,表示芯片型號為W25Q128
u16 W25QXX_ReadID(void)
{
u16 Temp = 0;
GPIOB->ODR &= ~(1<<14);
SPI1_ReadWriteByte(0x90);//發送讀取ID命令
SPI1_ReadWriteByte(0x00);
SPI1_ReadWriteByte(0x00);
SPI1_ReadWriteByte(0x00);
Temp|=SPI1_ReadWriteByte(0xFF)<<8;
Temp|=SPI1_ReadWriteByte(0xFF);
GPIOB->ODR |= 1<<14;
return Temp;
}
//讀取SPI FLASH
//在指定地址開始讀取指定長度的數據
//pBuffer:數據存儲區
//ReadAddr:開始讀取的地址(24bit)
//NumByteToRead:要讀取的字節數(最大65535)
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
GPIOB->ODR &= ~(1<<14); //使能器件
SPI1_ReadWriteByte(W25X_ReadData); //發送讀取命令
SPI1_ReadWriteByte((u8)((ReadAddr)>>16)); //發送24bit地址
SPI1_ReadWriteByte((u8)((ReadAddr)>>8));
SPI1_ReadWriteByte((u8)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer[i]=SPI1_ReadWriteByte(0XFF); //循環讀數
}
GPIOB->ODR |= 1<<14;
}
//SPI在一頁(0~65535)內寫入少于256個字節的數據
//在指定地址開始寫入最大256字節的數據
//pBuffer:數據存儲區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的字節數(最大256),該數不應該超過該頁的剩余字節數!!!
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i;
W25QXX_Write_Enable(); //SET WEL
GPIOB->ODR &= ~(1<<14); //使能器件
SPI1_ReadWriteByte(W25X_PageProgram); //發送寫頁命令
SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //發送24bit地址
SPI1_ReadWriteByte((u8)((WriteAddr)>>8));
SPI1_ReadWriteByte((u8)WriteAddr);
for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer[i]);//循環寫數
GPIOB->ODR |= 1<<14; //取消片選
W25QXX_Wait_Busy(); //等待寫入結束
}
//無檢驗寫SPI FLASH
//必須確保所寫的地址范圍內的數據全部為0XFF,否則在非0XFF處寫入的數據將失敗!
//具有自動換頁功能
//在指定地址開始寫入指定長度的數據,但是要確保地址不越界!
//pBuffer:數據存儲區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的字節數(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 pageremain;
pageremain=256-WriteAddr%256; //單頁剩余的字節數
if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256個字節
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個字節了
}
};
}
//寫SPI FLASH
//在指定地址開始寫入指定長度的數據
//該函數帶擦除操作!
//pBuffer:數據存儲區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的字節數(最大65535)
u8 W25QXX_BUFFER[4096];
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
u8 * W25QXX_BUF;
W25QXX_BUF=W25QXX_BUFFER;
secpos=WriteAddr/4096;//扇區地址
secoff=WriteAddr%4096;//在扇區內的偏移
secremain=4096-secoff;//扇區剩余空間大小
//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//測試用
if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096個字節
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++;//扇區地址增1
secoff=0;//偏移位置為0
pBuffer+=secremain; //指針偏移
WriteAddr+=secremain;//寫地址偏移
NumByteToWrite-=secremain; //字節數遞減
if(NumByteToWrite>4096)secremain=4096; //下一個扇區還是寫不完
else secremain=NumByteToWrite; //下一個扇區可以寫完了
}
};
}
//擦除整個芯片
//等待時間超長...
void W25QXX_Erase_Chip(void)
{
W25QXX_Write_Enable(); //SET WEL
W25QXX_Wait_Busy();
GPIOB->ODR &= ~(1<<14); //使能器件
SPI1_ReadWriteByte(W25X_ChipErase); //發送片擦除命令
GPIOB->ODR |= 1<<14; //取消片選
W25QXX_Wait_Busy(); //等待芯片擦除結束
}
//擦除一個扇區
//Dst_Addr:扇區地址 根據實際容量設置
//擦除一個山區的最少時間:150ms
void W25QXX_Erase_Sector(u32 Dst_Addr)
{
//監視falsh擦除情況,測試用
//printf("fe:%x\r\n",Dst_Addr);
Dst_Addr*=4096;
W25QXX_Write_Enable(); //SET WEL
W25QXX_Wait_Busy();
GPIOB->ODR &= ~(1<<14); //使能器件
SPI1_ReadWriteByte(W25X_SectorErase); //發送扇區擦除指令
SPI1_ReadWriteByte((u8)((Dst_Addr)>>16)); //發送24bit地址
SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));
SPI1_ReadWriteByte((u8)Dst_Addr);
GPIOB->ODR |= 1<<14; //取消片選
W25QXX_Wait_Busy(); //等待擦除完成
}
//等待空閑
void W25QXX_Wait_Busy(void)
{
while((W25QXX_ReadSR()&0x01)==0x01); // 等待BUSY位清空
}
//進入掉電模式
void W25QXX_PowerDown(void)
{
GPIOB->ODR &= ~(1<<14); //使能器件
SPI1_ReadWriteByte(W25X_PowerDown); //發送掉電命令
GPIOB->ODR |= 1<<14; //取消片選
Delay_ms(3); //等待TPD
}
//喚醒
void W25QXX_WAKEUP(void)
{
GPIOB->ODR &= ~(1<<14); //使能器件
SPI1_ReadWriteByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB
GPIOB->ODR |= 1<<14; //取消片選
Delay_ms(3); //等待TRES1
}
//以上是c文件 下面的是h文件
#ifndef __W25QXX_H
#define __W25QXX_H
#include "stm32f4xx.h"
//////////////////////////////////////////////////////////////////////////////////
//W25X系列/Q系列芯片列表
//W25Q80 ID 0XEF13
//W25Q16 ID 0XEF14
//W25Q32 ID 0XEF15
//W25Q64 ID 0XEF16
//W25Q128 ID 0XEF17
#define W25Q80 0XEF13
#define W25Q16 0XEF14
#define W25Q32 0XEF15
#define W25Q64 0XEF16
#define W25Q128 0XEF17
extern u16 W25QXX_TYPE; //定義W25QXX芯片型號
#define W25QXX_CS PBout(14) //W25QXX的片選信號
//////////////////////////////////////////////////////////////////////////////////
//指令表
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#define W25X_SectorErase 0x20
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F
void W25QXX_Init(void);
u16 W25QXX_ReadID(void); //讀取FLASH ID
u8 W25QXX_ReadSR(void); //讀取狀態寄存器
void W25QXX_Write_SR(u8 sr); //寫狀態寄存器
void W25QXX_Write_Enable(void); //寫使能
void W25QXX_Write_Disable(void); //寫保護
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead); //讀取flash
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//寫入flash
void W25QXX_Erase_Chip(void); //整片擦除
void W25QXX_Erase_Sector(u32 Dst_Addr); //扇區擦除
void W25QXX_Wait_Busy(void); //等待空閑
void W25QXX_PowerDown(void); //進入掉電模式
void W25QXX_WAKEUP(void); //喚醒
#endif
|