stm32 测频率(测频法)

   作为纯新手在跟随江协学习单片机的过程中,到使用输入捕获测量频率时,对测频法只是讲了原理,并未展示相关代码。到这里检索后也只看到了视频中展示的测周法测频率。故在这里对相应代码进行补充,供大家交流讨论。

对于测频法原理详细可见@江协科技

 这里直接展示代码部分

TIM2(定一个1s的频率,执行中断,读取频率后,清0计数)

#include "stm32f10x.h"                  // Device header

extern uint16_t  Freq_Count;  //来源于sensor,(没有改名字了)
uint16_t Freq_count;

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision =TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode =TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period =10000-1;//都要在0-65535之间(记录一万次)
	TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;//这是计数频率
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter =0;//高级计数器才用,不用直接0
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	TIM_ClearFlag(TIM2,TIM_FLAG_Update );
	TIM_ITConfig(TIM2,TIM_IT_Update ,ENABLE);
	
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2 );
	NVIC_InitTypeDef  NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel =TIM2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd =ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority =1;
	NVIC_Init (&NVIC_InitStruct);
	
	TIM_Cmd(TIM2,ENABLE);
	
}

void  TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		Freq_count=Freq_Count;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
		Freq_Count =0;
	}
	
}

uint16_t Timer_Get(void)
{
	return Freq_count;
}

 也是让自己输出自己测(TIM2被占用故选择TIM3输出PWM)

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//这里进行重映射试一下
//	
//	GPIO_PinRemapConfig (GPIO_PartialRemap1_TIM2 ,ENABLE);//这里映射到了PIN15,但有其他复用,需要关闭
//	GPIO_PinRemapConfig (GPIO_Remap_SWJ_JTAGDisable,ENABLE);
	
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure. GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure. GPIO_Pin = GPIO_Pin_6;//Pin15
	GPIO_InitStructure. GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA ,&GPIO_InitStructure );
	
	
	
	TIM_InternalClockConfig(TIM3);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision =TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode =TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period =100-1;//都要在0-65535之间(记录一万次)ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;//这是计数频率PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter =0;//高级计数器才用,不用直接0
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
	
	TIM_OCInitTypeDef TIM_OCInitStructre;
	TIM_OCStructInit(&TIM_OCInitStructre);
	TIM_OCInitStructre.TIM_OCMode =TIM_OCMode_PWM1 ;//输出比较模式,冻结模式,置有效电平。
	TIM_OCInitStructre.TIM_OCNPolarity =TIM_OCPolarity_High;//输出比较极性,有效电平是高电平
	TIM_OCInitStructre.TIM_OutputState = TIM_OutputState_Enable ;//输出状态,输出使能
	TIM_OCInitStructre.TIM_Pulse =50;//用来设置CCR寄存器的值的
	TIM_OC1Init(TIM3 ,&TIM_OCInitStructre);
	
	TIM_Cmd(TIM3,ENABLE);
	
}

void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM3,Compare);
}
void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM3,Prescaler,TIM_PSCReloadMode_Immediate);
}

 

就继续借用Countsenor进行编程(名字没有进行修改) 

#include "stm32f10x.h"                  // Device header

uint16_t  Freq_Count;


void CountSenor_Init(void)
{
	RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE );//优先级配置 GPIO 只用使能时钟
	RCC_APB2PeriphClockCmd (RCC_APB2Periph_AFIO,ENABLE );//优先级配置AFIO
	//中断->AFIO->EXTI->NVIC
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure. GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure. GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStructure. GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB ,&GPIO_InitStructure );
	
	GPIO_EXTILineConfig (GPIO_PortSourceGPIOB,GPIO_PinSource14);//中断配置 EXIT  进行中断的线路B 14
	
	  //外部中断配置,中断要进入NVIC需要有一个输入线  EXTI 10,14等是外部中断对应资源
	//监测指定的GPIO口的电平信号
	EXTI_InitTypeDef EXTI_structure;
	EXTI_structure. EXTI_Line =EXTI_Line14;
	EXTI_structure.EXTI_LineCmd =ENABLE;
	EXTI_structure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_structure.EXTI_Trigger =EXTI_Trigger_Falling;
	EXTI_Init ( &EXTI_structure);
	
	//优先级配置 NVIC(只有一个输出,通过优先级比较后输出,有抢占优先级与响应优先级(可插队,不同分组会产生影响
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//选择中断分组AIRCR
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel =EXTI15_10_IRQn ;//选择中断通道
	NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init (&NVIC_InitStructure);
}


uint16_t CountSensor_Get(void)
{
	return Freq_Count;
}

void EXTI15_10_IRQHandler(void)
{
   if (EXTI_GetFlagStatus(EXTI_Line14)== SET )
   {
	   Freq_Count ++;
	   EXTI_ClearFlag(EXTI_Line14);
   }
	
}
	

 这样即可达到目的。道友再会。

