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 primary control structure fills from 0 - 1023
- The alternate control structure fills from 1024 - 2047
- The primary control structure fills from 2048 - 3071
- And so on until 1024 * 50
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); }