Hi all,
I'm making an oscilloscope which acquires data from 2 channels, and I'm transferring data to memory using uDMA. I have decided to use uDMA channels ADC1 and ADC0 for this.
I have already made a working ping pong uDMA transfer using UDMA_CHANNEL_ADC0; however, I can't seem to get UDMA_SEC_CHANNEL_ADC10 to work at all.
I have used:
uDMAChannelSelectSecondary(UDMA_DEF_SSI1RX_SEC_ADC10);
But I'm not sure if that's all I need to do in order to use UDMA_SEC_CHANNEL_ADC10.
I've attached my code below.
Any help would be much appreciated. Thank you in advance.
/* ---------------------------------------------------------------------------- * Includes * ------------------------------------------------------------------------- */ #include <stdint.h> #include <stdbool.h> /* Tivaware Header files */ #include "inc/hw_memmap.h" #include "inc/hw_ints.h" #include "inc/hw_adc.h" #include "driverlib/timer.h" #include "driverlib/interrupt.h" #include "driverlib/sysctl.h" #include "driverlib/gpio.h" #include "driverlib/adc.h" #include "driverlib/udma.h" /* XDCtools Header files */ #include <xdc/std.h> #include <xdc/runtime/System.h> /* BIOS Header files */ #include <ti/sysbios/BIOS.h> #include <ti/sysbios/knl/Task.h> #include "adc.h" /* ---------------------------------------------------------------------------- * Defines * ------------------------------------------------------------------------- */ #define DEBUG_LED //#define DEBUG_DMA /* ---------------------------------------------------------------------------- * External variables * ------------------------------------------------------------------------- */ // Samples buffer for both channels uint16_t pui16SamplesA[MAX_NUM_SAMPLES + DMA_TRANSFER_SIZE]; uint16_t pui16SamplesB[MAX_NUM_SAMPLES + DMA_TRANSFER_SIZE]; // Flag to indicate sampling done uint8_t ui8DoneA; uint8_t ui8DoneB; uint16_t ui16TriggerPointA; /* ---------------------------------------------------------------------------- * Private variables * ------------------------------------------------------------------------- */ // Number of transfers required to obtain desired number of samples static uint8_t ui8DMATransfers; // DMA transfer destinations static uint16_t *pui16DMADestA; static uint16_t *pui16DMADestB; // Comparator levels for triggering // [CHANNEL (CHAN_A, CHAN_B)][BOUNDARY (HIGH, LOW)] static uint16_t pui16ComparatorLevels[2][2]; // Triggering utilities static volatile uint16_t ui16SamplesTracker; static volatile uint8_t ui8Triggered; static volatile uint16_t ui16SamplesTakenAfterTrigger; // uDMA control table static uint8_t pui8DMAControlTable[1024]; /* ---------------------------------------------------------------------------- * Private Functions * ------------------------------------------------------------------------- */ /* * @brief Modifies the uDMA destination address to point to the next block * to be written to. * @params The channel destination address to be changed * @retval None */ static void advance_dma_dest(uint8_t channel) { // // Increment the destination address by the uDMA transfer size. // When the address goes beyond the required nunber of samples, // have the address point to the start of the samples buffer. // switch (channel) { case CHAN_A: pui16DMADestA += DMA_TRANSFER_SIZE; if (pui16DMADestA >= (pui16SamplesA + (DMA_TRANSFER_SIZE * ui8DMATransfers))) { // Destination at end of buffer, point to start now pui16DMADestA = pui16SamplesA; } break; case CHAN_B: pui16DMADestB += DMA_TRANSFER_SIZE; if (pui16DMADestB >= (pui16SamplesB + (DMA_TRANSFER_SIZE * ui8DMATransfers))) { // Destination at end of buffer, point to start now pui16DMADestB = pui16SamplesB; } break; } } /* * @brief Interrupt handler for the ADC timer timeout. Used to keep track of * the number of samples taken after trigger, and where the latest * sample is at in the buffer. * @params None * @retval None */ void adc_timer_irq(void) { // Clear interrupt TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT); // Cycle the samples buffer trackers ui16SamplesTracker++; ui16SamplesTracker %= (DMA_TRANSFER_SIZE * ui8DMATransfers); // Count samples taken after trigger if (ui8Triggered) { ui16SamplesTakenAfterTrigger++; } } /* * @brief Helper function to test if a short is within a boundary. * @params Test value, low and high boundaries * @retval 1 if test value is within boundaries * 0 if test value is not within boundaries */ static int is_within(uint16_t test, uint16_t low, uint16_t high) { if (test > high) { return 0; } if (test < low) { return 0; } return 1; } /* * @brief Comparator interrupt for channel A. Stores triggering point, and * flips the ui8Triggered flag. * @params None * @retval None */ void adc_A_comp_irq(void) { uint16_t ui16TestSample; // Clear comparator interrupt ADCComparatorIntClear(ADC0_BASE, ADC_DCISC_DCINT0 | ADC_DCISC_DCINT1); // // Check that the current analog signal is near the comparator boundaries // If the signal is not, then this is a false trigger, return immediately // ui16TestSample = *(uint16_t *) (ADC0_BASE + ADC_O_SSFIFO0); if (!is_within(ui16TestSample, pui16ComparatorLevels[CHAN_A][LOW] - COMP_TOLERANCE, pui16ComparatorLevels[CHAN_A][LOW] + COMP_TOLERANCE) && !is_within(ui16TestSample, pui16ComparatorLevels[CHAN_A][HIGH] - COMP_TOLERANCE, pui16ComparatorLevels[CHAN_A][HIGH] + COMP_TOLERANCE) ) return; #ifdef DEBUG_LED static uint16_t i = 1; // Toggle GPIO pin for every valid trigger if (i++ % 2) { GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_4, GPIO_PIN_4); } else { GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_4, 0); } #endif // Store the triggering point, and flip the triggered flag ui16TriggerPointA = ui16SamplesTracker; ui8Triggered = 1; // Disable comparator interrupt. Reenabled when sampling begins again. IntDisable(INT_ADC0SS1); IntDisable(INT_ADC1SS1); } /* * @brief Comparator interrupt for channel B. Stores triggering point, and * flips the ui8Triggered flag. * @params None * @retval None */ void adc_B_comp_irq(void) { // Clear comparator interrupt ADCComparatorIntClear(ADC1_BASE, ADC_DCISC_DCINT2 | ADC_DCISC_DCINT3); } void adc_A_dma_irq(void) { uint32_t modePrimary; uint32_t modeAlternate; // Clear ADC uDMA interrupt ADCIntClearEx(ADC0_BASE, ADC_INT_DMA_SS0); // Get mode statuses modePrimary = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT); modeAlternate = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT); // Reload the control structures if ((modePrimary == UDMA_MODE_STOP) && (modeAlternate != UDMA_MODE_STOP)) { // Need to reload primary control structure uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), pui16DMADestA, DMA_TRANSFER_SIZE); advance_dma_dest(CHAN_A); } else if ((modePrimary != UDMA_MODE_STOP) && (modeAlternate == UDMA_MODE_STOP)) { // Need to reload alternate control structure uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), pui16DMADestA, DMA_TRANSFER_SIZE); advance_dma_dest(CHAN_A); } else { // Either both still not stopped, or both stopped. This is an error #ifdef DEBUG_DMA System_printf("Error in uDMA control structure modes\n"); System_printf("Primary mode: %d\nAlternate mode: %d\n", modePrimary, modeAlternate); System_printf( "For reference: Stop:%d, Basic:%d, Auto:%d, PingPong:%d, MemScatter:%d, PerScatter:%d\n", UDMA_MODE_STOP, UDMA_MODE_BASIC, UDMA_MODE_AUTO, UDMA_MODE_PINGPONG, UDMA_MODE_MEM_SCATTER_GATHER, UDMA_MODE_PER_SCATTER_GATHER); System_flush(); #endif } // TODO: MOdify so that sampling only stops when both channels are done // Stop udma if we have triggered, and obtained the correct amount of samples if (ui16SamplesTakenAfterTrigger >= (DMA_TRANSFER_SIZE * ui8DMATransfers / 2)) { ui8DoneA = 1; ui8DoneB = 1; } if (ui8DoneA && ui8DoneB) { sampling_stop(); } } void adc_B_dma_irq(void) { uint32_t modePrimary; uint32_t modeAlternate; // Clear ADC uDMA interrupt ADCIntClearEx(ADC1_BASE, ADC_INT_DMA_SS0); // Get mode statuses modePrimary = uDMAChannelModeGet(UDMA_SEC_CHANNEL_ADC10 | UDMA_PRI_SELECT); modeAlternate = uDMAChannelModeGet(UDMA_SEC_CHANNEL_ADC10 | UDMA_ALT_SELECT); // Reload the control structures if ((modePrimary == UDMA_MODE_STOP) && (modeAlternate != UDMA_MODE_STOP)) { // Need to reload primary control structure uDMAChannelTransferSet(UDMA_SEC_CHANNEL_ADC10 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC1_BASE + ADC_O_SSFIFO0), pui16DMADestB, DMA_TRANSFER_SIZE); advance_dma_dest(CHAN_B); } else if ((modePrimary != UDMA_MODE_STOP) && (modeAlternate == UDMA_MODE_STOP)) { // Need to reload alternate control structure uDMAChannelTransferSet(UDMA_SEC_CHANNEL_ADC10 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC1_BASE + ADC_O_SSFIFO0), pui16DMADestB, DMA_TRANSFER_SIZE); advance_dma_dest(CHAN_B); } else { // Either both still not stopped, or both stopped. This is an error #ifdef DEBUG_DMA System_printf("Error in uDMA control structure modes\n"); System_printf("Primary mode: %d\nAlternate mode: %d\n", modePrimary, modeAlternate); System_printf( "For reference: Stop:%d, Basic:%d, Auto:%d, PingPong:%d, MemScatter:%d, PerScatter:%d\n", UDMA_MODE_STOP, UDMA_MODE_BASIC, UDMA_MODE_AUTO, UDMA_MODE_PINGPONG, UDMA_MODE_MEM_SCATTER_GATHER, UDMA_MODE_PER_SCATTER_GATHER); System_flush(); #endif } // TODO: MOdify so that sampling only stops when both channels are done // Stop udma if we have triggered, and obtained the correct amount of samples if (ui16SamplesTakenAfterTrigger >= (DMA_TRANSFER_SIZE * ui8DMATransfers / 2)) { ui8DoneB = 1; } if (ui8DoneA && ui8DoneB) { sampling_stop(); } } /* * ========== adc_trigger_timer_init ========== * @brief Initializes the timer for triggering ADC samples */ void adc_trigger_timer_init(uint32_t ui32ClockFreq, uint32_t ui32SampleFreq) { // Enable timer peripheral clock SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER1)) { } // Register interrupt TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT); IntRegister(INT_TIMER1A, adc_timer_irq); IntEnable(INT_TIMER1A); TimerConfigure(TIMER1_BASE, TIMER_CFG_A_PERIODIC_UP); TimerLoadSet(TIMER1_BASE, TIMER_A, (ui32ClockFreq / ui32SampleFreq)); TimerControlTrigger(TIMER1_BASE, TIMER_A, true); TimerADCEventSet(TIMER1_BASE, TIMER_ADC_TIMEOUT_A); TimerEnable(TIMER1_BASE, TIMER_A); } void comparator_levels_set(uint16_t low, uint16_t high, uint8_t channel) { pui16ComparatorLevels[channel][LOW] = low; pui16ComparatorLevels[channel][HIGH] = high; switch (channel) { case CHAN_A: ADCComparatorConfigure(ADC0_BASE, 0, ADC_COMP_INT_LOW_HONCE); ADCComparatorConfigure(ADC0_BASE, 1, ADC_COMP_INT_HIGH_HONCE); ADCComparatorRegionSet(ADC0_BASE, 0, low, high); ADCComparatorRegionSet(ADC0_BASE, 1, low, high); break; case CHAN_B: ADCComparatorConfigure(ADC1_BASE, 2, ADC_COMP_INT_LOW_HONCE); ADCComparatorConfigure(ADC1_BASE, 3, ADC_COMP_INT_HIGH_HONCE); ADCComparatorRegionSet(ADC1_BASE, 2, low, high); ADCComparatorRegionSet(ADC1_BASE, 3, low, high); break; } } uint16_t comparator_level_get(uint8_t region, uint8_t channel) { return pui16ComparatorLevels[channel][region]; } /* * ========== adc_init ========== * @brief Initializes ADCs for both channels A and B */ void adc_init(void) { /* * Channel A: * ADC0 * Sequencer 0, Step 0 - Sample and uDMA * Sequencer 1, Step 0 - Falling edge trigger (CMP0) * Sequencer 1, Step 1 - Rising edge trigger (CMP1) * Pin PE5 (Channel A8) * * Channel B: * ADC1 * Sequencer 0, Step 0 - Sample and uDMA * Sequencer 1, Step 0 - Falling edge trigger (CMP2) * Sequencer 1, Step 1 - Rising edge trigger (CMP3) * Pin PE4 (Channel A9) */ // Enable GPIO peripheral clock for the ADC input SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE); SysCtlPeripheralReset(SYSCTL_PERIPH_GPIOE); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE)) { } // Enable ADC peripheral clock SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0)) { } SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1); SysCtlPeripheralReset(SYSCTL_PERIPH_ADC1); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC1)) { } // Configure GPIO pins as ADC inputs GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_4); GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_5); // Disable interrupts ADCIntDisableEx(ADC0_BASE, ADC_INT_DMA_SS0 | ADC_INT_DCON_SS1); ADCIntDisableEx(ADC1_BASE, ADC_INT_DMA_SS0 | ADC_INT_DCON_SS1); // Disable sequences ADCSequenceDisable(ADC0_BASE, 0); ADCSequenceDisable(ADC0_BASE, 1); ADCSequenceDisable(ADC1_BASE, 0); ADCSequenceDisable(ADC1_BASE, 1); // Configure the steps of each sequencer // Channel A ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH8 | ADC_CTL_END | ADC_CTL_IE); ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_CH8 | ADC_CTL_CMP0 | ADC_CTL_IE); ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_CH8 | ADC_CTL_CMP1 | ADC_CTL_END | ADC_CTL_IE); // Channel B ADCSequenceStepConfigure(ADC1_BASE, 0, 0, ADC_CTL_CH9 | ADC_CTL_END | ADC_CTL_IE); ADCSequenceStepConfigure(ADC1_BASE, 1, 0, ADC_CTL_CH9 | ADC_CTL_CMP2 | ADC_CTL_IE); ADCSequenceStepConfigure(ADC1_BASE, 1, 1, ADC_CTL_CH9 | ADC_CTL_CMP3 | ADC_CTL_END | ADC_CTL_IE); // Configure to use internal 3V reference ADCReferenceSet(ADC0_BASE, ADC_REF_INT); ADCReferenceSet(ADC1_BASE, ADC_REF_INT); // Configure ADC comparators comparator_levels_set(COMP_DEFAULT_LOW, COMP_DEFAULT_HIGH, CHAN_A); comparator_levels_set(COMP_DEFAULT_LOW, COMP_DEFAULT_HIGH, CHAN_B); // TODO ADCComparatorReset(ADC0_BASE, 0, true, true); ADCComparatorReset(ADC0_BASE, 1, true, true); // Enable ADC interrupts for uDMA transfer completion, // and digital comparator ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0 | ADC_INT_DCON_SS1); ADCIntEnableEx(ADC1_BASE, ADC_INT_DMA_SS0 | ADC_INT_DCON_SS1); // Register interrupts IntRegister(INT_ADC0SS1, adc_A_comp_irq); IntRegister(INT_ADC1SS1, adc_B_comp_irq); IntRegister(INT_ADC0SS0, adc_A_dma_irq); IntRegister(INT_ADC1SS0, adc_B_dma_irq); // Enable interrupts IntEnable(INT_ADC0SS0); IntEnable(INT_ADC1SS0); // Enable ADC to use uDMA ADCSequenceDMAEnable(ADC0_BASE, 0); ADCSequenceDMAEnable(ADC1_BASE, 0); // Enable ADC sequencers ADCSequenceEnable(ADC0_BASE, 0); ADCSequenceEnable(ADC0_BASE, 1); ADCSequenceEnable(ADC1_BASE, 0); ADCSequenceEnable(ADC1_BASE, 1); // Configure the trigger sources and priorities // All sequencers are set to be triggered by timer // Sequence 0 has higher priorty (priority 0) than sequence 1 (priority 1) ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0); ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_TIMER, 1); ADCSequenceConfigure(ADC1_BASE, 0, ADC_TRIGGER_TIMER, 0); ADCSequenceConfigure(ADC1_BASE, 1, ADC_TRIGGER_TIMER, 1); } /* * @params Number of transfers required to obtain requires number of samples */ void dma_start(uint8_t ui8NumTransfers, uint8_t channel) { switch (channel) { case CHAN_A: // Disable before configure uDMAChannelDisable(UDMA_CHANNEL_ADC0); uDMAChannelDisable(UDMA_SEC_CHANNEL_ADC10); // Set the number of transfers ui8DMATransfers = ui8NumTransfers; // Reset the destination pointer to start of buffer pui16DMADestA = pui16SamplesA; pui16DMADestB = pui16SamplesB; // Configure the transfer parameters uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), pui16DMADestA, DMA_TRANSFER_SIZE); advance_dma_dest(CHAN_A); uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), pui16DMADestA, DMA_TRANSFER_SIZE); advance_dma_dest(CHAN_A); uDMAChannelTransferSet(UDMA_SEC_CHANNEL_ADC10 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC1_BASE + ADC_O_SSFIFO0), pui16DMADestB, DMA_TRANSFER_SIZE); advance_dma_dest(CHAN_B); uDMAChannelTransferSet(UDMA_SEC_CHANNEL_ADC10 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC1_BASE + ADC_O_SSFIFO0), pui16DMADestB, DMA_TRANSFER_SIZE); advance_dma_dest(CHAN_B); uDMAChannelEnable(UDMA_CHANNEL_ADC0); uDMAChannelEnable(UDMA_SEC_CHANNEL_ADC10); break; } } void dma_init(void) { SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA)) { } // Enable uDMA uDMAEnable(); uDMAChannelSelectSecondary(UDMA_DEF_SSI1RX_SEC_ADC10); // Set the control table for uDMA uDMAControlBaseSet(&pui8DMAControlTable[0]); // Disable unneeded attributes of the uDMA channels uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0, UDMA_ATTR_ALL); uDMAChannelAttributeDisable(UDMA_SEC_CHANNEL_ADC10, UDMA_ATTR_ALL); // Channel A uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1); uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1); // Channel B uDMAChannelControlSet(UDMA_SEC_CHANNEL_ADC10 | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1); uDMAChannelControlSet(UDMA_SEC_CHANNEL_ADC10 | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1); } void sampling_start(uint16_t numSamples, uint32_t clockFreq, uint32_t samplingFreq) { ui16SamplesTracker = 0; ui8Triggered = 0; ui8DoneA = 0; ui8DoneB = 0; ui16SamplesTakenAfterTrigger = 0; ui16TriggerPointA = 0; #ifdef DEBUG_LED // Enable Port C SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOC)) { } // Set C4 to be output GPIOPinTypeGPIOOutput(GPIO_PORTC_BASE, GPIO_PIN_4); #endif dma_init(); dma_start((numSamples / DMA_TRANSFER_SIZE), CHAN_A); adc_init(); adc_trigger_timer_init(clockFreq, samplingFreq); Task_sleep(10); IntEnable(INT_ADC0SS1); IntEnable(INT_ADC1SS1); } // TODO: not channel independent void sampling_stop() { IntDisable(INT_ADC0SS0); IntDisable(INT_ADC1SS0); uDMAChannelDisable(UDMA_CHANNEL_ADC0); uDMAChannelDisable(UDMA_SEC_CHANNEL_ADC10); TimerDisable(TIMER1_BASE, TIMER_A); ADCSequenceDisable(ADC0_BASE, 0); ADCSequenceDisable(ADC1_BASE, 0); }