I'm trying to write data from SRAM to an external SPI DAC using DMA. The DAC requires 24bit transfers, so the framesize needs to be 24bits. I'm trying to load all 8 DAC channels in one DMA transfer, so 24bytes per DMA transfer. I don't want CPU intervention, so all needs to be configured in DMA setup, otherwise it defeats the purpose of using DMA.
At this point I won't share my code unless we get into it further, but I'm using the i.MX 1060 HAL driver layer (fsl_lpspi_edma.c, fsl_lpspi.c, etc...) based on the lpspi_edma_b2b_transfer_master SDK example. Transfers work fine if I configure framesize to 8 or 32bits, but I can't get it to work for 24bits (required by the AD5676 SPI DAC).
When I drill down into the relevant i.MX API functions LPSPI_MasterTransferPrepareEDMALite & LPSPI_MasterTransferEDMALite I can see that it reads the TCR to check the framesize and configures bytesEachWrite and there is a case statement in the Tx handling to handle bytesEachWrite with options of 1, 2, and 4 bytes. There is no case for 3 bytes. Is it possible? Is it not possible? Does it have to do with the DMA bus width of 4 bytes so the bytesEachWrite needs to be a multiple of 4 in order to pack the DMA width? Is it possible with a more sophisticated approach but just not possible with provided i.MX API?
ok thanks. I will reply again to confirm whether or not I get it figured out. It may not actually save much CPU time anyways but I would like to at least try it. I'm sure there are many people out there using 12/16-bit DACs and ADCs with 24bit frame sizes that may find this useful in the future..
Thanks, any comments are welcome!
Hi @azone
Please see my comments as following, I am not sure if my understanding is wrong.
It will do LPSPI_CheckTransferArgument
in function LPSPI_MasterTransferEDMALite
, it seems
And https://github.com/nxp-mcuxpresso/mcux-sdk/blob/1277588e8c34439fc08c657d3176d8a354853643/drivers/lps... shows that It will disable EDMA transfers with 3-byte remainder.
B.R,
Sam
ok thanks. I think I understand. Basically there can't be a remainder, so for transfers <= 4 bytes the transfer must be 1 bytes, 2 bytes or 4 bytes, and the DMA source and dest width would just be set to the transfer size? The case of 3 bytes wouldn't be possible as the PCS assertion/deassertion would only happen at the boundary of 1/2/4 bytes, and can't happen at the boundary of 3 bytes?
Is it possible that there is a solution by sending 1 byte + 2bytes by chaining multiple TCD's, then the data would be effectively framed at 3bytes boundary? Basically setting up something like this...
// Configure TCDs for 3-byte framesize
edma_tcd_t tcd1, tcd2;
// Reset the TCDs to default values
EDMA_TcdReset(&tcd1);
EDMA_TcdReset(&tcd2);
// Configure the first TCD for the first 1 byte
tcd1.SADDR = (uint32_t)data;
tcd1.SOFF = 1; // 1 byte offset
tcd1.ATTR = (tcd1.ATTR & ~DMA_ATTR_SSIZE_MASK) | DMA_ATTR_SSIZE(kEDMA_TransferSize1Bytes);
tcd1.ATTR = (tcd1.ATTR & ~DMA_ATTR_DSIZE_MASK) | DMA_ATTR_DSIZE(kEDMA_TransferSize1Bytes);
tcd1.NBYTES = 1; // Transfer 1 byte
tcd1.SLAST = -1; // Adjust source address back by 1 byte
tcd1.DADDR = (uint32_t)&base->TDR;
tcd1.DOFF = 0; // No offset for destination
tcd1.CITER = tcd1.BITER = length / 3; // Number of 3-byte frames
tcd1.DLAST_SGA = (uint32_t)&tcd2; // Link to the second TCD
tcd1.CSR = DMA_CSR_INTMAJOR_MASK; // Enable interrupt on major loop completion
// Configure the second TCD for the remaining 2 bytes
tcd2.SADDR = (uint32_t)data + 1;
tcd2.SOFF = 2; // 2 bytes offset
tcd2.ATTR = (tcd2.ATTR & ~DMA_ATTR_SSIZE_MASK) | DMA_ATTR_SSIZE(kEDMA_TransferSize2Bytes);
tcd2.ATTR = (tcd2.ATTR & ~DMA_ATTR_DSIZE_MASK) | DMA_ATTR_DSIZE(kEDMA_TransferSize2Bytes);
tcd2.NBYTES = 2; // Transfer 2 bytes
tcd2.SLAST = -2; // Adjust source address back by 2 bytes
tcd2.DADDR = (uint32_t)&base->TDR;
tcd2.DOFF = 0; // No offset for destination
tcd2.CITER = tcd2.BITER = length / 3; // Number of 3-byte frames
tcd2.DLAST_SGA = 0; // No further chaining
tcd2.CSR = DMA_CSR_INTMAJOR_MASK; // Enable interrupt on major loop completion
// Submit the TCDs to the DMA channel
EDMA_InstallTCD(dma->dma_base, dma->dma_tx_chan, &tcd1);
EDMA_InstallTCD(dma->dma_base, dma->dma_tx_chan, &tcd2);
// Start the DMA transfer
status_t status = LPSPI_MasterTransferEDMA(base, transfer_handle, &transfer);
Yes, your understanding is completely correct. For 3 bytes, it need to splite them(1+2).