一:芯片知识介绍:
W25X40CL 是 Winbond (华邦) 推出的一款 4Mbit (512K x 8) 容量的串行 NOR Flash 存储器,属于其经典的 SpiFlash 系列。
这款芯片最大特点是具备 Dual SPI 模式,能将读取速度提升至等效 208MHz,非常适合有代码下载、系统启动和少量数据存储需求的嵌入式应用。它目前仍在 大规模量产 状态,市场上货源充足。
以下是其核心参数与功能特性的详细介绍。
容量:4Mbit (4兆位) / 512K x 8 字节
接口:支持标准 SPI 和 Dual SPI
最高时钟频率:104MHz (Dual 模式下等效 208MHz)
工作电压:2.3V ~ 3.6V (宽电压范围,功耗更低)
工作温度:-40℃ ~ +85℃
架构特点:256 字节/页,支持 4KB 扇区擦除、32KB/64KB 块擦除
耐用性:至少 10万次 擦写,数据可保存 20年
封装形式:SO-8、VSOP-8、WSON、USON、PDIP 等多种封装
生产状态:Active (量产中)
存储架构与性能
该芯片的内部存储主要组织如下,结构清晰,使用灵活:
页编程:芯片有 2048 个可编程页,每页大小为 256 字节。每次可编程 1 到 256 字节,典型编程时间(页写入时间)为 0.4 毫秒 至 0.8 毫秒。
灵活擦除:为了让数据管理更灵活,它支持三种不同粒度的擦除方式:
4KB 扇区:共 128 个,适合删除少量数据。32KB 块:共 16 个。
64KB 块:共 8 个。典型块擦除时间在 120 到 150 毫秒 之间。
读性能:即支持标准的 SPI 读取,也支持 Dual SPI 模式。在该模式下,传输速率等效于 208MHz,能实现高效的“连续读取模式”,使微控制器能直接从 Flash 中执行代码 (XIP,Execute in Place),无需复制到内存中。
关键技术与特性
除了基础读写操作,W25X40CL 还有一些对设计和保护很重要的特性:
低功耗设计:非常适合电池供电或低功耗设备。正常工作电流仅约 1mA,掉电模式下功耗更是低至 1µA。
数据保护机制:
软件保护:可通过配置状态寄存器,对特定存储区域设置写保护。
硬件保护:通过 WP# (Write Protect) 引脚,可以硬件级地锁定状态寄存器,防止关键配置被意外修改。
高级功能:支持 UID (唯一标识符) 和 OTP (一次性可编程) 功能,可用于设备序列号、加密密钥存储或防伪认证。
编程/擦除暂停与恢复:在执行长时间擦除操作时,可以暂停该操作去执行其他高优先级任务,之后再恢复擦除,提高了系统的实时响应能力。
二:硬件SPI配置
主要配置SPI的通讯速度,相位,使能中断发送功能。
三:软件代码:3.1 SPI 的初始化void SPI_Init(void)
{
SPI_SwitchP2n(); //选择SPI数据口: SS(P2.2), MOSI(P2.3), MISO(P2.4), SCLK(P2.5)
SPI_MasterMode(); //设置SPI为主机模式
SPI_IgnoreSS(); //忽略SS脚
SPI_DataMSB(); //设置SPI数据顺序为MSB (高位在前)
SPI_SetMode0(); //设置SPI工作模式0 (CPOL=0, CPHA=0)
SPI_SetClockDivider8(); //设置SPI时钟分频
HSSPI_Enable(); //使能SPI高速模式
HSSPI_SetSSSetupTime(3); //设置SS建立时间
HSSPI_SetSSHoldTime(3); //设置SS保持时间
HSSPI_SetSSDeactTime(3); //设置SS空闲时间
SPI_SetIntPriority(0); //设置中断为最低优先级
SPI_EnableInt(); //使能SPI中断
fSPITransBusy = 0; //清除SPI忙标志位
SPI_Enable(); //使能SPI功能
DMA_SPI_AutoSS(); //使能SPI DMA自动控制SS
DMA_SPI_SetAutoSSP22(); //DMA自动控制SS (P2.2)
DMA_SPI_SetAmount(255); //设置SPI DMA发送/接收字节数
DMA_SPI_SetTxAddress(pu8SPIDMATxBuffer); //设置SPI DMA发送缓冲区地址
DMA_SPI_SetRxAddress(pu8SPIDMARxBuffer); //设置SPI DMA接收缓冲区地址
DMA_SPI_ClearFIFO(); //清空SPI DMA FIFO缓冲区
DMA_SPI_ClearFlag(); //清除SPI DMA中断标志
DMA_SPI_EnableTx(); //使能发送数据
DMA_SPI_EnableRx(); //使能接收数据
DMA_SPI_SetBusPriority(0); //设置总线访问为最低优先级
DMA_SPI_SetIntPriority(0); //设置中断为最低优先级
DMA_SPI_EnableInt(); //使能SPI DMA中断
DMA_SPI_Enable(); //使能SPI DMA功能
DMA_SPI_MasterTrigger(); //触发SPI主机DMA
//<<AICUBE_USER_SPI_INITIAL_BEGIN>>
// 在此添加用户初始化代码
//<<AICUBE_USER_SPI_INITIAL_END>>
}3.2 SPI发送一个字节函数:////////////////////////////////////////
// SPI主机模式发送字节函数
// 入口参数: dat (待发送的字节数据)
// 函数返回: 无
////////////////////////////////////////
void SPI_WriteByte(uint8_t dat)
{
fSPITransBusy = 1; //设置发送忙标志
SPI_SendData(dat); //触发主机发送数据
while (fSPITransBusy); //等待发送完成
}3.3 SPI读取一个字节函数:////////////////////////////////////////
// SPI主机模式读取字节函数
// 入口参数: 无
// 函数返回: 读取的字节数据
////////////////////////////////////////
uint8_t SPI_ReadByte(void)
{
fSPITransBusy = 1; //设置读取忙标志
SPI_SendData(0xff); //触发主机读取数据(主机发送时钟信号)
while (fSPITransBusy); //等待读取完成
return SPI_ReadData();
}3.4 发送硬件SPI字节函数存储:void SPI_Write_Nbytes(u32 addr, u8 size)
{
if(size == 0) return;
if(!B_FlashOK) return;
while(DmaFlag); //DMA忙检测
while(CheckFlashBusy() > 0); //Flash忙检测
FlashWriteEnable(); //使能Flash写命令
SPI_CE_Low(); // enable device
SPI_WriteByte(SFC_PAGEPROG); // 发送页编程命令
SPI_WriteByte(((u8 *)&addr)[1]); //设置起始地址
SPI_WriteByte(((u8 *)&addr)[2]);
SPI_WriteByte(((u8 *)&addr)[3]);
DmaFlag = 1;
DMA_SPI_AMT = size-1; //设置传输总字节数:n+1
DMA_SPI_CR |= 0x40; //开始SPI_DMA主模式操作
}3.5 发送硬件SPI字节函数读取部分:/************************************************
从Flash中读取数据
入口参数:
addr : 地址参数
buffer : 缓冲从Flash中读取的数据
size : 数据块大小
出口参数:
无
************************************************/
void SPI_Read_Nbytes(u32 addr, u16 size)
{
if(size == 0) return;
if(!B_FlashOK) return;
while(DmaFlag); //DMA忙检测
while(CheckFlashBusy() > 0); //Flash忙检测
SPI_CE_Low(); //enable device
SPI_WriteByte(SFC_READ); //read command
SPI_WriteByte(((u8 *)&addr)[1]); //设置起始地址
SPI_WriteByte(((u8 *)&addr)[2]);
SPI_WriteByte(((u8 *)&addr)[3]);
DmaFlag = 1;
DMA_SPI_AMT = size-1; //设置传输总字节数:n+1
DMA_SPI_CR |= 0x40; //开始SPI_DMA主模式操作
}3.6 检测外挂的存储芯片IDvoid FlashCheckID(void)
{
uint16_t Temp = 0;
SPI_CE_Low();
SPI_WriteByte(SFC_RDID); //发送读取ID命令
SPI_WriteByte(0x00); //空读3个字节
SPI_WriteByte(0x00);
SPI_WriteByte(0x00);
PM25LV040_ID1 = SPI_ReadByte(); //读取制造商ID1
PM25LV040_ID = SPI_ReadByte(); //读取设备ID
PM25LV040_ID2 = SPI_ReadByte(); //读取制造商ID2
SPI_CE_High();
printf("ID1=%x\r\n",PM25LV040_ID1);
printf("ID=%x\r\n",PM25LV040_ID);
printf("ID2=%x\r\n",PM25LV040_ID2);
if((PM25LV040_ID1 == 0x9d) && (PM25LV040_ID2 == 0x7f)) B_FlashOK = 1; //检测是否为PM25LVxx系列的Flash
else if(PM25LV040_ID == 0x12) B_FlashOK = 2; //检测是否为W25X4x系列的Flash
else if(PM25LV040_ID == 0x13) B_FlashOK = 3; //检测是否为W25X8x系列的Flash
else B_FlashOK = 0;
}四:串口验证如下所示:五:字符串的读写测试:
u8 xdata SPIWRITEBUFFER[50]={"STC32G128 SPI TEST !!!"};
u8 xdata SPIREADBUFFER[50] ;
void SPI_TEST(void)
{
UART1_DMA_Transmit(&SPIWRITEBUFFER,sizeof(SPIWRITEBUFFER));
memcpy(&pu8SPIDMATxBuffer,&SPIWRITEBUFFER,sizeof(SPIWRITEBUFFER));
delay_ms(100);
UART1_DMA_Transmit(&pu8SPIDMATxBuffer,sizeof(SPIWRITEBUFFER));
FlashSectorErase(0x001234);
delay_ms(100);
SPI_Write_Nbytes(0x001234,sizeof(SPIWRITEBUFFER));
delay_ms(100);
SPI_Read_Nbytes(0x001234,sizeof(SPIWRITEBUFFER));
delay_ms(10);
UART1_DMA_Transmit(&pu8SPIDMARxBuffer,sizeof(SPIWRITEBUFFER));
delay_ms(100);
memcpy(&SPIREADBUFFER,&pu8SPIDMARxBuffer,sizeof(SPIWRITEBUFFER));
delay_ms(100);
UART1_DMA_Transmit(&SPIREADBUFFER,sizeof(SPIWRITEBUFFER));
}在实际使用过程中,只有再执行SPI数据读写时候,需要增加适量的延时,要不然程序运行会存在问题。
在执行写操作时候,需要添加一条块擦除函数。
串口验证如下所示:


我要赚赏金
