2
votes

Plot of 60-80kHz using continuous conversion- DMAEdit 1:Image of 10kHz to 50kHz input signal using continuous conversion I am using the ADC on NUCLEO- STM32F401RE board. I have programmed the ADC using interrupts store about 10,000 values in an array and read them using a .ini file. The code does not work beyond 10kHz of input signal. ADC is of 12 bit resolution with 3 sampling cycles ; ADC clock frequency is 21Khz. Below is my code and plot of values for input frequency range of 1- 200Khz sine wave. The input voltage is 1.3V. What am I missing?

#include "main.h"
#include "stm32f4xx_hal.h" 
ADC_HandleTypeDef hadc1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);

uint32_t adc_data[10000];
uint32_t i=0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{   if (i<10000) {
adc_data[i]= HAL_ADC_GetValue(&hadc1);
    i++;
HAL_ADC_Start_IT(&hadc1);
}
    else {
    HAL_ADC_Stop_IT(&hadc1);
}
}

int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC1_Init();
  /* USER CODE BEGIN 2 */
HAL_ADC_Start_IT(&hadc1);

while (1)
  {

  }

}


void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /**Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
  /**Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 16;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /**Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                          |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};

  /**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 = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  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();
  }
  /**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_3CYCLES;
   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
   {
     Error_Handler();
   }
 }

 static void MX_GPIO_Init(void)
 { 
   /* GPIO Ports Clock Enable */
   __HAL_RCC_GPIOA_CLK_ENABLE();

 }

 void Error_Handler(void)
 {

 }

 #ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{ 
}
#endif /* USE_FULL_ASSERT */

plot of values for input frequency range of 1- 200Khz

2
ADC clock frequency is 21Khz Did you mean 21 MHz?followed Monica to Codidact
@berendi Yes, 21MHz. My bad, sorry!Archana Narayanan

2 Answers

1
votes

I'm assuming that the 21 kHz is a typo, and the ADC is running on APB2/4 = 21 MHz. Sampling time is 3 cycles, conversion time at 12 bits resolution is 12 cycles, so the sampling frequency of the ADC is = 1.4 MHz (21/(12+3)). When the core is running at 84 MHz, that's one sample every 60 core cycles.

60 cycles are barely enough to even start executing an interrupt handler, with flash wait states taken into account.

Looking at the plot you've attached it looks like the actual sampling frequency is 300 kHz (there are 30 samples in a full cycle of the 10 kHz signal), that's 3.33 µs, or 25200 clock cycles. Considering the ridiculous amount of overhead in HAL, it seems realistic.

Your code makes a single conversion with an interrupt triggered when it's complete, then reads and stores the value, and only then instructs the ADC to start the next conversion, introducing a variable delay that depends on a couple of hard to predict factors.

Use Continuous Conversion

The ADC is able to restart the coversion (or the sequence if there is one), either immediately after finishing the last one, or triggered by a timer, see the description of EXTSEL, EXTEN, SCAN and CONT bits of the control registers in the Reference Manual. It can even trigger a DMA transfer after each conversion, so you can set up a DMA channel to store the readings into a buffer. This would guarantee even sampling intervals even at the highest possible sampling frequency.

0
votes

Please habe a look at Nyquist Frequency

In short: With a ADC frequency of 21kHz you could sample the input with 7kHz and that means your highest possible signal frequency is at ~3.5kHz.