Help with STM32duino timers and frequency measurement - Page 1
Forum section: electronics / projects

author: timdf911
author_rank: Contributor
timestamp: December 01, 2022, 05:12:11 pm
content: Hi guys1st post but long term lurker !I've been trying to update a design I originally did 7 years ago which used a Teensy processor and was written entirely in C using the Atmel environmanr.I've recently discovered the delights of an STM32F401re and have started to port the code using stm32duino environment - but I've run into a road block and despite lots of googling I've made little progress due to being overwhelmed with all the registers associated with the timers and what I suspect is limited timer functionality in the stm32duino environment.Here's what I need to do - basically a one shot measurement of a frequency between 1 and 30MHz to a resolution of 10kHz.Here's how I think it should be done1. Use timer 2 with the signal using ETR input with possibly the prescaler if it can't handle 30MHz directly2. Set the timer2 counter to zero3. Open the gate for 1mS using a second timer (say timer3) then read the timer 2 to see how many times it's been incremented. The resulting count should be between 1000 and 30000To do the above seems to need a bunch of registers setting, I've had some limited success using some snippets of code from Andrewbcn GPSDO project, but frankly my attempt is a mess and I'd be ashamed to post it !I'm hoping someone can give me a few pointers on key registers and settings which will link the two timers together and operate as above.I've tried reading the data and using CubeMx but managed to get myself wrapped round the axle there aswell.Regards Tim

author: coromonadalix
author_rank: Super Contributor
timestamp: December 01, 2022, 05:44:23 pm
content: https://www.engineersgarage.com/how-to-use-an-stm32-microcontroller-to-measure-external-signal-frequencies/https://deepbluembedded.com/stm32-counter-mode-example-frequency-counter-timer-in-counter-mode/https://microcontrollerslab.com/stm32-blue-pill-timer-input-capture-mode-frequency-measurement/??

author: timdf911
author_rank: Contributor
timestamp: December 01, 2022, 07:15:47 pm
content: Thanks for those links, however during my extensive googling I had already come across them, but maybe I need to give them a second look / chance.I was deterred from following them as some used CubeMX and / or did not explicitly use the ETR input for maximum frequency operation.Maybe I need to practice figuring out how to use CubeMX more efficiently and how to embed the code it generates into mine.Regards Tim

author: paulca
author_rank: Super Contributor
timestamp: December 01, 2022, 07:55:34 pm
content: CubeMX will guide you in what way you 'can' configure all the timers, making it quick to try things.There is nothing preventing you from creating the basic project in a rapid throw away fashion using CubMX generated code and then put your own further register based code into an isolated C file. You can re-run the code generation when ever you want if you do it right.I did this when I wanted to use a single timer to do two different things based on a sequence of interrupt cascades. CubeMX would set the timer up for me in the first mode, I then take over using the registers (and a few HAL functions and macros) to reconfigure and restart the timer in PWM DMA mode or basic counter delay timer.

author: timdf911
author_rank: Contributor
timestamp: December 01, 2022, 09:20:04 pm
content: Thanks for the ideas -funnily enough the timers will need to be reconfigured depending on what's happening with the device.However given that my code size is already 150kB for all the other stuff going on before I add this frequency measurement part and the other code which goes round it (stepper motor control) I suspect it might be easier to integrate the CubeMX code into my code base rather than the other way around.I had tried with CubeMX to generate the timer code but came a bit unstuck with exactly how to interconnect the timers such that the gating timer could gate the signal into the main counter for counting and what modes thy needed to be in etc - if that makes sense.Basically how to configure using a two timer approach to count the incoming cycles over a fixed period of time.Tomorrows another day and I'll have another crack at CubeMX by first getting the gating timer going at 1mS and checking with a scope it's operating as intended.So much to learn and so few brain cells left :-)Regards Tim

author: paulca
author_rank: Super Contributor
timestamp: December 01, 2022, 10:02:31 pm
content: I've not slaved timers before. However I believe that is the area you want to be looking. That or "Triggers". One timer can be slaved to another.Use CubeMX for what it's good for. Rapid prototyping. Try the timer idea out on it's own using Cube to get the bulk of the code written. Then it will give you a base to work from and port back to your actual app.On code size. I need to look for a way to split the symbol file in CubeMX. It seems those it makes the elf with debug symbols in it. There should, hopefully be an option to save it externally and load it into the debugger, rather than flash the damn thing to the MCU flash memory!

