第三部分是定时器的输入捕获功能和主从触发模式,来实现测量方波频率
输入捕获和输出比较是相互共用的,对应的四个引脚也是同理,但是不能同时使用
输入捕获
IC(Input Capture)输入捕获
输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存(写入)到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
每个高级定时器和通用定时器都拥有4个输入捕获通道,没有区别
可配置为PWMI模式(PWM输入模式),同时测量频率和占空比
可配合主从触发模式,实现硬件全自动测量
频率测量
默认是数字信号,越往左频率越低,默认为上边沿;若要测量一个正弦波。需要搭建一个信号预处理电路,把正弦波转换为数字信号,再输入到STM32上;如果测量的信号电压非常高,则需要考虑隔离的问题(隔离问题指如何处理高电压信号与低压控制电路(如STM32)之间的电气连接)
正弦波:正弦波是一种连续的周期性波形,其电压(或电流)随时间按正弦函数规律变化,作模拟信号中的基准信号
方波:高电平波形通常指数字信号中的方波(Square Wave)或脉冲波(Pulse Wave),其特征是电压在高电平(如 5V)和低电平(如 0V)之间瞬时切换。
- 测频法(图左边):在闸门时间T内,对上升沿计次,得到N,则频率,
fx=N / T
此方法适合于频率较多的时候,结果更新慢,但是稳定,相当于自带一个滤波
- 测周法(图右边):两个上升沿内,以标准频率fc计次,得到N ,则频率
fx=fc / N
此方法适合于频率较少的时候,因为只测量一个周期,所以结果更新快,但是会受到噪音等影响
- 中界频率:测频法与测周法误差相等的频率点
fm=fc / T
测频法与测周法误差——正负1误差:指的是如果满足了计次N,但是波形并没有走,还留有,那会进行舍弃(-1)或者补全(+1)的操作。所以要使N的数大一点,误差就小。
中界频率来决定频率大小,从而合理的使用测频法还是测周法
电路实现测周法:
以四个引脚,通过引脚定义表来确认引脚的复用位置; 异或门:当三个输入引脚的任何一个有电平翻转,输出引脚则产生一个电平翻转,之后通过数据选择器,到达输入通道(如果选择了上面的通道,则会用三个引脚的异或值;如果选择下面的,则没有异或门。设计异或门:是为了三相无刷电机服务的),进入通道:输入滤波器对信号进行滤波,边沿检测器:选择高电平触发或者低电平触发;在通道里选择不同的滤波通道(一般有2个)
,形成交叉连接(1.可灵活切换后续的输入;2.(主要)可以把一个引脚的输入,同时映射到两个捕获单元,属于PWMI模式),之后就进入预分频器进行分频,分频后的触发信号,就可以触发捕获电路的工作,每来一个信号,CNT的值就向CCR转运一次。转运的同时,会发生一个捕获事件,此事件会在状态寄存器置标志位,同时也可以产生中断,在这里捕获之后会自动完成CNT的清零工作(通过以下图中的TI1FP1和TI1F_ED信号通过从模式中。从模式有能力自动进行清零)如果需要在捕获的瞬间处理事情的话,就可以开启捕获中断。
输入捕获通道
引脚进来,输入TI1经过一个滤波器,fDITS是滤波器的采样时钟来源,下面CCMR1寄存器里的ICF位可以控制滤波器的参数,这个滤波器原理:通过多个高电平后,才产生为高电平,若是一会高一会低,则产生一道稳定的电平,滤波后的信号为TI1F,通过边沿检测器进行=捕获上升沿或下降沿,用CC1P位选择极性,最终得到TI1FP1触发信号,通过数据选择器进入后续的捕获电路(有两个这样的电路通道)
主从触发模式:
分为:主模式、从模式和触发源选择
主模式:可以将定时器的内部信号映射到TRGO引脚,用于触发别的外设
从模式:接受其他外设或自身外设的信号,用于控制自身定时器的的运行,会被别的信号控制
触发源选择:选择从模式的触发信号源的,也可以理解是从模式的一个部分
TI1FP1信号通过TRGI选择Reset,这样就可以是CNT自动清零
输入捕获基本结构
PWMI基本结构
接线图
6-6 输入捕获模式测频率
目前是初始化了TIM2通道的1,产生了一个PWM的波形;通过单数调节CCR的值从而控制占空比,但是目前的PWM的频率是初始化中固定了的,所以需要单独调节PWM的频率=72MHz/(PSC+!)/(ARR+1),由于占空比=CCR/(ARR+1),改ARR会变两个,所以改PSC最合适。
- 开启RCC时钟,把GPIO和TIM的时钟打开
- GPIO初始化,设置成输入模式
- 配置时基单元,让CNT计数器在内部时钟的驱动下自增运行
- 配置输入捕获单元
- 选择从模式的触发源,触发源选择为TI1FP1
- 选择触发之后执行的操作,Reset操作
- 调用TIM_Cmd函数,开启定时器
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);//配置PSC参数
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct); 配置初始化输入单元的函数,只能一个通道一个通道的配置
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct); 初始化输入单元,可以快速配置两个通道,会自动把剩下的一个通道初始化为相反的配置,只支持通道1和2
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct); 可以给捕获结构体赋一个初始值
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource); 选择输入触发源TRGI,选择从模式的触发源的操作
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource); 选择输出触发源TRGO,选择主模式的触发源
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode); 选择从模式
//以下四个分别配置1,2,3,4分频器
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,与之前的SetCompare函数对应
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);
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_SetPSC(720-1); //目前频率是1kHz
PWM_set(50); //目前占空比是50%
while(1){
OLED_ShowNum(1,6,IC_GetFreq(),5);
}
}
IC.c
#include "stm32f10x.h" // Device header
/**
* 函 数:输入捕获初始化
* 参 数:无
* 返 回 值:无
*/
void IC_Init(void)
{
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*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); //将PA6引脚初始化为上拉输入
/*配置时钟源*/
TIM_InternalClockConfig(TIM3); //选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
/*时基单元初始化*/
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的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
/*输入捕获初始化*/
TIM_ICInitTypeDef TIM_ICInitStructure; //定义结构体变量
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的输入捕获通道
/*选择触发源及从模式*/
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); //触发源选择TI1FP1
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //从模式选择复位
//即TI1产生上升沿时,会触发CNT归零
/*TIM使能*/
TIM_Cmd(TIM3, ENABLE); //使能TIM3,定时器开始运行
}
/**
* 函 数:获取输入捕获的频率
* 参 数:无
* 返 回 值:捕获得到的频率
*/
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1); //测周法得到频率fx = fc / N,这里不执行+1的操作也可
}
6-7 PWMI模式测频率占空比
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");
OLED_ShowString(2,1,"Duty:00%");
PWM_SetPSC(720-1); //目前频率是1kHz
PWM_set(50); //目前占空比是50%
while(1){
OLED_ShowNum(1,6,IC_GetFreq(),5);
OLED_ShowNum(2,6,IC_GetDuty(),3);
}
}
IC.c
#include "stm32f10x.h" // Device header
/**
* 函 数:输入捕获初始化
* 参 数:无
* 返 回 值:无
*/
void IC_Init(void)
{
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*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); //将PA6引脚初始化为上拉输入
/*配置时钟源*/
TIM_InternalClockConfig(TIM3); //选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
/*时基单元初始化*/
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的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
/*输入捕获初始化 升级为一个引脚对2个通道:1.是分出两个结构体变量;2.使用函数TIM_PWMIConfig*/
TIM_ICInitTypeDef TIM_ICInitStructure; //定义结构体变量
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的输入捕获通道
// TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; //选择配置定时器通道1
// TIM_ICInitStructure.TIM_ICFilter = 0xF; //输入滤波器参数,可以过滤信号抖动
// TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; //极性,选择为上升沿触发捕获
// TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //捕获预分频,选择不分频,每次信号都触发捕获
// TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI; //输入信号交叉,选择直通,不交叉
// TIM_ICInit(TIM3, &TIM_ICInitStructure); //将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
TIM_PWMIConfig(TIM3,&TIM_ICInitStructure);
/*选择触发源及从模式*/
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); //触发源选择TI1FP1
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //从模式选择复位
//即TI1产生上升沿时,会触发CNT归零
/*TIM使能*/
TIM_Cmd(TIM3, ENABLE); //使能TIM3,定时器开始运行
}
/**
* 函 数:获取输入捕获的频率
* 参 数:无
* 返 回 值:捕获得到的频率
*/
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1); //测周法得到频率fx = fc / N,这里不执行+1的操作也可
}
//获取占空比
uint32_t IC_GetDuty(void){
//高电平的计数存在CCR2里,整个周期的计数在CCR2里
return TIM_GetCapture2(TIM3)+1 *100/TIM_GetCapture1(TIM3)+1;
}