cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F407VE sampling signal with ADC.

yuarth07
Associate II

Hello everyone, i'm sampling an analog signal, using an single ADC triggered by a timer, and to observed the reconstructed signal i use a DAC connected to an oscilloscope.

I have the next configurations:

ADC parameter settings:

Clock prescler: PCLK2 divided by 4 (PCLK is 84MHz in APB2)

Resolution: 8 bits (11 ADC clock cycles)

The rest parameters is predetermined 

ADC Regular conversions:

Number of conversions: 1
External trigger conversion source: Timer 2 trigger out event
Sampling time: 3 cycles

NVIC settings: ADC1, ADC2, ADC3 global interrupts: Enable

Timer configurations:

Mode: Internal clock

Parameter settings:

Prescaler: 1 (HCKL: 168MHz, APB1 timer clocks 84MHz)

Counter mode: up

Counter period (ARR 32 bits value): 1 (with that congurations i hava a sampling rate of 1MHz)

Trigger event selection: update event

Having these configurations i'm sampling a 100KHz signal, and when i observed the two signal on the oscilloscope, my reconstructed signal presents is out of phase and it also seems i was sampling below a sampling frecuency of 1MHz, because the reconstructed signal appears very distorted and very out of phase respect the analog signal.

How can i solve it? please i need help, i need to sample high frecuency signals, i know my fADC is 21 MHz, 84MHz/ div 4(prescaler). I need to solve it without using DMA and interleaved mode, just single ADC, it is posible to sample signals over the 100kHz just with that i have? or did my adc reach the limit?

I was also using my ADC with polling conversion (without using a timer) and it turned out that the most it could do was sampled signals of maximum 1Khz.

This is my code en cubeide:

 

ADC_HandleTypeDef hadc1;

DAC_HandleTypeDef hdac;

TIM_HandleTypeDef htim2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
static void MX_DAC_Init(void);
static void MX_TIM2_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

uint16_t adc_value;
//uint16_t dac_value;

/* USER CODE END 0 */

/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

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

/* USER CODE BEGIN Init */

/* USER CODE END Init */

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

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC1_Init();
MX_DAC_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */


HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
HAL_ADC_Start_IT(&hadc1);
HAL_TIM_Base_Start(&htim2);


/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

/**
* @brief System Clock Configuration
* @retval None
*/
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_SCALE1);

/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
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 = 8;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}

/** Initializes the CPU, AHB and APB buses 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_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

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

/**
* @brief ADC1 Initialization Function
* @PAram None
* @retval None
*/
static void MX_ADC1_Init(void)
{

/* USER CODE BEGIN ADC1_Init 0 */

/* USER CODE END ADC1_Init 0 */

ADC_ChannelConfTypeDef sConfig = {0};

/* USER CODE BEGIN ADC1_Init 1 */

/* USER CODE END ADC1_Init 1 */

/** 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_8B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO;
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_2;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */

/* USER CODE END ADC1_Init 2 */

}

/**
* @brief DAC Initialization Function
* @PAram None
* @retval None
*/
static void MX_DAC_Init(void)
{

/* USER CODE BEGIN DAC_Init 0 */

/* USER CODE END DAC_Init 0 */

DAC_ChannelConfTypeDef sConfig = {0};

/* USER CODE BEGIN DAC_Init 1 */

/* USER CODE END DAC_Init 1 */

/** DAC Initialization
*/
hdac.Instance = DAC;
if (HAL_DAC_Init(&hdac) != HAL_OK)
{
Error_Handler();
}

/** DAC channel OUT1 config
*/
sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN DAC_Init 2 */

/* USER CODE END DAC_Init 2 */

}

/**
* @brief TIM2 Initialization Function
* @PAram None
* @retval None
*/
static void MX_TIM2_Init(void)
{

/* USER CODE BEGIN TIM2_Init 0 */

/* USER CODE END TIM2_Init 0 */

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};

/* USER CODE BEGIN TIM2_Init 1 */

/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 8-1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */

/* USER CODE END TIM2_Init 2 */

}

/**
* @brief GPIO Initialization Function
* @PAram None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{

adc_value = HAL_ADC_GetValue(&hadc1); // Obtiene el valor convertido del ADC
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_8B_R, adc_value);

}

/* USER CODE END 4 */

/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @PAram file: pointer to the source file name
* @PAram line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

 

 

 

 

 

 

 

 

 

 

 

5 REPLIES 5
TDK
Guru

They will never be perfectly in phase due to the delay between sampling and setting the DAC.

You can't expect to interrupt at 1 MHz, the cpu isn't going to keep up, certainly not with HAL.

Make a DMA solution. You have the ADC side of this set up, now add the DAC side. Transfer the data in the buffer to the DAC at the same frequency the ADC is being sampled. Since you already have the update event tied to the ADC, use the ch1 event for the DAC.

If you feel a post has answered your question, please click "Accept as Solution".

Ok thans for answering, but is any other solution possible without dma? The challenge in this project is that i must find the maximum sampling frecuency without using dma or interleaved mode.

Now i ask you, could you explain a little more about why the cpu will not keep up with the frecuency of 1Mhz, what is the limit for HAL? I'm a beginner and i would like to lear more

> Ok thans for answering, but is any other solution possible without dma?

Sure, you can use interrupts and live with the delay. Lower your expectations in terms of responsiveness. You could also code your own IRQ handlers instead of using HAL to get more speed.

> could you explain a little more

The CPU can only execute instructions so fast. Say it's running at 40 MHz. You're trying to interrupt at 1 MHz. That leaves 40 cpu ticks per interrupt, which is not going to be enough. How fast it can go will depend on how long your IRQ routines are. A typical length is in the hundreds of CPU cycles, which is going to limit your interrupt speed to tens of kHz.

If you feel a post has answered your question, please click "Accept as Solution".

Thank you so much for the help, and regarding the sampling frequency that i will obtain from the code my own IQR Handlers, how can i measure the frequency? Could i toggle a pin and watch the time between samples on the oscilloscope? That to determine if i am sampling at the sample rate i defined.

Yes, toggling a pin in the IRQ and watching it on a scope will give you the rate at which the IRQ is being called.

If you feel a post has answered your question, please click "Accept as Solution".