espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.87k stars 7.32k forks source link

Half-duplex SPI in DMA mode reads 0 on some positions (IDFGH-3797) #5713

Open yaqwsx opened 4 years ago

yaqwsx commented 4 years ago

Environment

Problem Description

There is a half-duplex SPI bus, where ESP32 is master and talks to custom hardware. The write operation works as expected, the read operation reads several bytes correctly, the other bytes it reads as zero. The data are physically present on the bus (verified by an oscilloscope). This happens only in the DMA mode. When I call spi_bus_initialize with the dma_chan 0, everything starts to work as expected. However, DMA is not used.

Also note, that the function spi_device_transmit writes to all bytes of the output buffer, but the values are zero.

Steps to reproduce

Unfortunately, the bug is present in a fairly large codebase involving custom hardware. I wasn't able to create a simple reproducible example using only devkits and commonly available hardware (yet).

Code to reproduce this issue

Code to initialize the bus (the code uses custom C++ wrappers which are fairly simple and I can guarantee they work properly):

spi_bus_config_t busConfig = SpiBus()
    .mosiIoNum( dataPin )
    .sclkIoNum( clkPin )
    .flags( SPICOMMON_BUSFLAG_MASTER );
// This is a virtual device with no CS pin. CS pin is handled separately,
// as one SPI transaction can have variable length based on what we read from the bus.
spi_device_interface_config_t devConfig = SpiDeviceInterface()
    .mode( 0 )
    .flags( SPI_DEVICE_3WIRE | SPI_DEVICE_HALFDUPLEX )
    .queueSize( 1 )
    .clockSpeedHz( clkFrequency );
auto ret = spi_bus_initialize( bus, &busConfig, 1 ); // When we pass 0 as DMA channel, code starts to work
ESP_ERROR_CHECK( ret );
ret = spi_bus_add_device( bus, &devConfig, &_spiDev );
ESP_ERROR_CHECK( ret );

Reading from the bus:

inline void spiRead( spi_device_handle_t dev, uint8_t* where, int count ) {
    spi_transaction_t t = {};
    t.rx_buffer = where;
    memset( where, 0xAA, count ); // To recognize whether spi_device_transmit wrote something into the buffer
    t.length = t.rxlength = 8 * count;
    spi_device_transmit( dev, &t );
    // When reading 14 bytes, the last byte is zero;
    // When reading 4 bytes, the last two bytes are zero
    std::cout << "SPI READ: " << hexDump( where, count ) << "\n";
    std::cout << "SPI READ: " << charDump( where, count ) << "\n";
}
yaqwsx commented 4 years ago

Is there anything I can do to help you to resolve this issue?

davidjade commented 4 years ago

I am seeing similar problems. Using half duplex with DMA just the first byte is read then zeros for the rest. I pre-filled the buffer and it is actively changing it to zeros. I can see the SPI data with a logic analyzer and the data is on the lines, it is just not getting into the read buffer (except the first byte).