【STM32】标准库 TIM输入捕获

IC(Input Capture)输入捕获 输入捕获模式下,当通道输入引脚出现指定电平跳变时(这个需要配置,是上升沿还是下降沿),当前CNT的值将被锁存到CCR中(就是把CNT的值赋值给CCR),可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数 每个高级定时器和通用定时器都拥有4个输入捕获通道

可配置为PWMI模式,同时测量频率和占空比

可配合主从触发模式,实现硬件全自动测量

 CH1到CH4也是公用的,CCR寄存器也是公用的。

对于输入捕获功能,通用定时器以及高级定时器是一样的,基本定时器没有输入捕获功能。

 上图,越往右频率越低,都是高低电平的数字信号,STM32来说也只能测信号的,如果需要测试正弦波的信号,就应该搭建一个信号预处理电路,也就是信号比较器,把模拟信号转变为数字信号输送给STM32单片机就行了。

侧周法的核心就是测量一个周期的时间,然后取倒数就是等于频率了,两个上升沿之间就是一个周期的长度,在一个周期之内用已知的频率计数,得到计数次数N,fc倒数乘以N就是等于这段时间的长度,取倒数就是频率。

侧频法适合高频信号,在一定时间内,尽量多一些出现上升沿,多一些计数,减小误差;测周法适合低频信号,两个上升沿的时间尽量长,在一定频率下多次进行计数,减小误差。侧频法是一段时间频率的平均值,值比较平滑,但是不容易受外界影响;而测周法是计算两个上升沿之间的值,更新的速度比较快,但是容易受到影响。

无论这两种方法哪一种,都会有正负一的误差,比如测频法在一定时间内最后一个不一定是完整的周期,或者对于测周法,有时候频率fc计数的时间不到,第二个上升沿就出现了,所以会有一个正负一的差别。

所以要尽量N要大,减小误差,联立两条表达式,得到中介频率,大于和中介频率就用侧频法,小于就用测周法。

侧频法在STM32中具体的实现,顶一个定时中断,一秒取一次计数值,计数值在每一次上升沿计数一次,每一秒读取一次的计数值就是频率了。

1.2.3输入通道有一个三输入的异或门,当输入通道有任何一个电平反转的时候,经过异或门就产生一次电平反转,当数据选择器选择选择上面的一个通道,输出引脚就相当于三个输入引脚的亦或值。当数据选择器选择下面这个,就跟异或门没有任何关系。此设计与三相无刷电机息息相关,三相无刷电机有三个霍尔传感器检测转子的位置,可以根据转子的位置进行换相。有了异或门就可以在1.2.3通道街上三相无刷电机的霍尔传感器。 电视气就作为无刷电机的接口定时器,驱动环相电路工作。

进来,进入滤波器以及边沿检测信号器,滤波器避免一些高频毛刺的误触发。

在滤波以及边沿检测这一部分中,具有两套的极性选择以及滤波电路,所以在这一部分有两个输出,一个接着往后输出,一个船到别的地方形成交叉传输,这样做的目的有两个,一个是灵活切换后续电路的输入,一个是把一个引脚的输入同时映射到两个捕获单元(后续电路),经典的PWMI的经典结构。

PWMI模式:两条电路,一条高电平触发,用来捕获周期(也就是侧频法以及测周法),第二条用来低电平触发,用来捕获占空比。两个通道同时对一个引脚进行捕获,同时测量频率以及占空比。如图所示一二通道以及三四通道都是一样的结构。

TRC信号来自于图片的上面部分。

信号经过预分频值就可以按照触发条件进行触发了,每次触发就把CNT的值存入CCR之中,然后产生一个更新事件,更新事件会进行一个中断标志位,可以经过中断标志位产生中断,如果在不活的瞬间需要处理一些事情的话,这里就可以产生中断进入中断处理函数。

CNT的值是用前面部分内部已知时钟的频率驱动的,所以在每次输入信号触发捕获CNT的值存入CCR中就可以知道两个上升沿之剑的时间,这个时间间隔就是周期,倒数就是测周法的频率了。每次捕获的时候把CNT存入CCR之后,都会把CNT清零一次,所以每一次CNT中得到的才能计算两个上升沿的时间间隔。

这个不活一次自动清零的操作可以用主从触发模式来完成,就只用内部硬件结构就可以了,不用占用CPU。

TI1就是CH1的引脚,FDTS就是采样时钟来源,ICF可以控制滤波器的参数,滤波器的工作原理就是以采样频率的进行N此采样,若N此都是统一电平,就输出相应电平,否则保持原本的电平不变,采样频率越低,N值越大,滤波效果就越好。(频率低就是采样的点离得比较远,检测的范围就比较大,更有说服力)

