2019-02-12 05:43 AM
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)
{
}
}
2019-02-12 06:10 AM
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.