cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4 ADC in scan/discontinuous mode using HAL

hayet
Associate
Posted on March 12, 2016 at 10:13

Hi all,

I am using STM32F407 device and IAR EWARM.

I want to use the ADC in STM32 in the following way:

At each 2ms, ADC1 converts 3 channels. let's say ch0 then ch2 and then ch3.

After that the ADC stops waiting for the next 2ms.

On the 2nd 2ms ADC converts the 3 channels: ch0 then ch2 and then ch3.

This means for each timer trigger I want to get three conversions.

I am lost which ADC mode I can use.

I am testing the scan mode but without success. Each conversion needs its own trigger.

i.e I can't trigger three conversions with a single timer trigger.

Now I am trying the discontinuous mode and without success too.

For debugging, I am counting the number of ADC interrupts fired vs the number of TIM interrupts fired. I have to find ADC interrupts counter = 3 x TIM interrupts counter.

Which ADC mode do you think I can use?

I know I can use the software trigger with 1ms/3 but I want to use the suitable hardware feature.

Here below my ADC configuration. Let me know your comments:

   /* ADC Initialization */

  mhandle.Instance          = ADC1;

  mhandle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;

  mhandle.Init.Resolution = ADC_RESOLUTION_12B;

  mhandle.Init.ScanConvMode = DISABLE;

  mhandle.Init.ContinuousConvMode = DISABLE;

  mhandle.Init.DiscontinuousConvMode = ENABLE;

  mhandle.Init.NbrOfDiscConversion = 3;

  mhandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;

  mhandle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;

  mhandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;

  mhandle.Init.NbrOfConversion = 3;

  mhandle.Init.DMAContinuousRequests = ENABLE;

  mhandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;

 

  HAL_ADC_Init(&mhandle);

 

  /* Configure ADC3 regular channel */ 

  sConfig.Channel = ADC_CHANNEL_0;

  sConfig.Rank = 1;

  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;

  sConfig.Offset = 0;

 

  HAL_ADC_ConfigChannel(&mhandle, &sConfig);

  sConfig.Channel = ADC_CHANNEL_2;

  sConfig.Rank = 2;

  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;

  sConfig.Offset = 0;

 

  HAL_ADC_ConfigChannel(&mhandle, &sConfig);

 

  sConfig.Channel = ADC_CHANNEL_3;

  sConfig.Rank = 3;

  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;

  sConfig.Offset = 0;

 

  HAL_ADC_ConfigChannel(&mhandle, &sConfig);

and I am using HAL_ADC_Start_IT(&mhandle) then HAL_TIM_Base_Start_IT(&htim) once ADC is set up.

My ISR look like this:

void TIM8_UP_TIM13_IRQHandler(void)

{

  TIMCounter++;

  call hal tim irq handler

}

void ADC_IRQHandler(void)

{

   ADCCounter++; // ADCCounter should be three times TIMCounter

    call hal adc irq handler

}

#adc #hal
1 REPLY 1
kudryashovda
Associate II
Posted on March 12, 2016 at 11:08

Hi!

I am not experienced with HAL but with my eamples you can adapting it to your code. First code is ON-OFF Led every 1 second. You change it to 2 ms. Next code is ADC settings for 2 channels sampling - change it to 3 channels. AlsoADC_ContinuousConvMode_Disable. Every time timer goes to interrupt executes third code example (add your third channel ). It starts ADC ch1 once, stored data to Voltage var. Next ADC start reads ch2 and stored in Current var and so on.

// in options for Target - C/C++ tab - Preprocessor symbols define must be USE_STDPERIPH_DRIVER string
// enable timer for LED - 1000 ms ON and 1000 ms OFF
// led on pin PB1
#include ''stm32f10x.h'' 
void
gpio_init(
void
) {
GPIO_InitTypeDef gpio;
// enable clocking GPIOB block
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// set output for led pin PB1
GPIO_StructInit(&gpio);
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Pin = GPIO_Pin_1;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio);
}
void
Toggle_Led(
void
) {
if
(!GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_1)) {
GPIO_SetBits(GPIOB, GPIO_Pin_1); }
else
{
GPIO_ResetBits(GPIOB, GPIO_Pin_1); } 
}
void
tmr_init(
void
) {
// TIM2
// according startup file APB1 bus on 36MHz | APB2 on 72 MHz
// Page 90 RefManual stm32f10x says: if APB1 bus prescaler = 1 then TIM CLK mult = 1 else mult = 2
// so in my case APB2 bus presc = 2 then TIM CLK = 2 * 36 MHz = 72 MHz 
TIM_TimeBaseInitTypeDef myTmr;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
myTmr.TIM_Prescaler = 36000 - 1 ;
myTmr.TIM_Period = 2000; 
// for 1000 ms pulses
myTmr.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &myTmr);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
NVIC_EnableIRQ(TIM2_IRQn); 
}
void
TIM2_IRQHandler()
{
if
(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
Toggle_Led(); 
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
int
main()
{
gpio_init();
tmr_init();
while
(1) {}; 
}

// settings for ADC 
ADC_VoltageRegulatorCmd(ADC4, ENABLE);
ADC_SelectCalibrationMode(ADC4, ADC_CalibrationMode_Single);
ADC_StartCalibration(ADC4);
while
(ADC_GetCalibrationStatus(ADC4) != RESET );
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; 
ADC_CommonInitStructure.ADC_Clock = ADC_Clock_AsynClkMode; 
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; 
ADC_CommonInitStructure.ADC_DMAMode = ADC_DMAMode_OneShot; 
ADC_CommonInitStructure.ADC_TwoSamplingDelay = 0; 
ADC_CommonInit(ADC1, &ADC_CommonInitStructure); 
ADC_CommonInit(ADC4, &ADC_CommonInitStructure);
ADC_InitStructure.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Disable;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; 
ADC_InitStructure.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_0; 
ADC_InitStructure.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_None;
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 = 2;
ADC_Init(ADC4, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC4, 4, 1, ADC_SampleTime_61Cycles5);
ADC_RegularChannelConfig(ADC4, 12, 2, ADC_SampleTime_61Cycles5);
ADC_Cmd(ADC4, ENABLE); 
while
(!ADC_GetFlagStatus(ADC4, ADC_FLAG_RDY));

ADC4->CR |= ADC_CR_ADSTART; 
// get ADC
while
(!ADC_GetFlagStatus(ADC4, ADC_FLAG_EOC));
ADC_Voltage = ADC4->DR;
while
(!ADC_GetFlagStatus(ADC4, ADC_FLAG_EOC));
ADC_Current = ADC4->DR;