W25Q128.c
u16 W25QXX_TYPE;//FLASH型号
//FLASH初始化
void W25QXX_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//1. 使能GPIOB与GPIOG时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//GPIOB
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);//GPIOG
//2. 初始化GPIOF
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;//普通输出
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14;//PB14输出
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//内部上拉
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//100MHz
//应用设置
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;//PG7
//应用设置
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7连接到NRF,会同时连接到SPI,令PG7输出1,防止NRF干扰SPI FLASH通信
W25QXX_CS=1;//不选中FLASH
SPI1_init();//初始化SPI
SPI1_set_speed(SPI_BaudRatePrescaler_2);//1/2分频,时钟频率为42MHz,高速模式
W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID
}
//读取FLASH ID
u16 W25QXX_ReadID(void)
{
u16 ID_Temp=0;
W25QXX_CS=0;//片选
SPI1_RW_byte(0x90);//发送读取ID命令
SPI1_RW_byte(0x00);
SPI1_RW_byte(0x00);
SPI1_RW_byte(0x00);
ID_Temp |= SPI1_RW_byte(0xFF)<<8;
ID_Temp |= SPI1_RW_byte(0xFF);
W25QXX_CS=0;//取消片选
return ID_Temp;
}
//读取状态寄存器
/*
BIT 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为空闲
默认0
*/
u8 W25QXX_ReadSR(void)
{
u8 byte=0;
W25QXX_CS=0;//片选
SPI1_RW_byte(W25X_ReadStatusReg);//发送读取寄存器命令
byte=SPI1_RW_byte(0xff);//读取一个字节
W25QXX_CS=1;//取消片选
return byte;
}
//写状态寄存器
//只能写入SPR、TB、BP2、BP1、BP0(bit 7,5,4,3,2)
void W25QXX_Write_SR(u8 sr)
{
W25QXX_CS=0;//片选
SPI1_RW_byte(W25X_WriteStatusReg);//发送写寄存器命令
SPI1_RW_byte(sr);//读取一个字节
W25QXX_CS=1;//取消片选
}
//写使能 将WEL置位
void W25QXX_Write_Enable(void)
{
W25QXX_CS=0;//片选
SPI1_RW_byte(W25X_WriteEnable);//发送写使能命令
W25QXX_CS=1;//取消片选
}
//写保护 将WEL清零
void W25QXX_Write_Disable(void)
{
W25QXX_CS=0;//片选
SPI1_RW_byte(W25X_WriteDisable);//发送写禁止命令
W25QXX_CS=1;//取消片选
}
//等待空闲
void W25QXX_Wait_Busy(void)
{
while((W25QXX_ReadSR()&0x01)==0x01);
}
//进入掉电模式
void W25QXX_PowerDown(void)
{
W25QXX_CS=0;//片选
SPI1_RW_byte(W25X_PowerDown);//发送掉电命令
W25QXX_CS=1;//取消片选
delay_us(3);//等待TPD
}
//唤醒
void W25QXX_WAKEUP(void)
{
W25QXX_CS=0;//片选
SPI1_RW_byte(W25X_ReleasePowerDown);//发送唤醒命令
W25QXX_CS=1;//取消片选
delay_us(3);
}
//按页写入FLASH
//在指定地址开始写入最大256字节的数据
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i;
W25QXX_Write_Enable();//WEL置位
W25QXX_CS=0;//片选
SPI1_RW_byte(W25X_PageProgram);//发送写页命令
SPI1_RW_byte((u8)((WriteAddr)>>16));//发送24位地址
SPI1_RW_byte((u8)((WriteAddr)>>8));
SPI1_RW_byte((u8)WriteAddr);
for(i=0;i<NumByteToWrite;i++)//循环写入
SPI1_RW_byte(pBuffer[i]);
W25QXX_CS=1;//取消片选
W25QXX_Wait_Busy();//等待写入结束
}
//无保护写入FLASH
//必须确保所写地址内数据为0xFF
//可自动换页
//在指定地址开始写入指定长度的数据,但要确保地址不越界
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 page_remain;
page_remain=256-WriteAddr%256;//单页剩余的字节数
if(NumByteToWrite<=page_remain)//待写入字节不大于单页剩余字节
page_remain=NumByteToWrite;
while(1)
{
W25QXX_Write_Page(pBuffer,WriteAddr,page_remain);
if(NumByteToWrite==page_remain)
break;//写入结束
else//若待写入字节数比单页剩余字节数大
{
pBuffer+=page_remain;
WriteAddr+=page_remain;
NumByteToWrite-=page_remain;//减去已经写入的字节数
if(NumByteToWrite>256)//若剩余字节数大于一页字节数
page_remain=256;//下页能写满
else//若不够256个字节
page_remain=NumByteToWrite;//下页不能写满
}
}
}
//读取SPI FLASH
//在指定地址读取指定长度的数据
//pBuffer:数据储存区
//ReadAddr:开始读取的地址(24位)
//NumByteToRead:要读取的最大字节数(<=65535)
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
W25QXX_CS=0;//片选
SPI1_RW_byte(W25X_ReadData);//发送读取命令
SPI1_RW_byte((u8)((ReadAddr)>>16));//发送24位地址
SPI1_RW_byte((u8)((ReadAddr)>>8));
SPI1_RW_byte((u8)ReadAddr);
for(i=0;i<NumByteToRead;i++)
pBuffer[i]=SPI1_RW_byte(0xFF);//循环从FLASH中读数
W25QXX_CS=1;//取消片选
}
//写入FLASH
//在指定地址带擦除地开始写入指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始写入的地址(24位)
//NumByteToRead:要写入的最大字节数(<=65535)
u8 W25QXX_BUFFER[4096];//缓存:保存芯片内不为空的数据
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u32 sec_pos;
u16 sec_off;
u16 sec_remain;
u16 i;
u8* W25QXX_BUF;
W25QXX_BUF=W25QXX_BUFFER;
sec_pos=WriteAddr/4096;//扇区基地址
sec_off=WriteAddr%4096;//偏移地址
sec_remain=4096-sec_off;//扇区剩余空间
if(NumByteToWrite<sec_remain)
sec_remain=NumByteToWrite;//剩余地址不大于4096个字节
while(1)//反复执行直到完成擦除
{
W25QXX_Read(W25QXX_BUF,sec_pos*4096,4096);//读出全扇区内容
for(i=0;i<sec_remain;i++)
{
if(W25QXX_BUF[sec_off+i]!=0xFF)//若存在非空地址
break;//需要擦除
}
if(i<sec_remain)//如果需要擦除
{
W25QXX_Erase_Sector(sec_pos);//那么擦除这个扇区
for(i=0;i<sec_remain;i++)
{
W25QXX_BUF[sec_off+i]=pBuffer[i];//指针指向地址为扇区基地址+偏移地址
}
W25QXX_Write_NoCheck(W25QXX_BUF,sec_pos*4096,4096);//写入需要的部分
}
else//如果不需要擦除(检查到的部分都为空)
W25QXX_Write_NoCheck(W25QXX_BUF,sec_pos*4096,4096);//直接写入扇区区间
if(NumByteToWrite==sec_remain)//若待写字节数等于扇区剩余字节数(写完惹)
break;//结束写入
else
{
sec_pos++;//扇区地址+1
sec_off=0;//偏移地址为0,定位到扇区头部
pBuffer+=sec_remain;//指针偏移
WriteAddr+=sec_remain;//写入地址偏移
NumByteToWrite-=sec_remain;//待写字节数递减
if(NumByteToWrite>4096)//若待写字节数比一个扇区空间大
sec_remain=4096;//下个扇区还是写不完
else
sec_remain=NumByteToWrite;//下个扇区就可以写完
}
}
}
//整片擦除
//注意:等待时间较长
void W25QXX_Erase_Chip(void)
{
printf("ERASE ALL CHIP!");//显示警告
W25QXX_Write_Enable();//WEL置位并等待
W25QXX_Wait_Busy();
W25QXX_CS=0;//片选
SPI1_RW_byte(W25X_ChipErase);
W25QXX_CS=1;//取消片选
W25QXX_Wait_Busy();
}
//擦除一个扇区
//Dst_Addr为目标扇区地址
//擦除一个扇区最少用时150ms
void W25QXX_Erase_Sector(u32 Dst_Addr)
{
printf("%x will be erase!\r\n",Dst_Addr);//显示警告
Dst_Addr*=4096;
W25QXX_Write_Enable();//WEL置位并等待
W25QXX_Wait_Busy();
W25QXX_CS=0;//片选
SPI1_RW_byte(W25X_SectorErase);
SPI1_RW_byte((u8)((Dst_Addr)>>16));
SPI1_RW_byte((u8)((Dst_Addr)>>8));
SPI1_RW_byte((u8)Dst_Addr);
W25QXX_CS=1;//取消片选
W25QXX_Wait_Busy();
}
|