详细的使用在使用手册14.4.7中。

CC1E位置进行输出使能,icps进行分频器的选择,选择几分频。还有一条交叉的输出到后续电路这里没有画出来。

进过分频器之后就可以选择把CNT的值写入CCR中,但是在这之前要进行CNT的清零,如何完成这一操作呢?在上面的部分有两个方式可以被信号船到从模式之中,从模式之中后续有电路进行清零的操作。

 主从触发模式就是主模式从模式触发源选择三者的合成,主模式可以把懂事起内部的信号映射到TRGO引脚,用来触发别的外设,从模式就是接受自身外设以及别的外设的信号,控制自身定时器的运行,被别的信号控制,触发源选择,就是选择从模式的控制信号!

如果要进行CNT清零操作,就可以选择TI1FP1信号源,然后再从模式中选择RESTET的操作!

 至于各种信号的具体解释可以查看手册的14.4.2

加入进行定时器的级联,就可以选择一个定时器为主模式,输出更新信号映射到TRGO中,然后另一个定时器选择上一个定时器作为输入信号源,触发从模式,从模式选择触发外部信号输出模式一的操作。

往下翻还有信号源选择,从模式选择的介绍,知道就好,需要用的时候再翻阅。 

 次连接方式只使用了一个通道,功能是用来测试频率!

时基单元部分,72MHz的输入频率经过预分频器得到频率fc,控制CNT的计数。GPIO口输入一个左上角一样的方波信号,经过滤波边沿检测,根据设定上升沿还是下降沿触发CNT的转运功能。同时进行主从触发模式,惊醒清零操作。先转运值,在进行清零操作。

左上角的值就是上升沿转运,然后清零,第二次不活的就是0到CNT的值了。

计算两个上升沿的时间,就是先读取CCR的值,也就是测周法的N,然后又知道fc(也就是时基单元分频频率后的),ARR最大是65535,CNT也是65535,注意触发源的选择,只有TI1FP1以及2,没有3.4,所以对于通道3.4,就只能手动在中断之中进行清零操作了,不能运用硬件自动清零,没办法,手动中断清零只能占用CPU软件资源。

 上面通道一样,下面通道就是选择下降沿触发,根据左上角的图片来看,下降沿触发得到CNT1的值,不清零,然后一直到下一个上升沿然后捕获CNT2,那么CNT1/CNT2的值就是占空比啦,就与下面那个空白的通道,也可以用它的两个输出,理论是一样的,要灵活变通。

代码部分:

接线,PWM波从PA0输出传入PA6,根据输入捕获的图,输入引脚应该是定时器的某个通道,要到那张引脚图片上寻找对应的引脚,PA6对应的就是TIM3_CH1。

 利用原本输出PWM波的代码改进,输出想要的频率以及占空比的PWM信号。

PWM频率跟psc以及ARR有关,而占空比与ARR和CCR有关,我们的目的是设立两个函数,分别修改一个参数,并且这个参数就是可以表示占空比或者PWM频率的,就能得到相应的波形。

这里选择把ARR的值固定,假设设定为100-1,那么占空比就应该是通过函数传入的CCR的值。一般根据分辨率的要求确定ARR,然后再在函数中计算出相应的公式,把我们直接可调节的参数(比如需要多少占空比,多少频率)输入,得到我们想要的PWM波。

单独写入PSC的函数:void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);

第一参数是哪个定时器,第二个参数是新的预重装值,第三个参数是影子寄存器:选择更新事件再把预重装值更新,就会获得完整波形,把哪个要写入的值收入缓冲器中,等到周期结束,更新事件的时候,在同一更新,不会被中途输出一半然后更新,而改变输出的PWM的周期,开始新的周期,立即更新就是后者。

 根据上图进行输出捕获的配置。

1.开启RCC时钟,TIM以及GPIO的时钟。

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    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);
    
    TIM_InternalClockConfig(TIM3);

3.配置时基单元,让CNT在内部时钟的控制下不断自增

    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
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

4.配置输入捕获通道 void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);输入捕获以及输出比较都有四个通道,但是输入捕获的四个通道共用一个函数,其中有一个参数是选择通道的,方便运用。

void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);快速配置两个通道,也就是PWMI模式的配置,如下图:

