SPI交易提前终止 - ESP-IDF

8

使用ESP-IDF(ESP32 SDK)的ESP32应用程序与同一SPI总线上的两个SPI从机(ILI9341 TFT驱动程序,NRF24L01+ RF收发器)通信。总体而言,它工作得很好。然而,从RF收发器接收到的一些数据被截断了,即只有前几个字节是正确的,其余都是垃圾。

问题或多或少可以重现,仅在立即在接收截断数据之前与另一个从设备(TFT驱动程序)进行SPI通信时才会发生。

有问题的SPI事务是一个全双工事务,发送一个命令字节和10个虚拟字节,同时接收10个字节。它使用VSPI总线和DMA通道1。如果出现问题,则仅有前几个字节是正确的,而最后2到6个字节无效(为0或虚拟字节的值)。

我深入研究了SDK代码(spi_master.c),添加了调试代码,并观察到DMA的lldesc_t结构中有一个令人惊讶的值:

在事务开始时,它初始化为length = 0x0csize = 0x0c。0x0c是12个字节,即将10个字节舍入到下一个字。

在事务结束时,这些值为length = 0x07size = 0x0c(长度可能略有变化)。因此,事务仅读取7个字节,然后以某种方式终止。或者更确切地说,DMA操作终止了。

  • 你是否同意该数据表明存在早期终止?
  • 早期终止的原因是什么?
  • 是否有一些寄存器可以指示问题的原因?

代码非常简单:

uint8_t* buffer = heap_caps_malloc(32, MALLOC_CAP_DMA);

...

memset(buffer, CMD_NOP, len);
spi_transaction_t trx;
memset(&trx, 0, sizeof(spi_transaction_t));
trx.cmd = 0x61;
trx.tx_buffer = buffer;
trx.length = 8 * 10;
trx.rx_buffer = buffer;
trx.rxlength = 8 * 10;

esp_err_t ret = spi_device_transmit(spi_device, &trx);

1
除了在nCS线路上出现电气问题外,我不知道有什么方法可以提前终止SPI事务 - 也就是说,唯一的提前终止方式是主设备由于某种原因停止时钟输出。这暗示着其他设备SPI驱动程序的DMA完成中断中存在不干净或错误的代码,巧合地破坏了其他DMA通道的某些位。顺便问一下,系统如何检测事务结束? - Vroomfondel
我已经将它连接到逻辑分析仪上,SPI事务没有提前终止。似乎是DMA问题或中断处理不当。ESP-IDF代码可以在https://github.com/espressif/esp-idf/blob/master/components/driver/spi_master.c找到。中断处理程序从第405行开始。不幸的是,我对ESP32特别是SPI和DMA交互的了解有限。 - Codo
1个回答

2
似乎以下警告——在SPI从机驱动程序文档中找到——也适用于从从机接收数据的SPI主机:

警告:由于ESP32的设计特殊性,如果主机发送的字节数或从机驱动程序中传输队列的长度(以字节为单位)既不大于8且不可被4整除,则SPI硬件可能无法将最后一个到第七个字节写入接收缓冲区。

我现在已经改变了发送方,使其至少发送12个字节和4的倍数,问题已经解决。 如果您认为这只是因为运气好,我的假设是错误的,请让我知道。

1
考虑到在32位架构上处理DMA,需要4字节增量的要求并不难理解。数据长度的灵活性将在很大程度上取决于DMA启用的SPI驱动程序的实现方式。从您找到的注释中可以看出,驱动程序的作者没有实现您最初编程的功能。这与STM32F7 SPI和DMA外设有一些相似之处,特别是在STM32F7 SPI外设上发现的4字节FIFO - 所以这并不罕见......底线是 - 您找到了解决方案。 - bamos
我也遇到了这个问题(当你设置dma_chan=0并且不使用DMA时,它会立即消失),解决方法是告诉ESP32在一个事务中读取32位,而从设备只期望发送16位---当然要丢弃最后的16位。 - Keeley Hoek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接