OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3023|回复: 0

请教W25Q128能读取ID,但写数据不成功。附波形

[复制链接]

0

主题

0

帖子

0

精华

新手入门

积分
4
金钱
4
注册时间
2019-7-7
在线时间
1 小时
发表于 2022-3-19 11:04:44 | 显示全部楼层 |阅读模式
本帖最后由 hero245 于 2022-3-19 11:07 编辑

用的是原子哥的例程
微信截图_20220319103905.png


  1. #include "W25Q128.h"




  2. uint8_t  SPIEERPOM[10]={1,2,3,4,5,6,7,8,9,10};

  3. uint8_t SPIREEPROM[10]={0,0,0,0,0,0,0,0,0,0};



  4. void GPIO_SPI_int(void)
  5. {

  6.         stc_gpio_cfg_t SPIGPIOStruct;

  7.         DDL_ZERO_STRUCT(SPIGPIOStruct);

  8.         //Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE);

  9.         ///< SPI0引脚配置:主机
  10.         SPIGPIOStruct.enDrv = GpioDrvH;
  11.         SPIGPIOStruct.enDir = GpioDirOut;

  12.         Gpio_Init(SPI0_CS_PORT, SPI0_CS_PIN, &SPIGPIOStruct);
  13.         Gpio_SetAfMode(SPI0_CS_PORT, SPI0_CS_PIN, GpioAf1);             ///<配置SPI0_CS

  14.         Gpio_Init(SPI0_SCK_PORT, SPI0_SCK_PIN, &SPIGPIOStruct);
  15.         Gpio_SetAfMode(SPI0_SCK_PORT, SPI0_SCK_PIN, GpioAf1);           ///<配置SPI0_SCK

  16.         Gpio_Init(SPI0_MOSI_PORT, SPI0_MOSI_PIN, &SPIGPIOStruct);
  17.         Gpio_SetAfMode(SPI0_MOSI_PORT, SPI0_MOSI_PIN, GpioAf1);         ///<配置SPI0_MOSI

  18.         SPIGPIOStruct.enDir = GpioDirIn;
  19.         Gpio_Init(SPI0_MISO_PORT, SPI0_MISO_PIN, &SPIGPIOStruct);
  20.         Gpio_SetAfMode(SPI0_MISO_PORT, SPI0_MISO_PIN, GpioAf1);         ///<配置SPI0_MISO




  21. }

  22. /**
  23. ******************************************************************************
  24. ** \brief  初始化SPI
  25. **
  26. ** \return 无
  27. ******************************************************************************/
  28. static void App_SPIInit(void)
  29. {
  30.         stc_spi_cfg_t  SpiInitStruct;

  31.         ///< 打开外设时钟
  32.         Sysctrl_SetPeripheralGate(SysctrlPeripheralSpi0,TRUE);

  33.         ///<复位模块
  34.         Reset_RstPeripheral0(ResetMskSpi0);

  35.         //SPI0模块配置:主机
  36.         SpiInitStruct.enSpiMode = SpiMskMaster;     //配置位主机模式
  37.         SpiInitStruct.enPclkDiv = SpiClkMskDiv128;    //波特率:PCLK/2
  38.         SpiInitStruct.enCPHA    = SpiMskCphafirst;  //第一边沿采样
  39.         SpiInitStruct.enCPOL    = SpiMskcpollow;    //极性为低
  40.         Spi_Init(M0P_SPI0, &SpiInitStruct);
  41. }


  42. void initSPIFun(void)
  43. {

  44.         GPIO_SPI_int();
  45.         
  46.         Spi_SetCS(M0P_SPI0, TRUE);                             //取消片选
  47.         
  48.         
  49.         
  50.         
  51.         App_SPIInit();


  52. }



  53. //读取W25QXX的状态寄存器
  54. //BIT7  6   5   4   3   2   1   0
  55. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
  56. //SPR:默认0,状态寄存器保护位,配合WP使用
  57. //TB,BP2,BP1,BP0:FLASH区域写保护设置
  58. //WEL:写使能锁定
  59. //BUSY:忙标记位(1,忙;0,空闲)
  60. //默认:0x00
  61. uint8_t W25QXX_ReadSR(void)
  62. {
  63.         uint8_t byte=0;

  64.         Spi_SetCS(M0P_SPI0, FALSE);                        //使能器件
  65.         delay10us(2);  
  66.         Spi_RWByte(M0P_SPI0,W25X_ReadStatusReg);  //发送读取状态寄存器命令
  67.         byte=Spi_RWByte(M0P_SPI0,0Xff);             //读取一个字节
  68.         Spi_SetCS(M0P_SPI0, TRUE);                             //取消片选
  69.         return byte;
  70. }
  71. //写W25QXX状态寄存器
  72. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!




  73. void W25QXX_Write_SR(uint8_t sr)
  74. {
  75.         Spi_SetCS(M0P_SPI0, FALSE);                            //使能器件
  76.         delay10us(2);
  77.         Spi_RWByte(M0P_SPI0,W25X_WriteStatusReg);   //发送写取状态寄存器命令
  78.         Spi_RWByte(M0P_SPI0,sr);               //写入一个字节
  79.         Spi_SetCS(M0P_SPI0, TRUE);                             //取消片选
  80. }
  81. //W25QXX写使能
  82. //将WEL置位
  83. void W25QXX_Write_Enable(void)
  84. {
  85.         Spi_SetCS(M0P_SPI0, FALSE);                            //使能器件
  86.         delay10us(2);
  87.         Spi_RWByte(M0P_SPI0, W25X_WriteEnable);      //发送写使能
  88.         Spi_SetCS(M0P_SPI0, TRUE);                             //取消片选
  89. }
  90. //W25QXX写禁止
  91. //将WEL清零
  92. void W25QXX_Write_Disable(void)
  93. {
  94.         Spi_SetCS(M0P_SPI0, FALSE);                            //使能器件
  95.         delay10us(2);
  96.         Spi_RWByte(M0P_SPI0, W25X_WriteDisable);     //发送写禁止指令
  97.         Spi_SetCS(M0P_SPI0, TRUE);                             //取消片选
  98. }
  99. //读取芯片ID
  100. //返回值如下:
  101. //0XEF13,表示芯片型号为W25Q80
  102. //0XEF14,表示芯片型号为W25Q16
  103. //0XEF15,表示芯片型号为W25Q32
  104. //0XEF16,表示芯片型号为W25Q64
  105. //0XEF17,表示芯片型号为W25Q128
  106. uint16_t W25QXX_ReadID(void)
  107. {
  108.         uint16_t Temp = 0;
  109.         Spi_SetCS(M0P_SPI0, FALSE);
  110.         delay10us(2);
  111.         Spi_RWByte(M0P_SPI0, 0x90);//发送读取ID命令
  112.         Spi_RWByte(M0P_SPI0, 0x00);
  113.         Spi_RWByte(M0P_SPI0, 0x00);
  114.         Spi_RWByte(M0P_SPI0, 0x00);
  115.         Temp|=Spi_RWByte(M0P_SPI0,0xFF)<<8;
  116.         Temp|=Spi_RWByte(M0P_SPI0,0xFF);
  117.         Spi_SetCS(M0P_SPI0, TRUE);
  118.         return Temp;
  119. }


  120. //////////////////////////////////////////////////////////////////////////////////////////////////////////////



  121. //读取SPI FLASH
  122. //在指定地址开始读取指定长度的数据
  123. //pBuffer:数据存储区
  124. //ReadAddr:开始读取的地址(24bit)
  125. //NumByteToRead:要读取的字节数(最大65535)
  126. void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
  127. {
  128.         uint16_t i;
  129.         Spi_SetCS(M0P_SPI0, FALSE);                            //使能器件
  130.         delay10us(2);
  131.         Spi_RWByte(M0P_SPI0, W25X_ReadData);         //发送读取命令
  132.         Spi_RWByte(M0P_SPI0, (uint8_t)((ReadAddr)>>16));  //发送24bit地址
  133.         Spi_RWByte(M0P_SPI0, (uint8_t)((ReadAddr)>>8));
  134.         Spi_RWByte(M0P_SPI0, (uint8_t)ReadAddr);
  135.         for(i=0; i<NumByteToRead; i++)
  136.         {
  137.                 pBuffer[i]=Spi_RWByte(M0P_SPI0, 0XFF);   //循环读数
  138.         }
  139.         Spi_SetCS(M0P_SPI0, TRUE);
  140. }


  141. void W254_readtest(uint32_t ReadAddr,uint16_t NumByteToRead)
  142. {

  143.   uint16_t i;
  144.         Spi_SetCS(M0P_SPI0, FALSE);                            //使能器件
  145.         delay10us(2);
  146.         Spi_RWByte(M0P_SPI0, W25X_ReadData);         //发送读取命令
  147.         Spi_RWByte(M0P_SPI0, (uint8_t)((ReadAddr)>>16));  //发送24bit地址
  148.         Spi_RWByte(M0P_SPI0, (uint8_t)((ReadAddr)>>8));
  149.         Spi_RWByte(M0P_SPI0, (uint8_t)ReadAddr);
  150.         for(i=0; i<NumByteToRead; i++)
  151.         {
  152.                 //pBuffer[i]=Spi_RWByte(M0P_SPI0, 0XFF);   //循环读数
  153.                
  154.                 SPIREEPROM[i]=Spi_RWByte(M0P_SPI0, 0XFF);   //循环读数
  155.                
  156.                
  157.         }
  158.         Spi_SetCS(M0P_SPI0, TRUE);





  159. }


  160. //SPI在一页(0~65535)内写入少于256个字节的数据
  161. //在指定地址开始写入最大256字节的数据
  162. //pBuffer:数据存储区
  163. //WriteAddr:开始写入的地址(24bit)
  164. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
  165. void W25QXX_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
  166. {
  167.         uint16_t i;
  168.         W25QXX_Write_Enable();                  //SET WEL
  169.         Spi_SetCS(M0P_SPI0, FALSE);                            //使能器件
  170.         delay10us(2);
  171.         Spi_RWByte(M0P_SPI0, W25X_PageProgram);      //发送写页命令
  172.         Spi_RWByte(M0P_SPI0, (uint8_t)((WriteAddr)>>16)); //发送24bit地址
  173.         Spi_RWByte(M0P_SPI0, (uint8_t)((WriteAddr)>>8));
  174.         Spi_RWByte(M0P_SPI0, (uint8_t)WriteAddr);
  175.         for(i=0; i<NumByteToWrite; i++)
  176.         Spi_RWByte(M0P_SPI0, pBuffer[i]); //循环写数
  177.         
  178.         Spi_SetCS(M0P_SPI0, TRUE);                            //取消片选
  179.         W25QXX_Wait_Busy();                                           //等待写入结束
  180. }
  181. //无检验写SPI FLASH
  182. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
  183. //具有自动换页功能
  184. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
  185. //pBuffer:数据存储区
  186. //WriteAddr:开始写入的地址(24bit)
  187. //NumByteToWrite:要写入的字节数(最大65535)
  188. //CHECK OK
  189. void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
  190. {
  191.         uint16_t pageremain;
  192.         pageremain=256-WriteAddr%256; //单页剩余的字节数
  193.         if(NumByteToWrite<=pageremain)
  194.                 pageremain=NumByteToWrite;//不大于256个字节
  195.         while(1)
  196.         {
  197.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
  198.                 if(NumByteToWrite==pageremain)
  199.                         break;//写入结束了
  200.                 else //NumByteToWrite>pageremain
  201.                 {
  202.                         pBuffer+=pageremain;
  203.                         WriteAddr+=pageremain;

  204.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
  205.                         if(NumByteToWrite>256)
  206.                                 pageremain=256; //一次可以写入256个字节
  207.                         else
  208.                                 pageremain=NumByteToWrite;           //不够256个字节了
  209.                 }
  210.         };
  211. }
  212. //写SPI FLASH
  213. //在指定地址开始写入指定长度的数据
  214. //该函数带擦除操作!
  215. //pBuffer:数据存储区
  216. //WriteAddr:开始写入的地址(24bit)
  217. //NumByteToWrite:要写入的字节数(最大65535)
  218. uint8_t W25QXX_BUFFER[4096];
  219. void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
  220. {
  221.         uint32_t secpos;
  222.         uint16_t secoff;
  223.         uint16_t secremain;
  224.         uint16_t i;
  225.         uint8_t * W25QXX_BUF;
  226.         W25QXX_BUF=W25QXX_BUFFER;
  227.         secpos=WriteAddr/4096;//扇区地址
  228.         secoff=WriteAddr%4096;//在扇区内的偏移
  229.         secremain=4096-secoff;//扇区剩余空间大小
  230.         //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
  231.         if(NumByteToWrite<=secremain)
  232.                 secremain=NumByteToWrite;//不大于4096个字节
  233.         while(1)
  234.         {

  235.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
  236.                 for(i=0; i<secremain; i++) //校验数据
  237.                 {
  238.                         if(W25QXX_BUF[secoff+i]!=0XFF)
  239.                                 break;//需要擦除
  240.                 }
  241.                 if(i<secremain)//需要擦除
  242.                 {
  243.                         W25QXX_Erase_Sector(secpos);//擦除这个扇区
  244.                         for(i=0; i<secremain; i++)         //复制
  245.                         {
  246.                                 W25QXX_BUF[i+secoff]=pBuffer[i];
  247.                         }
  248.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区

  249.                 }
  250.                 else
  251.                         W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain); //写已经擦除了的,直接写入扇区剩余区间.
  252.                
  253.                
  254.                 if(NumByteToWrite==secremain)
  255.                         break;//写入结束了
  256.                 else//写入未结束
  257.                 {
  258.                         secpos++;//扇区地址增1
  259.                         secoff=0;//偏移位置为0

  260.                         pBuffer+=secremain;  //指针偏移
  261.                         WriteAddr+=secremain;//写地址偏移
  262.                         NumByteToWrite-=secremain;                                //字节数递减
  263.                         if(NumByteToWrite>4096)
  264.                                 secremain=4096;        //下一个扇区还是写不完
  265.                         else
  266.                                 secremain=NumByteToWrite;                        //下一个扇区可以写完了
  267.                 }
  268.         };
  269. }
  270. //擦除整个芯片
  271. //等待时间超长...
  272. void W25QXX_Erase_Chip(void)
  273. {
  274.         W25QXX_Write_Enable();                  //SET WEL
  275.         W25QXX_Wait_Busy();
  276.         Spi_SetCS(M0P_SPI0, FALSE);                            //使能器件
  277.         Spi_RWByte(M0P_SPI0, W25X_ChipErase);        //发送片擦除命令
  278.         Spi_SetCS(M0P_SPI0, TRUE);                            //取消片选
  279.         W25QXX_Wait_Busy();                                      //等待芯片擦除结束
  280. }
  281. //擦除一个扇区
  282. //Dst_Addr:扇区地址 根据实际容量设置
  283. //擦除一个山区的最少时间:150ms
  284. void W25QXX_Erase_Sector(uint32_t Dst_Addr)
  285. {
  286.         //监视falsh擦除情况,测试用
  287.         printf("fe:%x\r\n",Dst_Addr);
  288.         Dst_Addr*=4096;
  289.         W25QXX_Write_Enable();                  //SET WEL
  290.         W25QXX_Wait_Busy();
  291.         Spi_SetCS(M0P_SPI0, FALSE);                            //使能器件
  292.         Spi_RWByte(M0P_SPI0, W25X_SectorErase);      //发送扇区擦除指令
  293.         Spi_RWByte(M0P_SPI0, (uint8_t)((Dst_Addr)>>16));  //发送24bit地址
  294.         Spi_RWByte(M0P_SPI0, (uint8_t)((Dst_Addr)>>8));
  295.         Spi_RWByte(M0P_SPI0, (uint8_t)Dst_Addr);
  296.         Spi_SetCS(M0P_SPI0, TRUE);                            //取消片选
  297.         W25QXX_Wait_Busy();                                      //等待擦除完成
  298. }
  299. //等待空闲
  300. void W25QXX_Wait_Busy(void)
  301. {
  302.         while((W25QXX_ReadSR()&0x01)==0x01);   // 等待BUSY位清空
  303. }
  304. //进入掉电模式
  305. void W25QXX_PowerDown(void)
  306. {
  307.         Spi_SetCS(M0P_SPI0, FALSE);                            //使能器件
  308.         Spi_RWByte(M0P_SPI0, W25X_PowerDown);        //发送掉电命令
  309.         Spi_SetCS(M0P_SPI0, TRUE);                            //取消片选
  310.         delay10us(1);                               //等待TPD
  311. }
  312. //唤醒
  313. void W25QXX_WAKEUP(void)
  314. {
  315.         Spi_SetCS(M0P_SPI0, FALSE);                            //使能器件
  316.         Spi_RWByte(M0P_SPI0, W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB
  317.         Spi_SetCS(M0P_SPI0, TRUE);                            //取消片选
  318.         delay10us(1);                                //等待TRES1
  319. }

复制代码




成功读取ID 微信截图_20220319103702.png

写不成功

微信截图_20220319103558.png


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-5-29 09:40

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表