STM32 等精度测频 TIM定时器 分高低频测试

2023-10-25 12:11

本文主要是介绍STM32 等精度测频 TIM定时器 分高低频测试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

记录一下STM32稍显艰难的学习过程、、

使用的STM32开发板:微雪的Open407Z

相关资料:Open407Z-C - Waveshare Wiki

MCU型号:STM32F407ZE

本次想要实现一个单片机学习中很普遍也很简单的测频功能,但是实操起来还得进行很多定时器的以及中断的相关操作。。

/****以下为仅使用一个定时器进行捕获的代码,效果较差,可略过****/

首先想到的是用一个定时器进行输入捕获进行测频。本人想偷懒,于是沿用MSP430的那一套逻辑。(MSP430相关部分详细见下链接)

MSP430G2553 频率、占空比、脉冲宽度测量_Krism0912的博客-CSDN博客_msp430测量频率

但是实际效果并不理想,测量的误差非常大,尽管如此,仍然放出一部分代码供参考。

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_7)== GPIO_PIN_SET){if(Forward_Posedge_Flag)//First rising edge captured{TIM3->CNT=0;Forward_Posedge_Flag = 0;}else if(Forward_Posedge_Flag ==0 && Negedge_Flag==1) //Second rising edge captured{HAL_TIM_Base_Stop_IT(&htim3);HAL_TIM_IC_Stop_IT(&htim3, TIM_CHANNEL_2);Posedge_data=TIM3->CCR2;Posedge_data=Posedge_data+50000*(num1+num2);Negedge_data = Negedge_data+ 50000*num1;fre = 2000000.0 / Posedge_data ;duty_cycle = ((double)Negedge_data )/ ((double)Posedge_data);pulse_width = ( (double)Negedge_data) / 2.0 ; //unit:usLCD_Display();HAL_Delay(800);Parameter_Init();HAL_TIM_Base_Start_IT(&htim3);HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);}}else // Falling Edge was captured{if(Forward_Posedge_Flag==0 && Negedge_Flag==0){Negedge_data = TIM3->CCR2;Negedge_Flag = 1;}}
}

通过设置TIM3的CHANNEL2进行输入信号的双边沿捕获,以期计算频率和占空比。

上述代码中判断上升沿和下降沿,在if中用的判断语句是

HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_7)== GPIO_PIN_SET

感觉实际上STM32的寄存器里面应该有捕获信号极性的相关寄存器位,但是我暂时没找到。

测量结果不太理想,10kHz会测得10200Hz之类。

P.S信号源升级了,精度绝对没有问题。

无奈,尝试使用等精度测频法。

/******************************以下为等精度测频法**************************/

等精度测频法以前试过在FPGA和MSP430上试过一段时间(见以前的博客),但是这次还是打算从零开始配置。

本次使用的等精度测频法的时序逻辑如下:

(1)在捕捉到一个输入信号上升沿后开启测量闸门(软件闸门);

(2)开启后测量一段时间的信号,且自定义测量的最短时间,该时间硬件闸门控制;

(3)硬件闸门时间到后,硬件闸门关闭,但相关计数器仍继续工作,测量继续,直至捕获到下一个输入信号上升沿后,结束测量并锁存数据;

(4)测量结束后计算相关数据,重新初始化各个参数,准备进行下一次测量。

等精度测量法最为重要的就是一定要使测量时间为被测信号的周期的整数倍,这样才能保证精度。如此一来,实际的测量时间将与实际输入信号的频率有关,更多相关细节网上有很多,这里不再说了。

最终测量将会获得标准信号计数值Nx和被测信号计数值Nb,由此算得实际信号频率。

参考以上步骤,选取STM32定时器资源:

一个定时器用于控制闸门信号(最小测量时间),该定时器只需要实现一个定时功能,定时到后进入中断处理事件置位某个flag标志,以在下一个上升沿结束测量;

一个定时器用于计数标准信号Nx,使用输入捕获功能,捕获到一个上升沿时加1即可;

一个定时器用于计数标准信号,这里标准信号来自于内部时钟,故也仅需普通计数器模式即可,也不需要中断。

总配置:

定时器模式PrescalerCounter PeriodGlobal Interrupt
TIM2\00xFFFFFFFF×
TIM3(CH2)Input capture direct mode84-10
TIM5\8400-110000-1

说明:

