cancel
Showing results for 
Search instead for 
Did you mean: 

Two ADC working on two signals at the same time

antoine
Associate II
Posted on July 18, 2016 at 17:50

Hi,

I want to read two signals (supposedly quite similar) at the same time, in order to compare them. I have one ADC working, but I'm perplex about the other.

- Does it matter if I use for the second signal, say ADC1_IN2, or ADC12_IN9, or ADC2_IN5, knowing that the first signal is using ADC12_IN7 ? Based on the configuration I'm tempted to prefer ADC2, but I'd like to know why.

-Let's say I configure my second ADC just like the first one. How am I going to make them work simultaneously (or alternatively) when ADC_StartConversion(ADC1) owns the running until the end of conversion?

Since the signals are supposed to vary over time, they need to be converted at the same time, in order to compare sig1t with sig2t and sig1t+1 with sig2t+1, and avoid sig1t+1 with sig2t.

Also, I have at this moment ADC_StartConversion(ADC1) right before while(1) in the main function. If I want the signal to be kept updated, should I move it into the while loop?
6 REPLIES 6
Posted on July 18, 2016 at 18:42

The ''same time'' requires the use of two ADC, one in scanning mode time displaces the measurements.

This is a blind cut down of a previous F3 Quad example, should be viable

// STM32 ADC Dual Mode STM32F3-Discovery - sourcer32@gmail.com
/**************************************************************************************/
// ADC1 ADC2 ADC3 ADC4
// IN1 PA0* PA4 PB1 PE14*
// IN2 PA1 PA5* PE9* PE15*
// IN3 PA2 PA6* PE13* PB12
// IN4 PA3 PA7* --- PB14
// IN5 PF4 PC4 PB13 PB15
//
// *Used on STM32F3-Discovery
// Free pins located in STM32F3-Discovery manual, UM1570
// ADC1/2 Trigger EXT3 TIM2_CC2
// ADC3/4 Trigger EXT1 TIM2_CC3
// Pins for this example PA1, PA4
/**************************************************************************************/
#include ''stm32f3_discovery.h''
#include ''stm32f30x.h''
/**************************************************************************************/
#define ADC12_CDR_ADDRESS ((uint32_t)0x5000030C) // COMMON DATA REGISTER
#define BUFSIZE 10
volatile uint16_t ADC12DualConvertedValue[2 * BUFSIZE];
uint16_t CalibrationValue[2];
/**************************************************************************************/
void ADC_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOA Periph clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
/* ADC Channels configuration */
/* Configure as analog input */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/**************************************************************************************/
void ADC12_DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* DMA configuration */
/* DMA1 Channel1 Init Test */
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC12_CDR_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC12DualConvertedValue[0];
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = BUFSIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure); // ADC1
}
/**************************************************************************************/
void Delay(volatile int x)
{
volatile int y;
while(x--)
for(y=0; y<150; y++);
}
/**************************************************************************************/
void ADC_Configuration(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
/* Configure the ADC clocks */
RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div1);
/* Enable ADC1/2 clocks */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ADC12, ENABLE);
/* ADC GPIO configuration */
ADC_GPIO_Configuration();
/* ADC DMA Channel configuration */
ADC12_DMA_Configuration();
/* ADC Calibration procedure */
ADC_VoltageRegulatorCmd(ADC1, ENABLE);
ADC_VoltageRegulatorCmd(ADC2, ENABLE);
/* Insert delay equal to 10 µs */
Delay(10);
ADC_SelectCalibrationMode(ADC1, ADC_CalibrationMode_Single);
ADC_StartCalibration(ADC1);
ADC_SelectCalibrationMode(ADC2, ADC_CalibrationMode_Single);
ADC_StartCalibration(ADC2);
while(ADC_GetCalibrationStatus(ADC1) != RESET );
CalibrationValue[0] = ADC_GetCalibrationValue(ADC1);
while(ADC_GetCalibrationStatus(ADC2) != RESET );
CalibrationValue[1] = ADC_GetCalibrationValue(ADC2);
/* ADC Dual mode configuration */
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Clock = ADC_Clock_AsynClkMode;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1; // 12-bit
ADC_CommonInitStructure.ADC_DMAMode = ADC_DMAMode_Circular;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = 10;
ADC_CommonInit(ADC1, &ADC_CommonInitStructure);
/* */
ADC_InitStructure.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Disable; // Triggered
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_RisingEdge;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_OverrunMode = ADC_OverrunMode_Disable;
ADC_InitStructure.ADC_AutoInjMode = ADC_AutoInjec_Disable;
ADC_InitStructure.ADC_NbrOfRegChannel = 1;
ADC_InitStructure.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_3; // ADC1/2 EXT3 TIM2_CC2
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Init(ADC2, &ADC_InitStructure);
/* ADC1 regular configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_7Cycles5); // PA1
/* ADC2 regular configuration */
ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_7Cycles5); // PA4
/* Configures the ADC DMA */
ADC_DMAConfig(ADC1, ADC_DMAMode_Circular);
/* Enable the ADC DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC[1..2] */
ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
/* wait for ADC1 ADRDY */
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_RDY));
/* wait for ADC2 ADRDY */
while(!ADC_GetFlagStatus(ADC2, ADC_FLAG_RDY));
/* Enable the DMA channel */
DMA_Cmd(DMA1_Channel1, ENABLE);
/* Start ADC1 Software Conversion */
ADC_StartConversion(ADC1);
}
/**************************************************************************************/
void TIM2_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* Enable TIM2 Peripheral clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_Period = (SystemCoreClock / 50) - 1; // 50 Hz Sample Rate
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel2 & 3 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 10; // Some arbitary width
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_Cmd(TIM2, ENABLE);
}
/**************************************************************************************/
void DMA1_Channel1_IRQHandler(void) // 5 Hz
{
/* Test on DMA1 Channel1 Transfer Complete interrupt */
if(DMA_GetITStatus(DMA1_IT_TC1))
{
/* Clear DMA1 Channel1 Half Transfer, Transfer Complete and Global interrupt pending bits */
DMA_ClearITPendingBit(DMA1_IT_GL1);
STM_EVAL_LEDToggle(LED3); // 2.5 Hz
}
}
/**************************************************************************************/
void TIM2_IRQHandler(void) // 50 Hz
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
STM_EVAL_LEDToggle(LED4); // 25 Hz
}
}
/**************************************************************************************/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the DMA1 CH1 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_Init(&NVIC_InitStructure);
}
/**************************************************************************************/
int main(void)
{
/* Initialize LEDs available on STM32F-Discovery board */
STM_EVAL_LEDInit(LED3);
STM_EVAL_LEDInit(LED4);
/* Turn on LD3 */
STM_EVAL_LEDOn(LED3);
STM_EVAL_LEDOn(LED4);
NVIC_Configuration();
TIM2_Configuration();
ADC_Configuration();
/* Enable DMA1 Channel1 Transfer Complete interrupt */
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
/* TIM Interrupts enable */
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
while(1); // Don't want to exit
}
/**************************************************************************************/
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
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

