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.

[参考译文] ADS1258:SPI 通信问题

Guru**** 2394295 points
Other Parts Discussed in Thread: ADS1258

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

https://e2e.ti.com/support/data-converters-group/data-converters/f/data-converters-forum/847920/ads1258-spi-communication-issues

器件型号:ADS1258

我正在处理一个客户问题、即 FPGA 无法通过 SPI 端口与 ADS1258通信。  他们 已经尝试过 TI 网站上的示例 C 程序。 我们能够控制 PWDN 和复位引脚。 START 和 CLKSEL 通过硬接线保持高电平。 CS、SDI、SCLK 和 SDO 连接到 FPGA SPI 引脚。 FPGA 固件具有德州仪器模式、可保持时钟持续运行。 这似乎是必要的、因为数据表显示"如果 SCLK 在4096或256个 fclk 周期内保持未激活状态、正在进行的读取或写入操作将终止并且 SPI 接口复位。"

还有其他 FPGA 模式不是这样做的。 我们已成功地将不同模式用于不同的 TI 模数转换器。

问题:

1.我们是否需要使 SCLK 线路在此器件上以这种方式运行?
2.示例代码似乎通过 SPI 以8位块的形式读取数据和状态信号。 这是我们需要做的吗?
3、成功运行时、是否有任何 SPI 信号的示例?

