Skip to main content
MKlos.1
Associate
June 15, 2020
Solved

Conversion time ADC too low

  • June 15, 2020
  • 3 replies
  • 2678 views

Hi,

I have a NUCLEO-F429ZI board and am trying to get ADC1 to convert one channel (channel 3) when triggered by TIM2 (every second).

As I don't have an oscilloscope I wanted to measure the conversion time - to make sure everything is configured correctly - using Segger SystemView.

With the code below I set ADCCLK to 22.5 MHz and sample time to 480 clock cycles so I would expect the ADC interrupt to occur roughly 22 µs after the timer interrupt.

What I am seeing though is that the ADC interrupt (ISR34) occurs 2.6 µs after the timer ISR (ISR 44) (see screenshot).

0693W000001qkj0QAA.png

Quesion: Why is the ADC interrupt so fast?

The attached ZIP-file is the complete Segger Studio project, ready to compile.

Here's the complete code:

/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* (c) 2014 - 2020 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* conditions are met: *
* *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* - Neither the name of SEGGER Microcontroller GmbH *
* nor the names of its contributors may be used to endorse or *
* promote products derived from this software without specific *
* prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. *
* IN NO EVENT SHALL SEGGER Microcontroller GmbH BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
* DAMAGE. *
* *
**********************************************************************
 
-------------------------- END-OF-HEADER -----------------------------
 
File : main.c
Purpose : Generic application start
 
*/
 
#include <stm32f4xx.h>
#include "SEGGER_SYSVIEW.h"
 
