测量PWM周期原理
假设对计数器时钟72分频(为了方便计算),则计数器频率为1MHz,计数器累加一次的事件为1us
设置定时器自动重装载寄存器的值为65535。把这个值设置为最大,尽量避免溢出
假设测量的信号周期小于65535us,即频率大于16Hz
两种情况
假设测量信号周期小于65535us,即频率大于16Hz
在一个周期内,计数器不会溢出
当第一个上升沿到来时重置计数器的值(让计数器从0开始计数)
当第二个上升沿到来时,计数器的值会自动copy到捕获寄存器,读出捕获寄存器的值,这个值就表示信号周期。单位us

如果测量频率超过1MHz(超过计数器的时钟频率)
第二个上升沿到了之后,计数器还没有完成一次累加,则无法测量
可以考虑测试第一个上升沿和第n个上升沿的间隔。这种一般用来测量高频信号,频率超过了计数器的时钟频率

代码
基本步骤
输入通道滤波器的设置
输入通道信号边缘检测和捕获使能
捕获比较中断
寄存器实现
Driver_TIM4.h
#ifndef __DRIVER_TIM4_H
#define __DRIVER_TIM4_H
#include "stm32f10x.h"
void TIM4_Init(void);
void TIM4_Start(void);
double TIM4_GetPWMCycle(void);
double TIM4_GetPWMFreq(void);
#endifDriver_TIM4.c
#include "tim4.h"
void TIM4_Init(void)
{
// 1. 开启时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;// 2. GPIO工作模式,PB6:浮空输入 CNF-01,MODE-00
GPIOB->CRL &= ~GPIO_CRL_MODE6;
GPIOB->CRL &= ~GPIO_CRL_CNF6_1;
GPIOB->CRL |= GPIO_CRL_CNF6_0;
// 定时器配置
// 3. 时基部分
// 3.1 预分频值 71,得到1MHz
TIM4->PSC = 71;
// 3.2 重装载值,65535,尽量在信号一个周期内不要产生溢出
TIM4->ARR = 65535;
// 3.3 计数方向
TIM4->CR1 &= ~TIM_CR1_DIR;
// 4. 输入通道部分
// 4.1 TI1的输入选择
TIM4->CR2 &= ~TIM_CR2_TI1S;
// 4.2 输入滤波器
TIM4->CCMR1 &= ~TIM_CCMR1_IC1F;
// 4.3 配置极性:上升沿触发
TIM4->CCER &= ~TIM_CCER_CC1P;
// 4.4 选择通道1的输入映射为 TI1:CC1S - 01
TIM4->CCMR1 &= ~TIM_CCMR1_CC1S_1;
TIM4->CCMR1 |= TIM_CCMR1_CC1S_0;
// 4.5 预分频器
TIM4->CCMR1 &= ~TIM_CCMR1_IC1PSC;
// 4.6 通道1输入捕获使能
TIM4->CCER |= TIM_CCER_CC1E;
// 4.7 开启捕获中断使能
TIM4->DIER |= TIM_DIER_CC1IE;
// 5. NVIC配置
NVIC_SetPriorityGrouping(3);
NVIC_SetPriority(TIM4_IRQn, 3);
NVIC_EnableIRQ(TIM4_IRQn);
}
// 将定时器的开关包装成函数
void TIM4_Start(void)
{
TIM4->CR1 |= TIM_CR1_CEN;
}
void TIM4_Stop(void)
{
TIM4->CR1 &= ~TIM_CR1_CEN;
}
void TIM4_IRQHandler(void)
{
if (TIM4->SR & TIM_SR_CC1IF)
{
// 清除中断标志位
TIM4->SR &= ~TIM_SR_CC1IF;
// 直接清零计数器
TIM4->CNT = 0;
}
}
// cycle单位为us,返回周期单位为ms
double TIM4_GetPWMCycle(void)
{
return TIM4->CCR1 / 1000.0;
}
double TIM4_GetPWMFreq(void)
{
return 1000000.0 / TIM4->CCR1;
}main.c
#include "usart.h"
#include "tim5.h"
#include "tim4.h"
#include "delay.h"
int main(void)
{
// 初始化
USART_Init();
TIM4_Init();
TIM5_Init();printf("Hello, world!\n");
// 开启定时器
TIM5_Start();
TIM4_Start();
while (1)
{
printf("T = %.2f ms, f = %.2f Hz\n", TIM4_GetPWMCycle(), TIM4_GetPWMFreq());
Delay_ms(1000);
}
}
HAL库实现
基本设置



在tim.c添加代码
/* USER CODE BEGIN 1 */
/* 记录上升沿的个数 */
uint8_t raiseEdgeCount = 0;
/* 存储信号的周期 */
uint16_t t = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM4)
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
/* 上升沿个数 */
raiseEdgeCount++;
/* 如果是第1个上升沿,则清零计数器,让计数器从0开始计数 */
if (raiseEdgeCount == 1)
{
//TIM4->CNT = 0; /* 计数器清零 */
__HAL_TIM_SetCounter(htim, 0);
}
else if (raiseEdgeCount == 2)
{
/* 读取捕获寄存器的值,就是周期 单位是us*/
//t = TIM4->CCR1;
t = __HAL_TIM_GetCompare(htim, TIM_CHANNEL_1);
/* 上升沿的计数从0重新计数 */
raiseEdgeCount = 0;
}
}
}
}
/* 返回PWM的周期 ms*/
double Driver_TIM4_GetPWMCycle(void)
{
return t / 1000.0;
}
/* 返回PWM的频率 */
double Driver_TIM4_GetPWMFreq(void)
{
return 1000000 / t;
}
/* USER CODE END 1 */在main.c添加代码
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM4_Init();
MX_TIM5_Init();
MX_USART1_UART_Init();
HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_2); // 让tim5产生pwm
HAL_TIM_IC_Start_IT(&htim4, TIM_CHANNEL_1);;
double t, f;
while (1)
{
t = Driver_TIM4_GetPWMCycle();
f = Driver_TIM4_GetPWMFreq();
printf("t=%.4fms, f=%.4fHz\r\n", t, f);
HAL_Delay(1000);
}
}