TIM2的 Counter Period到32位 (2^32-1>4290M),而TIM2的时钟频率最大为84M,计数值完全够用。如果您的位数不够用可以考虑打开Global Interrupt,设置一个总计数器,在溢出中断里加上Period的值即可。

TIM3 CHANNEL2 为输入捕获模式,不需要设置Counter Period,当然 Prescaler也应该可以随便设置一下。输入的极性设置为上升沿(下降沿也同理)。

 TIM5采用最简单的设置,只需对其分频即可。

 考虑到TIM5挂在APB1总线上,时钟频率为84MHz,则分频后硬件闸门频率为:

                                                  84M/8400/10000=1Hz

也就是最小测量时间在这里设置成了1s。您完全可以更改上述参数设置您需要的时长。

接下来是代码部分,main.c见下

/********************************************************************************* File Name          : main.c* Description        : Main program body******************************************************************************** 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"
#include "tim.h"
#include "gpio.h"
#include "fsmc.h"/* USER CODE BEGIN Includes */
#include "openx07z_c_lcd.h"
#include "Myfunction.h"
#include "Fre_function.h"
/* USER CODE END Includes *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/uint16_t GATE_OPEN,GATE_READY2CLOSE;
uint32_t Nx,Nb;double fre;
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Error_Handler(void);/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*//* USER CODE END PFP *//* USER CODE BEGIN 0 *//* USER CODE END 0 */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();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init();MX_FSMC_Init();MX_TIM3_Init();MX_TIM2_Init();MX_TIM5_Init();/* USER CODE BEGIN 2 *////LCD SIZE 320*240BSP_LCD_Init();BSP_LCD_SetBackColor(LCD_COLOR_WHITE);BSP_LCD_SetTextColor(LCD_COLOR_BLUE);BSP_LCD_SetFont(&Font20);BSP_LCD_Clear(LCD_COLOR_WHITE);Parameter_Init();BSP_LCD_DisplayStringAt(0, 0, (uint8_t *)"Ready to measure", CENTER_MODE );HAL_Delay(1000);HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);    //启动输入捕获/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */}/** System Clock Configuration
*/
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_SCALE1);/**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 = 4;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 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_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){Error_Handler();}/**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);
}/* USER CODE BEGIN 4 */
//输入中断触发的回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if(!GATE_OPEN) //闸门未开启,捕获到上升沿信号后应该开启闸门以开始测量{//启动闸门 TIM5 开始计数标准信号
//		HAL_TIM_Base_Start(&htim5);__HAL_TIM_CLEAR_IT(&htim5, TIM_IT_UPDATE);HAL_TIM_Base_Start_IT(&htim5);HAL_TIM_Base_Start(&htim2);GATE_OPEN=1;}else{//测频中,捕捉到一次信号 输入计数值加1Nx++;}if(GATE_OPEN&&GATE_READY2CLOSE){//关闭闸门 停止捕捉 HAL_TIM_Base_Stop(&htim2);HAL_TIM_IC_Stop_IT(&htim3, TIM_CHANNEL_2);HAL_TIM_Base_Stop_IT(&htim5);//锁存Nx Nb数据 计算频率值 LCD显示Nb=__HAL_TIM_GetCounter(&htim2); //		或者 Nb=TIM2->CNT;fre=((double)Nx)/((double)Nb)*84000000.0;HAL_Delay(500);LCD_Display();//准备下一次测量Parameter_Init();__HAL_TIM_SetCounter(&htim2,0);  //注意!!!!!!!计数值不会因为定时器暂停而清零HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);    //启动输入捕获}}   /*** @brief  Period elapsed callback in non blocking mode* @param  htim: TIM handle* @retval None*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{//TIM5溢出, 1s测量时间到,下一个输入信号上升沿将结束测量if(TIM5 == htim->Instance){GATE_READY2CLOSE=1;}}
/* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @param  None* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler *//* User can add his own implementation to report the HAL error return state */while(1) {}/* USER CODE END Error_Handler */
}#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/*** @}*//*** @}
*//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

完完全全按照自己设想的逻辑写代码即可,并不复杂。

有一点稍微注意一下:停止TIM2获取Nb值后需要将CNT值清零,因为HAL_TIM_Base_Stop(&htim2); 并不会清零该值。

/********************下面部分与测频无关,不看也可*********************/

计划用LCD显示数据。

对于320*240的TFT液晶显示器,官方资料里有很多函数可以拿来直接用,全在openx07z_c_lcd.c文件里。而且根据其中基本函数,也可以自己造一些简单的功能函数。