void ClockInit(void)
{
 uint32_t timeout = 1000000;
 
 // enable flash prefetch
 FLASH->ACR |= FLASH_ACR_PRFTEN;
 // set 5 wait states (needed for high PLL frequency)
 FLASH->ACR &= ~FLASH_ACR_LATENCY_Msk;
 FLASH->ACR |= FLASH_ACR_LATENCY_5WS;
 do
 {
 timeout--;
 } while (((FLASH->ACR & FLASH_ACR_LATENCY_Msk) != FLASH_ACR_LATENCY_5WS) && (timeout > 0));
 
 // switch on external clock, bypass HSE with it and wait for clock to be ready
 RCC->CR |= (RCC_CR_HSEON | RCC_CR_HSEBYP);
 timeout = 1000000;
 do
 {
 timeout--;
 } while (((RCC->CR & RCC_CR_HSERDY_Msk) != RCC_CR_HSERDY) && (timeout > 0));
 
 // configure (HSE as clock source, M = 4, N = 180, P = 2), enable and wait for PLL
 RCC->PLLCFGR = (RCC_PLLCFGR_PLLSRC_HSE | (4 << RCC_PLLCFGR_PLLM_Pos) | (180 << RCC_PLLCFGR_PLLN_Pos));
 RCC->CR |= RCC_CR_PLLON;
 timeout = 1000000;
 do
 {
 timeout--;
 } while (((RCC->CR & RCC_CR_PLLRDY_Msk) != RCC_CR_PLLRDY) && (timeout > 0));
 
 // configure rest of clock tree
 RCC->CFGR &= ~(RCC_CFGR_PPRE2_Msk | RCC_CFGR_PPRE1_Msk | RCC_CFGR_HPRE_Msk);
 RCC->CFGR |= (RCC_CFGR_PPRE2_DIV2 | RCC_CFGR_PPRE1_DIV4 | RCC_CFGR_HPRE_DIV1);
 
 // set PLL as clock source and wait until clock is ready
 RCC->CFGR &= ~(RCC_CFGR_SW_Msk);
 RCC->CFGR |= RCC_CFGR_SW_PLL;
 timeout = 1000000;
 do
 {
 timeout--;
 } while (((RCC->CFGR & RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_PLL) && (timeout > 0));
 
 SystemCoreClockUpdate();
 
 // enable GPIOA, ADC1 and TIM2 clocks
 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
 RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
}
 
void TimerInit(void)
{
 // counter direction down, generate update event only on underflow
 TIM2->CR1 |= (TIM_CR1_DIR | TIM_CR1_URS);
 // set update event as trigger source
 TIM2->CR2 &= ~TIM_CR2_MMS_Msk;
 TIM2->CR2 |= TIM_CR2_MMS_1;
 // enable update interrupt
 TIM2->DIER |= TIM_DIER_UIE;
 // set timer and reload value for update generation every 1000 ms
 TIM2->ARR = TIM2->CNT = 90000000;
}
 
void TimerStart(void)
{
 TIM2->CR1 |= TIM_CR1_CEN;
}
 
void AdcInit(void)
{
 // PA3 as analog input
 GPIOA->MODER |= GPIO_MODER_MODE3;
 // set clock prescaler to 4 -> 22,5 MHz
 ADC123_COMMON->CCR |= ADC_CCR_ADCPRE_0;
 // enable timer 2 trigger event as start trigger on rising edge and generate EOC interrupt after each conversion
 ADC1->CR2 |= (ADC_CR2_EXTSEL_2 | ADC_CR2_EXTSEL_1 | ADC_CR2_EXTEN_0 | ADC_CR2_EOCS);
 // set 480 cycles sample time for channel 3
 ADC1->SMPR2 |= ADC_SMPR2_SMP3;
 // set channel 3 as first (any only) regular channel
 ADC1->SQR3 |= (ADC_SQR3_SQ1_2 | ADC_SQR3_SQ1_1);
 // enable EOC interrupt
 ADC1->CR1 |= ADC_CR1_EOCIE;
 // enable ADC
 ADC1->CR2 |= ADC_CR2_ADON;
}
 
void InterruptInit(void)
{
 // Timer 2
 NVIC_SetPriority(TIM2_IRQn, 3);
 NVIC_ClearPendingIRQ(TIM2_IRQn);
 NVIC_EnableIRQ(TIM2_IRQn);
 // ADC
 NVIC_SetPriority(ADC_IRQn, 2);
 NVIC_ClearPendingIRQ(ADC_IRQn);
 NVIC_EnableIRQ(ADC_IRQn);
}
 
void TIM2_IRQHandler(void)
{
 SEGGER_SYSVIEW_RecordEnterISR();
 NVIC_ClearPendingIRQ(TIM2_IRQn);
 TIM2->SR &= ~TIM_SR_UIF;
}
 
void ADC_IRQHandler(void)
{
 uint16_t Value;
 SEGGER_SYSVIEW_RecordEnterISR();
 Value = ADC1->DR;
 NVIC_ClearPendingIRQ(ADC_IRQn);
}
 
int main(void)
{
 ClockInit();
 
 SEGGER_SYSVIEW_Conf();
 
 TimerInit();
 AdcInit();
 InterruptInit();
 
 TimerStart();
 while(1);
}
 
/*************************** End of file ****************************/

This topic has been closed for replies.
Best answer by waclawek.jan

> // set channel 3 as first (any only) regular channel

> ADC1->SQR3 |= (ADC_SQR3_SQ1_2 | ADC_SQR3_SQ1_1);

Isn't this setting channel 6 rather than 3?

JW

3 replies

waclawek.jan
waclawek.janBest answer
Super User
June 16, 2020

> // set channel 3 as first (any only) regular channel

> ADC1->SQR3 |= (ADC_SQR3_SQ1_2 | ADC_SQR3_SQ1_1);

Isn't this setting channel 6 rather than 3?

JW

MKlos.1
MKlos.1Author
Associate
June 16, 2020

Yes, you are correct, that's the wrong channel! But does that really explain why the ADC interrupt occurs too fast?

I would have expected (without knowing for sure) the values to be different from the ones I would have expected but haven't even checked yet (as the input is not configured correctly) but not that the conversion is finished in a tenth of the time.

I can only try tonight as I don't have the hardware with me at the moment.

Best regards

waclawek.jan
Super User
June 16, 2020

> Yes, you are correct, that's the wrong channel! But does that really explain why the ADC interrupt occurs too fast?

How's channel 6 sample rate set?

JW

MKlos.1
MKlos.1Author
Associate
June 16, 2020

Oh man, you are so right! It was - of course - set to 0 (its reset value) and therefore to 3 cycles. I will have to get used to this per-channel sampling time configuration and take even better care about setting the correct bits!

After setting channel 3 in register ADC_SQR3 I now get a conversion time - measured with SystemView - of 21.9 µs!

Thanks a lot for the help!