搜索
bottom↓
回复: 11

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

[复制链接]

出0入97汤圆

发表于 2022-3-19 10:43:52 | 显示全部楼层 |阅读模式
本帖最后由 hero245 于 2022-3-19 10:47 编辑

用华大的 SPI 操作 华邦的 W25Q128  能读取在ID 但数据一直写不成功,不知道问题出在哪里?看下大神能不能帮忙看一下。用的是原点的例程。




  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. }

复制代码



正常读取ID0XEF17



向地址5000 写入数据1  ,从地址5000读取1个数据 ,不成功






本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入442汤圆

发表于 2022-3-19 11:05:07 来自手机 | 显示全部楼层
先wen再wr然后等idle

出0入97汤圆

 楼主| 发表于 2022-3-19 11:11:38 | 显示全部楼层
wye11083 发表于 2022-3-19 11:05
先wen再wr然后等idle
(引用自2楼)

已经在 写之前 将WEL置1    再写,然后 等待busy 置0 啊
不知问题出在哪里!

出0入90汤圆

发表于 2022-3-19 11:21:05 | 显示全部楼层
写之前擦除没有?

出0入97汤圆

 楼主| 发表于 2022-3-19 11:26:17 | 显示全部楼层
aammoo 发表于 2022-3-19 11:21
写之前擦除没有?
(引用自4楼)

有啊,写之前都擦除了

出0入0汤圆

发表于 2022-3-19 12:13:55 | 显示全部楼层
每次写或擦除之前都要先发写使能命令,仔细看手册

出100入0汤圆

发表于 2022-3-19 14:26:31 来自手机 | 显示全部楼层
前段时间遇到过差不多的情况:能读ID和数据,但不能擦和写。换颗芯片就好了

出0入97汤圆

 楼主| 发表于 2022-3-19 16:10:50 | 显示全部楼层
谢谢各位!问题解决了,原因不是程序的问题,原来是电路的问题。

flash的供电网络号是3.3V  ,正确应该是+3.3V才对,所以flash的电源的没供电的。后面飞根线就正常了,白调试了几天,因为能读取到ID号,所以就没有怀疑是电路的问题。大意啊!

出0入4汤圆

发表于 2022-3-20 08:49:50 | 显示全部楼层
不供电能读出ID,学到了.

出0入71汤圆

发表于 2022-3-20 09:41:11 | 显示全部楼层
FLASH功耗低,从IO口的漏电就可以让它工作起来,但是很快就会没有电的

出350入477汤圆

发表于 2022-3-20 10:24:42 来自手机 | 显示全部楼层
MAD_FISH 发表于 2022-3-20 08:49
不供电能读出ID,学到了.
(引用自9楼)

读id的指令应该不怎么耗电,因为不需要实际操作内部的flash存储。那个指令应该是SPI接口部分直接返回结果的。
等要读flash数据的时候,耗电大了,就错掉了

出10入120汤圆

发表于 2022-3-20 12:25:27 来自手机 | 显示全部楼层
MAD_FISH 发表于 2022-3-20 08:49
不供电能读出ID,学到了.
(引用自9楼)

确实很多怪异的事。
昨天用LA调试FPGA输出的串口信号,最后发现LA只连接了到FPGA串口输出,没连接地线,并且给FPGA供电的开关电源没有连接pe地,竟然一天工作下来正常,当然232电平的信号比较大,设定的通讯速率9600比较低,可能存在开关电源地和PE有耦合问题,连接LA的USB端口应该和相连的。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-16 06:14

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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