<think>好的,用户想了解STM32如何用实现频率量,以及相关的代码示例。我需要结合之前的引用资料来回答这个问题。首先,的原理应该是统计一定时间内的脉冲次数,然后计算频率。根据引用[2],可能使用定时器外部时钟模式,或者通过外部中断计数。不过引用[2]提到需要繁进入中断,可能影响性能。而引用[1]中的可能用了,但用户这里明确问的是。 根据引用[3],示例中使用三个定时器:一个生成PWM,一个计数上升沿,一个做1秒定时。这可能是一个的例子,因为通过定时器3记录脉冲次数,定时器4作为时间基准。用户可能需要这样的代码结构。 我需要整理的实现步骤:1.配置一个定时器作为外部时钟,输入捕获模式,或者使用外部中断。2.另一个定时器作为定时基准,比如1秒中断一次。在定时器中断中读取计数值并计算频率,然后重置计数器。需要注意定时器之间的配合,比如使用从模式或者触发机制。 引用[1]中提到可以用定时器的外部时钟模式,比如对射式红外传感器计次,这可能对应将定时器配置为外部时钟,即TI的输入作为计数时钟源。例如,TIMx的ETR引脚接信号,定时器在外部时钟模式下计数。这样不需要中断,每个上升沿自动计数,然后在固定时间读取计数值。 所以可能的实现方有两种:一是使用外部中断,在每次上升沿触发中断,计数器加1,定时器中断定时读取;二是使用定时器的外部时钟模式,自动计数,定时读取计数值。第二种方更高效,减少中断次数。 根据引用[3],他们用了TIM3来记录上升沿个数,这可能配置为外部时钟模式,或者使用输入捕获。例如,TIM3的通道配置为输入捕获,在每次上升沿触发捕获事件,然后计数器加1。或者TIM3配置为从模式,由外部信号触发计数。 代码示例可能需要初始化两个定时器:一个作为计数器,一个作为定时基准。比如,TIM3作为计数器,TIM4作为定时器。在TIM4的中断处理函数中读取TIM3的计数值,计算频率,然后重置TIM3的计数器。 需要注意的是,配置定时器为外部时钟模式的方,例如设置从模式为外部时钟模式1,触发源为TI1FP1。这样,定时器的计数器会在每个上升沿自动递增。 最后,示例代码的结构应该包括定时器初始化的部分,中断处理函数,以及频率计算的逻辑。需要确保代码正确配置定时器模式和中断,并处理可能的溢出情况。同时,用户可能需要根据具体硬件连接调整引脚配置,比如使用哪个定时器和哪个输入通道。</think>### STM32实现方及代码示例 通过统计固定时间窗口内的信号脉冲个数来计算频率,计算公式为: $$f = \frac{N}{T}$$ 其中$N$为脉冲计数,$T$为时间窗口。 #### 硬件配置要求 1. 使用**外部时钟模式**或**输入捕获模式**统计脉冲次数[^2] 2. 单独定时器产生固定时间窗口(如1秒) 3. 推荐使用TIMx的ETR引脚接收被信号[^3] #### 实现步骤 1. **配置计数器定时器**(以TIM3为例) - 工作模式:外部时钟模式 - 触发源:外部信号边沿 - 计数器自动递增 2. **配置基准定时器**(以TIM4为例) - 1秒定时中断 - 中断内读取TIM3计数值 3. **频率计算**:在基准定时器中断中计算$f = \frac{N}{T}$ #### 代码示例(CubeMX HAL库实现) ```c // TIM3配置为外部时钟模式 void MX_TIM3_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; htim3.Instance = TIM3; htim3.Init.Prescaler = 0; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 0xFFFF; // 最大计数值 htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim3); // 配置外部时钟模式2 sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_ETRMODE2; sClockSourceConfig.ClockPolarity = TIM_ETRPOLARITY_NONINVERTED; sClockSourceConfig.ClockPrescaler = TIM_CLOCKPRESCALER_DIV1; sClockSourceConfig.ClockFilter = 0; HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig); sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig); } // TIM4配置为1秒基准定时器 void MX_TIM4_Init(void) { htim4.Instance = TIM4; htim4.Init.Prescaler = 8400-1; // 84MHz/8400=10kHz htim4.Init.CounterMode = TIM_COUNTERMODE_UP; htim4.Init.Period = 10000-1; // 10kHz下10000次=1秒 HAL_TIM_Base_Init(&htim4); HAL_TIM_Base_Start_IT(&htim4); } // TIM4中断处理 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM4) { uint32_t count = __HAL_TIM_GET_COUNTER(&htim3); float frequency = (float)count; // f = N/1s __HAL_TIM_SET_COUNTER(&htim3, 0); // 重置计数器 printf("Frequency: %.2f Hz\r\n", frequency); } } ``` #### 性能优化建议 1. 使用**DMA传输**代替中断读取计数值 2. 选择**更高精度定时器**(如TIM2/TIM5) 3. 对高信号需配合**预分电路** 4. 采用**滑动窗口平均**提高量稳定性
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值