cancel
Showing results for 
Search instead for 
Did you mean: 

Trigger ADC1 scan using TIM1 CC1 on F410RB

massimiliano2399
Associate II
Posted on September 20, 2016 at 13:08

Hi,

I use STM32F410RB, and I need to trigger an ADC1 scan regular conversion every TIM1 CC1 event.

I am sure that TIM1 works, because I have an interrupt that is triggered every CC1 event.

I tried to configure ADC as follows:


ADC_ChannelConfTypeDef sConfig;


/* ADC Initialization */

AdcHandle.Instance = ADC1;


AdcHandle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;

AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;

AdcHandle.Init.ScanConvMode = ENABLE;

AdcHandle.Init.ContinuousConvMode = DISABLE;

AdcHandle.Init.DiscontinuousConvMode = DISABLE;

AdcHandle.Init.NbrOfDiscConversion = 0;

AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;

AdcHandle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;

AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;

AdcHandle.Init.NbrOfConversion = 2;

AdcHandle.Init.DMAContinuousRequests = DISABLE;

AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;


HAL_ADC_Init(&AdcHandle);


/* Configure ADC3 regular channel */

sConfig.Channel = ADC_CHANNEL_0;

sConfig.Rank = 1;

sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;

sConfig.Offset = 0;


HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);


/* Configure ADC3 regular channel */

sConfig.Channel = ADC_CHANNEL_1;

sConfig.Rank = 2;

sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;

sConfig.Offset = 0;


HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);


HAL_ADC_Start_IT(&AdcHandle);

But ADC no interrupt is triggered.

Which register, which bit are involved in the configuration?

best regards

Max

#adc #timer #stm32f410rb

Note: this post was migrated and contained many threaded conversations, some content may be missing.
12 REPLIES 12
Walid FTITI_O
Senior II
Posted on September 21, 2016 at 11:51

Hi cialdi.massimiliano,

To trigger ADC each TIM cc1 event , you should apply the following modfication :

- Enable

DiscontinuousConvMode

- Enable

DMAContinuousRequests

- Configure DMA

I recommend to check further for more help the example ''ADC_RegularConversion_DMA'' : STM32Cube_FW_F4_V1.13.0\Projects\STM324xG_EVAL\Examples\ADC\ADC_RegularConversion_DMA

-Hannibal-

nico 2010
Associate II
Posted on October 28, 2017 at 10:24

Hello,

I faced with the same problem.

Insert

hadc1.Instance->CR2 |= ADC_CR2_SWSTART;

in stm32f4xx_it.c 

where TIM IRQ is handled.

It is a bug in library.

Best regards

Posted on November 07, 2017 at 11:59

 ,

 ,

Are you sure this is correct?

From ,STM32Cube_FW_F4_V1.17.0\Projects\STM324xG_EVAL\Examples\ADC\ADC_RegularConversion_DMA:

/* ♯ ♯ -1- Configure the ADC peripheral ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ */

 ,

AdcHandle.Instance = ADCx,

 ,

 ,

AdcHandle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV2,

 ,

AdcHandle.Init.Resolution = ADC_RESOLUTION_12B,

 ,

AdcHandle.Init.ScanConvMode = DISABLE,

 ,

AdcHandle.Init.ContinuousConvMode = ENABLE,

 ,

AdcHandle.Init.DiscontinuousConvMode = DISABLE,

 ,

AdcHandle.Init.NbrOfDiscConversion = 0,

 ,

AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE,

 ,

AdcHandle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1,

 ,

AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT,

 ,

AdcHandle.Init.NbrOfConversion = 1,

 ,

AdcHandle.Init.DMAContinuousRequests = ENABLE,

 ,

AdcHandle.Init.EOCSelection = DISABLE,

This does not seem to enable conversions triggered by TIM1 CC1 with ,ADC_EXTERNALTRIGCONVEDGE_NONE.

Posted on November 07, 2017 at 12:08

What you describe is manually triggering conversions from software in an interrupt service routine; that's not what the hardware is supposed to do and incurs interrupt service overhead.

What I found to work on the STM32F410RB here is to configure TIM1 CC1 for 'Toggle Output', then configure ADC1 to trigger on TIM1 CC1 on both rising and falling edges. I am using DMA but believe that's not related to this.

The initialization produced by STM32CubeMX:

TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_TOGGLE;

TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;

TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;

TIM_OC_InitStruct.CompareValue = 0;

TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;

TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_HIGH;

TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW;

TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;

LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);

and:

hadc1.Instance = ADC1;

hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;

hadc1.Init.Resolution = ADC_RESOLUTION_12B;

hadc1.Init.ScanConvMode = DISABLE;

hadc1.Init.ContinuousConvMode = DISABLE;

hadc1.Init.DiscontinuousConvMode = DISABLE;

hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISINGFALLING;

hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;

Posted on November 08, 2017 at 08:32

Hello!

I see you use a mixture of LL and HAL and I don't work with LL libraries.

What I want to say is that using this settings for ADC1 and TIMER1, ADC didn't read any value until I made the modification in Timer1 IRQ Handler, like below. So I guess that is a bug in the library of ADC, as I said in my topic 'Nucleo F410RB ADC dilema'.

static void MX_ADC1_Init(void)

{ ADC_ChannelConfTypeDef sConfig; /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) */

hadc1.Instance = ADC1;

hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;

hadc1.Init.Resolution = ADC_RESOLUTION_12B;

hadc1.Init.ScanConvMode = DISABLE;

hadc1.Init.ContinuousConvMode = DISABLE;

hadc1.Init.DiscontinuousConvMode = ENABLE;

hadc1.Init.NbrOfDiscConversion = 1;

hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISINGFALLING;

hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC2;

hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;

hadc1.Init.NbrOfConversion = 1;

hadc1.Init.DMAContinuousRequests = DISABLE;

hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;

if (HAL_ADC_Init(&hadc1) != HAL_OK) { _Error_Handler(__FILE__, __LINE__);

} /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_0;

sConfig.Rank = 1;

sConfig.SamplingTime = ADC_SAMPLETIME_112CYCLES;

if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)

{ _Error_Handler(__FILE__, __LINE__); } }

static void MX_TIM1_Init(void)