比如在指定位置显示一个指定位数的双精度浮点数:

void BSP_LCD_Show_double(uint16_t x,uint16_t y,double f,int n);

#include "stm32f4xx_hal.h"
#include "openx07z_c_lcd.h"
#include "Myfunction.h"
#include "../Fonts/fonts.h"extern LCD_DrawPropTypeDef DrawProp;//x,y是坐标;f是要显示的数字,n是展示的小数位数 
void BSP_LCD_Show_double(uint16_t x,uint16_t y,double f,int n)
{unsigned int temp,i,j;uint16_t num[10];  //预留的存储空间,不够时更改即可uint16_t size;// 按我自己的喜好调整的字符间隔。可更改。 switch(DrawProp.pFont->Width){case 17:case 14:case 11:size=DrawProp.pFont->Width -3; break; //Font24,Font20,Font16case 7: size=DrawProp.pFont->Width -2; break;  //Font12;case 5: size=DrawProp.pFont->Width; break;  //Font8;default:size=DrawProp.pFont->Width;}temp = (int)f;if(temp!=0){for(i=0;temp!=0;i++)      //计算整数位数temp /=10;temp = (int)f;for(j=i;j>0;j--){num[j] = temp%10+'0';temp /=10;}for(j=1;j<=i;j++)           //依次展示{BSP_LCD_DisplayChar(x,y,num[j]); x+=size;}}else{BSP_LCD_DisplayChar(x,y,'0'); x+=size;}BSP_LCD_DisplayChar(x,y,'.'); x+=size;   f -=(int)f;for(i=0;i<n;i++){f*=10;BSP_LCD_DisplayChar(x,y,(int)f+'0'); x+=size;  f-=(int)f;}
}

当然上述功能也可以直接通过sprintf函数实现,想怎么用就怎么用。 

/****************************下面是测试效果*********************************/

0.5Hz

1Hz

 50kHz
  500kHz

测试的频率范围:0.5Hz-500kHz

测量误差在0.02% (万分之二)左右 

/**************************************END**********************************/

/********************************更新**********************************/

/***************************高频测量部分***************************/

  上述等精度测频方法,由于使用了定时器的捕获功能,捕获到输入信号上升沿时需要进入中断将Nx的值进行加一操作,而本身进入中断进行处理就会需要执行大量语句,进而耗费大量的时间和资源,从而当频率过高时,需要进入中断的次数过多,无法完成测频功能,如上所述,上限约为500kHz。

今日有幸了解到,将待测信号作为外部时钟输入,可以在不进入中断的情况下对其进行计数,有机会测量更高的频率。

首先先看看该方法能否测高频。

将TIM3原有的的输入捕获设置进行更改,并将其从模式设置为外部输入时钟模式1

 具体设置:不分频(Prescaler=0);

                   CounterPeriod设置为0xFFFF(65535);

                    上升沿触发;

此外,输入通道将为TIM3的CHANNEL_1;

上图来自:DS8626: Arm® Cortex®-M4 32b MCU+FPU, 210DMIPS, up to 1MB Flash/192+4KB RAM, USB OTG HS/FS, Ethernet, 17 TIMs, 3 ADCs, 15 comm. interfaces & camera

Pinouts and pin description 部分 

 上图来自:RM0090 中文参考手册 通用定时器框图

剩余两个定时器仍保持原样,不做更改(即TIM2:计数基频值Nb,TIM5控制闸门时间为1s)。

由于是高频,实际采样时间完全可以设置为1s。

比如:高频时1秒计数值Nx约为500k,等精度测频会延长测频时间至测量完整的整数个待测信号整个周期,实际测频时间可能为1s*(1+1/500k)。高频时完全可以忽略该差别。但是低频就不可以咯(1Hz输入可能得测2秒)。

回到高频测量,如上所述,在TIM5的中断里即可结束整个测频过程并开始计算。这样完全抛弃了等精度测频的方法

完整代码:

