2025-02-21 3:56 PM - edited 2025-02-21 4:05 PM
#Intention : Simultaneous measurement of 8 frequencies ( Squre wave, upto 1.5 Mhz ).
I have tested this youtube tutorial by Controllerstech on my board Nucleo-STM32U545REQ. First of all, i could not setup the board with HSE oscillator according to the YouTube tutorial so, i proceeded with HSI clock, It has some 1% precision.
Here is my clock configuration running at 160MHz.
Secondly, my timer 1 as PWM1 Generation.
Them, Timer 2 as Input capture direct mode for rising edge
And GPDMA1 as follows, I tried as much as I understand from the tutorial because in the tutorial they could setup the DMA in respective Timer setup.
Finally in code section as I do not need the duty cycle portion of the code i did not setup for falling edge as well as removed falling edge calculation code.
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include<math.h>
#include<stdio.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
COM_InitTypeDef BspCOMInit;
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim2;
DMA_HandleTypeDef handle_GPDMA1_Channel0;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void SystemPower_Config(void);
static void MX_GPIO_Init(void);
static void MX_GPDMA1_Init(void);
static void MX_TIM2_Init(void);
static void MX_ICACHE_Init(void);
static void MX_TIM1_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/**************** PWM INPUT **************/
/* define the capturing TIMER's CLOCK and the Prescalar you are using */
#define TIMCLOCK 160000000
#define PSCALAR 0
#define MIN(a,b) ((a) < (b) ? (a) : (b))
/* Define the number of samples to be taken by the DMA
For lower Frequencies, keep the less number for samples
*/
#define numval 100
int riseCaptured = 0;
uint32_t riseData[numval];
float frequency = 0;
int isMeasured = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
// If the Interrupt is triggered by 1st Channel
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
riseCaptured = 1;
}
/* Rest of the calculations will be done,
* once both the DMAs have finished capturing enough data */
if (riseCaptured)
{
// calculate the reference clock
float refClock = TIMCLOCK/(PSCALAR+1);
int indxr = 0;
int countr = 0;
float riseavg = 0;
/* In case of high Frequencies, the DMA sometimes captures 0's in the beginning.
* increment the index until some useful data shows up
*/
while (riseData[indxr] == 0) indxr++;
/* Again at very high frequencies, sometimes the values don't change
* So we will wait for the update among the values
*/
while ((MIN((riseData[indxr+1]-riseData[indxr]), (riseData[indxr+2]-riseData[indxr+1]))) == 0) indxr++;
/* riseavg is the difference in the 2 consecutive rise Time */
/* Assign a start value to riseavg */
riseavg += MIN((riseData[indxr+1]-riseData[indxr]), (riseData[indxr+2]-riseData[indxr+1]));
indxr++;
countr++;
/* start adding the values to the riseavg */
while (indxr < (numval))
{
riseavg += MIN((riseData[indxr+1]-riseData[indxr]), riseavg/countr);
countr++;
indxr++;
}
/* Find the average riseavg, the average time between 2 RISE */
riseavg = riseavg/countr;
indxr = 0;
/* Calculate Frequency
* Freq = Clock/(time taken between 2 Rise)
*/
frequency = (refClock/(float)riseavg);
//printf("Freq %f\r\n", frequency);
riseCaptured = 0;
isMeasured = 1;
}
}
/* 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 Power */
SystemPower_Config();
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_GPDMA1_Init();
MX_TIM2_Init();
MX_ICACHE_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
TIM1->CCR1 = 5;
if (HAL_TIM_Base_Start(&htim1) != HAL_OK){
Error_Handler();
}
if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1) != HAL_OK){
Error_Handler();
}
HAL_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_1,(uint32_t*)&riseData, numval);
/* USER CODE END 2 */
/* Initialize COM1 port (115200, 8 bits (7-bit data + 1 stop bit), no parity */
BspCOMInit.BaudRate = 115200;
BspCOMInit.WordLength = COM_WORDLENGTH_8B;
BspCOMInit.StopBits = COM_STOPBITS_1;
BspCOMInit.Parity = COM_PARITY_NONE;
BspCOMInit.HwFlowCtl = COM_HWCONTROL_NONE;
if (BSP_COM_Init(COM1, &BspCOMInit) != BSP_ERROR_NONE)
{
Error_Handler();
}
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay (1000);
/* Call the measurement whenever needed */
if (isMeasured)
{
TIM2->CNT = 0;
HAL_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_1, riseData, numval);
isMeasured = 0;
}
}
/* 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
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses 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.PLLMBOOST = RCC_PLLMBOOST_DIV1;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 10;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 1;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLLVCIRANGE_1;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
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_CLOCKTYPE_PCLK3;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief Power Configuration
* @retval None
*/
static void SystemPower_Config(void)
{
/*
* Switch to SMPS regulator instead of LDO
*/
if (HAL_PWREx_ConfigSupply(PWR_SMPS_SUPPLY) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN PWR */
/* USER CODE END PWR */
}
/**
* @brief GPDMA1 Initialization Function
* @param None
* @retval None
*/
static void MX_GPDMA1_Init(void)
{
/* USER CODE BEGIN GPDMA1_Init 0 */
/* USER CODE END GPDMA1_Init 0 */
/* Peripheral clock enable */
__HAL_RCC_GPDMA1_CLK_ENABLE();
/* GPDMA1 interrupt Init */
HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);
/* USER CODE BEGIN GPDMA1_Init 1 */
/* USER CODE END GPDMA1_Init 1 */
/* USER CODE BEGIN GPDMA1_Init 2 */
/* USER CODE END GPDMA1_Init 2 */
}
/**
* @brief ICACHE Initialization Function
* @param None
* @retval None
*/
static void MX_ICACHE_Init(void)
{
/* USER CODE BEGIN ICACHE_Init 0 */
/* USER CODE END ICACHE_Init 0 */
/* USER CODE BEGIN ICACHE_Init 1 */
/* USER CODE END ICACHE_Init 1 */
/** Enable instruction cache in 1-way (direct mapped cache)
*/
if (HAL_ICACHE_ConfigAssociativityMode(ICACHE_1WAY) != HAL_OK)
{
Error_Handler();
}
if (HAL_ICACHE_Enable() != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ICACHE_Init 2 */
/* USER CODE END ICACHE_Init 2 */
}
/**
* @brief TIM1 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 160-1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 9;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.BreakFilter = 0;
sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;
sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
sBreakDeadTimeConfig.Break2Filter = 0;
sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
HAL_TIM_MspPostInit(&htim1);
}
/**
* @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};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 4294967295;
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();
}
if (HAL_TIM_IC_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != 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 */
/* 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();
printf("Error_Handler() called! File: %s, Line: %d\n", __FILE__, __LINE__);
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 */
#Issure 1 : Finally I do get output, for case of 160MHz clock and Prescaler of 160 and ARR of 10, I am supposed to get 10KHz (160MHz/Prescaler/ARR) , but i do get frequency measurements of 40KHz , which has been consistent with the change of prescaler and ARR. i.e., 400KHz instead of 100KHz. ( Sometime its 396KHz , although it could be because HSI input)
Note that, I checked the PWM output generation by Timer 1 , it really produces what it is supposed produce for example 10KHz or 100KHz, i have tested the PWM frequency by another microcontroller. so it is really the issue of detecting the frequency of timer 1 input capture.
#Issure 2 : Another issue is the code is not running the while loop more than once as it is supposed to be, turns out the code in while loops just runs once because of isMeasured status doen't change from 0 to 1. As, I have no idea why is it not updating.
/* Call the measurement whenever needed */
if (isMeasured)
{
TIM2->CNT = 0;
HAL_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_1, riseData, numval);
isMeasured = 0;
}
Here is output of the loop it only prints once :
---- Opened the serial port COM3 ----
____
Frequency : 825000.000000
____
____
____
____
____
____
---- Closed the serial port COM3 ----
#Issue 3 :
when i did setup like the YouTube tutorial for enabling HSE I got this error , how to circumvent it on Nucleo-STM32U545REQ ?
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}''
I have attached my project file that I have been working on.
# Suggestion: Please suggest me a suitable and efficient way to measure 8 frequencies. @Sarra.S
2025-02-23 3:35 PM
I could implement PWM input mode with DMA,
Frequency = 1012658.187500 , Count = 158
Frequency = 1012658.187500 , Count = 158
Frequency = 1012658.187500 , Count = 158
Frequency = 1012658.187500 , Count = 158
Frequency = 1012658.187500 , Count = 158
Frequency = 1012658.187500 , Count = 158
Frequency = 1012658.187500 , Count = 158
Frequency = 0.00 Hz , Count = 0
Frequency = 0.00 Hz , Count = 0
Frequency = 0.00 Hz , Count = 0
Frequency = 0.00 Hz , Count = 0
Frequency = 0.00 Hz , Count = 0
Frequency = 0.00 Hz , Count = 0
Frequency = 0.00 Hz , Count = 0
Frequency = 0.00 Hz , Count = 0
Frequency = 1012658.187500 , Count = 158
Frequency = 1012658.187500 , Count = 158
Frequency = 1012658.187500 , Count = 158
Frequency = 1012658.187500 , Count = 158
Frequency = 1012658.187500 , Count = 158
Frequency = 1012658.187500 , Count = 158
Currently, looking into how to enable HSE in the Nucleo board.
2025-02-24 1:51 AM
Hello @frogrammer,
Regarding issue3,
>>when i did setup like the YouTube tutorial for enabling HSE I got this error , how to circumvent it on Nucleo-STM32U545REQ ?
The HSE can be generated from two possible clock sources:
Check this article: How to use STM32CubeMX to configure HSE (High-Spee... - STMicroelectronics Community
Also, Guidelines for oscillator design on STM8AF/AL/S and STM32 MCUs/MPU
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.