STM32 输入捕获
·
捕获:
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的操作也可
}
更多推荐



所有评论(0)