/********************************************************************************* File Name          : main.c* Description        : Main program body******************************************************************************** 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"
#include "tim.h"
#include "gpio.h"
#include "fsmc.h"/* USER CODE BEGIN Includes */
#include "openx07z_c_lcd.h"
#include "Myfunction.h"
#include "Fre_function.h"
/* USER CODE END Includes *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/uint32_t Nx,Nb;double fre;
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Error_Handler(void);/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*//* USER CODE END PFP *//* USER CODE BEGIN 0 *//* USER CODE END 0 */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();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init();MX_FSMC_Init();MX_TIM2_Init();MX_TIM5_Init();MX_TIM3_Init();/* USER CODE BEGIN 2 */__HAL_TIM_CLEAR_IT(&htim5, TIM_IT_UPDATE);__HAL_TIM_CLEAR_IT(&htim3, TIM_IT_UPDATE);BSP_LCD_Init();BSP_LCD_SetBackColor(LCD_COLOR_WHITE);BSP_LCD_SetTextColor(LCD_COLOR_BLUE);BSP_LCD_SetFont(&Font20);BSP_LCD_Clear(LCD_COLOR_WHITE);Parameter_Init();BSP_LCD_DisplayStringAt(0, 0, (uint8_t *)"Ready to measure", CENTER_MODE );HAL_Delay(1000);//  HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);    //启动输入捕获HAL_TIM_Base_Start_IT(&htim5);HAL_TIM_Base_Start_IT(&htim3);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 */}/** System Clock Configuration
*/
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_SCALE1);/**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 = 4;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 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_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){Error_Handler();}/**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);
}/* USER CODE BEGIN 4 *//*** @brief  Period elapsed callback in non blocking mode* @param  htim: TIM handle* @retval None*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{//规定1s测量时间到if(TIM5 == htim->Instance){HAL_TIM_Base_Stop_IT(&htim5);HAL_TIM_Base_Stop_IT(&htim3);HAL_TIM_Base_Stop(&htim2);Nx+=TIM3->CNT;Nb=TIM2->CNT;fre=((double)Nx)/((double)Nb)*84000000.0;LCD_Display();			Parameter_Init();__HAL_TIM_SetCounter(&htim2,0);  //注意!!!!!!!计数值不会因为定时器暂停而清零__HAL_TIM_SetCounter(&htim3,0);  //注意!!!!!!!计数值不会因为定时器暂停而清零HAL_TIM_Base_Start_IT(&htim5);HAL_TIM_Base_Start_IT(&htim3);HAL_TIM_Base_Start(&htim2);}else if(TIM3 == htim->Instance){Nx+=0xFFFF;}}
/* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @param  None* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler *//* User can add his own implementation to report the HAL error return state */while(1) {}/* USER CODE END Error_Handler */
}#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/*** @}*//*** @}
*//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

说明:

1.TIM3是16位的,高频计数Nx时可能不够值,所以在TIM3的溢出中断里及时更新Nx的值(虽然还是进中断了,但是次数变成了原来的1/65535,测试情况完全可以接受)。对于此,如果您使用TIM2,那么32位计数将完全够用。

2.开头带BSP的函数以及LCD_Display()仍然是LCD显示相关函数,可以不用care。

3.不要忘记开NVIC全局中断!!

测试效果还可以,不放图了,下面一起放。

/************************上述两种方法结合************************/

如上所述,高频部分测低频不准,低频部分测不了高频。既然做了以上工作,遂想方设法将其结合。

有一点小小说一下:咱们不可能说低频测试的时候效果OK,高频不行了,把输入信号手动换到高频的输入引脚去,之后测低频又换回来。这种比较麻烦,而且一般设计会避免这种问题。

所以,等精度测频时TIM3的输入捕获将为CHANNEL_1,与高频测量保持一致,这样信号输入口将保持为PA6,不用换。

说是结合,但是我们看到高低频测量TIM3的配置完全不一样,就不像稍微改一个Prescaler或者CNT的值那样简单。

作者偷懒(其实是不会自己一点点设置),遂将高频部分的TIM3初始化函数复制了过来。,放到tim.c中当作用户函数用。 

本来以为这样估计不行,但是居然可以。。接下来补上一些高低频率测量模式的转换逻辑就可以了。

转换逻辑很简单:给一个判断条件,抛弃当前测频结果,停止当前所有的计时器,重新配置TIM3引脚,重开计时器即可。

我设置的是10000Hz以上认为是高频,用高频方法,否则低频,用等精度测频法。

