This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Hello,
I'm trying to use uDMA ping pong mode in order to fill a buffer with ADC samples.
The uint16_t buffer is of size 1024 * 50, and I'm filling it 1024 samples per transfer. So for instance:
The pointer that I am using in uDMAChannelTransferSet is going to be incremented by 2048 each time the control structure is reloaded, and when it reaches 50*1024 or more, it will go back to 0 and begin writing from there again.
I used memory browser and confirmed that the uDMA is filling the buffer from 0 to (50*1024 - 1), however;
My problem is when the pointer goes back to 0, it cannot overwrite the values that are already there. Is it possible to over write it? I would like to have a buffer that is constantly updated with the latest samples.
How do I fix this? Or is there a better alternative to what I am currently doing?
Please take a look at the code below, and if needed, the project attached in this post.
Any help would be much appreciated. Thank you in advance.
#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> /* TI-RTOS Header files */ // #include <ti/drivers/EMAC.h> #include <ti/drivers/GPIO.h> // #include <ti/drivers/I2C.h> // #include <ti/drivers/SDSPI.h> // #include <ti/drivers/SPI.h> // #include <ti/drivers/UART.h> // #include <ti/drivers/USBMSCHFatFs.h> // #include <ti/drivers/Watchdog.h> // #include <ti/drivers/WiFi.h> /* Board Header file */ #include "Board.h" /* Defines */ #define TASK_STACK_SIZE 131072 /* Private variable declarations */ // // Task object handles // Task_Struct ADCInitTask; // // Stacks for task objects // Char ADCInitTaskStack[TASK_STACK_SIZE]; #define ADC_BUF_SIZE 1024 #define UDMA_NUM_TRANSFERS 50 #define CLK_FREQ 30000000 #define SAMPLE_FREQ 1000000 uint8_t pui8DMAControlTable[1024]; uint16_t pui16Samples[ADC_BUF_SIZE * UDMA_NUM_TRANSFERS]; uint16_t *pui16APri = pui16Samples; uint16_t *pui16AAlt = pui16Samples + ADC_BUF_SIZE; uint32_t uDMATransferCount = 0; /* =========================== Private functions ================================== */ void adc_irq_handler(void) { // Clear the interrupt ADCIntClearEx(ADC0_BASE, ADC_INT_DMA_SS0); /* Check whether primary control has completed transfer */ if (uDMAChannelModeGet( UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT) == UDMA_MODE_STOP) { // Calculate the base pointer to write to for the primary control structure pui16APri += ADC_BUF_SIZE * 2; if (pui16APri >= (pui16Samples + (ADC_BUF_SIZE * 10))) { pui16APri = pui16Samples; } // Reload the primary control structure uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), pui16APri, ADC_BUF_SIZE); uDMATransferCount++; return; } /* Check whether alternate control has completed transfer */ if (uDMAChannelModeGet( UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT) == UDMA_MODE_STOP) { // Calculate base pointer to write to for the alternate control structure pui16AAlt += ADC_BUF_SIZE * 2; if (pui16AAlt >= (pui16Samples + (ADC_BUF_SIZE * 10))) { pui16AAlt = pui16Samples + ADC_BUF_SIZE; } // Reload the alternate control structure uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), pui16AAlt, ADC_BUF_SIZE); uDMATransferCount++; return; } } void timer_init(void) { uint32_t ui32ClockFreq; // Set clock frequency ui32ClockFreq = SysCtlClockFreqSet(SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480, CLK_FREQ); // Enable timer peripheral clock SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); SysCtlPeripheralReset(SYSCTL_PERIPH_TIMER0); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER0)) { } // Configure the timer TimerConfigure(TIMER0_BASE, TIMER_CFG_A_PERIODIC_UP); TimerLoadSet(TIMER0_BASE, TIMER_A, (ui32ClockFreq / SAMPLE_FREQ)); // Enable timer to trigger ADC TimerControlTrigger(TIMER0_BASE, TIMER_A, true); // Enable event to trigger ADC TimerADCEventSet(TIMER0_BASE, TIMER_ADC_TIMEOUT_A); // Enable timer TimerEnable(TIMER0_BASE, TIMER_A); } void ADC_init(void) { // 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)) { } // Set PE5 to be ADC input GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_5); // Disable before configuring ADCSequenceDisable(ADC0_BASE, 0); // Configure ADC0 sequencer 0: // Triggered by timer, highest priority (0) ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0); // Configure ADC0 sequencer 0 step 0: // Channel 8, last step insequence, causes interrupt when step is complete ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH8 | ADC_CTL_END | ADC_CTL_IE); ADCReferenceSet(ADC0_BASE, ADC_REF_INT); // Register and enable ADC interrupt ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0); IntRegister(INT_ADC0SS0, adc_irq_handler); IntEnable(INT_ADC0SS0); // Enable ADC to use uDMA ADCSequenceDMAEnable(ADC0_BASE, 0); // Enable ADC ADCSequenceEnable(ADC0_BASE, 0); } void DMA_init(void) { /* Enable uDMA clock */ SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA)) ; /* Enable uDMA */ uDMAEnable(); /* Set the control table for uDMA */ uDMAControlBaseSet(&pui8DMAControlTable[0]); /* Disable unneeded attributes of the uDMA channels */ uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0, UDMA_ATTR_ALL); // Channel A udma control set 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 A transfer set uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), pui16APri, ADC_BUF_SIZE); uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), pui16AAlt, ADC_BUF_SIZE); /* Enable the channels */ uDMAChannelEnable(UDMA_CHANNEL_ADC0); } Void ADC_init_task(UArg arg0, UArg arg1) { DMA_init(); ADC_init(); timer_init(); } /* * ======== main ======== */ int main(void) { Task_Params taskParams; /* Call board init functions */ Board_initGeneral(); // Construct adc create task thread Task_Params_init(&taskParams); taskParams.stackSize = TASK_STACK_SIZE; taskParams.stack = &ADCInitTaskStack; Task_construct(&ADCInitTask, (Task_FuncPtr) ADC_init_task, &taskParams, NULL); /* Start BIOS */ BIOS_start(); return (0); }
The DMA engine doesn't "Know" that you already wrote to the bottom of the memory area. It can't refuse. That means its your code :-(
I don't think this is right:
pui16APri += ADC_BUF_SIZE * 2;
The compiler knows that it needs to multiply ADC_BUF_SIZE by 2.
The other potential hazard here is that you call
uDMAChannelModeGet()
Twice, with some work in between. That creates a hazard. You want this:
status1 = uDMAChannelModeGet(PRI); status2 = uDMAChannelModeGet(ALT); if ( status1 == stopped && status2 != stopped ) {} etc
Ping-pong DMA is a little tricky. You have to get the math right. The simplest way that I've found is to simply keep a pointer to the next spot in the destination buffer. Keeping two pointers just makes your life harder. I suggest starting by getting rid of your pui16alt pointer.
Thanks R Sexton,
I've used a single pointer, and called uDMAChannelModeGet twice with no work in between.
It also turns out that for some reason I needed to make my buffer slightly larger. Before, at 49 transfers, the primary mode goes to 4, which is a bit strange. When I made the buffer slightly larger, the error stopped. It was probably some memory calculation errors.
Anyway, here is the working code if anybody is interested.
#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> /* TI-RTOS Header files */ // #include <ti/drivers/EMAC.h> #include <ti/drivers/GPIO.h> // #include <ti/drivers/I2C.h> // #include <ti/drivers/SDSPI.h> // #include <ti/drivers/SPI.h> // #include <ti/drivers/UART.h> // #include <ti/drivers/USBMSCHFatFs.h> // #include <ti/drivers/Watchdog.h> // #include <ti/drivers/WiFi.h> /* Board Header file */ #include "Board.h" /* Defines */ #define TASK_STACK_SIZE 131072 /* Private variable declarations */ // // Task object handles // Task_Struct ADCInitTask; // // Stacks for task objects // Char ADCInitTaskStack[TASK_STACK_SIZE]; #define ADC_BUF_SIZE 1024 #define UDMA_NUM_TRANSFERS 50 #define CLK_FREQ 30000000 #define SAMPLE_FREQ 1000000 uint8_t pui8DMAControlTable[1024]; uint16_t pui16Samples[ADC_BUF_SIZE * (UDMA_NUM_TRANSFERS + 1)]; uint16_t *p = pui16Samples; uint32_t uDMATransferCount = 0; /* =========================== Private functions ================================== */ void adc_irq_handler(void) { uint32_t modePrimary; uint32_t modeAlternate; // Clear the interrupt ADCIntClearEx(ADC0_BASE, ADC_INT_DMA_SS0); // Get the mode statuses of primary and alternate control structures 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 p += ADC_BUF_SIZE * 2; if (p >= (pui16Samples + (ADC_BUF_SIZE * UDMA_NUM_TRANSFERS))) { p = pui16Samples; } uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), p, ADC_BUF_SIZE); uDMATransferCount++; } 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), (p + ADC_BUF_SIZE), ADC_BUF_SIZE); uDMATransferCount++; } else { // Either both still not stopped, or both stopped. This is an error System_printf("Error in uDMA control structure modes\n"); System_printf("uDMATransferCount: %d\n", uDMATransferCount); 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(); } } void timer_init(void) { uint32_t ui32ClockFreq; // Set clock frequency ui32ClockFreq = SysCtlClockFreqSet(SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480, CLK_FREQ); // Enable timer peripheral clock SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); SysCtlPeripheralReset(SYSCTL_PERIPH_TIMER0); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER0)) { } // Configure the timer TimerConfigure(TIMER0_BASE, TIMER_CFG_A_PERIODIC_UP); TimerLoadSet(TIMER0_BASE, TIMER_A, (ui32ClockFreq / SAMPLE_FREQ)); // Enable timer to trigger ADC TimerControlTrigger(TIMER0_BASE, TIMER_A, true); // Enable event to trigger ADC TimerADCEventSet(TIMER0_BASE, TIMER_ADC_TIMEOUT_A); // Enable timer TimerEnable(TIMER0_BASE, TIMER_A); } void ADC_init(void) { // 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)) { } // Set PE5 to be ADC input GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_5); // Disable before configuring ADCSequenceDisable(ADC0_BASE, 0); // Configure ADC0 sequencer 0: // Triggered by timer, highest priority (0) ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0); // Configure ADC0 sequencer 0 step 0: // Channel 8, last step insequence, causes interrupt when step is complete ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH8 | ADC_CTL_END | ADC_CTL_IE); ADCReferenceSet(ADC0_BASE, ADC_REF_INT); // Register and enable ADC interrupt ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0); IntRegister(INT_ADC0SS0, adc_irq_handler); IntEnable(INT_ADC0SS0); // Enable ADC to use uDMA ADCSequenceDMAEnable(ADC0_BASE, 0); // Enable ADC ADCSequenceEnable(ADC0_BASE, 0); } void DMA_init(void) { /* Enable uDMA clock */ SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); while (!SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA)) ; /* Enable uDMA */ uDMAEnable(); /* Set the control table for uDMA */ uDMAControlBaseSet(&pui8DMAControlTable[0]); /* Disable unneeded attributes of the uDMA channels */ uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0, UDMA_ATTR_ALL); // Channel A udma control set 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 A transfer set uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), p, ADC_BUF_SIZE); uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), (p + ADC_BUF_SIZE), ADC_BUF_SIZE); /* Enable the channels */ uDMAChannelEnable(UDMA_CHANNEL_ADC0); } Void ADC_init_task(UArg arg0, UArg arg1) { DMA_init(); ADC_init(); timer_init(); } /* * ======== main ======== */ int main(void) { Task_Params taskParams; /* Call board init functions */ Board_initGeneral(); // Construct adc create task thread Task_Params_init(&taskParams); taskParams.stackSize = TASK_STACK_SIZE; taskParams.stack = &ADCInitTaskStack; Task_construct(&ADCInitTask, (Task_FuncPtr) ADC_init_task, &taskParams, NULL); /* Start BIOS */ BIOS_start(); return (0); }