'', file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
/**************************************************************************************/

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
AvaTar
Lead
Posted on July 19, 2016 at 07:39

> The ''same time'' requires the use of two ADC, one in scanning mode time displaces the measurements.

That depends on the meaning of ''same time''. E.g. for sampling the mains voltage with a few hundred Hertz, multiplexed channels of the same ADC would still count as such.

Qualification of the required sampling frequency would be helpful.

antoine
Associate II
Posted on July 19, 2016 at 10:27

Avatar, The signals are expected to be between 100 and 180 kHz, although at this moment I work with 30 up to 60 kHz, I prefer focus on 170 kHz.

I don't know the required sampling frequency.

Thanks clive, as always, I'm testing this with what I have.

antoine
Associate II
Posted on July 20, 2016 at 14:10

RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div1);

Does this mean the ADC clock (fADC) is 72 MHz ?

What is the purpose of the Delay function, used like this ? How do you know it waits for 10 µs, with the while(x--) and the 150 condition in the for? How does it work?

What is the ADC_TwoSamplingDelay parameter? How do you chose it?

ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_7Cycles5); // PA1

What is the SampleTime? I mean, what does 7,5 cycles represent? Cycles of what? Again, why this value?

Considering 170 kHz input signal, isn't 50 Hz too low for the timer? Or this is not related?

