2013-07-22 06:27 AM
Hi everyones,
I am stuying the example from STM32 libraries (ADC_DualModeRegulSimu). I understand how the whole program works but I need some information and I have few questions about this code confronting to my project.First of all, I need to convert 2 analog values simultaneously because I plan to compute a phase correlation (that's why I need the simultaneously conversion).In the code, the converted value are put in a word of length 32 bits : extern __IO uint16_t aADCDualConvertedValue[2];I want to stock the first and second value (half-word) in two vector that I have defined. I first decided to create a while statement like this :while(counter<Max && ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC && ADC_GetFlagStatus(ADC2, ADC_FLAG_EOC)) { vector1[counter] = aADCDualConvertedValue[0]; vector2[counter] = aADCDualConvertedValue[1]; counter++;}But, this method seems to be unsafe because, we can't be certain that aADCDualConvertedValue[0] and aADCDualConvertedValue[1] are sampled at the same time owing to the number of cycles taken by while conditions.So, what's the best way to stock the simultaneously data safely ?Another problem, the conversion starts like this (after initializing the two ADC) : ADC_SoftwareStartConv(ADC1);ADC_SoftwareStartConv(ADC2);
Does-it mean that ADC2 starts to convert one cycle after ADC1, thus first both samples are not converted simultaneously ?Thks. #adc #stm32
2013-07-22 08:22 AM
If it is using DMA, I'm not sure of the merit of waiting on EOC. The two ADC are in lock-step, so EOC on one will infer EOC on the other.
The DMA implementation reads a single 32-bit word containing both samples from the ADC, and writes that to an output array.2013-07-22 11:44 PM
So, to you, I just need to wait on EOC of one ADC to acquire a new sample ?
And what about the starting of both ADC, how start the both simultaneously (in reference to my second question).Thks.2013-07-23 02:34 AM
I have many problems to have my ADC work.
Please, have a look on my code :main :
/////////////////////////////////////////// ADC configuration
/////////////////////////////////////////
ADC_CommonInitTypeDef ADC_CommonInitStructure;
// 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);
DMA_InitTypeDef DMA_InitStructure;
DMA_Config(&DMA_InitStructure);
GPIO_Config();
// common init for ADC
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_20Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
// ADC1
ADC1_CH10_Config();
// ADC2
ADC2_CH11_Config();
// DMA can transfer DATA after sampling
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
// starting the conversion
ADC_SoftwareStartConv(ADC1);
ADC_SoftwareStartConv(ADC2);
/////////////////////////////////////////
// main loop
/////////////////////////////////////////
while(1)
{
/////////////////////////////////////////
// acquire data && windowing the signal
/////////////////////////////////////////
// MAXLEN data
while(k<MAXLEN){
while(DMA_GetITStatus(DMA2_Stream0,DMA_IT_HTIF0)==RESET){
// SIGNAL : conversion is occurring
LEDOn(LED4);
}
// END of Conversion
// take data
i=1;
k++;
}
// SIGNAL : end of global conversion
LEDOff(LED4);
// SIGNAL : start the signal analysis
LEDOn(LED3);
k=0;
i=0;
}
ADC 1 Config :
void ADC1_CH10_Config(void){
/* defined values in stm32f4xx_adc.h and main.h */
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_8b;
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 = 1;
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channels 11 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_112Cycles);
}
ADC 2 config :
void ADC2_CH11_Config(void){
/* defined values in stm32f4xx_adc.h and main.h */
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_8b;
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 = 1;
ADC_Init(ADC2, &ADC_InitStructure);
/* ADC2 regular channels 12 configuration */
ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 1, ADC_SampleTime_112Cycles);
}
DMA config :
void DMA_Config(DMA_InitTypeDef* DMA_InitStructure){
/* defined values in stm32f4xx_dma.h and main.h */
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 = 2;
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);
}
GPIO config :
void GPIO_Config(void){
/* defined values in stm32f4xx_gpio.h and main.h */
GPIO_InitTypeDef GPIO_InitStructure;
/* ADC Channel 10 -> PC0
ADC Channel 11 -> PC1
ADC Channel 12 -> PC2
*/
GPIO_InitStructure.GPIO_Pin = 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);
}I test DMA_GetITStatus(DMA2_Stream0,DMA_IT_HTIF0)==RESET and ADC_GetFlagStatus and DMA_GetFlagStatus but none works. Either I have an infinite loop, either I have my LED blinking but if I change the prescaler for example, sample time don't change...More over, I discover that ADC 1 and ADC 2 doesn't take the same duration to convert...I am a bit confused with this ADC configuration. I need some advices on this.Thks.
2013-07-23 07:47 AM
There seem to be some inconsistencies in the channel selections, I don't think you want to be scan converting with a single channel per ADC (scan implies you have more than one). I personally prefer to pace ADC conversions with a timer rather than jam them back-to-back. This way the sample rate in unambiguous. Check also the rated speed of the ADC, the CPU should perhaps be running at 144 MHz, not 168 MHz
I suspect the HT (half-transfer) of a 2 16-bit word buffer churns at an excessive rate. I would make the buffer bigger, for HT to make any sense you'd want at least 4 16-bit words to how two pairs of samples. This is how I'd attack a dual channel acquisition.// STM32 ADC IQ Sample @ 200 KHz (PC.1, PC.2) STM32F4 Discovery - sourcer32@gmail.com
// Assumptions per system_stm32f4xx.c CPU @ 168 MHz, APB2 @ 84 MHz (/2), APB1 @ 42 MHz (/4)
#include ''stm32f4_discovery.h''
/**************************************************************************************/
void RCC_Configuration(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
}
/**************************************************************************************/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* ADC Channel 11 -> PC1
ADC Channel 12 -> PC2
*/
GPIO_InitStructure.GPIO_Pin = 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);
}
/**************************************************************************************/
void ADC_Configuration(void)
{
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
/* ADC Common Init */
ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1; // 2 half-words one by one, 1 then 2
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 1 Channel
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // Conversions Triggered
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Init(ADC2, &ADC_InitStructure); // Mirror on ADC2
/* ADC1 regular channel 11 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_15Cycles); // PC1
/* ADC2 regular channel 12 configuration */
ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 1, ADC_SampleTime_15Cycles); // PC2
/* Enable DMA request after last transfer (Multi-ADC mode) */
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC2 */
ADC_Cmd(ADC2, ENABLE);
}
/**************************************************************************************/
#define BUFFERSIZE 800 // I+Q 200KHz x2 HT/TC at 1KHz
__IO uint16_t ADCDualConvertedValues[BUFFERSIZE]; // Filled as pairs ADC1, ADC2
static void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCDualConvertedValues[0];
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)0x40012308; // CDR_ADDRESS; Packed ADC1, ADC2
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = BUFFERSIZE; // Count of 16-bit words
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);
/* Enable DMA Stream Half / Transfer Complete interrupt */
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE);
/* DMA2_Stream0 enable */
DMA_Cmd(DMA2_Stream0, ENABLE);
}
/**************************************************************************************/
void TIM2_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* Time base configuration */
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = (84000000 / 200000) - 1; // 200 KHz, from 84 MHz TIM2CLK (ie APB1 = HCLK/4, TIM2CLK = HCLK/2)
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM2 TRGO selection */
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // ADC_ExternalTrigConv_T2_TRGO
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}
/**************************************************************************************/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**************************************************************************************/
void DMA2_Stream0_IRQHandler(void) // Called at 1 KHz for 200 KHz sample rate, LED Toggles at 500 Hz
{
/* Test on DMA Stream Half Transfer interrupt */
if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0))
{
/* Clear DMA Stream Half Transfer interrupt pending bit */
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0);
/* Turn LED3 off: Half Transfer */
STM_EVAL_LEDOff(LED3);
// Add code here to process first half of buffer (ping)
}
/* Test on DMA Stream Transfer Complete interrupt */
if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0))
{
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
/* Turn LED3 on: End of Transfer */
STM_EVAL_LEDOn(LED3);
// Add code here to process second half of buffer (pong)
}
}
/**************************************************************************************/
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
NVIC_Configuration();
TIM2_Configuration();
DMA_Configuration();
ADC_Configuration();
STM_EVAL_LEDInit(LED3); /* Configure LEDs to monitor program status */
STM_EVAL_LEDOn(LED3); /* Turn LED3 on, 500 Hz means it working */
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConv(ADC1);
while(1); // Don't want to exit
}
2013-07-25 02:40 AM
Why on the first way the sample rate would be ambiguous ? We fix the prescaler, the time delay between two conversion... It seems sure.
About your code, ok the timer send trigger with a frequency of 200 khz. But I don't understand why the DMA IRQ Handler is called with a frequency of 1Khz...To fill table of BUFFERSIZE length, it takes 400*TimeOfConversion, here it seems to be at a frequency higher than 1 kHz...Thks.2013-07-27 04:57 AM
Up !
Thks.2013-07-27 08:55 AM
Don't badger, I'm not on staff.
It's the weekend, I don't have a scope to hand. Unless I messed up the timer settings, and math, the 200 sample pairs at 200000 KHz will occur 1000 times a second, ie 1 KHz Ambiguous, perhaps there is a more elegant term. I don't spend a lot of time working with ADCs, I do however believe that the start time of the conversion in the time domain is critical, the completion time of the conversion less so. While there can be a case for saturating the ADC and be tied to sample times, and dwell times, I find it clearer to have a hard timebase to disciplining the ADC against that. I want to change the rate, I change one timer setting, and don't have to play games with ADC internal behaviours. It's also possible to advance/retard the timer in a very controlled fashion.2013-07-30 01:57 AM
Hi,
Thanks for all you explanation.Obviously, I am confronted to a new problem : I test the output ADC's values. I send to PC2 an signal between 0 and 1 volt -don't consider impedance issue, and it is not a sampling frequency issue- I check the voltage value on PC2 with a voltmeter.I look converted values with STMStudio for example, but I obtain funky values (It seems to be random values)...Let's have a look of this :I don't understand why whereas my circuit seems well done, and I don't forget to link the GND pin... The only What can be the cause of such a problem with the ADC's converted values ?Thks.2013-07-30 08:40 AM
I don't know how you are generating the 8-bit value you are watching. If you are getting a lot of variability you could increase the ADC's sample time cycles.