cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4-Discovery ADC Dual Mode DMA Sample Alignment

derek2
Associate
Posted on March 11, 2014 at 00:54

For my project, I am doing audio mixing from three inputs (pins PC1, PC2, PC4 over Channel 11, 12, 14, respectively) and outputting it the external DAC on the F4-Discovery board. The mixing seems to work properly, albeit its done in a somewhat haphazard way - I'm drastically oversampling the inputs into a circular array and then pulling samples from the array at the I2S clock speed. The sample rate isn't my current concern at this point, however. The mixing is done by summing the three values from the circular array. This is currently working, however, its clear that my understanding how the DMA works in this example isn't correct.

My biggest concern is understanding alignment of samples in the circular buffer. I naively assumed that the samples would just continuously be placed in order (e.g. [sample_from_PC1, sample_from_PC2, sample_from_PC4, sample_from_PC1, ...]. However, this is clearly not the case. How do I determine the alignment of samples in the circular array so that I can accurately get a single sample from each input?


#include ''main.h''


#define ADC_CCR_ADDRESS ((uint32_t)0x40012308)


/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

__IO uint16_t aADCDualConvertedValue[12];


/* Private function prototypes -----------------------------------------------*/

static
void
DMA_Config(
void
);

static
void
GPIO_Config(
void
);

static
void
ADC1_CH10_CH11_Config(
void
);

static
void
ADC2_CH11_CH12_Config(
void
);


/* Private functions ---------------------------------------------------------*/


int
main(
void
)

{

ADC_CommonInitTypeDef ADC_CommonInitStructure;


/*!< At this stage the microcontroller clock setting is already configured,

this is done through SystemInit() function which is called from startup

files (startup_stm32f40xx.s/startup_stm32f427x.s) before to branch to

application main.

To reconfigure the default setting of SystemInit() function, refer to

system_stm32f4xx.c file

*/


/* Enable peripheral clocks *************************************************/

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);


/* DMA2 Stream0 channel0 configuration **************************************/

DMA_Config();


/* ADCs configuration ------------------------------------------------------*/

/* Configure ADC Channel 10, 11, 12 pin as analog input */

GPIO_Config();


/* ADC Common Init */

ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;

ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;

ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1;

ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;

ADC_CommonInit(&ADC_CommonInitStructure);


/* ADC1 regular channels 10, 11 configuration */

ADC1_CH10_CH11_Config();


/* ADC2 regular channels 11, 12 configuration */

ADC2_CH11_CH12_Config();


/* Enable DMA request after last transfer (Multi-ADC mode) */

ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);


/* Enable ADC1 */

ADC_Cmd(ADC1, ENABLE);


/* Enable ADC2 */

ADC_Cmd(ADC2, ENABLE);


/* Start ADC1 Software Conversion */

ADC_SoftwareStartConv(ADC1);


SystemInit();

codec_init();

codec_ctrl_init();


I2S_Cmd(CODEC_I2S, ENABLE);


while
(1)

{

if
(SPI_I2S_GetFlagStatus(CODEC_I2S, SPI_I2S_FLAG_TXE))

{

SPI_I2S_SendData(CODEC_I2S, (int16_t) sample);


//only update on every second sample to insure that L & R ch. have the same sample value

if
(sampleCounter & 0x00000001)

{

sample = (((int32_t) aADCDualConvertedValue[0] + aADCDualConvertedValue[1] + aADCDualConvertedValue[2]));


if
(sample > 0xFFFF)

{

sample = 0xFFFF;

}

}

sampleCounter++;

}

}

return
0;

}


static
void
ADC1_CH10_CH11_Config(
void
)

{

ADC_InitTypeDef ADC_InitStructure;


ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;

ADC_InitStructure.ADC_ScanConvMode = ENABLE;

ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

ADC_InitStructure.ADC_NbrOfConversion = 2;

ADC_Init(ADC1, &ADC_InitStructure);


/* ADC1 regular channels 14, 11 configuration */

ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, ADC_SampleTime_28Cycles);

ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_28Cycles);

}


static
void
ADC2_CH11_CH12_Config(
void
)

{

ADC_InitTypeDef ADC_InitStructure;


ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;

ADC_InitStructure.ADC_ScanConvMode = ENABLE;

ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

ADC_InitStructure.ADC_NbrOfConversion = 2;

ADC_Init(ADC2, &ADC_InitStructure);


/* ADC2 regular channels 11, 12 configuration */

ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_28Cycles);

ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 2, ADC_SampleTime_28Cycles);

}


static
void
DMA_Config(
void
)

{

DMA_InitTypeDef DMA_InitStructure;


DMA_InitStructure.DMA_Channel = DMA_Channel_0;

DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&aADCDualConvertedValue;

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CCR_ADDRESS;

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

DMA_InitStructure.DMA_BufferSize = 3;

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

DMA_InitStructure.DMA_Priority = DMA_Priority_High;

DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;

DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;

DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

DMA_Init(DMA2_Stream0, &DMA_InitStructure);


/* DMA2_Stream0 enable */

DMA_Cmd(DMA2_Stream0, ENABLE);

}


static
void
GPIO_Config(
void
)

{

GPIO_InitTypeDef GPIO_InitStructure;


/* ADC Channel 14 -> PC4

ADC Channel 11 -> PC1

ADC Channel 12 -> PC2

*/


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_1 | GPIO_Pin_2;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

GPIO_Init(GPIOC, &GPIO_InitStructure);

}


#ifdef USE_FULL_ASSERT


void
assert_failed(uint8_t* file, uint32_t line)

{

/* User can add his own implementation to report the file name and line number,

ex: printf(''Wrong parameters value: file %s on line %d\r\n'', file, line) */


/* Infinite loop */

while
(1)

{

}

}

#endif

#discovery #stm32 #dma #adc-stm32-discovery
1 REPLY 1
Posted on March 11, 2014 at 01:14

DMA_InitStructure.DMA_BufferSize = 3;

Good grief, why three? It's a prime number with no relationship to a) the size of the buffer, and b) not divisible by 2 or 4. If you have 3 groups of 4 samples, then surely that's 12 half-words (16-bit)? But again why 3? You have to keep things balanced, otherwise yes things are going to get jumbled up and harder to track. Don't do it. If you have dual simultaneous mode wouldn't you want to describe the buffer as 6 x 32-bit to the DMA? The common ADC register wants to be read as a word (32-bit) with the ADC1 and ADC2 packed as two 16-bit samples. PAIRS not TRIPLES In a world where you split the samples in two buffers ping/pong with HT/TC interrupts, then you double the buffer size.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..