Why is it ok to do StartConversion(ADC1) and not doing it for ADC2? ADC2 is not linked with the DMA either, so how does it work? How does it know it has to manage a signal, and since ADC2 is apparently not linked with the DMA, how do the converted values from ADC2 end up in the array?

Also, just to be sure: the buffer size of the DMA is 10 because the ConvertedValues array can then be filled with ADC1 in the first ten slots, and with ADC2 in the ten following slots?

What is the purpose of CalibrationValue ? It is never used.

In fact, could it be possible to explain what happens step by step? Like ''ADC1 performs X conversions during X µs, then the values are sent to the DMA. After the end of ADC1 action, ADC2 performs this and that...'', including the roles of TwoSamplingDelay, the timer frequency, etc. ?

 

It would help me a lot.

EDIT: Apparently the timer is used only for PWM, but PWM... Is it here to be used as an input signal by the ADC? If yes, what is the related pin, and what is the point to have a single input signal for this configuration? If not, what is the purpose of the timer and PWM?

Posted on July 20, 2016 at 15:02

It is an example of several concepts using parameters I chose because the original post lacked any specificity. It combines, ADC, DMA, TIM.

If you want to increase the sample rate, and make the buffer bigger, you are free to do so. Each would require a single line adjustment.

You are going to need to review this with respect to the hardware as described in the Reference Manual. If I need to read and explain the manual and code to you, normal hourly rates will apply.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
antoine
Associate II
Posted on July 21, 2016 at 12:08

You're right, I may have been greedy. I apologize.

I'm going to stick with precise questions. In order to verify what is in the array I use printf in debug mode. I have a problem with the display.

while
(1) 
//; // Don't want to exit
{
printf(
''test2
''
);
if
(ADC12DualConvertedValue[(2*BUFSIZE)-1] != 0xFFFF) 
// Check for new values loaded
{
int
i;
printf(
''test3
''
);
for
(i=0; i<2*BUFSIZE; i++)
{
printf(
''%04d
''
, ADC12DualConvertedValue[i]);
}
printf(
''

''
);
ADC12DualConvertedValue[(2*BUFSIZE)-1] = 0xFFFF; 
// Invalidate
}
}

This is the infinite loop in the main function, used for the display. I set several breakpoints. As a result, I have test2 and test3 displayed, but the code doesn't seem to reach the bp in the for loop. It seems to have entered in the HardFault_Handler function's infinite loop. During the running, the DR and CDR registers of the ADC keep updating. What could be the reason of this HardFault ? I also noticed that in the common regular data register (CDR), only the RDATA_MST (master) bits update. The RDATA_SLV (slave) bits stay at 0. It may be a detail, but I thought the ADC in dual mode were ADC1 is master and ADC2 is slave. Did I just made a mistake in my code? Thank you. ______________________________________________________

Edit:

Meh. The HardFault was caused by the lack of parentheses in the for loop. Now this is solved, I can confirm the operation of the DMA: the array is filled alternatively by each ADC (and not like 50/50, all ADC1 values first, then all ADC2 values, like I thought). So I am sure now that the ADC2 doesn't send anything to the memory array. This is quite obvious because we don't enable ADC2 DMA, nor making ADC_StartConversion(ADC2), but I thought somehow it was because of the dual mode, ADC1 being in charge, I don't know. Apparently it is not. Was the code supposed to operate like this?