我已附上示波器屏幕截图、其中显示了尝试进行通信的情况。 您可以看到时钟持续运行。 CS 在通信开始时变为高电平。 SDI 引脚通过尝试命令切换。 不过、SDO 引脚似乎几乎是随机通信。

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

    您好 Jeramie、

    我可以帮助回答您的问题...

    1. SCLK 不需要是连续的...也就是说、它并不总是需要打开。

      在活动 SPI 帧期间(同时/CS 保持低电平)、SCLK 是"连续"的(具有一致的时钟周期)是可以的;但是、每个 SPI 帧应该有非常清晰的开始和结束、以确保数据对齐并满足 SPI 时序要求。

      例如、根据图1、/CS 应变为低电平、然后在第一个 SCLK 上升沿之前必须有一个静默时间(tCSSC)、以确保 ADC 识别第一个 SCLK。 但是、完成 SPI 帧所需的接下来7-39个时钟周期可以短暂地"连续"。  

      无效 SCLK 超时的目的是允许使用/CS 永久连接低电平的情况。 如果系统无法控制/CS 并且发生数据错位、则 SPI 非活动超时是在不切换/CS 引脚的情况下恢复对齐的唯一方法。 如果客户控制/CS 引脚、则很可能永远不会遇到此超时、假设他们迅速完成 SPI 事务、并且长时间不中断 SPI 通信以处理其他任务。


    2. 否 示例代码是为 MSP432E 微控制器编写的、该微控制器以8位块处理 SPI 通信。 大多数微控制器一次需要发送整数字节数;但是、在 FPGA 上、您不需要像这样对数据进行块处理。 我建议"持续"发送最少数量的 SCLK (字节边界之间没有任何时钟延迟)、直到 SPI 命令或帧完成。 SPI 通信完成后、停止 SCLK 以防止数据错位并降低系统功耗。


    3. 查看 ADS1258数据表中的图57至60、了解 SPI 帧的外观。 DIN 或 DOUT 上的实际数据将取决于您要发送的命令或正在读取的数据。 所有信号都应清晰且易于在示波器上读取、并且不会出现较大的过冲/下冲或过度振铃。 为了避免这些类型的信号完整性问题、我建议使用 单个实心接地层 (对于模拟和数字电源、使所有 SPI 信号都直接布置在该接地层上方、并尽量减少用于路由这些信号的过孔数量、尤其是对于 SCLK 布线。 在 SCLK 布线上添加10-100欧姆串联电阻有时也会有所帮助。

    遗憾的是、它看起来没有通过附件。 是否可以重新发送?

    注意:将任何文件或图像发布到 E2E 时、请确保使用"插入媒体"按钮、因为拖放到文本编辑器中通常会失败。

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

    尊敬的 Chris:

    感谢您的快速回复。 我们回到了没有连续时钟的旧版本。   在该模式下,时钟开始低电平,数据在时钟的上升沿从 ADC 读取,而到 ADC 的数据在时钟的下降沿发生变化。 我们使用它来运行 SPI。

    尽管如此,我们仍有一些问题。 TI 网站上的示例代码包含我们正在执行的单元测试。 我们未通过基本功能测试、包括两次重置功能测试(RESET 引脚和 RESET 命令)。

    下面的代码显示了复位引脚的单元测试。

    //测试读取(非零)寄存器是否返回预期结果。

    bool test_read_register (void)

      bool b_pass = true;

      uint8_t 值;

     

      toggleRESET();

     

      值= readSingleRegister (REG_ADDR_CONFIG0);

      b_pass &=(值= CONFIG0_DEFAULT);

     

      返回 b_pass;

    我们能够在示波器上对此进行监控。 我们看到提出了复位线路、发送了配置寄存器0的读取命令以及 ADC 的返回。 遗憾的是、返回值不是预期的 CONFIG0_DEFAULT (0x0A)、而是0x00。 您可以在下面看到交易。

      

    我们还使用 opcode_reset 命令测试复位 ADC 的函数。 相关代码如下:

    //测试 RESET SPI 命令是否导致寄存器返回其默认值。

    bool test_reset_command (void)

      bool b_pass = true;

      uint8_t writeValue、readValue;

     

      //将非默认值写入寄存器

      writeValue =(~CONFIG0_DEFAULT)&(0x7E);

      writeSingleRegister (REG_ADDR_CONFIG0、writeValue);

     

      //发出重置命令

      sendCommand (opcode_reset);

     

      //检查内部数组是否已自动更新

      b_pass &=(getRegisterValue (REG_ADDR_CONFIG0)=CONFIG0_DEFAULT);

     

      //检查读取寄存器命令是否返回默认值

      readValue = readSingleRegister (REG_ADDR_CONFIG0);

      b_pass &=(readValue == CONFIG0_DEFAULT);

     

      返回 b_pass;

    同样、我们可以在示波器上监控这一点。 我们可以看到在 DI 线上写入包含预期数据的单个寄存器命令。 我们可以看到发出了操作码_RESET (0xC0)。 最后、我们可以看到读取寄存器命令、该命令的地址设置为配置寄存器0。 这种情况下的响应是0x88而不是0 (来自复位引脚单元测试)、但这不是预期的 CONFIG0_DEFAULT。 反复运行此测试会从 ReadSingleRegister 命令返回不同的值。 它看起来几乎是随机的。

      

    所以:

    我们的行为是否有问题?

    如果上述情况不明显,您是否可以建议执行一些附加测试?

    谢谢、

    John Stolan

     

     

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

    尊敬的 John:

    从发送给我的附件中、似乎/CS 正在每个 SPI 数据字节之间切换。

    要执行读取或写入寄存器命令、您需要将/CS 引脚保持在低电平、直到整个多字节命令完成。 读取数据、读取寄存器和写入寄存器命令都需要在单个 SPI"帧"内发送多个 SPI 字节。 当我使用术语"帧"时、我指的是在/CS 下降沿和/CS 上升沿之间 DIN 和 DOUT 信号上发送的所有数据字节。

    典型的读取命令帧包括以下操作...   

    1. 将/CS 设为低电平
    2. 在发送数据之前延迟 tCSSC
    3. 在 DIN 上发送0x40、忽略 DOUT 上接收到的数据字节
    4. 在 DIN 上发送0x00、捕获在 DOUT 上接收到的寄存器数据字节
    5. 将/CS 设为高电平

    这是数据表中的一个示例图...

    如果在"Command Byte 1"之后切换/CS、则以下"无关"字节将需要是读取命令字节、因为第一条命令将被/CS 变为高电平中止。

    注意:/CS 信号可能需要由 GPIO 引脚实现、该引脚与 SPI 外设生成的任何内置帧选择信号分离。 通常、微控制器希望具有固定的 SPI 帧长度、否则它们不会在/CS 低电平和 SCLK 的第一个上升沿之间提供"tCSSC"延迟。 在这些情况下、使用 GPIO 通常可以更好地控制/CS 信号的行为、允许您在单个帧中发送多个 SPI 字节并延迟第一个 SCLK 上升沿。


     

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

    Chris

    再次感谢快速响应。 这使我们能够取得一些迅速的进展。 单元测试中的所有低级例程现在都起作用。 我们可以从软件读取单个寄存器、写入单个寄存器和控制复位。

    我们相信我们也可以设置 GPIO、尽管它没有经过完全测试。 我们现在尝试使用 opcode_pulse_convert 命令读取 ADC。   第一步、我们使用 unit_test.c 中的 test_gpio_setup 设置控制寄存器 已修改 GPIOD 以匹配我们所需的通道设置。

    bool test_gpio_setup (void)

      //将 GPIO 的测试设置设置为所需状态

      //状态位被置位

      writeSingleRegister (REG_ADDR_CONFIG0、0x02);

      // 6168SPS

      writeSingleRegister (REG_ADDR_CONFIG1、0x01);

      writeSingleRegister (REG_ADDR_MUXSCH、0x00);

      writeSingleRegister (REG_ADDR_MUXDIF、0x00);

      writeSingleRegister (REG_ADDR_MUXSG0、0x00);

      // AIN1

      writeSingleRegister (REG_ADDR_MUXSG1、0x04);

      writeSingleRegister (REG_ADDR_GPIOC、0x00);

      //压力增益= 32

      writeSingleRegister (REG_ADDR_GPIOD、0xC6);// WAD 0xBA

      //读回数据

      uint8_t readGPIOD = readSingleRegister (REG_ADDR_GPIOD);

      if (readGPIOD ==0xC6)

      {

          返回 true;

      }

      返回 false;

     

    我们的主例程中嵌入了用于读取 ADC 的代码、如下所示。

    MAIN ()

      write_reg32 ((volatile uint32_t *) single_field_or定向 固件_0 + 5、

    (uint32_t)启用);

      InitADCPeripheral();

      adcStartupRoutine();

     run_unit_tests();

      while (1)

      {

          uint32_t received = spiSendReceiveByte (opcode_pulse_convert<<24);

          uint32_t Status_Byte =(Received & 0x000000)>> 24;

          uint32_t Reading = Received & 0x00ffff;

          delay();

      }

      返回0;

     

    尽管名称为 spiSendReceiveByte,但实际上仍发送和接收32位字(我们正在更改代码,速度非常快)。 提取状态字节显示的读数相当一致、为92、这表示这是新数据、并且我们正在读取通道10010、这正是我们所期望的。 但是、数据似乎都分布在地图上。 我们尝试监控3.3伏信号、期望值约为0xD33333。

    这是至少在试验基础上对 A/D 采样的正确方法吗? 您是否看到我们有任何错误? 我们应该尝试其他采样方法吗?

    出于完整性考虑、我已附上 SPI 信号的样本、尽管这些信号似乎被正确读取。

    此致、

    John

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

    尊敬的 John:

     您是否对 test_gpio_setup()测试函数有任何问题,或者是否按预期工作?

    对于脉冲转换命令、这通常是一个单字节命令、不使用该命令读取数据...

    • 您可以将"opcode_pulse_converpy"作为单字节发送、并在发送该字节后立即将/CS 设置为高电平。 也许可以在脉冲转换命令之后发送额外的"0x00"字节来启动下一个 ADC 转换、但我之前没有亲自尝试过。

    • 要读取 ADC 的数据、您需要发出脉冲转换命令、等待/DRDY 变为低电平、然后使用"通道数据直接读取"或"通道数据读取命令"来检索数据。 此命令总共需要3或4个字节的 Iong、如上一个帖子中的图像所示。

    我认为、如果您等待/DRDY 下降沿、然后发出读取命令之一、您应该开始看到更一致的结果。 在脉冲转换命令期间 DOUT 上的数据是"无关"数据。

    此外、我认为、在4.096V 基准电压下、3.3V 输入应作为更接近0x60AE00的 ADC 代码输出。 对于 4.096V 基准、该器件的 LSB 大小为"VREF/0x780000"或0.52uV。 ADC 代码是2的补码、因此0xD33333为-2、936、013 * LSB =-1.520 V

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

    Chris

    很抱歉、在报告问题时出现了一些空白。 我认为我们真的很接近、实际上我们可以读取符合我们预期的数据、但似乎插入了一个额外的字节、这会阻止我们读取24字节转换的最后8位。

    首先、我们对配置寄存器的命令如下、并附有一些注释:

      //设置为获取状态、连续采样模式

      writeSingleRegister (REG_ADDR_CONFIG0、0x02);

      //设置数据速率

      writeSingleRegister (REG_ADDR_CONFIG1、0x01);

     

      //设置为获取通道 AIN10

      writeSingleRegister (REG_ADDR_MUXSCH、0x00);

      writeSingleRegister (REG_ADDR_MUXDIF、0x00);

      writeSingleRegister (REG_ADDR_MUXSG0、0x00);

    writeSingleRegister (REG_ADDR_MUXSG1、0x04);

     

      //设置 GPIO,我们对此并不关心

      writeSingleRegister (REG_ADDR_GPIOC、0x00);

      writeSingleRegister (REG_ADDR_GPIOD、0xC6);

    这一切似乎都很好、我们只是想向您展示我们是如何配置转换器的。

    我们认为这将设置系统、以便我们持续读取通道 AIN10、该通道连接到4伏范围内的3.3伏电压。 正如您在上一封电子邮件中指出的、这应该接近60AE00。 除了我在下面描述的异常情况外,这正是我们所得到的。

    当我们读取数据时、我们希望看到类似于数据表上的图58的内容。   这是状态字节、后跟三个字节的数据。 由于我们希望状态字节包括要设置的新位,并且由于状态字节包括正在读取的通道,因此预期的字节为0x80 (新)或带有0x12 (AIN10)的字节将获得0x92。

    这正是我们得到的结果、但0x92重复了两次。 换言之、我们期望看到的是:

    0x92 0x60 0xEE 0x00

    相反、我们看到:

    0x92 0x92 0x60 0xEE

    如果我们将 REG_ADDR_MUXSG1值更改为0x0C、以便对 AIN10和 AIN11进行采样、我们将获得状态的交替值0x92和0x93、完全符合预期。 除非再次如此、否则会复制第一个字节。

    我们用于读取这些字节的代码非常简单、只需一行即可读取值、接下来是两行提取相关值。 (opcode_read_command = 0x30、包括 MUL 位)

    数据= SendspiReceiveByte (opcode_read_command<<24);

    新=数据和0x8000000;

    uint32_t Data24 =数据和0x00ffff;

     

    我们查看了 SPI 信号、并确认 ADC 确实在传输开始时发送了重复字节、这不是我们的缓冲区中的剩余内容。

     

    您能建议为什么会发生这种情况吗? 实际上、我们只有一个16位模数转换器、因为我们没有接收到最后一个字节。 这是可能的、但将 CS 延长到足以读取最后一个值的时间会带来一些麻烦。 希望您可以建议一种更好的解决方法。

     

    谢谢

     

    John

     

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

    尊敬的 John:

    很抱歉、我这次延迟了回答。

    读取数据的方式会对您观察到的行为产生影响...

    1. 直接读取数据
      如果您只是在/DRDY 变为低电平后开始计时数据、则不会看到重复的状态字节。 您将获得如图57所示的输出。

    2. 通过命令读取数据
      如果您发出读取命令、然后开始计时输出数据、DOUT 上的第一个字节将是状态字节(图58显示此字节为"无关"值)、然后当您开始计时输出数据时、您将再次看到状态字节。

    这里发生的情况是、有一个保存状态+数据字节的输出移位寄存器、当您向 ADC 发出时钟时、您将看到状态字节立即出现在 DOUT 上(如果已启用)。  

    但是、如果在第一个字节的 DIN 上发出读取命令、ADS1258会将输出移位寄存器数据复制到单独的缓冲寄存器中、然后从该缓冲寄存器开始计时数据(有效地重新启动数据读取操作)。 您将看到状态字节出现两次、但您可以忽略第一次出现的情况。

    使用读取数据命令的好处是、虽然它需要额外的字节来时钟输出数据、但它会缓冲数据、这样、如果您没有在下一个/DRDY 下降沿之前完成所有数据的时钟输出、 您仍然可以完成读取操作、而不会出现任何问题。 但是、如果您直接时钟输出数据且不发出读取数据命令、则新转换完成后、非缓冲输出移位寄存器将立即更新。 因此、在/DRDY 下降沿期间直接读取数据会导致损坏的结果被移出。

    这有道理吗?

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

    Chris

    谢谢、是的、这很清楚。  我们似乎需要使用读取数据命令、因为我们的当前设计无法访问/DRDY 信号。  (我们正在构建的新电路板中对此进行更正)。  但同时、我认为我们无法避免仅为数据计时而出现损坏的数据问题。

    我的理解是、我们需要做的是:

    1.  将 ADC 配置为通过相关通道进行扫描。

    2. 发送读取数据命令。

    3. 读回5个字节的数据,同时始终将 CS 线路保持在低电平。

    4. 从状态字节确认我们正在读取所需通道、并且新位已设置。

    5. 如果在步骤 4中检查的状态字节满足条件,则有效数据位于最后3个字节中。

    显然、我们在配置器件时还有其他选项、但我认为上述选项应该能够满足我们的需求。

    您是否看到此问题有任何问题?  在数据传输的整个时间内、CS 线路必须保持低电平、这是正确的吗?

    谢谢

    John

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

    尊敬的 John:

    为了澄清步骤2和3中的某些内容、读取数据命令是回读序列的一部分、因此、包括此命令字节、您将发送总共5个 SPI 字节来时钟输出数据(例如:DIN = 0x30、0x00、0x00、0x00、0x00、0x00、0x00、0x00、 DOUT =无关、状态、MSB、中字节、LSB)。 /CS 将需要在这一整个时间内保持低电平(从读取命令开始直到所有数据都被计时输出)。

    您可能会定期读取数据、因为您尚未使用/DRDY 在新数据可用时向 FPGA 发送信号。 轮询这样的新数据时、我通常建议轮询速度比预期输出数据速率快2至4倍、以确保不会错过任何转换结果。 由于您的 ADS1258多路复用和数据是由 sinc 数字滤波器平均的、因此您需要查看表7以查看各种数据速率和延迟配置的有效输出数据速率。