This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] 编译器/MSP430F5328:使用 SPI 与 W25X10 winbound 串行闪存连接的 MSP430F5328

Guru**** 2051860 points
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/565303/compiler-msp430f5328-msp430f5328-interface-with-w25x10-winbound-serial-flash-using-spi

器件型号:MSP430F5328

工具/软件:TI C/C++编译器

您好!

我使用 MSP430F5328 接口和使用 SPI 通信协议的 W25X10 winbound 串行闪存。 我使用的是 SMCLK 16MHz。

我已根据从器件和源代码配置 SPI、如下所示:

#define SPICLK       32.
uint8_t SourceAddr [16];     
uint8_t DestAddr[16];

void main( void )
{  

uint16_t IDcode = 0;

       //将 P2.6设置为从器件复位
       GPIO_setAsOutputPin (GPIO_PORT_P2、GPIO_PIN6);
       GPIO_setOutputHighOnPin (GPIO_PORT_P2、GPIO_PIN6);              
       
       //P3.3、4选项选择  
       //FLASH_SPISIMO - P3.3
       //FLASH_SPISOMI - P3.4
       GPIO_setPeripheralModuleFunctionInputPin (GPIO_PORT_P3、GPIO_PIN3 + GPIO_PIN4);
       //P2.7 FLASH_SPICLK
       GPIO_setPeripheralModuleFunctionInputPin (GPIO_PORT_P2、GPIO_PIN7);
    
       //初始化主设备
       USCI_A_SPI_initMasterParam param ={0};
       param.selectClockSource = USCI_A_SPI_CLOCKSOURCE_SMCLK;
       param.clockSourceFrequency = UCS_getSMCLK();
       param.desiredSpiClock = SPICLK;
       param.msbFirst = USCI_A_SPI_MSB_FIRST;
       param.clockPhase = USCI_A_SPI_PHASE_DATA_Changed_ONFIRST_captured_on_next;
       param.clockPolarity = USCI_A_SPI_CLOCKPOLARITY_INACT_LOW;
       返回值= USCI_A_SPI_initMaster (USCI_A0_BASE、&param);                

       如果(STATUS_FAIL =返回值)
               返回0;
       
       //启用 SPI 模块
       USCI_A_SPI_ENABLE (USCI_A0_BASE);
       
       //现在初始化 SPI 信号、复位从器件
       GPIO_setOutputLowOnPin (GPIO_PORT_P2、GPIO_PIN6);   

       /*读取制造商 ID */

IDcode = SerialFlash_readid();

uint16_t SerialFlash_readid (void)

   uint16_t IDcode;


    // P2.6 --0,CS =0选择 SPI 闪存
   GPIO_setOutputLowOnPin (GPIO_PORT_P2、GPIO_PIN6);

         
   //读取设备命令    0x90
   SourceAddr[0]= 0x90;    
   //器件地址0x00的上部
   SourceAddr[1]= 0x00;    
   //器件地址0x00的下半部分  
   SourceAddr[2]= 0x00;    
   SourceAddr[3]= 0x00;   
   //串行闪存发送数据    
   SerialFlash_SendData ((uint8_t *)&SourceAddr[0]、 4);  
   _DELAY_CYCLES (160000);
   //串行闪存接收数据
   SerialFlash_RcvData ((uint8_t *)&DestAddr[0],2);


   // P2.6 - 1、CS = 1释放 SPI 闪存
   GPIO_setOutputHighOnPin (GPIO_PORT_P2、GPIO_PIN6);


   //获取 IDcode
   IDCODE =(DestAddr[0]<< 8)|(DestAddr[1]);


   返回 IDCODE;       

void SerialFlash_SendData (uint8_t *buf、uint32_t Length)

 uint32_t i;      
 对于(i = 0;i <长度;i++)
 {    
     // USCI_A0 TX 缓冲器就绪?
     while (!(UCA0IFG&UCTXIFG));     
     UCA0TXBUF =*缓冲;
     buf++;     
 }  

void SerialFlash_RcvData (uint8_t *buf、uint32_t 长度)

   uint32_t i;   
   对于(i = 0;i <长度;i++)
   {          
       // USCI_A0 RX 缓冲器准备好了吗?
       while (!(UCA0IFG&UCRXIFG));
       * buf = USCI_A_SPI_receiveData (USCI_A0_BASE);
       buf++;
   }  

我已经检查了示波器上的 MOSI 和时钟信号并变得非常完美。

但我无法在接收器缓冲器上正确读取制造商 ID。 大多数情况下、获取0xFF、0xFF、有时获取一些随机值。


能不能有人帮我解决这个问题。


谢谢、此致、

John

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    尊敬的 John:

    e2e.ti.com/.../563518

    您的 MOSI 和 CLK 示波器屏幕截图未正确连接、无论如何我也希望看到一个带有 MISO 的示波器。 您需要在 SerialFlash_RcvData 函数内发送一些虚拟值、以便生成 SPI CLK、从而允许 W25X10在 MISO 线路上做出响应。 而是解释0xFF、因为线路保持上拉状态且无响应。 确保您的 SPI 通信遵循 W25X10数据表的图18、我还注意到、时钟相位应在第一个边沿(上升)捕获、并在下一个边沿(下降)更改。

    此致、
    Ryan
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Ryan、

    我将为制造商 ID 命令发送虚拟字节、如下所示:

    uint16_t SerialFlash_readid (void)

      uint16_t IDcode;

      // P2.6 --0,CS =0选择 SPI 闪存

      GPIO_setOutputLowOnPin (GPIO_PORT_P2、GPIO_PIN6);

      //读取设备命令  0x90

      SourceAddr[0]= 0x90;   

      //器件地址0x00的上部

      SourceAddr[1]= 0x00;   

      //器件地址0x00的下半部分

      SourceAddr[2]= 0x00;   

      SourceAddr[3]= 0x00;  

      //串行闪存发送数据   

      SerialFlash_SendData ((uint8_t *)&SourceAddr[0]、 4);

      _DELAY_CYCLES (160000);

      //串行闪存接收数据

      SerialFlash_RcvData ((uint8_t *)&DestAddr[0],2);

      // P2.6 - 1、CS = 1释放 SPI 闪存

      GPIO_setOutputHighOnPin (GPIO_PORT_P2、GPIO_PIN6);

      //获取 IDcode

      IDCODE =(DestAddr[0]<< 8)|(DestAddr[1]);

      返回 IDCODE;     

    然后尝试读取2个字节的制造商 ID、但未获取任何内容。

    示波器屏幕截图如下所示:  

    请帮我解决这个问题。

    谢谢、此致、

    John

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您似乎没有应用我之前的任何建议、只传输四个字节而不是六个字节、UCCKPH 位似乎没有被置位。

    此致、
    Ryan
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Ryan、

    我已如上所述更改 W25X10_RcvData 函数并设置 UCCKPH 位。 现在能够成功读取制造商 ID。
    非常感谢。 但我对串行闪存数据表中提到的仅支持 Mode0和 mode3有疑问。

    现在我尝试读取串行闪存状态。

    Recv 函数如下所示:

    voidW25X10_RcvData( uint8_t *buf, uint32_t Length )
    {
      uint32_t i;
      while(UCA0STAT & UCBUSY) /*EMPTY*/; // let last Tx byte through
      (void)UCA0RXBUF; // Clear RXIFG
      for( i = 0; i < Length; i++ )
      {
        UCA0TXBUF = 0xFF; // Dummy byte to run the SPI
        // USCI_A0 RX buffer ready?
        while(!(UCA0IFG&UCRXIFG));
        *buf = USCI_A_SPI_receiveData(USCI_A0_BASE);
        buf++;
      }
    }



    uint8_t SerialFlash_ReadStatus( void )

    // P2.6 --0,CS =0选择 SPI 闪存
    SerialFlash_CS0();
    //读取寄存器1命令
    SourceAddr[0]= 0x05;
    //发送命令
    SerialFlash_SendData((uint8_t *)&SourceAddr[0],1 );
    W25X10_RcvData ((uint8_t *)&DestAddr[0]、2);
    // P2.6 - 1、CS = 1释放 SPI 闪存
    SerialFlash_CS1();
    //返回寄存器1的内容
    返回 DestAddr[0];


    但它将返回0xFF。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    如果您可以成功读取制造商 ID、则您将使用正确的 SPI 模式进行通信。 使用示波器确认您的通信序列遵循 W25X10数据表的图6。

    此致、
    Ryan
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Ryan、

    我正在尝试擦除串行闪存、因此我将执行以下步骤:

    1) 1)读取串行闪存状态

    uint8_t SerialFlash_ReadStatus( void )

      // P2.6 --0,CS =0选择 SPI 闪存

      SerialFlash_CS0();

      //读取寄存器1命令

      SourceAddr[0]= 0x05;

      //发送命令

      SerialFlash_SendData((uint8_t *)&SourceAddr[0],1 );   

      //接收注册器1的内容

      SerialFlash_RcvData ((uint8_t *)&DestAddr[0],2);

      // P2.6 - 1、CS = 1释放 SPI 闪存

      SerialFlash_CS1();

      //返回寄存器1的内容

      返回 DestAddr[0];

    2) 2)写入使能命令

    void SerialFlash_WEnable (void)(空)

      // P2.6 --0,CS =0选择 SPI 闪存

      SerialFlash_CS0();

      //写入启用命令

      SourceAddr[0]= 0x06;

      SerialFlash_SendData ((uint8_t *)&SourceAddr[0]、1);

      // P2.6 - 1、CS = 1释放 SPI 闪存

      SerialFlash_CS1();

    3) 3)读取上述串行闪存状态

    但 WriteEnable 位未设置

    下面是示波器屏幕截图。

    使能位未设置是什么错误?

    谢谢、此致、

    John

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您正在尝试读取状态寄存器(05h)、而不是串行闪存(03h)、该闪存只有一个字节长(根据第10.2.2节)、并继续直到 CS 将其释放。 下一条命令是写使能(06h)、但在发送另一个状态寄存器读取之前、您是否还会复位 CS 引脚? 您还应查看使能位、以验证它是否被正确控制。

    此致、
    Ryan
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Ryan、

    是的、我将05h 发送给读取状态寄存器、如上面的函数"SerialFlash_ReadStatus"中所示、它只有1个字节。 我正在重置 CS 引脚。

    以下是代码:

    uint8_t SerialFlash_Erase (void)

       uint8_t 温度;
       uint32_t i、j;
       //检查状态寄存器
       while (1)                    
       {                      
           //读取状态寄存器1
           temp = SerialFlash_ReadStatus();        
           //检查忙位       
           温度&= 0x01;           
           if (温度==0x00)        
                   中断;           
           for (i=0;i<10;i++);  
       }             
       //写入使能
       SerialFlash_WEnable();      
       //检查 状态寄存器
       while (1)          
       {                                           
           for (i=0;i<10;i++);
           //读取状态寄存器1
           temp = SerialFlash_ReadStatus();    
           //检查忙位
           temp &= 0x03;             
           //如果写入使能 WEL = 1
           if (Temp ==0x02)          
             中断;    
       }
       // P2.6 - 0、CS = 0选择 SPI 闪存
       SerialFlash_CS0();           
       SourceAddr[0]= 0xC7;
       SerialFlash_SendData((uint8_t *)&SourceAddr[0],1 );
       // P2.6 - 1、CS = 1释放 SPI 闪存
       SerialFlash_CS1();       

       for (i=0;i<65000;i++);       
       for (j=0;j<30;j++);
       返回1;


    uint8_t SerialFlash_ReadStatus( void )

       // P2.6 --0,CS =0选择 SPI 闪存
       SerialFlash_CS0();    
       //读取寄存器1命令
       SourceAddr[0]= 0x05;    
       //发送命令
       SerialFlash_SendData((uint8_t *)&SourceAddr[0],1 );       
       //接收注册器1的内容
       SerialFlash_RcvData ((uint8_t *)&DestAddr[0],2);
       // P2.6 - 1、CS = 1释放 SPI 闪存
       SerialFlash_CS1();
       //返回寄存器1的内容
       返回 DestAddr[0];

    void SerialFlash_WEnable (void)(空)

       // P2.6 --0,CS =0选择 SPI 闪存
       SerialFlash_CS0();
       //写入启用命令
       SourceAddr[0]= 0x06;    
       SerialFlash_SendData ((uint8_t *)&SourceAddr[0]、1);    
       // P2.6 - 1、CS = 1释放 SPI 闪存
       SerialFlash_CS1();    

    您能否检查上述 API、我做了什么错误?

    谢谢、此致、

    John

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    能不能有人帮我解决这个问题。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    尊敬的 John:

    当没有提供新信息时、很难对问题进行调试、正如我在写入使能和第二状态寄存器读取命令看起来在不转换 CS 引脚的情况下彼此紧随其后、但这可以通过示波器捕捉来证明。 您可能希望尝试使用其他 W25X10指令或阅读数据表以获得有关正确通信协议的更多线索。

    此致、
    Ryan
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Ryan、

    下面是 CS 的示波器屏幕截图。  

    我正在发送前1)读取状态命令(0x05)

                      2) 2)写入使能命令(0x06)

                      3) 3)按如下方式读取状态命令(0x05)

    如上所示、我为每条命令选择 CS 并释放它。 但在示波器中未显示。 可能是因为它非常快。


    请您检查我犯了什么错误吗?

    此致、

    John

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    CS 引脚需要切换、要么提高示波器上的采样率、要么添加一些延迟来分离命令。 如果 CS 引脚仍然无法切换、则您知道问题是什么、并应相应地解决问题。

    此致、
    Ryan
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Ryan、

    我已通过添加延迟来检查 CS 引脚、CS 引脚正在切换、但当我发送 ENABLE 命令时、它不会变为低电平、如下所示。

    实际上、我不明白为什么启用命令不会变为低电平。

    我是否需要为写入命令发送任何额外字节或任何其他内容。

    请帮助我解决此问题。

    此致、

    John

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    John、

    您正在取得良好的进展、现在您只需单步执行并调试 SerialFlash_WEnable 函数、即可了解 CS 引脚如何未正确驱动。

    此致、
    Ryan