一、输入捕获简介
IC(Inpute Capture)输入捕获
输入捕获模式下,当通道输入引脚出现指定电平跳变,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
每个高级定时器和通用定时器都拥有4个输入捕获通道
可配合PWMI模式,同时测量频率和占空比
可配合主从触发模式,实现硬件全自动测量
二、频率测量
首先,测频法适合测量高频信号,测周法适合测量低频信号
然后是测频法测 量结果更新慢一些,数值相对稳定,测周法更新的快,数据跳变也非常快
当待测信号频率小于中界频率时测周法误差更小, 当待测信号频率大于中界频率时测频法误差更小
三、电路实现测周法
3.1看图说话
从左到右,最左边,是四个通道的引脚,参考引脚定义表就能知道引脚是复用在那个引脚,引脚进来,有一个三输入的异或门,这个异或门的输入接在了通道1、2、3端口,异或门的执行逻辑是,当输入引脚的任何一个有电平翻转时,输出引脚就产生一次电平翻转,之后输出通过数据选择器,到达输入捕获通道1,数据选择器如果选择上面一个,那输入捕获通道一的输入就是3个引脚的异或值,如果选择下面一个,那异或门就没有用,4个通道各用各的引脚。
输入信号过来,来到滤波器和边缘检测器,输入滤波器可以对信号进行滤波,避免一些高频的毛刺信号误触发,然后边沿检测器,这就和外部中断那里是一样的了,可以选择高电平触发,或者低电平触发,当出现指定的电平时,边缘检测电路就会触发后续电路执行动作
另外,其实设计了两套滤波和边沿检测电路,第一套电路经过滤波和极性选择得到TIFP1,输入给通道1的后续电路,第二套电路,经过另一个滤波和极性选择得到TIFP2,输入给下面通道2的后续电路,同理下面TI2信号进来,也经过两套滤波和极性选择得到TI2FP1和TI2FP2,其中TI2FP1输入给上面。TI2FP2输入给本路.在这里通道1和通道2可以各走各的也可以进行一个交叉(交叉的目的1、可以灵活切换后续捕获电路的输入,2、可以把一个引脚的输入,同时映射到两个捕获单元,这也是PWMI模式的经典结构)
然后经过预分频器就可以触发捕获电路进行工作了,每来一个触发信号,CNT的值就会向CCR转运一次,转运的同时,会发生一个捕获事件,这个事件会在状态寄存器置标志位,同时也可以产生中断,如果需要在捕获的瞬间去处理一些事情的话就可以开启这个捕获中断
(设计这个异或门,其实还是为三相无刷电机服务的,无刷电机有3个霍尔传感器检测转子的位置,可以根据转子的位置进行换相,有了这个异或门,就可以在前三个通道上接上无刷电机的霍尔传感器,然后这个定时器就作为无刷电机的接口定时器去驱动换相电路工作)
3.2详细的电路
引脚进来,经过一个滤波器,滤波器的输入是TI1,就是CH1的引脚,输出的是TI1F,就是滤波后的信号,FDTS是滤波器的采样时钟来源,下面CCMR1寄存器里的ICF位可以控制滤波器的参数,(滤波器的主要工作是:以采样频率对输入信号进行采样,当连续N个值为高电平,输出才为高电平,连续N个值为低电平,输出才为低电平,采样频率越低,采样个数越大,滤波效果越好)滤波后的信号通过边沿检测器捕获上升沿或者下降沿,用CCER寄存器里的CC1P位就可以选择极性了,最终得到TI1FP1触发信号,通过数据选择器进入通道1的后续捕获电路,当然,应该还有一套相同的电路得到TI1FP2触发信号,连到通道2的后续电路,同样,通道2有TI2FP1连到通道一的后续电路,通道2也还有TI2FP2,共四种连接电路
3.3主从触发模式
CCR对CNT进行捕获之后,需要对CNT进行一次清0操作,这样每次捕获得到的值才是测周法两个上升沿(下降沿)之间的时间间隔。这个清0操作,就需要用到主从触发模式来自动完成。由输入捕获通道1的详细框图可得:经过滤波和极性选择的TI1FP1信号和经过滤波的边沿信号TI1F_ED都可以通向从模式控制器,之后便可以通过硬件电路自动完成CNT的清0操作。
主从触发模式,即主模式、从模式和触发源选择三个功能的简称。主模式可以将定时器内部的信号映射到TRGO引脚,用于触发其他外设的操作;从模式可以接收其他外设或自身外设的一些信号,用于触发自己的一些操作(定时器的运行);触发源选择,即选择从模式的触发信号源功能,也可以认为它是从模式的一部分。
在从模式下,可以通过触发源选择功能选择一个信号产生TRGI信号,之后去触发从模式。关于主从模式的详细说明可以参见手册
3.4输入捕和PWMI基本结构
下图是输出捕获模式测频率的基本结构图。下图清晰地展示了输入捕获模式测量频率的过程,同时也是编程的逻辑基础。在这里我们只使用了一个通道,所以它只能测量频率。
首先,配置时基单元,启动寄存器,则CNT就会在时钟驱动下不断自增。我们使用CNT来计数,间接实现计时的功能。经过预分频后的时钟频率,就是测周法的标准频率F
之后,GPIO输入一个待测的方波信号,经过TI1FP1为上升沿触发,之后数据选择器选择直连通道,分频器选择不分频。当TI1FP1出现上升沿之后,CNT的值就会被CCR1捕获;同时触发源选择模块选择TI1FP1为触发信号,从模式选择复位操作。则TI1FP1信号完成两个操作:捕获CNT的值到CCR1中、清零CNT。当然这里存在先后顺序,先进行捕获后进行清0;或者存在非阻塞的同时转移。当电路不断工作是,CCR1中的值始终是最新一个周期的计数值,即测周法的计次数 N
这里需要注意以下两点:
1、CNT的计数值是有上限的。由于ARR最大为65535,故CNT最大也只能计65535个数。如果信号频率太低,CNT的计数值可能会溢出。
2、从模式的触发源选择中有TI1FP1和TI2FP2,但是没有TI3和TI4的信号。所以如果要使用从模式自从清零CNT,就必须使用CH1或CH2作为输入。对于CH3和CH4,就只能开启捕获中断,在中断中手动清0了(程序会处于频繁中断的状态,比较占用软件资源)。
四、程序代码
4.1输入捕获相关函数
4.1.1输入捕获初始化配置
// 输入捕获IC初始化函数
// 输入捕获有四个通道,但是只有一个初始化函数,通道选择在结构体成员变量中,这一点与输出比较不同
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
// 给IC初始化函数中的结构体参数赋一个默认值
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);
// 快速配置PWMI模式(自动将另一个通道配置为相反的模式)
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
// 单独写入时基单元的PSC,第三个参数与预装载有关
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
// 单独配置四个通道的预分频器,结构体中也可以配置,效果相同
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
// 分别读取四个通道的CCR的值(与输出比较中的TIM_SetComparex函数对应)
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);
4.1.2主从触发模式配置
// 选择输入(从模式)触发源TRGI
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
// 选择输入(主模式)触发源TRGO
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
// 选择从模式需要执行的操作
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);
4.2输入捕获模式测频率
4.2.1接线图
4.2.2程序
PWM.c
#include "stm32f10x.h" // Device header
/**
* @brief PWM输出初始化函数
* @param 无
* @retval 无
*/
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, 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 = 100 - 1; // ARR,确定后即确定了分辨率,这里分辨率为1%
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // PSC(调节ARR和PSC都可以改变频率,而ARR同时影响占空比,故这里通过调节PSC来调节PWM波形的频率)
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// GPIO初始化,输出端口为PA0
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);
// OC初始化
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct); // 结构体成员赋初始值
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // OC输出极性(有效电平为高电平)
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // OC输出使能
TIM_OCInitStruct.TIM_Pulse = 0; // CCR
TIM_OC1Init(TIM2, &TIM_OCInitStruct);
TIM_Cmd(TIM2, ENABLE);
}
/**
* @brief 更改比较/捕获寄存器的值CCR(当 ARR + 1 == 100 时 CCR 即为占空比)
* @param Compare 无符号16位整型数,注意:它只能是正数
* @retval 无
*/
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}
/**
* @brief 更改与分频器的值
* @param Prescaler 写入的新预分频器的值
* @retval 无
*/
void PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate); // 单独设置PSC(立刻生效)
}
InputCaputure.c
#include "stm32f10x.h" // Device header
/**
* @brief 输入捕获初始化函数
* @param 无
* @retval 无
*/
void IC_Init(void)
{
// 1. 开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 使用TIM2输出PWM波形,TIM3进行输入捕获
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 2. GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure;
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);
// 3. 配置时基单元
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 = 65536 - 1; // ARR,该值应该设置的尽量大,防止计数溢出
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; // PSC,它的值决定了测周法的标准频率fc,它的值要根据测量信号的频率范围来调整,这里fc为1MHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
// 4. 配置输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1; // IC通道
TIM_ICInitStruct.TIM_ICFilter = 0xF; // 滤波属性(滤波检测频率应远高于被测频率)
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; // 边沿检测
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 触发信号分频器
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; // 配置数据选择器(这里选择直连通道)
TIM_ICInit(TIM3, &TIM_ICInitStruct);
// 5. 选择从模式的触发源
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
// 6. 选择从模式触发后执行的操作
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
// 7. 开启定时器
TIM_Cmd(TIM3, ENABLE);
}
/**
* @brief 获取测得的频率
* @param 无
* @retval 测得的频率
*/
uint32_t IC_GetFreq(void)
{
// 测周法标准频率为1MHz
return 1000000 / TIM_GetCapture1(TIM3);
// return 1000000 / (TIM_GetCapture1(TIM3) + 1) // 面向结果编程否则逼死强迫症(?)
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "InputCapture.h"
int main()
{
OLED_Init();
IC_Init();
PWM_Init();
PWM_SetPrescaler(720 - 1); // Freq = 72M / (PSC + 1) / (ARR + 1),这里ARR + 1 == 100
PWM_SetCompare1(50); // Duty = CCR / (ARR + 1)
OLED_ShowString(1, 1, "Freq:00000Hz");
while(1)
{
OLED_ShowNum(1, 6, IC_GetFreq(), 5);
}
}
4.3PWMI模式同时测量频率和占空比
4.3.1接线图
4.3.2程序
PWM.c
#include "stm32f10x.h" // Device header
/**
* @brief PWM输出初始化函数
* @param 无
* @retval 无
*/
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, 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 = 100 - 1; // ARR,确定后即确定了分辨率,这里分辨率为1%
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // PSC(调节ARR和PSC都可以改变频率,而ARR同时影响占空比,故这里通过调节PSC来调节PWM波形的频率)
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// GPIO初始化,输出端口为PA0
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);
// OC初始化
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct); // 结构体成员赋初始值
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // OC输出极性(有效电平为高电平)
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // OC输出使能
TIM_OCInitStruct.TIM_Pulse = 0; // CCR
TIM_OC1Init(TIM2, &TIM_OCInitStruct);
TIM_Cmd(TIM2, ENABLE);
}
/**
* @brief 更改比较/捕获寄存器的值CCR(当 ARR + 1 == 100 时 CCR 即为占空比)
* @param Compare 无符号16位整型数,注意:它只能是正数
* @retval 无
*/
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}
/**
* @brief 更改与分频器的值
* @param Prescaler 写入的新预分频器的值
* @retval 无
*/
void PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate); // 单独设置PSC(立刻生效)
}
InputCaputure.c文章来源:https://www.toymoban.com/news/detail-545767.html
#include "stm32f10x.h" // Device header
/**
* @brief 输出捕获PWMI模式初始化函数
* @param 无
* @retval 无
*/
void IC_Init(void)
{
// 1. 开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 使用TIM2输出PWM波形,TIM3进行输入捕获
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 2. GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure;
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);
// 3. 配置时基单元
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 = 65536 - 1; // ARR,该值应该设置的尽量大,防止计数溢出
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; // PSC,它的值决定了测周法的标准频率fc,它的值要根据测量信号的频率范围来调整,这里fc为1MHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
// 4. 配置输入捕获单元
// 通道1测量频率
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1; // IC通道,通道1
TIM_ICInitStruct.TIM_ICFilter = 0xF;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; // 边沿检测,上升沿触发
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; // 配置数据选择器,直连通道
TIM_ICInit(TIM3, &TIM_ICInitStruct);
// 通道2测量占空比
TIM_PWMIConfig(TIM3, &TIM_ICInitStruct); // 在该函数中会快捷地将另一个通道初始化为“相反”的配置,这个函数执行的操作和下面的操作是等价的
// TIM_ICInitStruct.TIM_Channel = TIM_Channel_2; // IC通道,通道2
// TIM_ICInitStruct.TIM_ICFilter = 0xF;
// TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling; // 边沿检测,下降沿触发
// TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
// TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_IndirectTI; // 配置数据选择器,交叉通道
// TIM_ICInit(TIM3, &TIM_ICInitStruct);
// 5. 选择从模式的触发源
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
// 6. 选择从模式触发后执行的操作
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
// 7. 开启定时器
TIM_Cmd(TIM3, ENABLE);
}
/**
* @brief 获取测得的频率
* @param 无
* @retval 测得的频率
*/
uint32_t IC_GetFreq(void)
{
// 测周法标准频率为1MHz
return 1000000 / TIM_GetCapture1(TIM3);
// return 1000000 / (TIM_GetCapture1(TIM3) + 1); // 面向结果编程否则逼死强迫症(?)
}
/**
* @brief 获取测得的占空比
* @param 无
* @retval 测得的占空比(百分之)
*/
uint32_t IC_GetDuty(void)
{
return TIM_GetCapture2(TIM3) * 100 / TIM_GetCapture1(TIM3);
// return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);
}
main.c文章来源地址https://www.toymoban.com/news/detail-545767.html
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "InputCapture.h"
int main()
{
OLED_Init();
IC_Init();
PWM_Init();
PWM_SetPrescaler(720 - 1); // Freq = 72M / (PSC + 1) / (ARR + 1),这里 ARR + 1 == 100
PWM_SetCompare1(80); // Duty = CCR / (ARR + 1)
OLED_ShowString(1, 1, "Freq:00000Hz");
OLED_ShowString(2, 1, "Duty:00%");
while(1)
{
OLED_ShowNum(1, 6, IC_GetFreq(), 5);
OLED_ShowNum(2, 6, IC_GetDuty(), 2);
}
}
到了这里,关于STM-32:TIM输入捕获—输入捕获模式测频率/PWMI模式同时测量频率和占空比的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!