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);
}