[APM32F4] 【APM32F402R Micro-EVB】09:硬件SPI驱动W25Q64

[复制链接]
聪聪哥哥 发表于 2025-8-25 16:52 | 显示全部楼层 |阅读模式
本帖最后由 聪聪哥哥 于 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) 可用于识别器件
三:原理图如下所示:
09-1.png
四:软件代码如下所示:
  1. void SPI_FullDuplex_Config(void)
  2. {
  3.     SPI_Config_T spiConfig;
  4.     GPIO_Config_T gpioConfig;
  5.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
  6.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SPI1);

  7.     /* SPI1 gpio configuration */
  8.     gpioConfig.pin = (GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
  9.     gpioConfig.speed = GPIO_SPEED_50MHz;
  10.     gpioConfig.mode = GPIO_MODE_AF_PP;
  11.     GPIO_Config(GPIOA, &gpioConfig);

  12.     /* SPI1 Configuration */
  13.     spiConfig.mode = SPI_MODE_MASTER;
  14.     spiConfig.length = SPI_DATA_LENGTH_8B;
  15.     spiConfig.phase = SPI_CLKPHA_1EDGE;
  16.     spiConfig.polarity = SPI_CLKPOL_LOW;
  17.     spiConfig.nss = SPI_NSS_SOFT;
  18.     spiConfig.firstBit = SPI_FIRSTBIT_MSB;
  19.     spiConfig.direction = SPI_DIRECTION_2LINES_FULLDUPLEX;
  20.     spiConfig.baudrateDiv = SPI_BAUDRATE_DIV_2;
  21.     SPI_Config(SPI1, &spiConfig);

  22.     SPI_EnableSSOutput(SPI1);
  23.     SPI_Enable(SPI1);
  24. }
硬件的SPI与例程有所不同,
  1.     spiConfig.polarity = SPI_CLKPOL_HIGH;
添加对W25Q64的驱动代码:
通过对SPI1 发送字节的底层驱动函数:
  1. uint16_t W25QXX_TYPE;        //默认是W25Q64
  2. //SPI1 读写一个字节
  3. //TxData:要写入的字节
  4. //返回值:读取到的字节
  5. uint8_t SPI1_ReadWriteByte(uint8_t TxData)
  6. {
  7.     uint8_t Rxdata;
  8.                 while(!SPI_I2S_ReadStatusFlag(SPI1, SPI_FLAG_TXBE));
  9.                 SPI_I2S_TxData(SPI1, TxData);
  10.                 while(!SPI_I2S_ReadStatusFlag(SPI1, SPI_FLAG_RXBNE));
  11.                 Rxdata = SPI_I2S_RxData(SPI1);
  12.            return Rxdata;                              //返回收到的数据               
  13. }
写入数据:
  1. void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)   
  2. {
  3.         uint32_t secpos;
  4.         uint16_t secoff;
  5.         uint16_t secremain;           
  6.          uint16_t i;   
  7.         uint8_t * W25QXX_BUF;         
  8.            W25QXX_BUF=W25QXX_BUFFER;            
  9.          secpos=WriteAddr/4096;//扇区地址  
  10.         secoff=WriteAddr%4096;//在扇区内的偏移
  11.         secremain=4096-secoff;//扇区剩余空间大小   
  12.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
  13.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
  14.         while(1)
  15.         {        
  16.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
  17.                 for(i=0;i<secremain;i++)//校验数据
  18.                 {
  19.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            
  20.                 }
  21.                 if(i<secremain)//需要擦除
  22.                 {
  23.                         W25QXX_Erase_Sector(secpos);//擦除这个扇区
  24.                         for(i=0;i<secremain;i++)           //复制
  25.                         {
  26.                                 W25QXX_BUF[i+secoff]=pBuffer[i];         
  27.                         }
  28.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  

  29.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    
  30.                 if(NumByteToWrite==secremain)break;//写入结束了
  31.                 else//写入未结束
  32.                 {
  33.                         secpos++;//扇区地址增1
  34.                         secoff=0;//偏移位置为0         

  35.                            pBuffer+=secremain;  //指针偏移
  36.                         WriteAddr+=secremain;//写地址偏移           
  37.                            NumByteToWrite-=secremain;                                //字节数递减
  38.                         if(NumByteToWrite>4096)secremain=4096;        //下一个扇区还是写不完
  39.                         else secremain=NumByteToWrite;                        //下一个扇区可以写完了
  40.                 }         
  41.         };         
  42. }
读取数据:
  1. //读取SPI FLASH  
  2. //在指定地址开始读取指定长度的数据
  3. //pBuffer:数据存储区
  4. //ReadAddr:开始读取的地址(24bit)
  5. //NumByteToRead:要读取的字节数(最大65535)
  6. void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)   
  7. {
  8.          uint16_t i;                                                                                       
  9.         FLASH_CS_LOW() ;                           //使能器件   
  10.     SPI1_ReadWriteByte(W25X_ReadData);      //发送读取命令  
  11.     if(W25QXX_TYPE==W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
  12.     {
  13.         SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>24));   
  14.     }
  15.     SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>16));   //发送24bit地址   
  16.     SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
  17.     SPI1_ReadWriteByte((uint8_t)ReadAddr);   
  18.     for(i=0;i<NumByteToRead;i++)
  19.         {
  20.         pBuffer[i]=SPI1_ReadWriteByte(0XFF);    //循环读数  
  21.     }
  22.                 FLASH_CS_HIGH();;                                                            
  23. }
效果仿真如下所示:
可以正常读取到芯片的型号类型:
09-2.png
07-3.jpg
SPI通信中的时钟极性(CPOL)和时钟相位(CPHA)是两个关键参数。它们定义了时钟信号在空闲状态下的电平和数据采样的时机。当时是利用硬件的SPI读取两个芯片,一个是存储芯片(W25Q64),一个是AD芯片(CS5530),由于这个两个芯片的时序不一致,一个是在上升沿读取数据,另外一个是在下降沿读取数据,当时也是调试好久才发现,时序不一致,当初也是刚毕业不久,也没有太多的工作经验,还以为是芯片损坏,现在想起来还是真的小白了。后来工作时间久了才发现,影响SPI通讯不正常的原因有很多,需要注意硬件连接、通信协议与时序、数据传输等方面。


空灵回声 发表于 2025-8-26 10:20 | 显示全部楼层
硬件的SPI读取两个芯片,一个是存储芯片(W25Q64),一个是AD芯片(CS5530),由于这个两个芯片的时序不一致,一个是在上升沿读取数据,另外一个是在下降沿读取数据,

学习到了。我直的也以为只有mode 0,也一直没有遇到其它模式的SPI Flash呢
您需要登录后才可以回帖 登录 | 注册

本版积分规则

93

主题

238

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部