Possible to use 3 bytes frame size w/ DMA and LPSPI? Can't get to work.

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Possible to use 3 bytes frame size w/ DMA and LPSPI? Can't get to work.

783 Views
azone
Contributor I

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_MasterTransferPrepareEDMALiteLPSPI_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? 

0 Kudos
Reply
5 Replies

697 Views
azone
Contributor I

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..  

0 Kudos
Reply

688 Views
Sam_Gao
NXP Employee
NXP Employee

Thanks, any comments are welcome!

0 Kudos
Reply

739 Views
Sam_Gao
NXP Employee
NXP Employee

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 

  • Small frames (≤4B) require total data length alignment
  • Large frames (>4B) are based on whether they are 4-byte aligned:
    • Non-quadruple frames: Force a single complete frame transfer (avoid DMA alignment issues)
    • Quadruple frames: Allow multiple frame transfers (compatible with DMA block transfers)

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

 

0 Kudos
Reply

734 Views
azone
Contributor I

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);
0 Kudos
Reply

708 Views
Sam_Gao
NXP Employee
NXP Employee

Yes, your understanding is completely correct. For 3 bytes, it need to splite them(1+2).

0 Kudos
Reply