author: Kleinstein
author_rank: Super Contributor
timestamp: December 01, 2022, 10:19:58 pm
content: The ELF and similar output file is not directly giving the flash usage. The files tend to be a lot larger.In the debug mode the compiler uses not optimization and this can make the code a lot longer. The prodution type make creates shorter code that is however harder to debug with the debugger. One can still use it for debugging: decalre the variables in question volatile, which restricts much of the optimizations where those variables are used.The CubeMx code adds a bit to the code - to a large part for the initialization of the HW parts used. So when adding the actual frequency measurement this would not add that much code. It is more like some 16 kB to start with for the init part.

author: langwadt
author_rank: Super Contributor
timestamp: December 01, 2022, 10:20:13 pm
content: Quote from: paulca on December 01, 2022, 10:02:31 pmsurely the debugger only flashes the srec/hex/bin extracted from the elf

author: langwadt
author_rank: Super Contributor
timestamp: December 01, 2022, 10:24:25 pm
content: Quote from: timdf911 on December 01, 2022, 07:15:47 pmyou might want to change the cube code generation to use the LL instead of HAL

author: ozcar
author_rank: Frequent Contributor
timestamp: December 02, 2022, 06:22:45 am
content: Quote from: timdf911 on December 01, 2022, 09:20:04 pmI have mostly only used CubeMX to set up clock trees - it seems quite useful for that. Anyway, I tried to generate some code using the old version of CubeMX I have installed, and I sort-of got it to work. It was really just a matter of setting the more-or-less obvious things for the timers on the Pinout and Configuration tabs (myself, I don't quite follow the logic of what is under the one tab vs the other). I could not get it to enable the interrupt to grab the timer counter so in the end just gave up and set the required flag in the DIER register directly in the generated code.I set it up using TIM3 (16 bit) to gate TIM2 (32 bit), with TIM2 counting the signal to measure. For 84MHz timer clock I set TIM3 prescaler to divide by 300, channel count of 28,000 and ARR = 28,280, for gate time of 100ms, repeated every 101ms, and it gives plausible results. I did not put any code in to display the result, I just ran it under the debugger to see what it got.For what it is worth, this is the resulting main.c. I enabled both the MCO outputs and also got it to blink the user LED on the Nucleo board in the interrupt routine as part of my sanity checking. Except for the few lines added to main.c, I inserted a call to the TIM3_IRQHandler_main() into the generated stm32f4xx_it.c.Code:/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************** This notice applies to any and all portions of this file* that are not between comment pairs USER CODE BEGIN and* USER CODE END. Other portions of this file, whether* inserted by the user or by software development tools* are owned by their respective copyright owners.** COPYRIGHT(c) 2022 STMicroelectronics** Redistribution and use in source and binary forms, with or without modification,* are permitted provided that the following conditions are met:* 1. Redistributions of source code must retain the above copyright notice,* this list of conditions and the following disclaimer.* 2. Redistributions in binary form must reproduce the above copyright notice,* this list of conditions and the following disclaimer in the documentation* and/or other materials provided with the distribution.* 3. Neither the name of STMicroelectronics nor the names of its contributors* may be used to endorse or promote products derived from this software* without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.********************************************************************************//* Includes ------------------------------------------------------------------*/#include "main.h"#include "stm32f4xx_hal.h"/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private variables ---------------------------------------------------------*/TIM_HandleTypeDef htim2;TIM_HandleTypeDef htim3;/* USER CODE BEGIN PV *//* Private variables ---------------------------------------------------------*/volatile uint32_t TimeCount=0;uint32_t TimeCountm=0;/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/void SystemClock_Config(void);static void MX_GPIO_Init(void);static void MX_TIM2_Init(void);static void MX_TIM3_Init(void);/* USER CODE BEGIN PFP *//* Private function prototypes -----------------------------------------------*//* USER CODE END PFP *//* USER CODE BEGIN 0 */void TIM3_IRQHandler_main(void){// at this time, TIM2 counter is stoppedTimeCount = TIM2->CNT;TIM2->CNT = 0; // resetHAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // Nucleo LED}/* USER CODE END 0 *//*** @brief The application entry point.** @retval None*/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_TIM2_Init();MX_TIM3_Init();/* USER CODE BEGIN 2 */TIM2->CNT = 0;TIM3->DIER |= TIM_DIER_CC1IE; // enable interrupt for channel 1 compareHAL_TIM_Base_Start(&htim3); // turns on CEN in CR1HAL_TIM_Base_Start(&htim2); // turns on CEN in CR1/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){TimeCountm = TimeCount;if ( TimeCountm )HAL_Delay(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;RCC_ClkInitTypeDef RCC_ClkInitStruct;/**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_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 8;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(__FILE__, __LINE__);}/**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(__FILE__, __LINE__);}HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);HAL_RCC_MCOConfig(RCC_MCO2, RCC_MCO2SOURCE_SYSCLK, RCC_MCODIV_4);/**Configure the Systick interrupt time*/HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);/**Configure the Systick*/HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);/* SysTick_IRQn interrupt configuration */HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);}/* TIM2 init function */static void MX_TIM2_Init(void){TIM_ClockConfigTypeDef sClockSourceConfig;TIM_SlaveConfigTypeDef sSlaveConfig;TIM_MasterConfigTypeDef sMasterConfig;htim2.Instance = TIM2;htim2.Init.Prescaler = 0;htim2.Init.CounterMode = TIM_COUNTERMODE_UP;htim2.Init.Period = 0xffffffff;htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;if (HAL_TIM_Base_Init(&htim2) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_ETRMODE2;sClockSourceConfig.ClockPolarity = TIM_CLOCKPOLARITY_NONINVERTED;sClockSourceConfig.ClockPrescaler = TIM_CLOCKPRESCALER_DIV1;sClockSourceConfig.ClockFilter = 0;if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}sSlaveConfig.SlaveMode = TIM_SLAVEMODE_GATED;sSlaveConfig.InputTrigger = TIM_TS_ITR2;if (HAL_TIM_SlaveConfigSynchronization(&htim2, &sSlaveConfig) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}}/* TIM3 init function */static void MX_TIM3_Init(void){TIM_ClockConfigTypeDef sClockSourceConfig;TIM_MasterConfigTypeDef sMasterConfig;TIM_OC_InitTypeDef sConfigOC;htim3.Instance = TIM3;htim3.Init.Prescaler = 299;htim3.Init.CounterMode = TIM_COUNTERMODE_UP;htim3.Init.Period = 28280;htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;if (HAL_TIM_Base_Init(&htim3) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}if (HAL_TIM_PWM_Init(&htim3) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 28000;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}}/** Configure pins as* Analog* Input* Output* EVENT_OUT* EXTIPC9 ------> RCC_MCO_2PA8 ------> RCC_MCO_1*/static void MX_GPIO_Init(void){GPIO_InitTypeDef GPIO_InitStruct;/* GPIO Ports Clock Enable */__HAL_RCC_GPIOH_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);/*Configure GPIO pin : PA5 */GPIO_InitStruct.Pin = GPIO_PIN_5;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/*Configure GPIO pin : PC9 */GPIO_InitStruct.Pin = GPIO_PIN_9;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate = GPIO_AF0_MCO;HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);/*Configure GPIO pin : PA8 */GPIO_InitStruct.Pin = GPIO_PIN_8;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate = GPIO_AF0_MCO;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @param file: The file name as string.* @param line: The line in file as a number.* @retval None*/void _Error_Handler(char *file, int line){/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */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,tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */}#endif /* USE_FULL_ASSERT *//*** @}*//*** @}*//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/Edit:This is from the CubeMX "report" showing the settings there:Code:PERIPHERALSMODESFUNCTIONSPINSRCCCrystal/Ceramic ResonatorRCC_OSC_INPH0 - OSC_INRCCCrystal/Ceramic ResonatorRCC_OSC_OUTPH1 - OSC_OUTRCCClock-out-1RCC_MCO_1PA8RCCClock-out-2RCC_MCO_2PC9SYSSysTickSYS_VS_SystickVP_SYS_VS_SystickTIM2Gated ModeTIM2_VS_ControllerModeGatedVP_TIM2_VS_ControllerModeGatedTIM2ITR2TIM2_VS_ClockSourceITRVP_TIM2_VS_ClockSourceITRTIM2ETR2TIM2_ETRPA0-WKUPTIM3Internal ClockTIM3_VS_ClockSourceINTVP_TIM3_VS_ClockSourceINTTIM3PWM Generation No OutputTIM3_VS_no_output1VP_TIM3_VS_no_output1

author: timdf911
author_rank: Contributor
timestamp: December 02, 2022, 09:10:42 am
content: Hi guys,many thanks for your useful tips - I didn't know that CubeMX could be configured to use LL rather than HAL - I'll give that a go.Thanks ozcar for your code snippets and tips, I'll have a play around and experiment, hopefully learning a few things along the way as I customize it for my exact needs. I seem to learn best by doing, testing and repeating until I get what I'm aiming for.I wish I'd posted here last week rather going down rabbit holes and getting totally lost ! At least now I've got a starting point.I'll keep you posted as to how it comes along and maybe a bit more info about the completed project.Here's a summary.The first part is in next months RadCom describing how it controls an antenna rotator.The second part describes how it uses a Goertzel filter to facilitate doppler radio direction finding and the third and final part (where this timer issues comes in ) is to automatically tune a magnetic loop antenna.System uses a 480x320 touch SPI LCD and parts 1 & 2 work really well. Part 3 was going to be a port of my Teensy code, but is now looking for like a complete re-write.Again many thanks to everyone.Regards Tim

author: paulca
author_rank: Super Contributor
timestamp: December 02, 2022, 10:07:53 am
content: Quote from: Kleinstein on December 01, 2022, 10:19:58 pmInteresting. I thought the elf had the symbols in it and it went onto the device. I recall hitting 101% memory on an F030F4 and changing to Release mode dropped that to 40% or something. I assumed it was symbols. I write my code pretty verbosely. Maybe, it's so verbose the compiler removes 50% of it during Release compile, but that sounds excessive! Granted the total memory in that device was only 4K.Then again, it does ask for the ELF file if you want to attach to a running MCU (which doesn't work by the way). So maybe on flash it filters them out, or maybe it does a really, really poor job of filtering them out.

