I have built a uDMA scatter-gather DMA task list which is triggered by a falling edge of an external ADC BUSY signal on a GPIO A7. The task list writes four 16-bit words to the SSI1 transmit FIFO, writes 0x80 to GPIO_PORTA_AHB_BASE+GPIO_O_ICR to attempt to clear the DMA request, then copies the starting state of the primary uDMA DMACHCTL back into that word to loop the DMA. Prior to adding the loop task, I got one 56(=14*4)-pulse burst on the SSI on starting the uDMA, indicating that the SSI is set up correctly and the DMA transfer is working.
However, when I add the loop task, I get a continuous stream of clock pulses on the SSI, instead of the expected 56-pulse burst, once after each falling edge of GPIO A7. Reducing the GPIO A7 trigger pulse frequency to 10 KHz has no effect on the continuous stream. This indicates to me that the DMA request is stuck "on." Writing to GPIO_O_ICR doesn't help. How do I clear the request, to prevent continuous clock pulses?
Here is the start of the channel control structure table, just prior to starting the process:
Here is the task list pointed to by the primary entry above:
Here are the GPIO register contents:
In symbolic form, here are the primary channel controls structure and task contents, some of which is initialized dynamically by a routine similar to uDMATaskStructEntry():
//! Coil pusher primary uDMA entry pointing to task list
const tDMAControlTable adcPusherUdmaPrimary = {
((char *)adcPusherUdmaTask)+sizeof(adcPusherUdmaTask)-1,
&(uDmaCcs[ni::udmaAlternate][ni::adcBusyCoilUdmaChannel].ui32Spare),
UDMA_CHCTL_DSTINC_32 | UDMA_CHCTL_DSTSIZE_32 |
UDMA_CHCTL_SRCINC_32 | UDMA_CHCTL_SRCSIZE_32 |
UDMA_CHCTL_ARBSIZE_4 |
((sizeof(adcPusherUdmaTask)/sizeof(uint32_t)-1) << UDMA_CHCTL_XFERSIZE_S) |
UDMA_CHCTL_XFERMODE_PER_SG,
0
};
uint32_t adcPusherUdmaPrimaryInit;
//! Udma source for pusher self-loop copy
uint32_t adcPusherUdmaRequestClear = ni::adcBusyGpioCoilMask;
//! Udma source for pusher uDMA request clear
...
// Point primary ADC pusher uDMA to task list.
uDmaCcs[ni::udmaPrimary][ni::adcBusyCoilUdmaChannel] = adcPusherUdmaPrimary;
// Save off reinitialization value for loop.
adcPusherUdmaPrimaryInit = adcPusherUdmaPrimary.ui32Control;
// Push a burst of clocks to the SSI.
uDMATaskAssign(
adcPusherUdmaTask[0],
sizeof(adcPusher)/sizeof(adcPusher[0]), UDMA_SIZE_16,
UDMA_SRC_INC_16, adcPusher,
UDMA_DST_INC_NONE, (void *)(ni::adcSsi + SSI_O_DR),
UDMA_ARB_1, UDMA_MODE_MEM_SCATTER_GATHER
);
// Clear uDMA request.
uDMATaskAssign(
adcPusherUdmaTask[1],
1, UDMA_SIZE_32,
UDMA_SRC_INC_32, &adcPusherUdmaRequestClear,
UDMA_DST_INC_32, (void *)(ni::adcBusyGpioCoilPort + GPIO_O_ICR),
UDMA_ARB_4, UDMA_MODE_MEM_SCATTER_GATHER
);
// Loop to repeat the pusher task.
uDMATaskAssign(
adcPusherUdmaTask[2],
1, UDMA_SIZE_32,
UDMA_SRC_INC_32, &adcPusherUdmaPrimaryInit,
UDMA_DST_INC_32,
(void *)&(uDmaCcs[ni::udmaPrimary][ni::adcBusyCoilUdmaChannel].ui32Control),
UDMA_ARB_4, UDMA_MODE_MEM_SCATTER_GATHER
);
Best regards,
Leo Bredehoft