The SPI DMA block transfer call back for transfer complete is based on DMA TX complete. The SPI interface may still be busy which may cause following single byte transfers to fail.
Here is part of a routine that writes a block to an SD card.
spiSend(src, 512); // send data
spiSend(crc >> 8); // send dummy crc byte
spiSend(crc & 0XFF); // send dummy crc byte
m_status = spiReceive(); // get status token
chipSelectHigh(); // set chip select high and send byte to make MISO high-Z
This transfer works correctly if the 512 data bytes are sent byte at a time. Here is the end of the transfer. D0 is CS, D1 is SCK, D2 is MOSI, and D3 is MISO.
You can see a few clocks of the last data byte, two bytes of CRC, the return status and the dummy byte to release MISO.
If the 512 byte data transfer is done with a block transfer like this, The status read returns the wrong value.
static volatile bool SPI_DMA_TransferCompleted = false;
void SD_SPI_DMA_TransferComplete_Callback(void) {
SPI_DMA_TransferCompleted = true;
}
void SdSpiLib::send(const uint8_t* buf , size_t n) {
SPI_DMA_TransferCompleted = false;
SPI.transfer((void *)buf, 0, n, SD_SPI_DMA_TransferComplete_Callback);
while(!SPI_DMA_TransferCompleted);
}
The problem appears to be that Transfer complete callback happens too soon. The byte transfers seem to overlap with the DMA. SCK has no gaps as expected for single byte transfer.
Here is a scope picture.
If I add a bit of delay after the transfer complete call the SD write works correctly.
void SdSpiLib::send(const uint8_t* buf , size_t n) {
SPI_DMA_TransferCompleted = false;
SPI.transfer((void *)buf, 0, n, SD_SPI_DMA_TransferComplete_Callback);
while(!SPI_DMA_TransferCompleted);
delayMicroseconds(10); // <<== ADDED DELAY
}
Here is the SPI trace after the delay.
You can see the 10 μs delay after the DMA followed by the single byte transfers.
Various SPI transfers fail depending on clock rate and program timing unless the delay is added. I suspect basing the SPI transfer done call on RX done would solve the problem.