author: timdf911
author_rank: Contributor
timestamp: December 02, 2022, 07:50:41 pm
content: Hi Guys,making some good progress manipulating registers to control timers and irqs etc - but have run into another problem. I'm using the Arduino environment which is C++ and previously I've attached an interupt by simply doing something like:tim1Hz->attachInterrupt(Timer_ISR_1Hz)now I'm using :NVIC_EnableIRQ(TIM9_IRQn);with matching irqhandler, it compiles ok but the handler doesn't execute.Preceding the isr with extern "C" doesn't fix it and the complier complains of multiple definitions so I'm wondering if there's anotherway to associated isr with a timer generated irq within the stm32arduino environment ?Anyway having lots of fun exploring all the registers and things are making much more sense than a couple of days ago.Regards Tim

author: ozcar
author_rank: Frequent Contributor
timestamp: December 02, 2022, 11:25:56 pm
content: Quote from: timdf911 on December 02, 2022, 07:50:41 pmStm32duino does provide some timer support. It does not cover fangled things like slave mode or using DMA with a timer, but should be able to do what you are asking about now.Have a look at this example:https://github.com/stm32duino/STM32Examples/blob/main/examples/Peripherals/HardwareTimer/Timebase_callback/Timebase_callback.ino

author: timdf911
author_rank: Contributor
timestamp: December 03, 2022, 08:21:00 am
content: Hi ozcar,yes that's the way I have been attaching interupts within Arduino, very easy to do but somewhat limited.In my quest to learn and write better code I was hoping to avoid using attachinterupt and to achieve my goal by setting registers (which I largely understand now) and using nvic. The latter seems to conflict with arduino environment, so in the short term I'll use registers to chain the timers together and attachinterupt to check the timings until I find a better way.Thanks again for your support and guidanceRegards im