/********************************************************************************* File Name          : main.c* Description        : Main program body******************************************************************************** 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"
#include "tim.h"
#include "gpio.h"
#include "fsmc.h"/* USER CODE BEGIN Includes */
#include "openx07z_c_lcd.h"
#include "Myfunction.h"
#include "Fre_function.h"
/* USER CODE END Includes *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/uint16_t GATE_OPEN,GATE_READY2CLOSE;
uint32_t Nx,Nb;
uint8_t Fre_State=LOW_FREQUENCY;
double fre;
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Error_Handler(void);/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*//* USER CODE END PFP *//* USER CODE BEGIN 0 *//* USER CODE END 0 */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();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init();MX_FSMC_Init();MX_TIM2_Init();MX_TIM5_Init();MX_TIM3_Init();/* USER CODE BEGIN 2 */BSP_LCD_Init();BSP_LCD_SetBackColor(LCD_COLOR_WHITE);BSP_LCD_SetTextColor(LCD_COLOR_BLUE);BSP_LCD_SetFont(&Font20);BSP_LCD_Clear(LCD_COLOR_WHITE);Parameter_Init();BSP_LCD_DisplayStringAt(0, 0, (uint8_t *)"Ready to measure", CENTER_MODE );BSP_LCD_DisplayStringAt(0, 100, (uint8_t *)"INPUT PIN:PA6", CENTER_MODE );HAL_Delay(1000);HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);    //启动输入捕获/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */}/** System Clock Configuration
*/
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_SCALE1);/**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 = 4;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 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_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){Error_Handler();}/**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);
}/* USER CODE BEGIN 4 */
//输入中断触发的回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if(!GATE_OPEN){//启动闸门 TIM5 开始计数标准信号__HAL_TIM_CLEAR_IT(&htim5, TIM_IT_UPDATE);HAL_TIM_Base_Start_IT(&htim5);HAL_TIM_Base_Start(&htim2);GATE_OPEN=1;}else{//测频中,捕捉到一次信号 输入计数值加1Nx++;if(Nx>20000)  {HAL_TIM_Base_Stop(&htim2);HAL_TIM_IC_Stop_IT(&htim3, TIM_CHANNEL_1);HAL_TIM_Base_Stop_IT(&htim5);Turn2HIGH_FREQUENCY_MODE();}}if(GATE_OPEN&&GATE_READY2CLOSE){//关闭闸门 停止捕捉 HAL_TIM_Base_Stop(&htim2);HAL_TIM_IC_Stop_IT(&htim3, TIM_CHANNEL_1);HAL_TIM_Base_Stop_IT(&htim5);//锁存Nx Nb数据 计算频率值Nb=__HAL_TIM_GetCounter(&htim2); fre=((double)Nx)/((double)Nb)*84000000.0;//判断if(fre<10000.0){// LCD显示LCD_Display();//准备下一次测量Parameter_Init();__HAL_TIM_SetCounter(&htim2,0);  //注意!!!!!!!计数值不会因为定时器暂停而清零HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);    //启动输入捕获}else{Turn2HIGH_FREQUENCY_MODE();}}}   /*** @brief  Period elapsed callback in non blocking mode* @param  htim: TIM handle* @retval None*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{//闸门溢出 规定测量时间到if(TIM5 == htim->Instance&&Fre_State==LOW_FREQUENCY){GATE_READY2CLOSE=1;}else if(TIM5 == htim->Instance&&Fre_State==HIGH_FREQUENCY){HAL_TIM_Base_Stop_IT(&htim5);HAL_TIM_Base_Stop_IT(&htim3);HAL_TIM_Base_Stop(&htim2);Nx+=TIM3->CNT;Nb=TIM2->CNT;fre=((double)Nx)/((double)Nb)*84000000.0;if(Nx>=10000.0){LCD_Display();Parameter_Init();__HAL_TIM_SetCounter(&htim2,0);  __HAL_TIM_SetCounter(&htim3,0); HAL_TIM_Base_Start_IT(&htim5);HAL_TIM_Base_Start_IT(&htim3);HAL_TIM_Base_Start(&htim2);}else{Turn2LOW_FREQUENCY_MODE();}}else if(TIM3 == htim->Instance&&Fre_State==HIGH_FREQUENCY){Nx+=0xFFFF;}}
/* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @param  None* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler *//* User can add his own implementation to report the HAL error return state */while(1) {}/* USER CODE END Error_Handler */
}#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/*** @}*//*** @}
*//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

说明:

1.设置了变量Fre_State作为当前高频模式和低频模式的标志。

2.低频模式下,如果输入频率过高(2M左右),闸门关不了,无法算出频率并重新改成高频模式。Debug的时候发现Nx蹭蹭蹭往上涨,遂只能在捕获语句的Nx++后进行一个判断以改变模式,不然直接卡死。这是为什么作者也不明白。。。= =