void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);就是给结构体富裕一个初始值的,不用手动一个个配置,具体是什么可以跳转查看

 代码:

    TIM_ICInitTypeDef TIM_ICInitStructure; 
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//因为是公用一个函数,所以是通过这种方式进行选择通道
    TIM_ICInitStructure.TIM_ICFilter = 0xF;//选择滤波器,数值越大效果越好,具体的看使用手册,滤波器的采样频率一般都会高于原本的频率,所以只会滤出一些高频噪声,是信号更加平滑。
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//极性上升沿触发
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//预分频器,1分频是不分频,2分频是隔着一个有效,以此类推
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//选择信号是从那个通道输入的,是直连通道还是交叉通道


    TIM_ICInit(TIM3, &TIM_ICInitStructure);

5.配置触发源的选择,根据需要选择,这里选择TI1FP1void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);

    TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
 

6.配置从模式执行的功能,void TIM_SelectMasterSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_MasterSlaveMode);

    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);

 

   

 跳转的函数参数说明对应的是下面的四个,而上面的三个是编码器接口使用的。

 

7.调用TIM_cmd函数开启定时器

当我们需要读取最新一个周期的频率的时候,直接读取CCR的值,然后根据fc/n计算一下就可以了。

***这个函数的作用是配置主模式的输出的触发源:void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);

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);

//这四个函数配置的是输入捕获通道的预分频器。

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);

//这四个是读取四个通道的CCR

在输出比较模式下,CCR是只写的,在输入捕获的模式下,CCR是只读的。

如果要配置PWMI模式:

其余不变,多增加了一条路,应该增加相关配置,方法一:直接利用    TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);函数进行代替方法二后面那一大串东西,该函数的逻辑是配置一个与通道一配置相反的通道,通道一,直连,上升沿,对应通道二,交叉,下降沿。具体变化看跳转函数源码就知道了,该函数在方法一的作用是与方法二再次通道配置部分是一致的。所以该函数只能使用在通道一二!!

    TIM_ICInitTypeDef TIM_ICInitStructure;
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_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_PWMIConfig(TIM3, &TIM_ICInitStructure);

方法二:再次复制一遍相关的输入捕获通道配置,进行相应的修改

    TIM_ICInitTypeDef TIM_ICInitStructure;
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_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_ICInitStructure.TIM_Channel = TIM_Channel_2;
    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  ;

测试的最低频率,就是固定的内部时钟72MHz除以设定好的72PSC,就是标准频率,然后除以ARR的最大值65535,就得到了最小频率大概15Hz。如果想要更小的频率,就需要把PSC增大。

最大一般认为是1MHz,但是此时的误差已经非常大了,没什么意义,所以想要知道频率上限在哪里,一般就要看你的误差允许范围在哪里,相对误差等于误差数/ARR,如果相对误差要1/1000,那么就应该是标准频率1MHz*相对误差等于1kHz,就是频率上限。想要提高频率,就要把psc减小。如果频率太高,就选择测频法了!!!

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32标准库中,输入捕获可以用来测量外部信号的周期、脉宽等参数,下面是一个简单的例子,以测量一个脉冲的时间为例: ```c #include "stm32f10x.h" void TIM2_IRQHandler(void) { static uint32_t start_time = 0; static uint32_t end_time = 0; if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) // 判断是否是输入捕获中断 { if (start_time == 0) // 第一次捕获到上升沿 { start_time = TIM_GetCapture1(TIM2); // 获取计数器的值 TIM_CCxCmd(TIM2, TIM_Channel_1, TIM_ICPolarity_FallingEdge); // 配置为下降沿触发捕获 } else // 第二次捕获到下降沿 { end_time = TIM_GetCapture1(TIM2); // 获取计数器的值 TIM_CCxCmd(TIM2, TIM_Channel_1, TIM_ICPolarity_RisingEdge); // 配置为上升沿触发捕获 uint32_t pulse_width = end_time - start_time; // 计算脉冲宽度 uint32_t frequency = SystemCoreClock / pulse_width; // 计算频率 start_time = 0; // 重置计时器 end_time = 0; // 处理测量结果 // ... } TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); // 清除中断标志位 } } int main(void) { // 初始化GPIO、定时器等 // 配置输入捕获 TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter = 0x0; TIM_ICInit(TIM2, &TIM_ICInitStructure); // 使能输入捕获中断 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 启动定时器 TIM_Cmd(TIM2, ENABLE); while (1) { // 主程序 // ... } } ``` 这个例子中,我们使用了TIM2的通道1来进行输入捕获,捕获到上升沿和下降沿时,会触发中断,并在中断服务函数中读取计数器的值,计算脉冲的宽度和频率。注意,在第一次捕获到上升沿时,需要将捕获模式设置为下降沿触发,以便捕获到完整的脉冲。在第二次捕获到下降沿时,需要将捕获模式设置为上升沿触发,以便下一次捕获。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值