author: timdf911
author_rank: Contributor
timestamp: December 03, 2022, 11:58:33 am
content: I've been making progress of sorts using TIM2 as a slave gated by TIM3 every 100mSHere's my settings for TIM2 with what I think I'm doingTIM2->DIER |= TIM_DIER_CC3IE; // enable capture compare irq for channel 3TIM2->ARR = 0xffffffff; // count to 2^32, then wraparound (approximately every 429 seconds)TIM2->SMCR |= TIM_SMCR_ECE; // select external clock source mode 2 by writing ECE=1 in the TIM2_SMCR registerTIM2->SMCR |= TIM_TS_ITR2 ; // input trigger from timer 3TIM2->CR1 |= 0x01; //Enable countingHere's my settings for TIM3 with what I think I'm doingTIM3 -> PSC = 300; // divides clock 84MHz to 280kHzTIM3 -> ARR = 28280 ; // generates a 100mS overflowTIM3->DIER |= 0x01; //Enable interruptTIM3->SMCR |= TIM_TS_ITR2 ; // generate output triggerTIM3->CR2 |= TIM_TRGO_OC1REF ; // Master mode selection generate TRGO for OC1REF to trigger capture compare on timer 2TIM3->CR1 |= 0x01; //Enable countingTIM3 -> CCMR2 |= TIM_CCMR2_OC3M ; // output compare mode 3TIM3 -> CCMR2 |= TIM_CCMR2_OC3PE ; // Output compare 3 preload enableTIM3 -> CCER |= TIM_CCER_CC3P ; //Capture/Compare 3 output PolarityTIM3 -> CCER |= TIM_CCER_CC3E ; //Capture/Compare 3 output enableIn the main loop I check this every secondFreq_Raw = TIM2->CCR3;then serial print, but always shows 0.I know my 100mS irq is running because I can see it if I attach an interupt to TIM3 and toggle a GPIO, so I suspect I have a disconnect / wrong settting between the two timers.Any ideas ?Regards Tim

