I have been using various configurations of DMA and generally with a little work I have managed to get them all running. Now I am trying a continuous DMA stream triggered by the PIT.
I want two 16-bit transfers per minor cycle (ie PIT trigger) from two contiuguous table entries in data RAM to two rigsters offset by 16 byte addresses. Then I want a bunch of minor cycles before the table addresses go back to the beginning.
It is not working.
I am getting a huge number of transfers at the very first PIT trigger, instead of just one full minor loop per PIT trigger.
For instance I set the PIT for several seconds and then a few seconds after starting I suddenly get a horde of DMA transfers, not just one minor loop.
Some of the DMA and PIT setup code for this DMA channel:
// uint16_t table; DMA_TCD tmpTCD;tmpTCD.SADDR = (void *)&warble;tmpTCD.DADDR = (void *)&FTM1_BASE_PTR->MOD;// target first register address is base + 8// target second register address is base + 16 + 8 * n (24 with n = 1)// all destination registers and source table entries are 16 bits// DMA destination set to toggle between addresses 8 and 24, so offset by 16 (8 to 24, or 24 to 40) modulo 2 ^ 5 (32, so 40 maps back to 8 etc)// modulo disabled on source table entriestmpTCD.ATTR = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DMOD(5) | DMA_ATTR_DSIZE(1);tmpTCD.DOFF = 16;// and source table offset is just the size of one entry, or 2 (16 bits)tmpTCD.SOFF = 2; // two 16-bit transfers per channel timer request is 4 bytes per requesttmpTCD.NBYTES_MLNO = 4;// the cycle completes after the source table entries have been exhaustedtmpTCD.CITER_ELINKNO = sizeof(table) / sizeof(table);tmpTCD.BITER_ELINKNO = sizeof(table) / sizeof(table);// when the cycle completes, reset the source address to the first source table entrytmpTCD.SLAST = -sizeof(warble);tmpTCD.DLAST_SGA = NULL;// use no SGA, no DREQ, no interrupts, just keep streaming table data to the PWM pintmpTCD.CSR = 0;startDMA(&tmpTCD, ALWAYS_ENABLED_54);PIT_BASE_PTR->MCR &= ~PIT_MCR_MDIS_MASK; // clear MDIS to enable modulePIT_BASE_PTR->MCR |= PIT_MCR_FRZ_MASK;PIT_BASE_PTR->CHANNEL[FTM_SPKR_DMA].LDVAL = 150000; // 3ms update at 50 MHz K60 internal buss clockPIT_BASE_PTR->CHANNEL[FTM_SPKR_DMA].TCTRL |= PIT_TCTRL_TEN_MASK; // enable requests
Any ideas what I am messing up so badly?
Just in case anyone wonders looking at the code snippets... warble is actual name I am using, table is what I tried to put in the snippet just for the post, but I missed a few places. They are the same in the actual code however.
And yes the two registers I am trying to target with the DMA are the FTM module MOD and channel CnV registers. And I need to target them on a 3ms interval, not as they expire, which is why I am trying to use the PIT trigger channel. It may be that I should try another FTM channel instead but the PIT seemed so very much simpler to use and my code is already a nightmare for someone else to maintain.
And I should note that the actual data transferred over DMA seems to be perfectly correct, the table entires go into the two registers right on target without messing up anything else in between or around etc.
The issue is just that I get infinite minor cycles per PIT trigger, not just one minor cycle (2 16-bit transfers) like I would like.
I would think the TCD.MLBYTES = 4 would do what I want, but for whatever reason that parameter does not seem to be having the desired effect.
Okay, this did not work, so I implemented a work-around.
In this particular case I was able to change from one of the "always enabled" triggers (which really seem busted with the PIT even though they are designed to work together) to use the actual target channel FTM trigger and enable it for DMA.
Now every time the FTM crosses the mid-point and sets the channel flag it requests DMA, and the next time the PIT timer kicks off it gets its one request satisfied (one full minor loop). Then since it is a peripheral triggered event and not always enabled it stops until the next trigger.
So this time it does the same thing as what I wanted and it is working. But if I wanted some other application without a discrete trigger available and wanted the PIT to provide a reliable trigger mechanism I doubt it would work. Although I'd be happy to hear from someone who has made this work letting me know what I did wrong.
Turns out this is a bug in the silicon.
It will be announced soon, but here is the errata description:
e4588: DMAMUX: When using PIT with "always enabled" request, DMA request does not deassert correctly
Errata type: Errata
Description: The PIT module is not assigned as a stand-alone DMA request source in the DMA request
mux. Instead, the PIT is used as the trigger for the DMAMUX periodic trigger mode. If you want
to use one of the PIT channels for periodic DMA requests, you would use the periodic trigger
mode in conjunction with one of the "always enabled" DMA requests. However, the DMA
request does not assert correctly in this case.
Instead of sending a single DMA request every time the PIT expires, the first time the PIT
triggers a DMA transfer the "always enabled" source will not negate its request. This results in
the DMA request remaining asserted continuously after the first trigger.
Workaround: Use of the PIT to trigger DMA channels where the major loop count is greater than one is not
recommended. For periodic triggering of DMA requests with major loop counts greater than
one, we recommended using another timer module instead of the PIT.
If using the PIT to trigger a DMA channel where the major loop count is set to one, then in
order to get the desired periodic triggering, the DMA must do the following in the interrupt
service routine for the DMA_DONE interrupt:
1. Set the DMA_TCDn_CSR[DREQ] bit and configure DMAMUX_CHCFGn[ENBL] = 0
2. Then again DMAMUX_CHCFGn[ENBL] = 1, DMASREQ=channel in your DMA DONE
interrupt service routine so that "always enabled" source could negate its request then DMA
request could be negated.
This will allow the desired periodic triggering to function as expected.
I think I have the same problem with you.Only the first PIT trigger is useful,and then it seems to be bypassed.The channel is always triggered.It is different from the Fig.7-5 in KQRUG.pdf.
Can anyone explain?