cancel
Showing results for 
Search instead for 
Did you mean: 

STM32f401re Nucleo ADC speed test

JSubr
Associate

According to STM32F401re data sheet conversion time for 12 bit , 10 bit, 8 bit and 6 bit ADC at 30MHz ADC clock are 0.5us, 0.43us. 0.37us and 0.3us respectively with 3 sampling cycles. I also used exactly the same configurations to achieve the exact ADC speed. I am toggling an output pin after every EOC flag raised using EOCIE interrupt. Frequency of this toggle is used to determine conversion speed. However I am not able to achieve the same speed. I am getting 0.552us, 0.495us. 0.477us and 0.515us for 12, 10, 8 and 6 bit ADC respectively. However I see that I am very close to 12 and 10 bit readings but 8 bit and 6 bit ADC speed too slow than specified in Data Sheet.

Can somebody help me to figure the the mistake in my code. 

#include "stm32f4xx.h"
 
	//Variables
	
	uint32_t tstab = 3U; //ADC stabilization time 3us
	uint32_t count = 0U;
  uint32_t ADCVal;
	uint32_t i;
 
void ADC_IRQHandler(void)
{
	
	GPIOA->ODR ^= 0x00000020; // Toggle GPIOA pin 5
	ADC1->SR &= ~ADC_SR_EOC; // Clear EOC flag
	//ADCVal = ADC1->DR; // Read Data Register
	
}
 
int main(void)
	
{
 
	
	
		//GPIO initialization
	
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Reset and clock control
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN;
	GPIOA->MODER |= (GPIO_MODER_MODE1_0 | GPIO_MODER_MODE1_1);
	GPIOA->MODER |= GPIO_MODER_MODE5_0;
	GPIOA->MODER &= ~(GPIO_MODER_MODE5_1);
	//output type register
	GPIOA->OTYPER &= ~(GPIO_OTYPER_OT_5);
	//Speed register
	GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED5_0 | GPIO_OSPEEDR_OSPEED5_1;
	//Push-pull register
	GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD5);
	
 
	//Select the clock source for ADC
 
	//activate HSI clock source
	
	RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; 
	RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN ;
	RCC->CR |= RCC_CR_HSION;
while((RCC->CR & RCC_CR_HSIRDY)==0)
{
}
 
// FLASH Latency
      FLASH->ACR &= ~(FLASH_ACR_LATENCY);
      FLASH->ACR |= FLASH_ACR_LATENCY_5WS | FLASH_ACR_ICEN | FLASH_ACR_DCEN| FLASH_ACR_PRFTEN;
 
 
// dividers for APB1 and APB2
//APB1 set to 101 -> divided by 2
RCC->CFGR &= ~(RCC_CFGR_PPRE1_1);
RCC->CFGR |= (RCC_CFGR_PPRE1_2);
//APB2 set to 000 -> not divided
RCC->CFGR &= ~(RCC_CFGR_PPRE2_0 | RCC_CFGR_PPRE2_1);
RCC->CFGR &= ~(RCC_CFGR_PPRE2_2);
 
// set pll division and multiplication factors in PLLCFGR M=16, N=336, P=4, Q = 4;
//PLLM
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLM);	//all zeros
RCC->PLLCFGR |= RCC_PLLCFGR_PLLM_4;		//puts in 16
//PLLN
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLN);	//all zeros
RCC->PLLCFGR |= (RCC_PLLCFGR_PLLN_7 | RCC_PLLCFGR_PLLN_6 | RCC_PLLCFGR_PLLN_6 | RCC_PLLCFGR_PLLN_4); //puts in 101010000 = 240;
//PLLP
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLP);	//all zeros configures it to 2
RCC->PLLCFGR |= (RCC_PLLCFGR_PLLP_0);	//configures it to 4
//PLLQ
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLQ);	//all zeros
RCC->PLLCFGR |= RCC_PLLCFGR_PLLQ_2 | RCC_PLLCFGR_PLLQ_1 | RCC_PLLCFGR_PLLQ_0; 			//puts in a 7
 
//set HSI as PLL input source in PLLCFGR
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLSRC);
 
 
//enable PLL in RCC->CR and wait for flag
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));
 
//switch clock source
RCC->CFGR &= ~(RCC_CFGR_SW_0);
RCC->CFGR |= RCC_CFGR_SW_1;
 
	//ADC Configure
	
	ADC->CCR &= ~(ADC_CCR_ADCPRE_1 | ADC_CCR_ADCPRE_0); //ADC prescalar 2
	//ADC->CCR |= ADC_CCR_ADCPRE_0;
	
	ADC1->CR2 |= ADC_CR2_ADON; // Turn on ADC
	count = (tstab * (SystemCoreClock / 1000000U)); //stabilization time delay for ADC
    while(count != 0U)
    {
      count--;
    }
		
	
	//ADC1->CR1 |= ADC_CR1_OVRIE;
	ADC1->SQR1 &= ~(ADC_SQR1_L);
	ADC1->SQR3 |= ADC_SQR3_SQ1_0;
	ADC1->CR1 |= ADC_CR1_AWDCH_0; //Channel 1
		
	// ADC Resolution - Uncomment according to the ADC resolution
		
	ADC1->CR1 &= ~(ADC_CR1_RES_1 | ADC_CR1_RES_0); //12 bit ADC
	//ADC1->CR1 |= (ADC_CR1_RES_0); 							// 10 bit ADC
	//ADC1->CR1 |= (ADC_CR1_RES_1); 							// 8 bit ADC
	//ADC1->CR1 |= ADC_CR1_RES;										//6 bit ADC
		
		
	ADC1->CR2 |= ADC_CR2_CONT; //Continous mode
	ADC1->SMPR2 &= ~(ADC_SMPR2_SMP1_0 | ADC_SMPR2_SMP1_1 | ADC_SMPR2_SMP1_2);  //3 cycles samplingtime //0x00000000U;
	ADC1->CR1 |= ADC_CR1_EOCIE; // Enable ADC EOC interrupt
	NVIC_EnableIRQ(ADC_IRQn);   // Enable ADC interrupt
	NVIC_SetPriority(ADC_IRQn, 0); //Interrupt priority set to zero
	
	
	
	//Start conversion
	ADC1->CR2 |= ADC_CR2_SWSTART; // ADC conversion start	
	
 
	while(1)
	{
			
	}
	
	
}

1 REPLY 1

Some of the code here is rather inefficient.

Biggest problem I see with the IRQ Handler is that it doesn't manage the pipeline issues properly so is likely to re-enter.

You should validate the source, and clear it earlier. Read the ADC data register rather than do that RMW on the status, and toggle the LED *after* you've cleared the source as this will perform a fencing action.

For speed people normally use DMA, and have buffers sized so the interrupt load is decimated. You're not going to be able to sustain a 1 or 2 MHz interrupt rate.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..