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);