2020-06-15 12:01 PM
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).
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 ****************************/
Solved! Go to Solution.
2020-06-16 12:18 AM
> // 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
2020-06-16 12:18 AM
> // 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
2020-06-16 12:37 AM
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
2020-06-16 01:31 AM
> 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
2020-06-16 10:46 AM
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!