本帖最后由 聪聪哥哥 于 2025-8-24 17:14 编辑
一: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) 可用于识别器件
三:原理图如下所示:
四:软件代码如下所示:
- void SPI_FullDuplex_Config(void)
- {
- SPI_Config_T spiConfig;
- GPIO_Config_T gpioConfig;
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SPI1);
- /* SPI1 gpio configuration */
- gpioConfig.pin = (GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
- gpioConfig.speed = GPIO_SPEED_50MHz;
- gpioConfig.mode = GPIO_MODE_AF_PP;
- GPIO_Config(GPIOA, &gpioConfig);
- /* SPI1 Configuration */
- spiConfig.mode = SPI_MODE_MASTER;
- spiConfig.length = SPI_DATA_LENGTH_8B;
- spiConfig.phase = SPI_CLKPHA_1EDGE;
- spiConfig.polarity = SPI_CLKPOL_LOW;
- spiConfig.nss = SPI_NSS_SOFT;
- spiConfig.firstBit = SPI_FIRSTBIT_MSB;
- spiConfig.direction = SPI_DIRECTION_2LINES_FULLDUPLEX;
- spiConfig.baudrateDiv = SPI_BAUDRATE_DIV_2;
- SPI_Config(SPI1, &spiConfig);
- SPI_EnableSSOutput(SPI1);
- SPI_Enable(SPI1);
- }
硬件的SPI与例程有所不同,
- spiConfig.polarity = SPI_CLKPOL_HIGH;
添加对W25Q64的驱动代码:
通过对SPI1 发送字节的底层驱动函数:
- uint16_t W25QXX_TYPE; //默认是W25Q64
- //SPI1 读写一个字节
- //TxData:要写入的字节
- //返回值:读取到的字节
- uint8_t SPI1_ReadWriteByte(uint8_t TxData)
- {
- uint8_t Rxdata;
- while(!SPI_I2S_ReadStatusFlag(SPI1, SPI_FLAG_TXBE));
- SPI_I2S_TxData(SPI1, TxData);
- while(!SPI_I2S_ReadStatusFlag(SPI1, SPI_FLAG_RXBNE));
- Rxdata = SPI_I2S_RxData(SPI1);
- return Rxdata; //返回收到的数据
- }
写入数据:
- void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
- {
- uint32_t secpos;
- uint16_t secoff;
- uint16_t secremain;
- uint16_t i;
- uint8_t * 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; //下一个扇区可以写完了
- }
- };
- }
读取数据:
- //读取SPI FLASH
- //在指定地址开始读取指定长度的数据
- //pBuffer:数据存储区
- //ReadAddr:开始读取的地址(24bit)
- //NumByteToRead:要读取的字节数(最大65535)
- 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();;
- }
效果仿真如下所示:
可以正常读取到芯片的型号类型:
SPI通信中的时钟极性(CPOL)和时钟相位(CPHA)是两个关键参数。它们定义了时钟信号在空闲状态下的电平和数据采样的时机。当时是利用硬件的SPI读取两个芯片,一个是存储芯片(W25Q64),一个是AD芯片(CS5530),由于这个两个芯片的时序不一致,一个是在上升沿读取数据,另外一个是在下降沿读取数据,当时也是调试好久才发现,时序不一致,当初也是刚毕业不久,也没有太多的工作经验,还以为是芯片损坏,现在想起来还是真的小白了。后来工作时间久了才发现,影响SPI通讯不正常的原因有很多,需要注意硬件连接、通信协议与时序、数据传输等方面。
|