author: ozcar
author_rank: Frequent Contributor
timestamp: December 04, 2022, 12:38:25 am
content: Quote from: timdf911 on December 03, 2022, 08:21:00 amI thought you were saying that something that worked with TeensyDuino (or whatever Arduino for Teensy is called - I'm not familiar with it at all), did not work for stm32duino.If that works but is not good enough, I've seen some mention over on stm32duino.com about using a header file called hal_conf_extra.h to selectively disable parts of the core - eg herehttps://www.stm32duino.com/viewtopic.php?t=1072.However I have never had occasion to use that, so I'm not sure if that will help in any way. Best place to ask about that would be over there, where the guys who wrote the stm32duino code hang out. I see you have posted your original question there.

author: ozcar
author_rank: Frequent Contributor
timestamp: December 04, 2022, 12:43:08 am
content: Quote from: timdf911 on December 03, 2022, 11:58:33 amI've lost track of what you are doing in terms of generating code using CubeMX, or using stm32duino instead, or a bit of both, or maybe neither of those.I also don't know if you are now just trying to duplicate what I posted before without using HAL, or if you have come up with a better way. I get the feeling you that you are maybe trying to use input capture somehow? Short of wiring the two timers together externally I don't know how you would do that, but then there are more ways in heaven and STM32 timers than in my philosophy.The HAL code I posted before results in the timers ending up as shown below (just stopped at some random point).Besides deciding what gate time would be best, with 84MHz timer clock, you'd have to set the external trigger prescaler to divide by 2 to reach 30MHz (ETPS = 01 in SMCR).

author: ozcar
author_rank: Frequent Contributor
timestamp: December 04, 2022, 01:36:54 am
content: I just noticed that you set TIM3 PSC to 300, saying that divides 84MHz down to 280KHz. Close, but not quite right. PSC=0 divides by 1, and PSC=299 divides by 300.Also, it is the CCR1 value of 28,000 in my attempt that sets the gate time to 100ms. ARR=28,280 is a bit arbitrary, and serves only to give a little time for the ISR to grab the then frozen count.

author: timdf911
author_rank: Contributor
timestamp: December 04, 2022, 09:43:26 pm
content: Hi ozcarthanks for taking the time to reply again - I've not had as much spare time today as I would have like but I've come to the conclusion that setting all the required registers manually is probably not the smartest thing to try, on the plus side I now have a much better understanding how the registers work and control the timers. Surprising what you learn when you study the data sheets !Anyway I'm going to use cubemx and cubeide to get something going using HAL then try the samething using LL before inserting the resulting code into the STM32duino environment where the rest of my code is developed.OK on your comments about my register setting - at this point I'd be happy to get anything workingRegards Tim

author: paulca
author_rank: Super Contributor
timestamp: December 05, 2022, 08:37:56 am
content: Once they gave me a working IDE and a working (to professional level) debugger et. al. Eclipse (aka CubeMXIDE) I couldn't go back to the "maker" IDEs.As the previous screenshot highlights. CubeMXIDe will allow you to not only breakpoint the core, but also view the live registers and .... even modify them as you please from the debugger. You can even browse and modify memory while it's running.It makes the Arduino IDE look like Notepad.

author: paulca
author_rank: Super Contributor
timestamp: December 05, 2022, 08:41:44 am
content: From my memory of baremetal programming the timers. There is a gotcha. The EGR register has an "update" bit. It's like the "commit bit" on the timer registers. It forces the timer to update based on it's registers. If you don't set that bit the timer will carry on doing whatever it was doing until it hits an update event. Setting the bit forces the timer to do that NOW.

author: ozcar
author_rank: Frequent Contributor
timestamp: December 05, 2022, 10:08:12 am
content: Quote from: paulca on December 05, 2022, 08:41:44 amThe deferred update, aka "preload" applies to the ARR, CCRn and PSC registers, but for ARR and CCRn preload is optional, and is actually turned off out of reset (controlled by CR1/ARPE and CCMRn/OCnPE flags).

author: timdf911
author_rank: Contributor
timestamp: December 05, 2022, 06:06:55 pm
content: paulcayou've convinced me to invest more time learning how to drive cubemx / ide properly - certainly stm32duino enables quick results but as I'm discovering it lacks the fine control I desire, hence my attempts at baremetal programming.I think I'll go and find a simple project to strat with which wil enable me to explore the debug facilities that way I can hopefully answer my own questions with having to post here.Regards Tim

author: DavidAlfa
author_rank: Super Contributor
timestamp: December 06, 2022, 03:04:24 am
content: Seems like a job for DMA.16bit TimerA:- Pwm mode, one channel, no output.- Timebase slightly larger than 100ms.- Channel 1 would be "Frequency reading", adjust for 100ms.Enable capture ch1 DMA, peripheral to memory, both Word size, no increment, circular mode.- Enable TRGO output, use update flag.32bit TimerB:- Setup external clock, prescaler(I think 30MHz is fine), etc...- Slave in Reset Mode, configure ITR to take TRGO from Timer A- Disable interrupts for all timers and their dmas.Workflow:Start TimerA Ch1 DMA: From TimerB->CNT to a 32-bit variable.Start TimerA, start TimerB.At 100ms, TimerA pwm will trigger a DMA request and transfer the counter value from TimerB into your variable.Shortly after, TimerA will overflow and reset, the TRGO signal will also reset TimerB.Now you can read your variable at any time to check the latest reading.

author: DavidAlfa
author_rank: Super Contributor
timestamp: December 06, 2022, 12:43:25 pm
content: Attached sample project.- 401 working at 84MHz with external 25MHz crystal.- TIM2 is the frequency counter. Slave Reset mode, taking TRGO signal from TIM3, external clock, no prescaler.- TIM3 is the master. Prescaler=1399+1, 84MHz/1400=60KHz input clock.PWM match happens at 60.000 (1000ms), triggers a DMA transfer from TIM2 counter, overflows at 60.000+1 (16us later), resetting TIM2.If you want faster readings (ex. every 100/10mS, adjust TIM3 prescaler:Code:Prescaler TIM3 clock Update rate Resolution1399 60KHz 0,999983Hz 1Hz139 600KHz 9,99983Hz 10Hz13 6MHz 99,9983Hz 100HzMy clock generator is not very accurate, neither will the chinesium 25MHz crystal, yet results were pretty good (50ppm error):Code:Freq Counter10KHz 10.000100KHz 99.99510MHz 9.999.54325MHz 24.998.860The whole thing takes just 4 lines, excluding the initialization generated by CubeMX:Code:volatile uint32_t counter;HAL_DMA_Start(htim3.hdma, (uint32_t)&TIM2->CNT, (uint32_t)&counter, 1);__HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_CC4);__HAL_TIM_ENABLE(&htim3);__HAL_TIM_ENABLE(&htim2);Ensure to read the STM32F401datasheet, you'll find the AC characteristics there.For the timer clock input input, max frequency is Ftim/2, where Ftim is the peripheral clock.In this case, it's working at 84MHz, so max external clock will be 42MHz.Also check:Reference manual,Timer cookbook.Faster STM32 will sample faster frequencies, ex. the STM32G431 works at 170MHz, timer will be able to sample up to 85MHz.

author: timdf911
author_rank: Contributor
timestamp: December 06, 2022, 07:21:35 pm
content: Hi DavidAlfathanks for taking the time creating this bit of code - I had read that using DMA would be the better way to go but was beyond my understanding.I've down loaded your code and am studying it to learn how it works etc.As I'm only interested in freqs up to 30MHz the F410 should handle it ok.Your code looks very elegant and I've complied and run it ok - just got to add a couple of sanity checks to it for my own learning - such as flashing led on the gate signal and uart comms for frequency indication etc.Then if that all goes well I'll try converting it to LL.Regards Tim

author: timdf911
author_rank: Contributor
timestamp: December 13, 2022, 07:00:33 pm
content: Hi DavidAlfahope you're still following this thread - I've been doing a bit of digging and while your example works nicely I'm puzzled how the DMA is working.Seems according to the CubeMx file you're using DMA1 Stream 2 for peripheral to memory - but yet the data sheet says DMA1 is only for memory to memory so I'm confused as to how it can be accessing the timer register.I would have thought DMA2 would be the correct one to use.I mention this as I've been building my own bare metal code - I'm a glutton for punishment, but it helps me learn !Basically I'm using TIM3 as a counter (16 bits is ok for my needs) with PD2 ETR2 input and TIM2 as the timebase generating TRGO into ITR2 of TIM3 - this forced me to use DMA1 and it didn't work - at which point I found from the data sheet I should have been using DMA2 - but yet your example works !FYI for hardware reasons I needed to use PD2 ETR2 as the signal input. I have it working quite well as a frequency counter using a capture input but wanted to try DMA as a learning exercise.Any thoughts (anyone) ?Edit I've just found this page which seems to answer my DMA1 <> peripheral questionhttps://civilpedia.org/p/?t=STM32-DMA-Cheat-Sheet&pid=315Regards Tim

author: ozcar
author_rank: Frequent Contributor
timestamp: December 13, 2022, 08:00:29 pm
content: Quote from: DavidAlfa on December 06, 2022, 12:43:25 pmI have seen, in various places in the ST documentation the statement that maximum external clock is Ftim/3 or Ftim/4. I never had to determine which was the correct figure, and perhaps there was something that I was misunderstanding that lead to the apparent contradiction. Now, a third possibility, but whatever the true answer is, the prescaler mayl be required to reach 30MHz on the F401.

author: ozcar
author_rank: Frequent Contributor
timestamp: December 13, 2022, 09:51:42 pm
content: I tried the code I posted earlier (without using prescaler) and found it maxes out at around 38MHz, so does not make it to 42MHz, but at 30MHz, or even 37MHz it seems OK.Did a few more tests:Changed ETPS in SMCR to divide by two, and it worked OK up to 60MHz (limit of the cheap-and-cheerful signal generator.)Changed the timer clock to be 8.4MHz instead of 84MHz (gate time now 1 second), and it was good to around 4.1MHz. Not sure why that is slightly over 1/10 of the max I saw at 84MHz Ftim, but it does look like Something BadTMhappens near to Ftim/2.

author: timdf911
author_rank: Contributor
timestamp: December 14, 2022, 12:53:40 pm
content: Hi OzcarI'm not using the DMA code but using my simpler capture compare code I found that it would only work upto 4MHz (using PD2 ETR2 input) but by using the ETPS prescaler to /8 it will work upto 55MHz which is odd as I was only expecting 8 x 4MHz.The resolution of 1kHz is ok for my needs, now just need to tweak my code as currently the time base signal pops out from TIM2 on PA5 then is hard wired back into PB0 to gate the counter on TIM3.I still plan on getting DMA going but I was spending too much time on DMA rather than the rest of the project - so for now I'll stick with my capture compare solution.Regards Tim

author: ozcar
author_rank: Frequent Contributor
timestamp: December 15, 2022, 12:19:57 am
content: Quote from: timdf911 on December 14, 2022, 12:53:40 pmSo, if I have got this right, you changed to using TIM2 for the gate time, and TIM3 to count your external signal, with TIM2 output wired externally to one of the TIM3 channels to use input capture?If so, I'm not sure where the maximum of 4MHz would come from. Maybe show us the actual code.Using the 16-bit TIM3 to count the external signal, you would have to keep the gate time below about 2ms though to avoid complications with the counter overflowing.

author: profdc9
author_rank: Frequent Contributor
timestamp: December 15, 2022, 03:11:47 am
content: I built an antenna tuner based on the STM32F103 which includes code for measuring the frequency of the RF signal. I included a 74HC393 so I could cut down the frequency and implement an inverse frequency counter if necessary.https://github.com/profdc9/ModularTunerhttps://github.com/profdc9/ModularTuner/blob/master/code/tuner/frequencyCounter.cppThere's also circuitry there that helps condition the RF signal for counting that took some effort to get right, as well as circuitry to measure the phase of the reflected RF signal to get the complex phase coefficient.

author: timdf911
author_rank: Contributor
timestamp: December 15, 2022, 08:32:42 pm
content: Hi ozcarthe 4MHz limit was because I had the system clock set to about 8MHz rather than 84MHz. Yes because the frequency counter is only 16 bits I have to make sure the max count is within limits so I have the ETR2 prescaler set to /8 and use a 8mS gate as this gives more stable results compared to no ETR2 prescaler and a 1mS gate.The counter easily goes to over 55MHz with 1kHz resolution.As mentioned above due to HW constraints I'm forced to us PD2 input pin which connects to 16 bit timer.Certainly has been a good learning exercise and worth the effort and time - I appreciate your help.Regards Tim

author: timdf911
author_rank: Contributor
timestamp: December 15, 2022, 08:37:14 pm
content: profdc9Thanks for the links - I'll study them and hopefully learn something.Looks like an interesting project, has it been published anywhere in full ?Regards Tim

author: profdc9
author_rank: Frequent Contributor
timestamp: December 16, 2022, 02:37:07 am
content: I attempted to publish in QST/QEX but they rejected. Also in another UK amateur radio society journal.There is a 60 page document in the project explaining how it works.https://github.com/profdc9/ModularTuner/blob/master/doc/ModularTuner-Guide.pdf

author: timdf911
author_rank: Contributor
timestamp: December 16, 2022, 08:37:52 pm
content: Surprised it was rejected as they are usually begging for contributions. I have subscriptions to both QEX and RadCom.I've had several articles published in RadCom over the years most recently in January 2023 and this problem of measuring frequency is part of a follow up article.I'm licensed as KT6UK / G4WIMMaking good headway with the design now I've found an adequate frequency measuring solution.Edit - just been reading your document and what a great piece of work which has many parallels to what I'm working on.Regards Tim

author: ozcar
author_rank: Frequent Contributor
timestamp: December 16, 2022, 11:05:06 pm
content: Quote from: timdf911 on December 15, 2022, 08:32:42 pmDefinitely something not right there in regard to the 55MHz max. I tried with 8.4MHz timer clock, and ETPS set to divide by 8, so expected max frequency of (8.4/2)*8 = 33.6MHz. I found that it worked for input frequency 33MHz, but failed for 34MHz.It somehow hurts to think that you have two 32-bit timers, but can't make use of either of them. You don't seem to be short of pins if you can afford to use two of them, PA5 and PB0, to tie two timers together, so what is it exactly that forces you to used PD2 as the input?And why the 8MHz system clock?

author: timdf911
author_rank: Contributor
timestamp: December 17, 2022, 07:32:10 pm
content: yes it's very sad that I'm confined to use a 16 bit counter but none the less it provides adequate performance for my needs.As for using some other counter / pin which is 32 bit, sadly as this part of the design is an after thought all the other counter pins have other stuff attached to them for control / sensing of ancilliary hardware.If I had a clean sheet of paper I would have mapped things differently - but as it stands I've effectively painted myself into a corner and thus PD2 is the only option.Just to clarify when I accidentally set the system clock to 8MHz I was not using the /8 ETPS thus 4MHz maximum.Regards Tim

author: ozcar
author_rank: Frequent Contributor
timestamp: December 17, 2022, 09:23:33 pm
content: Quite likely I am flogging a horse that you have already interred, but my excuse is that I want to learn more about the STM32 timers myself.Quote from: timdf911 on December 17, 2022, 07:32:10 pmHowever, prior to this afterthought, you were evidently not using PA5 and PB0, because according to this, you have now tied them together:Quote from: timdf911 on December 14, 2022, 12:53:40 pmNow, it just so happens that one of the alternate functions for PA5, is the 32-bit TIM2 ETR input. That would require using some other pair of available pins to tie together, but did you really go through all the spare pins you have without finding a way to get an input capture trigger to a TIM2 channel? Even if there really is no pair of pins that allow that, the gated slave mode, which does not require any external cross link, is still possible.

author: timdf911
author_rank: Contributor
timestamp: December 17, 2022, 10:38:36 pm
content: Ha Ha, yes dead and buried, train has left the station, ship has sailed etc......Anyway to answer your question I was experimenting with timers generally on a stand alone STM32F401 with no hard io limitations - just to see how things worked without worrying about pin mapping.I knew at some point I'd have to migrate my experimental code to the final target with its IO limitations.I've learnt a lot from this process - not only related to timers but other things as well such as MODER and alternate functions etc.When time permits I'll have another go at bare metal DMA as I find using bare metal really helps me understand whats actually going rather than use HAL and cubemx etc.Regarding other pins, I have an IO map and have crossed checked using CubeMX to identify which pins have timers hidden behind them and due to my bad choices I have blocked them all - doh !This design uses a lot of IO as it has a lot of peripherals - spi LCD plus touch screen , I2C for EEPROM, stepper motor drives, PWM output, four analog inputs, serial comms, 6 push buttons, rotary encoder plus push button, 3 relays drives, 4 logic inputs.Code size is currently 157kB and growing...Regards Tim