Files
2025-06-27 00:32:57 +08:00

369 lines
11 KiB
C
Raw Permalink 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.

#include "stm32f4xx.h"
#include "flash.h"
#include "mymem.h"
#include "base.h"
//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK战舰STM32开发板
//W25Q64 代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/9
//版本V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//////////////////////////////////////////////////////////////////////////////////
static u16 SPI_FLASH_Type=W25Q64;//默认就是25Q64
#define W25Q_MOSI PAout(4)
#define W25Q_CLK PAout(5)
#define W25Q_MISO PAin(7)
#define W25Q_CS PCout(3)
void delay_us(u32 us)
{
// us=us*5;
// while (us--);
}
//4Kbytes为一个Sector
//16个扇区为1个Block
//W25X16
//容量为2M字节,共有32个Block,512个Sector
//初始化SPI FLASH的IO口
void SPI_Flash_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
RCC_AHB1PeriphClockCmd ( RCC_AHB1Periph_GPIOA, ENABLE); //初始化GPIOG时钟
RCC_AHB1PeriphClockCmd ( RCC_AHB1Periph_GPIOC, ENABLE); //初始化GPIOD时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度选择
//初始化 LED1 引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度选择
//初始化 LED1 引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_FLASH_Type=SPI_Flash_ReadID();//读取FLASH ID.
}
u8 W25Q_ReadWriteByte(u8 da)
{
u8 i=0;
u8 ret=0;
for (i=0;i<8;i++)
{
W25Q_CLK=0;
delay_us(5);
if (da&0x80)
W25Q_MOSI=1;
else
W25Q_MOSI=0;
da<<=1;
delay_us(5);
W25Q_CLK=1;
delay_us(5);
ret<<=1;
ret|=W25Q_MISO;
delay_us(5);
}
return ret;
}
//读取SPI_FLASH的状态寄存器
//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 SPI_Flash_ReadSR(void)
{
u8 byte=0;
W25Q_CS=0; //使能器件
W25Q_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令
byte=W25Q_ReadWriteByte(0Xff); //读取一个字节
W25Q_CS=1; //取消片选
return byte;
}
//写SPI_FLASH状态寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
void SPI_FLASH_Write_SR(u8 sr)
{
W25Q_CS=0; //使能器件
W25Q_ReadWriteByte(W25X_WriteStatusReg); //发送写取状态寄存器命令
W25Q_ReadWriteByte(sr); //写入一个字节
W25Q_CS=1; //取消片选
}
//SPI_FLASH写使能
//将WEL置位
void SPI_FLASH_Write_Enable(void)
{
W25Q_CS=0; //使能器件
W25Q_ReadWriteByte(W25X_WriteEnable); //发送写使能
W25Q_CS=1; //取消片选
}
//SPI_FLASH写禁止
//将WEL清零
void SPI_FLASH_Write_Disable(void)
{
W25Q_CS=0; //使能器件
W25Q_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令
W25Q_CS=1; //取消片选
}
//读取芯片ID W25X16的ID:0XEF14
u16 SPI_Flash_ReadID(void)
{
u16 Temp = 0;
W25Q_CS=0;
W25Q_ReadWriteByte(0x90);//发送读取ID命令
W25Q_ReadWriteByte(0x00);
W25Q_ReadWriteByte(0x00);
W25Q_ReadWriteByte(0x00);
Temp|=W25Q_ReadWriteByte(0xFF)<<8;
Temp|=W25Q_ReadWriteByte(0xFF);
W25Q_CS=1;
return Temp;
}
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void SPI_Flash_Read(u8* pBuffer,u32 ReadAddr,u32 NumByteToRead)
{
u16 i;
W25Q_CS=0; //使能器件
W25Q_ReadWriteByte(W25X_ReadData); //发送读取命令
W25Q_ReadWriteByte((u8)((ReadAddr)>>16)); //发送24bit地址
W25Q_ReadWriteByte((u8)((ReadAddr)>>8));
W25Q_ReadWriteByte((u8)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer[i]=W25Q_ReadWriteByte(0XFF); //循环读数
}
W25Q_CS=1; //取消片选
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void SPI_Flash_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i;
SPI_FLASH_Write_Enable(); //SET WEL
W25Q_CS=0; //使能器件
W25Q_ReadWriteByte(W25X_PageProgram); //发送写页命令
W25Q_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址
W25Q_ReadWriteByte((u8)((WriteAddr)>>8));
W25Q_ReadWriteByte((u8)WriteAddr);
for(i=0;i<NumByteToWrite;i++)W25Q_ReadWriteByte(pBuffer[i]);//循环写数
W25Q_CS=1; //取消片选
SPI_Flash_Wait_Busy(); //等待写入结束
}
//无检验写SPI FLASH
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void SPI_Flash_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u32 NumByteToWrite)
{
u16 pageremain;
pageremain=256-WriteAddr%256; //单页剩余的字节数
if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
while(1)
{
SPI_Flash_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 SPI_FLASH_BUF[4096];
void SPI_Flash_Write(u8* pBuffer,u32 WriteAddr,u32 NumByteToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
u8 *SPI_FLASH_BUF=mymalloc(4096);
if (SPI_FLASH_BUF==0)
{
return;
}
mymemset(SPI_FLASH_BUF,0xaa,4096);
secpos=WriteAddr/4096;//扇区地址 0~511 for w25x16
secoff=WriteAddr%4096;//在扇区内的偏移
secremain=4096-secoff;//扇区剩余空间大小
if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
while(1)
{
SPI_Flash_Read(SPI_FLASH_BUF,secpos*4096,4096);//读出整个扇区的内容
for(i=0;i<secremain;i++)//校验数据
{
if(SPI_FLASH_BUF[secoff+i]!=0XFF)break;//需要擦除
}
if(i<secremain)//需要擦除
{
SPI_Flash_Erase_Sector(secpos);//擦除这个扇区
for(i=0;i<secremain;i++) //复制
{
SPI_FLASH_BUF[i+secoff]=pBuffer[i];
}
SPI_Flash_Write_NoCheck(SPI_FLASH_BUF,secpos*4096,4096);//写入整个扇区
}else SPI_Flash_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; //下一个扇区可以写完了
}
};
myfree(SPI_FLASH_BUF);
}
//擦除整个芯片
//整片擦除时间:
//W25X16:25s
//W25X32:40s
//W25X64:40s
//等待时间超长...
void SPI_Flash_Erase_Chip(void)
{
SPI_FLASH_Write_Enable(); //SET WEL
SPI_Flash_Wait_Busy();
W25Q_CS=0; //使能器件
W25Q_ReadWriteByte(W25X_ChipErase); //发送片擦除命令
W25Q_CS=1; //取消片选
SPI_Flash_Wait_Busy(); //等待芯片擦除结束
}
//擦除一个扇区
//Dst_Addr:扇区地址 0~511 for w25x16
//擦除一个山区的最少时间:150ms
void SPI_Flash_Erase_Sector(u32 Dst_Addr)
{
//Dst_Addr*=4096;
SPI_FLASH_Write_Enable(); //SET WEL
SPI_Flash_Wait_Busy();
W25Q_CS=0; //使能器件
W25Q_ReadWriteByte(W25X_SectorErase); //发送扇区擦除指令
W25Q_ReadWriteByte((u8)((Dst_Addr)>>16)); //发送24bit地址
W25Q_ReadWriteByte((u8)((Dst_Addr)>>8));
W25Q_ReadWriteByte((u8)Dst_Addr);
W25Q_CS=1; //取消片选
SPI_Flash_Wait_Busy(); //等待擦除完成
}
//等待空闲
void SPI_Flash_Wait_Busy(void)
{
while ((SPI_Flash_ReadSR()&0x01)==0x01); // 等待BUSY位清空
}
//进入掉电模式
void SPI_Flash_PowerDown(void)
{
W25Q_CS=0; //使能器件
W25Q_ReadWriteByte(W25X_PowerDown); //发送掉电命令
W25Q_CS=1; //取消片选
delay_us(3); //等待TPD
}
//唤醒
void SPI_Flash_WAKEUP(void)
{
W25Q_CS=0; //使能器件
W25Q_ReadWriteByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB
W25Q_CS=1; //取消片选
delay_us(3); //等待TRES1
}
u16 SPI_Flash_GetType (void)
{
return SPI_FLASH_Type;
}