{

  TIM_ClockConfigTypeDef sClockSourceConfig;

  TIM_MasterConfigTypeDef sMasterConfig;

  TIM_OC_InitTypeDef sConfigOC;

  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;

  htim1.Instance = TIM1;

  htim1.Init.Prescaler = 0;

  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;

  htim1.Init.Period = 1000;

  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

  htim1.Init.RepetitionCounter = 0;

  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  if (HAL_TIM_OC_Init(&htim1) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC2REF;

  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;

  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  sConfigOC.OCMode = TIM_OCMODE_TIMING;

  sConfigOC.Pulse = 100;

  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;

  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;

  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;

  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;

  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;

  if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;

  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;

  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;

  sBreakDeadTimeConfig.DeadTime = 0;

  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;

  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;

  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;

  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

}

void TIM1_CC_IRQHandler(void)

{

  /* USER CODE BEGIN TIM1_CC_IRQn 0 */

    HAL_GPIO_TogglePin(LD2_GPIO_Port,LD2_Pin);

    hadc1.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART;

  /* USER CODE END TIM1_CC_IRQn 0 */

  HAL_TIM_IRQHandler(&htim1);

  /* USER CODE BEGIN TIM1_CC_IRQn 1 */

  /* USER CODE END TIM1_CC_IRQn 1 */

}

Posted on November 08, 2017 at 18:05

nico 2010 wrote:

Hello!

I see you use a mixture of LL and HAL and I don't work with LL libraries.

What I want to say is that using this settings for ADC1 and TIMER1, ADC didn't read any value until I made the modification in Timer1 IRQ Handler, like below. So I guess that is a bug in the library of ADC, as I said in my topic 'Nucleo F410RB ADC dilema'.

hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISINGFALLING;

hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC2;

  sConfigOC.OCMode = TIM_OCMODE_TIMING;

I read your topic 'Nucleo F410RB ADC dilema' already before answering here and understand what you're doing. Perhaps I can explain it to you better. What you're doing here is configuring TIM1 CC2 to produce a periodic interrupt, and software-triggering ADC1 from the TIM1 CC2 interrupt service routine. That succeeds in triggering ADC1 as desired but it brings the undesired overhead of the interrupt service routine. The hardware specifies that ADC1 can be triggered directly from TIM1 CC unit without using software trigger at all - this is very desirable as the trigger rate increases. Your code is not doing this. If the goal is to directly trigger the ADC directly from TIM1, it's not working.

ADC1 is configured to trigger directly from the TIM1 CC2 condition, the documentation says this is supposed to work. I attempted a similar configuration to yours: using TIM_OCMODE_TIMING, and found that didn't work. TIM1 is being clocked from the system clock of 96MHz, and auto-reload is set to 2499 (period of TIM1 should be 1 / 38400 seconds, or around 26uS). I then tried changing TIM1 CC2 to 'CC Compare Output'. Meaning, the Capture Compare unit is configured and generating compare matches and the associated GPIO pin shows the output externally (you can hang an oscilloscope on it to watch it). In this setting, I was seeing no activity on the GPIO pin, and no ADC triggers. Thinking that perhaps the Pulse value of 0 was creating a very short output condition (TIM1 is clocked at 96MHz, so perhaps a Pulse value of 0 is creating an output pulse of ~10nS; far too fast to see). I set the value of OC Pulse to 1250, so I'd expect to see a pulse around 13uS wide; no such luck. So then I set OCMode to Toggle - and immediately see a 19200Hz square wave on the GPIO, and the ADC is being triggered 19200 times/second. Since my goal was to trigger the ADC at 38400 samples/sec, I configured it to trigger on rising and falling edge, and it works as desired. I then configured the OC for 'CC Compare, no output', so the GPIO is no longer active, but the ADC continues to trigger as expected.

It occurs to me that the 96MHz clock rate of TIM1 may be too high to trigger the ADC; particularly since the trigger circuit offers the option of rising edge/falling edge/both edges. This suggests that the internal signal is being synchronized using the same system clock as TIM1 - and that won't work. So I'll experimentally try dividing the clock to TIM1 so it's running at, say, 24MHz, and use an auto-reload value of 624 (2500 / 4 - 1). Perhaps that'll make the hardware behave as expected.

Re: HAL vs LL with STM32CubeMX. I find that I prefer using the LL settings (look in the Project Settings on CubeMX) for most of the APIs, since it drags in less library code with fewer side-effects, and CubeMX is happy to generate LL. The exceptions to this are when I'm using DMA; I prefer to let HAL code do the set-up of DMA. It's not like HAL vs LL is a religion or food allergy; many things that CubeMX generates are easier to understand when using LL APIs.

Posted on November 08, 2017 at 19:31

Myers.Dana wrote:

It occurs to me that the 96MHz clock rate of TIM1 may be too high to trigger the ADC; particularly since the trigger circuit offers the option of rising edge/falling edge/both edges. This suggests that the internal signal is being synchronized using the same system clock as TIM1 - and that won't work. So I'll experimentally try dividing the clock to TIM1 so it's running at, say, 24MHz, and use an auto-reload value of 624 (2500 / 4 - 1). Perhaps that'll make the hardware behave as expected.

... and reducing the TIM1 clock to 24MHz with CKDIV 4 did not make a difference.

Posted on November 08, 2017 at 19:55

Experimenting may be fun, but the primary source of information is (ought to be) the RM/DS.

using TIM_OCMODE_TIMING, and found that didn't work.

The major joy of using a 'library' such as Cube/HAL is that whenever in doubts and needs consulting the RM, one has to reverse engineer the various haphazardly created 'helpful' symbols:

&sharpdefine TIM_OCMODE_TIMING                   0x00000000U

So that (together with examining its usage) reveals that you set TIMx_CCMRy.OCzM to

000: Frozen - The comparison between the output compare register TIMx_CCR1 and the

counter TIMx_CNT has no effect on the outputs.(this mode is used to generate a timing

base).

I have no idea whatsoever what ST means by 'generate a timing base' here, but the facts are clear: this setting results in no change of TIMx_CHz output - that's why you've experienced no change of output.

So you wanted to use one of the PWM modes. I don't know how are they called in the Cube parlance.

Toggle is fine too if it fits other purposes that timer may be used for.

JW

Dana Myers
Senior
Posted on November 08, 2017 at 21:40

I was beginning to conclude the same thing myself. As much as I rather like the STM32 parts, using them is sometimes a bit of an 'adventure'.

Thank you for the help (and

Preda.Cristinel

‌ you probably want to review your code that's using an ISR; it's probably not what you

really want and this is).