本文是下文的续篇
通用定时器框图:
定时器的触发信号分两大类:
触发输入信号(TRGI,本定时器就处于主从模式中的从模式):从外部来,或者从自己的输入通过来到本定时器。
功能:控制定时器具体操作,例如:复位,使能,计数(呼吸灯和测量输入信号的周期和频率都是默认内部时钟控制计数,来一个上升沿就计数+1,溢出就触发溢出事件,本例子我们就是选用输入捕获信号来控制本定时器的复位操作)
触发输出信号(TRGO,主模式):用本定时器输出到其他定时器或其他外设的信号,用于与其他定时器的级联(触发定时器的一些工作)或触发一些其他外设工作
注意:触发输入信号 != 本定时器时钟源。在常规输入捕获配置中,TRGI仅作为触发源,定时器的时钟源仍由APB总线决定。若需使用外部信号作为时钟源,需额外配置外部时钟模式。
相关寄存器介绍
配从模式
100:上升沿和下降沿都会发生TI1F_ED,注意:边沿检测器只是决定捕获寄存器捕获上升沿还是
下降沿,不会滤波和整波
我们选用101模式为例子,实现通用定时器的触发输出和从模式测量输入信号的占空比:
TI1经过滤波器和边沿检测器给到T1FP1和T2FP2上,T2FP2映射到IC2上
IC1捕获高电平有效(CCER_CC1P),IC2捕获低电平有效(CCER_CC2P)
IC1捕获到第一次高电平时计数器清零后记录IC2捕获到第一次低电平的自动将计数器数值给到CCR2,即为高电平在一个周期的时间,当IC1捕获到第二次高电平时,自动计数器值给到CCR1,那么CCR1/CCR2就是占空比
配置通道:
配捕获选择:注意ETP和TIMx_ETP外部输入相关,我这是截多了
100:复位模式:输入信号给到TRGI,依赖输入信号的周期触发本定时器的更新事件
配置意义:捕获比较寄存器当捕获到上升沿还是下降沿时,将计数器的值赋值给自己(CCR)
寄存器代码实现:
和上篇代码搭配使用:
//注意要看功能框图找寄存器,根据业务要求写代码,不要一个一个寄存器找啊,看啊,效率太低
void Driver_TIM4_Init(void)
{
//和一致用通用定时器的输入捕获功能测量输入信号周期和频率(寄存器和HAL两种代码实现方式)中代码一致,只是多添加了通道2的配置,删除了中断,以及配置了时钟来源
//配置TIM4时钟来源:TRGI TS-101
TIM4->SMCR |= TIM4_SMCR_TS_0;
TIM4->SMCR &=~TIM4_SMCR_TS_1;
TIM4->SMCR |= TIM4_SMCR_TS_2;
//配置从模式为复位模式100
TIM4->SMCR &=~TIM4_SMCR_SMS_0;
TIM4->SMCR &=~TIM4_SMCR_SMS_1;
TIM4->SMCR |= TIM4_SMCR_SMS_2;
/* 4.4 通道1配置为输入,并把信号映射到IC2 CC1S=01*/
TIM4->CCMR1 &= ~TIM_CCMR1_CC2S_0;
TIM4->CCMR1 |= TIM_CCMR1_CC2S_1;
/* 4.3 通道2: 上升沿 CCER_CC1P=0(0:不反相:捕获发生在IC2的上升沿;当用作外部触发器时,IC2不反相) , 1下降沿捕获*/
TIM4->CCER |= TIM_CCER_CC1P;
/* 4.6 通道2输入捕获使能 CCER_CC1E=1 */
TIM4->CCER |= TIM_CCER_CC2E;
/* 4.7 不需要开启通道捕获中断 和不用配优先级了,因为在从模式下通过输入信号做本定时器的复位处理*/
}
double TIM4_GetPWMDutyCycle(void)
{
//占空比
return TIM4->CCR2/TIM4->CCR1;
}
#include <stdio.h>
#include "stm32f4xx_hal.h"
#define BUFFER_SIZE 64
void Send_Data(UART_HandleTypeDef *huart, float t, float f,double c) {
char buffer[BUFFER_SIZE];
int len = snprintf(buffer, sizeof(buffer), "t=%.4fms, f=%.4fHz , duty = %.2lf%% \r\n", t, f,c);
HAL_UART_Transmit(huart, (uint8_t*)buffer, len,0xff);
}
//这个处理函数放主函数里
Send_Data(&huart1, Driver_TIM4_GetPWMCycle(), Driver_TIM4_GetPWMFreq(),TIM4_GetPWMDutyCycle()*100);
HAL库代码实现含CubeMX配置:
TI1经过滤波器和边沿检测器给到T1FP1和T2FP2上,T2FP2映射到IC2上
IC1捕获高电平有效(CCER_CC1P),IC2捕获低电平有效(CCER_CC2P)
IC1捕获到第一次高电平时计数器清零后记录IC2捕获到第一次低电平的自动将计数器数值给到CCR2,即为高电平在一个周期的时间,当IC1捕获到第二次高电平时,自动计数器值给到CCR1,那么CCR1/CCR2就是占空比
按下面的选择配置: Input Capture indirect mode用T1映射到IC2
Slave Mode 从属模式的配置已经在上面定死了,所以下面 Slave Mode Controller只有一种选择
由于我们没有用到TRGO就默认值即可,TIM5的配置看用通用定时器的输入捕获功能测量输入信号周期和频率(寄存器和HAL两种代码实现方式)-CSDN博客
是一样的,注意TIM5不用配TRGI和TRGO
Polarity Selection:选择 IC1捕获高电平有效(CCER_CC1P),IC2捕获低电平有效(CCER_CC2P)
//在主函数中开启通道
//开启定时器5通道2
HAL_TIM_PWM_Start_IT(&hitm5,TIM_CHANNEL_1);
//开启定时器4通道1
HAL_TIM_IC_Start_IT(&hitm4,TIM_CHANNEL_1);
//开启定时器4通道2
HAL_TIM_IC_Start_IT(&hitm4,TIM_CHANNEL_2);
注意我们TIM4不再需要中断处理,因为:
/* 返回PWM的周期 ms*/
double Driver_TIM4_GetPWMCycle(void)
{
return __HAL_TIM_GetCompare(&htim4, TIM_CHANNEL_1) / 1000.0;
}
/* 返回PWM的频率 */
double Driver_TIM4_GetPWMFreq(void)
{
return 1000000 / __HAL_TIM_GetCompare(&htim4, TIM_CHANNEL_1);
}
double TIM4_GetPWMDutyCycle(void)
{
//占空比CCR2/CCR1
return __HAL_TIM_GetCompare(&htim4, TIM_CHANNEL_2)/__HAL_TIM_GetCompare(&htim4, TIM_CHANNEL_1);
}
#include <stdio.h>
#include "stm32f4xx_hal.h"
#define BUFFER_SIZE 64
void Send_Data(UART_HandleTypeDef *huart, float t, float f,double c) {
char buffer[BUFFER_SIZE];
int len = snprintf(buffer, sizeof(buffer), "t=%.4fms, f=%.4fHz , duty = %.2lf%% \r\n", t, f,c);
HAL_UART_Transmit(huart, (uint8_t*)buffer, len,0xff);
}
//这个处理函数放主函数里
Send_Data(&huart1, Driver_TIM4_GetPWMCycle(), Driver_TIM4_GetPWMFreq(),TIM4_GetPWMDutyCycle()*100);