一:W25Q64芯片介绍:
W25Q64 是一款常见的串行 Flash 存储器芯片,由华邦(Winbond)公司生产。它的存储容量为 64Mb(8MB),采用 SPI(串行外设接口)进行通信,常用于嵌入式系统中存储代码或数据。
二:W25Q64操作注意事项:
W25Q64 的基本操作遵循 SPI 协议,一般流程是:片选拉低 → 发送命令字 → 发送地址(如需)→ 交换数据 → 片选拉高。
2.1:写使能(Write Enable, 06h):在执行任何编程或擦除操作前,必须先发送写使能指令。该指令会将状态寄存器中的写使能锁存(WEL)
2.2:页编程(Page Program, 02h):向指定地址写入数据。一次最多写入256字节(一页),且不能跨页写入。如果写入数据地址会跨页,超出页首地址部分会从当前页的开头覆写8。写入前必须确保目标区域已被擦除。
2.3:扇区擦除(Sector Erase, 20h):擦除指定4KB扇区。擦除操作会将所有位变为1。擦除前需写使能。
2.4:块擦除(Block Erase):提供32KB (52h) 和64KB (D8h) 块擦除选项。
2.5:芯片擦除(Chip Erase, C7h / 60h):擦除整个芯片。谨慎使用。
2.6:读数据(Read Data, 03h):从指定地址开始读取数据。读取操作没有页的限制8。
2.7:读状态寄存器(Read Status Register, 05h):主要用于检查 BUSY 位(S0)和 WEL 位(S1)。在编程、擦除或写状态寄存器期间,BUSY位会置1,此时芯片仅响应读状态寄存器和擦除暂停等少数指令。
读设备ID:如 Read JEDEC ID (9Fh) 可用于识别器件
三:STM32cube MX 软件配置过程:
四:软件代码编程:
重新定义一下SPI发送字节的函数:
uint8_t SPI1_ReadWriteByte(uint8_t TxData)
{
uint8_t Rxdata;
HAL_SPI_TransmitReceive(&hspi1,&TxData,&Rxdata,1, HAL_MAX_DELAY);
return Rxdata; //返回收到的数据
}
4.1 检测有无W25Q芯片
/*******************************************************************************
* Function Name : void WriteBufferTo24C02(int addr,uint8_t *pdata,char length)
* Description :
* Input :
* Output : None
* Return : None
*******************************************************************************/
void W25QXX_Init(void)
{
uint8_t temp;
__HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB时钟
W25QXX_TYPE=W25QXX_ReadID(); //读取FLASH ID.
if(W25QXX_TYPE==W25Q256) //SPI FLASH为W25Q256
{
temp=W25QXX_ReadSR(3); //读取状态寄存器3,判断地址模式
if((temp&0X01)==0) //如果不是4字节地址模式,则进入4字节地址模式
{
FLASH_CS_LOW() ; //选中
SPI1_ReadWriteByte(W25X_Enable4ByteAddr);//发送进入4字节地址模式指令
FLASH_CS_HIGH();; //取消片选
}
}
}
4.2 写入数据
void W25QXX_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t i;
W25QXX_Write_Enable(); //SET WEL
FLASH_CS_LOW() ; //使能器件
SPI1_ReadWriteByte(W25X_PageProgram); //发送写页命令
if(W25QXX_TYPE==W25Q256) //如果是W25Q64的话地址为4字节的,要发送最高8位
{
SPI1_ReadWriteByte((uint8_t)((WriteAddr)>>24));
}
SPI1_ReadWriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址
SPI1_ReadWriteByte((uint8_t)((WriteAddr)>>8));
SPI1_ReadWriteByte((uint8_t)WriteAddr);
for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer[i]);//循环写数
FLASH_CS_HIGH();; //取消片选
W25QXX_Wait_Busy(); //等待写入结束
}
4.3 读取数据
void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
{
uint16_t i;
FLASH_CS_LOW() ; //使能器件
SPI1_ReadWriteByte(W25X_ReadData); //发送读取命令
if(W25QXX_TYPE==W25Q256) //如果是W25Q256的话地址为4字节的,要发送最高8位
{
SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>24));
}
SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>16)); //发送24bit地址
SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>8));
SPI1_ReadWriteByte((uint8_t)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer[i]=SPI1_ReadWriteByte(0XFF); //循环读数
}
FLASH_CS_HIGH();;
}
4.4 测试程序:
void Test_SPI_Funtion(void)
{
WriteBuffer24C02[0]= 0xFA ;
WriteBuffer24C02[1]= 0xFB ;
WriteBuffer24C02[2]= 0xFC ;
WriteBuffer24C02[3]= 0x05 ;
WriteBuffer24C02[4]= 0xC0 ;
WriteBuffer24C02[5]= 0x01 ;
WriteBuffer24C02[6]= 0x02 ;
W25QXX_Write(&WriteBuffer24C02[0],0,7);
HAL_Delay(20);
W25QXX_Read(&ReadBuffer24C02[0],0,7) ;
HAL_UART_Transmit(&huart1, &ReadBuffer24C02[0], 7, 100);
}
当然为了快速验证,将之前操作24C02的buffer 写入到W25Q64里面,然后通过串口输出。
五:验证如下:
六:一些调试SPI的心得如下:
SPI通信中的时钟极性(CPOL)和时钟相位(CPHA)是两个关键参数。它们定义了时钟信号在空闲状态下的电平和数据采样的时机。当时是利用硬件的SPI读取两个芯片,一个是存储芯片(W25Q64),一个是AD芯片(CS5530),由于这个两个芯片的时序不一致,一个是在上升沿读取数据,另外一个是在下降沿读取数据,当时也是调试好久才发现,时序不一致,当初也是刚毕业不久,也没有太多的工作经验,还以为是芯片损坏,现在想起来还是真的小白了。后来工作时间久了才发现,影响SPI通讯不正常的原因有很多,需要注意硬件连接、通信协议与时序、数据传输等方面。
就是在调试这两款SPI从机的时候,当时为了减少硬件的引脚,从而将省出的引脚复用其他的功能,从而导致了自己在软件调试时候遇到了不小的麻烦。当时都使用逻辑分析仪一点点的查出来的。
全部评论