I'm having a very strange problem and I'm running out of leads. I've built a burst measurement system using Ping-Pong DMA to pull averaged data out of ADC0 sampler #0. I'm generating triggers with PWM0, Generator 1. What I see is that the ADC system appears to be free-running and not actually triggered by the PWM Generator. I am trying to produce 64x oversampled data at 1kHz.
Heres what I know so far:
- I can drive an IO with the PWM block that triggers the ADC, and I can see that PWM0 Generator 1 is running at 64kHz.
- I can see the DMA ISR toggle an IO, and I know that the DMA engine is doing the correct number of transfers. The math is right.
- I can speed up or slow down the transfer by adjusting the sample/hold settings.
- The interrupt handler runs at uniform intervals (until the last transfer), the correct number of times - I have counters.
- It doesn't trigger an under-run (ADCUSTAT is clear).
I'm either missing something subtle or something obvious. I can't tell which. I've scrutinized the registers in the PWM block and the ADC block several times. I must be missing something.
Code for initializing the ADC and the PWM:
SysCtlPeripheralReset(SYSCTL_PERIPH_ADC1); ADCClockConfigSet(ADC1_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, 30); // 16MHz ADCReferenceSet(ADC1_BASE,ADC_REF_EXT_3V); ADCHardwareOversampleConfigure(ADC1_BASE,16); // Over-ridden later. uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_ATTR_HIGH_PRIORITY ); uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_ATTR_HIGH_PRIORITY ); uDMAChannelControlSet( UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_4); uDMAChannelControlSet( UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_4); ADCSequenceDMAEnable(ADC0_BASE,BURST_SEQUENCE); ADCIntEnableEx(ADC0_BASE,ADC_INT_DMA_SS0); // --------------------------------------------------- // Configure the PWM System to generate the events // --------------------------------------------------- // Use the PWM module for event generation. PWMClockSet(PWM0_BASE,PWM_SYSCLK_DIV_1); // The math should come out at the full rate. PWMGenConfigure (PWM0_BASE, PWM_GEN_1, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC); PWMGenPeriodSet (PWM0_BASE, PWM_GEN_1, samplerate2divisor(1000)); PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, 200); // That should be a safe value. PWMOutputState (PWM0_BASE, PWM_OUT_2_BIT, true); PWMGenEnable (PWM0_BASE, PWM_GEN_1);
Prior to activating the ADC capture, I can see PWM0 generating the 64kHz sampling clock.
Heres the code that sets up the ADC Transfer (error checking code omitted). I use tables & functions to configure the sampling rate, averaging, and S&H params:
void adc_burst_sample(unsigned samplecount, unsigned samplerate, tADCChannelSelection channel) { PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, samplerate2divisor(samplerate)); adc_burst_state.samplerate = samplerate; // Record what happened. adc_burst_state.dma_nextp = (int16_t *) ADC_AREA; adc_burst_state.requested = 0; // Nothing is scheduled yet. adc_burst_state.completed = 0; // Nothing is done yet. adc_burst_buffer_reset(); adc_burst_state.channel = channel; // Remember unsigned int channel_encoded = adc_channel2mask(channel); channel_encoded |= samplerate2SH(samplerate); // Set Sample&Hold Params ADCHardwareOversampleConfigure(ADC0_BASE,samplerate2average(samplerate));
ADCSequenceDisable(ADC0_BASE, BURST_SEQUENCE); ADCSequenceConfigure(ADC0_BASE, BURST_SEQUENCE, ADC_TRIGGER_PWM_MOD0|ADC_TRIGGER_PWM1, 0); ADCSequenceStepConfigure(ADC0_BASE, BURST_SEQUENCE, 0, channel_encoded|ADC_CTL_END); ADCSequenceEnable(ADC0_BASE,BURST_SEQUENCE); uDMAChannelTransferSet( UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void*) (ADC0_BASE + ADC_O_SSFIFO0), adc_burst_state.dma_nextp, DMA_WORKUNIT); adc_burst_state.dma_nextp += DMA_WORKUNIT; adc_burst_state.requested += DMA_WORKUNIT; uDMAChannelTransferSet( UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void*) (ADC0_BASE + ADC_O_SSFIFO0),adc_burst_state.dma_nextp, DMA_WORKUNIT); uDMAChannelEnable(UDMA_CHANNEL_ADC0); adc_burst_state.dma_nextp += DMA_WORKUNIT; adc_burst_state.requested += DMA_WORKUNIT; PWMGenIntTrigEnable(PWM0_BASE,PWM_GEN_1, PWM_TR_CNT_ZERO); adc_burst_state.duration_ms = 0; adc_burst_state.startstamp_ms = g_ulSystemTimeMS; }
And here's the handler:
void ADC0SS0Handler() { adc_burst_state.completed += DMA_WORKUNIT; // It might be less. adc_burst_state.requested -= DMA_WORKUNIT; // There are fewer in flight. ADCIntClearEx(ADC0_BASE,ADC_INT_DMA_SS0); // Clear the Irq to prevent re-trigger out of the ISR. // Check to see if we're finished. if ( adc_burst_state.completed >= adc_burst_state.totalcount ) { adc_burst_sample_wrapup(); return; } unsigned long ulMode1 = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT); unsigned long ulMode2 = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT); if ( (ulMode1 == UDMA_MODE_STOP ) && ( ulMode2 == UDMA_MODE_STOP ) ) { usprintf(trace_scribble,"!ADC Ur %x", adc_burst_state.dma_nextp); UARTprintf("%s\n",trace_scribble); return; } if ( (ulMode1 != UDMA_MODE_STOP ) && ( ulMode2 != UDMA_MODE_STOP ) ) { trace_add("!ADC Oddness"); // This should not happen. } // Count them - Remaining work is total - completed - in flight. int remaining = adc_burst_state.totalcount - \ ( adc_burst_state.requested + adc_burst_state.completed ); // We have already done the checks - We know that at least one // channel is in the stopped state, but not both. if ( remaining ) { int workunit; if ( remaining >= DMA_WORKUNIT ) workunit = DMA_WORKUNIT; else workunit = remaining; if ((ulMode1 == UDMA_MODE_STOP ) && (ulMode2 != UDMA_MODE_STOP ) ) { uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void*) (ADC0_BASE + ADC_O_SSFIFO0), adc_burst_state.dma_nextp, workunit); count_adc0_prichannel++; } if ( (ulMode1 != UDMA_MODE_STOP ) && (ulMode2 == UDMA_MODE_STOP ) && remaining ) { uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void*) (ADC0_BASE + ADC_O_SSFIFO0), adc_burst_state.dma_nextp, workunit); count_adc0_altchannel++; } adc_burst_state.dma_nextp += workunit; adc_burst_state.requested += workunit; } }
Heres a a partial register dump of the PWM block during the operation - Note that the trigger is enabled (0x44):
4002:8000 0000:0000 0000:0000 0000:0004 0000:0000 ................
4002:8010 0000:0000 0000:0000 0000:0000 0000:0000 ................
4002:8020 0000:0000 0000:0000 0000:0000 0000:0000 ................
4002:8030 0000:0000 0000:0000 0000:0000 0000:0000 ................
4002:8080 0000:0001 0000:0100 0000:000B 0000:0000 ................
4002:8090 0000:0752 0000:0300 0000:068A 0000:0000 R...L...........
4002:80A0 0000:008C 0000:080C 0000:0000 0000:0000 ................
4002:80B0 0000:0000 0000:0000 0000:0000 0000:0000 ................