1. 输入捕获简介
- IC (Input Capture) 输入捕获输入
- 捕获模式下,当通道输入引脚出现指定
电平跳变
时,当前CNT
的值将被锁存到CCR
中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数 - 每个高级定时器和通用定时器都拥有4个输入捕获通道
- 可配置为PWMI模式,同时测量频率和占空比可配合主从触发模式,实现硬件全自动测量
(STM32C8T6)
当前CNT
的值将被锁存到CCR
中的意思是:把当前CNT的值读出来,写入到CCR中去。
(定时器的结构图)
2. 频率测量
频率降低的方波波形图(如上图),越往左边频率越高,越往右边频率越低。这里的型号都是只有高低电平的数字信号,STM32测量频率时,只能测量数字信号
。
如果要测量正弦波信号,还需要搭建一个信号预处理电路。最简单的就是用运放搭建一个比较器。把正弦波转换为数字信号,再输入给STM32。
如果测量的信号电压非常高,电路还需要考虑隔离问题,隔离高压端和低压端,保证电路安全。比如隔离放大器、电压互感器等元件。
测频法
直接按照频率定义来进行测量的方法,就叫测频法。
例如想要测量下图黑色线段标记的频率,首先自定一个闸门时间T(通常设置为1S),在1S时间内对信号上升沿计次,从0开始计次,每来一个上升沿(周期),计次+1。
每来一个上升沿其实就是来了一个周期信号。所以在1S时间内,来了多少个周期
,那么它的频率就是多少Hz。
(频率的定义:1S内出现了多少个重复周期)
测周法
测周法的基本原理就是,周期的倒数就是频率。
捕获信号的两个上升沿,然后测量这两个上升沿之间持续的时间(如下图)。
但实际上我们并没有一个精确到无穷大的秒表来测量时间。测量时间的方法,实际上也是定时器计数。我们使用一个已知的标准频率fc的计次时钟,来驱动计数器。从一个上升沿开始计数,计数器从0开始,一直计到下一个上升沿停止。 计一个数的时间是1/fc,计N个数,时间就是N/fc
。
N/fc就是周期,再取个倒数,就得到了公式,fk=fe/N。
问:测频法和测周法都是两种重要的测量频率的方法。那么这两种方法有什么区别?实际情况使用哪种方法更好?
答:
1.测频法适合测量高频信号,测周法适合测量低频信号。
2. 测频法测量结果更新慢一些,数值相对稳定;测周法更新快,数据跳变也非常快。
高频使用测频法,低频使用测周法。那么多高算高,多低算低呢?
这就涉及到中界频率的概念了。
中界频率
中界频率时测频法和测周法误差相等的频率点。
测频法计次和测周法计次,这个计次数量N越大越好
,相对误差越小。
在这些方法中,计次可能存在正负1误差
。
3. 输入捕获通道
4. 主从触发模式
(注意:主从触发模式,这个名字是作者自己取的,手册中并没有这个名字。)
5. 输入捕获基本结构
输入捕获初始化步骤:
第一步:RCC开启时钟,GPIO和TIM的时钟打开;
第二步:GPIO初始化,GPIO配置成输入模式;
第三步:配置时基单元,让CNT计数器在内部时钟的驱动下自增运行;
第四步:配置输入捕获单元,滤波器、极性、直连通or交叉连通、分频器等等参数配置;
第五步:选择从模式的触发源,触发源选择为TI1FP1 ;
第六步:选择触发之后执行的操作,执行Reset操作;
最后,调用TIM_Cmd 函数,开启定时器。这样所有的电路就能配合起来,按照我们的要求工作了。
// 配置TIM1 CH2
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; // PD2 通道2
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 = 0xF; // 滤波器
TIM_ICInit(TIM1,&TIM_ICInitStructure); // 单通道
// TIM_PWMIConfig(TIM1, &TIM_ICInitStructure); // 配置两个通道同时捕获
TIM_PWMIConfig(TIM1, &TIM_ICInitStructure);
// 使用这个函数, 自动将另外一个通道参数配置成相反的配置。
高电平的计数值存在 CCR1, 整个周期的计数值存在CCR2里面。
因此,占空比:
DutyCycle = (IC1Value * 100) / IC2Value;
// 乘以100表示,将范围扩大到0~100
uint16_t IC2Value = 0; // 计数值
uint16_t IC1Value = 0; // 计数值
uint16_t DutyCycle = 0;// 占空比
uint32_t Frequency = 0;// 频率
/* USER CODE BEGIN 1 */
void TIM1_CC_IRQHandler(void)
{
// 清除捕获比较中断标志位
TIM_ClearITPendingBit(TIM1, TIM_IT_CC2);
/* 获取输入捕获值 */
IC2Value = TIM_GetCapture2(TIM1); // TIM1 的 通道2 PD2
if (IC2Value != 0)
{
/* 占空比计算 */
// TIM1 的 通道1 PD1
IC1Value = TIM_GetCapture1(TIM1);
DutyCycle = (IC1Value * 100) / IC2Value; // 乘以100表示,将范围扩大到0~100
/* 频率计算 */
Frequency = SystemCoreClock / IC2Value;
}
else
{
DutyCycle = 0;
Frequency = 0;
}
}
资料下载
工程下载:TIM-高级定时器-PWM输入捕获(20230827)
参考资料
- [1] 【B站@江协科技】STM32入门教程-2023持续更新中 [6-5] TIM输入捕获
- [2] STM32入门笔记(02):定时器之定时器中断、输入捕获和PWM输出(SPL库函数版)
输入捕获
PWM信号 周期和占空比的计算
/* ---------------- PWM信号 周期和占空比的计算--------------- */
// ARR :自动重装载寄存器的值
// CLK_cnt:计数器的时钟,等于 Fck_int / (psc+1) = 72M/(psc+1)
// PWM 信号的周期 T = (ARR+1) * (1/CLK_cnt) = (ARR+1)*(PSC+1) / 72M
// 占空比P=CCR/(ARR+1)
航顺HK32F030M 输入捕获
高级定时器TIM1 的 CH1 和 CH2 通道输入捕获N20编码电机(6V300rpm)脉冲数,占空比、频率计算。
hk32f030m_it.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file hk32f030m_it.c
* @brief Interrupt Service Routines.
******************************************************************************
*/
#include "hk32f030m.h"
/* USER CODE END Header */
/******************************************************************************/
/* Cortex-M0 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
/* USER CODE END NonMaskableInt_IRQn 1 */
}
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
/**
* @brief This function handles System service call via SWI instruction.
*/
void SVC_Handler(void)
{
/* USER CODE BEGIN SVC_IRQn 0 */
/* USER CODE END SVC_IRQn 0 */
/* USER CODE BEGIN SVC_IRQn 1 */
/* USER CODE END SVC_IRQn 1 */
}
/**
* @brief This function handles Pendable request for system service.
*/
void PendSV_Handler(void)
{
/* USER CODE BEGIN PendSV_IRQn 0 */
/* USER CODE END PendSV_IRQn 0 */
/* USER CODE BEGIN PendSV_IRQn 1 */
/* USER CODE END PendSV_IRQn 1 */
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
uint16_t IC2Value = 0; // 计数值
uint16_t IC1Value = 0; // 计数值
uint16_t DutyCycle = 0;// 占空比
uint32_t Frequency = 0;// 频率
/* USER CODE BEGIN 1 */
void TIM1_CC_IRQHandler(void)
{
// 清除捕获比较中断标志位
TIM_ClearITPendingBit(TIM1, TIM_IT_CC2);
/* 获取输入捕获值 */
IC2Value = TIM_GetCapture2(TIM1); // TIM1 的 通道2 PD2
if (IC2Value != 0)
{
/* 占空比计算 */
// TIM1 的 通道1 PD1
IC1Value = TIM_GetCapture1(TIM1);
DutyCycle = (IC1Value * 100) / IC2Value; // 乘以100表示,将范围扩大到0~100
/* 频率计算 */
Frequency = SystemCoreClock / IC2Value;
}
else
{
DutyCycle = 0;
Frequency = 0;
}
}
//uint16_t ReadValue1 = 0, ReadValue2 = 0;
//uint16_t CaptureNumber = 0; // 捕获成功/失败 标识位
//uint32_t Capture = 0; // 捕获
//uint32_t TIM1Freq = 0;//频率
定时器1 中断你服务函数
//void TIM1_CC_IRQHandler(void)
//{
// if(TIM_GetITStatus(TIM1, TIM_IT_CC2) == SET)
// {
// // Clear TIM1 Capture compare interrupt pending bit
// // 清除捕获比较中断标志位
// TIM_ClearITPendingBit(TIM1, TIM_IT_CC2);
//
// if(CaptureNumber == 0)
// {
// /* Get the Input Capture value */
// ReadValue1 = TIM_GetCapture2(TIM1);
// CaptureNumber = 1;
// }
// else if(CaptureNumber == 1)
// {
// /* Get the Input Capture value */
// ReadValue2 = TIM_GetCapture2(TIM1);
//
// /* Capture computation */
// if (ReadValue2 > ReadValue1)
// {
// Capture = (ReadValue2 - ReadValue1);
// }
// else if (ReadValue2 < ReadValue1)
// {
// Capture = ((0xFFFF - ReadValue1) + ReadValue2);
// }
// else
// {
// Capture = 0;
// }
// /* Frequency computation */
// TIM1Freq = (uint32_t) SystemCoreClock / Capture;//频率计算
// CaptureNumber = 0;
// }
// }
//}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT HKMicroChip *****END OF FILE****/
main.c
/**
******************************************************************************
* @file main.c
* @author Alexander
* @version V1.0
* @date 2022-xx-xx
* @brief 高级定时器-PWM输入捕获
******************************************************************************
* @attention
*
* 实验平台:HK32F030M开发板
* 论坛 :https://bbs.21ic.com/iclist-1010-1.html
*
******************************************************************************
*/
#include "hk32f030m.h"
#include "bsp_led.h"
#include "bsp_AdvanceTim.h"
volatile uint32_t time = 0; // ms 计时变量
#define SOFT_DELAY Delay(0x0FFFFF);
void Delay(__IO uint32_t nCount);
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
SOFT_DELAY // 由于BKIN复用在PB5引脚,而PB上电默认功能为SWD的SWCLK功能,为了防止下次烧录无法烧录,特此在这里加入一段延时
/* 高级定时器初始化 */
ADVANCE_TIM_Init();
while (1)
{
}
}
void Delay(__IO uint32_t nCount) //简单的延时函数
{
for(; nCount != 0; nCount--);
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(char* file , uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif /* USE_FULL_ASSERT */
bsp_AdvanceTim.c
#include "bsp_AdvanceTim.h"
static void ADVANCE_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 输出比较通道GPIO初始化
RCC_AHBPeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);
// 配置GPIO的复用功能
GPIO_PinAFConfig(ADVANCE_TIM_CH2_PORT, ADVANCE_TIM_CH2_PinSource, ADVANCE_TIM_CH2_GPIO_AF);
GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH2_PIN; // PD2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Schmit = GPIO_Schmit_Disable;
// 初始化IO配置
GPIO_Init(ADVANCE_TIM_CH2_PORT, &GPIO_InitStructure);
}
/******************************************************************************/
/* hk32f030m Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_hk32f030m.s). */
/******************************************************************************/
static void ADVANCE_TIM_Mode_Config(void)
{
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 开启定时器时钟,即内部时钟CK_INT=32M
ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE);
// 配置TIM1 CH2
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; // PD2
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 = 0xF; // 滤波器
// TIM_ICInit(TIM1,&TIM_ICInitStructure);
TIM_PWMIConfig(TIM1, &TIM_ICInitStructure); // 使用这个函数,配置两个通道同时捕获同一个引脚
// 使能TIM1捕获比较中断
NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 0; //中断优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 选择TIM1触发源输入触发:TI2FP2
TIM_SelectInputTrigger(TIM1,TIM_TS_TI2FP2);//
// 配置从模式复位计数器
TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset);
// 使能主从模式
// TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);
// 使能TIM1
TIM_Cmd(TIM1, ENABLE);
// 使能TIM1捕获比较2中断
TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE);
}
void ADVANCE_TIM_Init(void)
{
ADVANCE_TIM_GPIO_Config();
ADVANCE_TIM_Mode_Config();
}
/*********************************************END OF FILE**********************/
bsp_AdvanceTim.h
#ifndef __BSP_ADVANCETIME_H
#define __BSP_ADVANCETIME_H
#include "hk32f030m.h"
/************高级定时器TIM参数定义,只限TIM1和TIM8************/
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意
// 这里我们使用高级控制定时器TIM1
#define ADVANCE_TIM TIM1
#define ADVANCE_TIM_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADVANCE_TIM_CLK RCC_APB2Periph_TIM1
// PWM 信号的频率 F = TIM_CLK/{(ARR+1)*(PSC+1)}
#define ADVANCE_TIM_PERIOD (8-1)
#define ADVANCE_TIM_PSC (32-1)
#define ADVANCE_TIM_PULSE 4
#define ADVANCE_TIM_IRQ TIM1_UP_TRG_COM_IRQn
#define ADVANCE_TIM_IRQHandler TIM1_UP_TRG_COM_IRQHandler
// TIM1 CH1 输入捕获通道 PD1
#define ADVANCE_TIM_CH1_GPIO_CLK RCC_AHBPeriph_GPIOD
#define ADVANCE_TIM_CH1_PORT GPIOD
#define ADVANCE_TIM_CH1_PIN GPIO_Pin_1
#define ADVANCE_TIM_CH1_PinSource GPIO_PinSource1
#define ADVANCE_TIM_CH1_GPIO_AF GPIO_AF_3
// TIM1 CH2 输入捕获通道 PD2
#define ADVANCE_TIM_CH2_GPIO_CLK RCC_AHBPeriph_GPIOD
#define ADVANCE_TIM_CH2_PORT GPIOD
#define ADVANCE_TIM_CH2_PIN GPIO_Pin_2
#define ADVANCE_TIM_CH2_PinSource GPIO_PinSource2
#define ADVANCE_TIM_CH2_GPIO_AF GPIO_AF_3
/**************************函数声明********************************/
void ADVANCE_TIM_Init(void);
#endif /* __BSP_ADVANCETIME_H */