捕获:

IC(Input Capture)输入捕获

输入捕获:
        输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
每个高级定时器和通用定时器都拥有4个输入捕获通道
可配置为PWMI模式,同时测量频率和占空比
可配合主从触发模式,实现硬件全自动测量
1和2两部分加起来是输入捕获的电路,2和3两部分加起来是输出比较的电路。由图中可以看出有4个输入捕获和输出比较通道,输入捕获和输出比较共用4个CCR寄存器,4个通道的引脚(CH1、CH2、CH3、CH4)输入捕获和输出比较也是共用的,所以对于同一个定时器,输入捕获和输出比较只能使用其中一个,不能同时使用。

输入捕获电路与外部中断的相同于区别

相同:
        都是检测电平跳变,然后执行动作。
区别:
  • 外部中断执行的动作是向CPU申请中断。
  • 输入捕获执行的动作是控制后续电路,让当前CNT的值锁存到CCR寄存器中。

输出比较和输入捕获对比总结

  • 输出比较引脚是输出端口;
  • 输入捕获引脚是输入端口。
  • 输出比较是根据CNT和CCR的大小关系来执行输出动作;
  • 输入捕获是接收到输入信号,执行CNT锁存到CCR的动作。

计数:

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"

int main(void)
{
	OLED_Init();		
	PWM_Init();			
	IC_Init();			//输入捕获初始化
	
	OLED_ShowString(1, 1, "Freq:00000Hz");	
	
	/*使用PWM模块提供输入捕获的测试信号 
	ARR在时基单元初始化的时候设置为100 PSC初始化为720*/
	PWM_SetPrescaler(720 - 1);					//PWM频率Freq = 72M /((PSC + 1) * 100)=1KHZ
	/*输入捕获的是PWM频率 跟CCR没关系 下面这行可有可无*/
	PWM_SetCompare1(70);								//PWM占空比Duty = CCR / 100
	
	while (1)
	{
		OLED_ShowNum(1, 6, IC_GetFreq(), 5);	//不断刷新显示输入捕获测得的频率
	}
}

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA0引脚初始化为复用推挽输出		
	
	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 = 100 - 1;									//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;							//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;          
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
	
	TIM_OCInitTypeDef TIM_OCInitStructure;							
	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值 则最好执行此函数,给结构体所有成员都赋一个默认值

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	
	TIM_OCInitStructure.TIM_Pulse = 0;									//初始的CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

/**
  * 函    数:PWM设置CCR
  * 参    数:Compare 要写入的CCR的值,范围:0~100
  * 返 回 值:无
  * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
  *           占空比Duty = CCR / (ARR + 1)
  */
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);		//设置CCR1的值
}

/**
  * 函    数:PWM设置PSC
  * 参    数:Prescaler 要写入的PSC的值,范围:0~65535
  * 返 回 值:无
  * 注意事项:PSC和ARR共同决定频率,此函数仅设置PSC的值,并不直接是频率
  *           频率Freq = CK_PSC / (PSC + 1) / (ARR + 1)
  */
void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);		//设置PSC的值
}

IC.c

#include "stm32f10x.h"                  // Device header



/*1. 时基单元(PSC=71) → 计数频率 = 1MHz,每1μs CNT加1
2. 外部信号从 PA6(TIM3_CH1)进入
3. 经过滤波器(0xF)→ 边沿检测(上升沿)
4. 触发源选择(TIM_TS_TI1FP1)→ 告知定时器“这个信号是触发源”
5. 从模式(Reset)→ 告知定时器“收到触发信号后,把CNT值锁存到CCR1,然后把CNT清零”
6. 每次上升沿,硬件自动完成“锁存 → 清零”
7. 软件调用 TIM_GetCapture1(TIM3),读到的是上一个周期的计数值 N
8. 频率 fx = 1MHz / N*/

void IC_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟 TIM3挂在APB1下面
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	/*在内部通过一个电阻(约30-50kΩ)接到VCC。这个电阻会微弱地将引脚电平拉向高电平,同时保留外部信号的驱动能力。*/
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);										//将PA6引脚初始化为上拉输入
	
	TIM_InternalClockConfig(TIM3);		//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*OCinit 需要配置时基单元*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;    
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	/*ARR(自动重装载值)决定了计数器能数到的最大值。你配成了 65536 - 1,也就是 0xFFFF。
	当 CNT 从 0 数到 65535 后,再数一个脉冲就会溢出归零。对于输入捕获来说,ARR 决定了你能测量的最大周期。
	用 1MHz 计数,65535 个脉冲对应 65535μs,也就是约 65.5ms,能测到的最低频率大约是 15Hz。
	换句话说,ARR 设置了计数器的“量程”。如果你需要测更低的频率,可以把 PSC 改大(比如 720 - 1,计数频率变成 100kHz),
	让每个计数周期更长,从而测量更长的周期。关于测量范围,如果你对测更低频率时应该怎么调整参数有疑问,可以继续提出来。*/
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               //ARR
	/*标准频率 1MHz 就是由 TIM_Prescaler = 72 - 1 决定的*/
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;               //PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
	
	/*输入捕获初始化 Input Caputure*/
	TIM_ICInitTypeDef TIM_ICInitStructure;							
	/*TIM_Channel_1 是定时器的4个捕获/比较通道之一。选择通道1,是因为你要把外部信号接到TIM3的通道1引脚(通常是PA6)。*/
	/*你可以参考数据手册的“复用功能映射表”,比如:
		TIM3的通道1(CH1) → PA6
		TIM3的通道2(CH2) → PA7
		你选择通道1,就必须把待测信号接到PA6;选择通道2,就接到PA7。这两者必须对应,否则信号无法进入定时器。*/
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;								//选择配置定时器通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;													//输入滤波器参数,可以过滤信号抖动
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;			//极性,选择为上升沿触发捕获
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;						//捕获预分频,选择不分频,每次信号都触发捕获
		/*输入信号的路径,即“信号从哪个引脚进入捕获通道”*/
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;	//输入信号交叉,选择直通,不交叉
	TIM_ICInit(TIM3, &TIM_ICInitStructure);													//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
	
	/*选择触发源及从模式*/
	/*告诉TIM3:“盯着通道1(PA6)上输入的信号。” 这个信号,就是你要测频率的那个外部方波。
		TI1FP1 表示信号来自 通道1 的输入引脚,并且经过了输入滤波器(你之前配置的 TIM_ICFilter = 0xF),这能滤除高频噪声。*/
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					//触发源选择TI1FP1
	/*就是在告诉TIM3:“每当通道1上出现一个上升沿,就把计数器(CNT)清零。”*/
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);				//从模式选择复位,即TI1产生上升沿时,会触发CNT归零
	
	/*TIM使能*/
	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}

/**
  * 函    数:获取输入捕获的频率
  * 参    数:无
  * 返 回 值:捕获得到的频率
  */
uint32_t IC_GetFreq(void)
{
	/*TIM_GetCapture1(TIM3) 得到的是“CCR1寄存器的值”,它等于“两个上升沿之间,CNT计数的次数”*/
		/*输出源的上升沿使得CNT存储到 CCR,
	 CNT存储的值,实际代表了“一个周期内,标准频率计数了多少次” 下面的1000000 就是fc 代表了标频率
	这里fc的标准频率1000000 由 TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;  所决定*/
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);		//测周法得到频率fx = fc / N,这里不执行+1的操作也可
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