3.其他一些函数见下,也许能帮助您理解作者设计的转换流程,当然您也可以自己尝试。

#include "openx07z_c_lcd.h"
#include "Myfunction.h"
#include "tim.h"
#include "Fre_function.h"
extern unsigned int Forward_Posedge_Flag,Negedge_Flag;extern double fre;
extern uint16_t GATE_OPEN,GATE_READY2CLOSE;
extern uint32_t Nx,Nb;
extern uint8_t Fre_State;
void Parameter_Init(void)
{GATE_OPEN=0;GATE_READY2CLOSE=0;Nx=0;Nb=0;fre=0.0;
}void Turn2HIGH_FREQUENCY_MODE(void)
{Fre_State=HIGH_FREQUENCY;MX_TIM3_Init_ETR();Parameter_Init();__HAL_TIM_SetCounter(&htim2,0);  __HAL_TIM_SetCounter(&htim3,0);  HAL_TIM_Base_Start_IT(&htim5);HAL_TIM_Base_Start_IT(&htim3);HAL_TIM_Base_Start(&htim2);
}void Turn2LOW_FREQUENCY_MODE(void)
{Fre_State=LOW_FREQUENCY;Parameter_Init();__HAL_TIM_SetCounter(&htim2,0);  MX_TIM3_Init();HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);    //启动输入捕获
}void LCD_Display(void)
{BSP_LCD_SetFont(&Font20);BSP_LCD_Clear(LCD_COLOR_WHITE);BSP_LCD_DisplayStringAt(0, 0, (uint8_t *)"Frequency:", LEFT_MODE );if(fre<10000.0){BSP_LCD_Show_double(150,0,fre,4);BSP_LCD_DisplayStringAt(280, 0, (uint8_t *)"Hz", LEFT_MODE );BSP_LCD_DisplayStringAt(0, 200, (uint8_t *)"LOW FREQUENCY", CENTER_MODE );}else{BSP_LCD_Show_double(150,0,fre/1000,4);BSP_LCD_DisplayStringAt(260, 0, (uint8_t *)"kHz", LEFT_MODE );BSP_LCD_DisplayStringAt(0, 200, (uint8_t *)"HIGH FREQUENCY", CENTER_MODE );}BSP_LCD_Show_int(200,60,(int)Nx);BSP_LCD_Show_int(200,120,(int)Nb);BSP_LCD_SetFont(&Font16);BSP_LCD_DisplayStringAt(0, 30, (uint8_t *)"Input Signal Counter Nx:", LEFT_MODE );BSP_LCD_DisplayStringAt(0, 90, (uint8_t *)"Internal Signal Counter Nb:", LEFT_MODE );}

放一些测试结果。。

 

 

目前高频测试到10MHz,低频仍然测试到0.5Hz。

本文代码有点多,有些地方留有一大堆空行,有些甚至还没对齐,但作者懒得改了。。。

感谢您读到这里,祝您生活愉快!

这篇关于STM32 等精度测频 TIM定时器 分高低频测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/282490

相关文章

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

STM32内部闪存FLASH(内部ROM)、IAP

1 FLASH简介  1 利用程序存储器的剩余空间来保存掉电不丢失的用户数据 2 通过在程序中编程(IAP)实现程序的自我更新 (OTA) 3在线编程(ICP把整个程序都更新掉) 1 系统的Bootloader写死了,只能用串口下载到指定的位置,启动方式也不方便需要配置BOOT引脚触发启动  4 IAP(自己写的Bootloader,实现程序升级) 1 比如蓝牙转串口,

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

寻迹模块TCRT5000的应用原理和功能实现(基于STM32)

目录 概述 1 认识TCRT5000 1.1 模块介绍 1.2 电气特性 2 系统应用 2.1 系统架构 2.2 STM32Cube创建工程 3 功能实现 3.1 代码实现 3.2 源代码文件 4 功能测试 4.1 检测黑线状态 4.2 未检测黑线状态 概述 本文主要介绍TCRT5000模块的使用原理,包括该模块的硬件实现方式,电路实现原理,还使用STM32类

STM32 ADC+DMA导致写FLASH失败

最近用STM32G070系列的ADC+DMA采样时,遇到了一些小坑记录一下; 一、ADC+DMA采样时进入死循环; 解决方法:ADC-dma死循环问题_stm32 adc dma死机-CSDN博客 将ADC的DMA中断调整为最高,且增大ADCHAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_Buffer_Size); 的ADC_Bu