lpcware

GPDMA + DAC, V2.12

Discussion created by lpcware Employee on Jun 15, 2016
Latest reply on Nov 23, 2017 by Denis Tcherniak
Content originally posted in LPCWare by mch0 on Sat Sep 13 15:25:48 MST 2014
Hi,

when I tried to run the DAC by GPDMA I ran into two problems with the current LPCOpen libraries (2.12). The good news first: Finally all runs as expected. But I think there is room for improvement for an upcoming 2.13 of LPCOpen.

1. Transfer width
The DAC has a 32bit register for the value, all right. However, only the lower halfword contains the value that will be converted, the upper halfword contains static information.
So when you create a table im memory for DAC output, you would naturally use a table with 16bit entries.
However, in LPCOpen 2.12 the "natural width" for the DAC is looked up as a WORD, i.e. 32 bit.
These values are predefined in a lookup table in gpdma_18xx_43cc.c:

/* Optimized Peripheral Source and Destination transfer width (18xx,43xx) */
static const uint8_t GPDMA_LUTPerWid[] = {
...


I suggest to change the entry for the DAC (third from bottom) from GPDMA_WIDTH_WORD to GPDMA_WIDTH_HALFWORD.

With that the DMA will write only the lower 16 bits into the DAC value register - works perfectly. This cuts the table space in memory in half. The DMA also uses less bandwith, since the memory read requests are also halved.

2. GPDMA setup
I found it very complicated to set up a linked list transfer, when a peripheral is involved. I am not sure whether I missed the "easy" way, but here it goes.
When you use linked lists you have to provide the (chain of) LLI descriptors. There is a function to help you prepare one: Chip_GPDMA_PrepareDescriptor().
That's fine, because you don't want to calculate the control word entry yourself.
But: This function expects a lookup value for a peripheral, if one is used.
So you don't enter the "real target" but e.g. GPDMA_CONN_DAC for the DAC. The function then looks up the target address for you (fine) and uses this value also for other calculations.
After the call this entry is replaced by the real address (fine again).
But after preparing the LLI you have to pass a pointer to it when you prepare the actual transfer with
Chip_GPDMA_SGTransfer() that function also expects the "lookup value", not the already transformed one!
If you give it the LUT value (again) the GPDMA will crash later, if you give it the real address, it will crash when trying to use it for look-up.

I found no way to resolve the problem with the current DAC API.
My workaround does not look nice, I had to create a temporary LLI to get the second function working:


// fill in the real LLI, this also calculates the correct control word
Chip_GPDMA_PrepareDescriptor(LPC_GPDMA,&daclli, (uint32_t)dacfield, GPDMA_CONN_DAC, 1024, GPDMA_TRANSFERTYPE_M2P_CONTROLLER_DMA, &daclli);

// create a temporary LLI for the API call, this can be discarded after the call
templli.ctrl=daclli.ctrl;
templli.lli=(uint32_t)&daclli;
templli.src=daclli.src;
templli.dst=GPDMA_CONN_DAC;

// now arm the channel for transfers , the chain starts with the real LLI
Chip_GPDMA_SGTransfer(LPC_GPDMA, dmaChannelNum, &templli, GPDMA_TRANSFERTYPE_M2P_CONTROLLER_DMA);


If I have not overlooked the simple & easy solution I'd suggest a change in the API to use "cooked" and "real" values in a more consistent way.

However, once you get the GPDMA/DAC running, it's a charm  :) . With a single LLI descriptor, linked back to itself, you have a complete function generator running in the background ...

Best regards,

Mike




Outcomes