First shot at separating the setting up of the DMA transfer, and the firing of the transfer.
Basically I just separated the code that was previously in dmaSend in two parts, dmaSensdSet configured the transfer, except for the length, and enabling the channel. Next you call dmaSendRepeat (I dont really like that name, but can’t think on other appropriate one that I like) with the length of the transfer, and that sets the DMACNDTR register, which needs to be reloaded for a new transfer, and enables the channel. If a callback function has been set previously, doesn’t block and return 0 for success. If a callback function was not set, it will block.
SPI.dmaSend has the exact same functionality as before, so any code using it should not break in any way, but now it uses the other 2 functions to do the work.
This way if you want to send a number of bytes from the same buffer pointer, you only call SPI.dmaSendRepeat with 1 parameter, the number of bytes to send, everything else stays the same as the last transfer, so you only need to fill the buffer and fire the transfer.
What do you guys think? This still doesn’t implement the internal buffer Steven had been testing, but code that repeat transmissions from the same buffer should get less overhead.
EDIT: I realize it may be good to add checks to confirm whether there is a transmission already in progress before changing settings. It depends if we prefer that little overhead for safety.
uint8 SPIClass::dmaSendRepeat(uint16 length)
{
if (length == 0) return 1;
dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length);
dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit
if (_currentSetting->TXcallback){
return 0;
}
uint32_t m = millis();
uint8 b = 0;
while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & DMA_ISR_TCIF1)==0) {//Avoid interrupts and just loop waiting for the flag to be set.
if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; }
}
dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..."
while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI."
dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
spi_tx_dma_disable(_currentSetting->spi_d);
//uint16 x = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove!
return b;
}
void SPIClass::dmaSendSet(void * transmitBuf, bool minc)
{
uint32 flags = ( (DMA_MINC_MODE*minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT);
dma_init(_currentSetting->spiDmaDev);
// TX
spi_tx_dma_enable(_currentSetting->spi_d);
dma_xfer_size dma_bit_size = (_currentSetting->dataSize==SPI_DATA_SIZE_16BIT) ? DMA_SIZE_16BITS : DMA_SIZE_8BITS;
dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size,
transmitBuf, dma_bit_size, flags);// Transmit buffer DMA
}
uint8 SPIClass::dmaSend(void * transmitBuf, uint16 length, bool minc)
{
dmaSendSet(transmitBuf, minc);
return dmaRepeat(length);
}