实验的基本原理:
- 利用通用定时器产生4路占空比不同的PWM波形(频率100k,占空比0.5 0.4 0.3 0.2);
- 利用高级定时器的PWM输入模式来测量PWM参数;
有几个值得注意的地方:
- 捕获寄存器CCR1和CCR2的值在计算占空比和频率的时候必须加1;
- 计数周期不能设置的太小,如果我们设置的计数周期 < PWM周期,那么就无法捕获PWM脉冲,一般驱动电机的PWM是10k ~ 25kHz,当设置的周期为1ms,对应频率为1kHz,那么就可以捕获1kHz以上的PWM 信号;
下面是测量pwm的代码:
bsp_AdvanceTim.c
#include "bsp_AdvanceTim.h"
static void ADVANCE_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 设置中断组为0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = ADVANCE_TIM_IRQ;
// 设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 设置子优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
static void ADVANCE_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(ADVANCE_TIM_CH1_PORT, &GPIO_InitStructure);
}
static void ADVANCE_TIM_Mode_Config(void)
{
// 开启定时器时钟,即内部时钟CK_INT=72M
ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM_PERIOD;
// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM_PSC;
// 时钟分频因子 ,配置死区时间时需要用到
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);
/*--------------------输入捕获结构体初始化-------------------*/
// 使用PWM输入模式时,需要占用两个捕获寄存器,一个测周期,另外一个测占空比
TIM_ICInitTypeDef TIM_ICInitStructure;
// 捕获通道IC1配置
// 选择捕获通道
TIM_ICInitStructure.TIM_Channel = ADVANCE_TIM_IC1PWM_CHANNEL;
// 设置捕获的边沿
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
// 设置捕获通道的信号来自于哪个输入通道,有直连和非直连两种
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
// 1分频,即捕获信号的每个有效边沿都捕获
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
// 不滤波
TIM_ICInitStructure.TIM_ICFilter = 0x0;
// 初始化PWM输入模式
TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure);
// 当工作做PWM输入模式时,只需要设置触发信号的那一路即可(用于测量周期)
// 另外一路(用于测量占空比)会由硬件自带设置,不需要再配置
// 捕获通道IC2配置
// TIM_ICInitStructure.TIM_Channel = ADVANCE_TIM_IC1PWM_CHANNEL;
// TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
// TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;
// TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
// TIM_ICInitStructure.TIM_ICFilter = 0x0;
// TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure);
// 选择输入捕获的触发信号,触发的那一路信号对应的就是周期
TIM_SelectInputTrigger(ADVANCE_TIM, TIM_TS_TI1FP1);
// 选择从模式: 复位模式
// PWM输入模式时,从模式必须工作在复位模式,当捕获开始时,计数器CNT会被复位
TIM_SelectSlaveMode(ADVANCE_TIM, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(ADVANCE_TIM,TIM_MasterSlaveMode_Enable);
// 使能捕获中断,这个中断针对的是主捕获通道(测量周期那个)
TIM_ITConfig(ADVANCE_TIM, TIM_IT_CC1, ENABLE);
// 清除中断标志位
TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1);
// 使能高级控制定时器,计数器开始计数
TIM_Cmd(ADVANCE_TIM, ENABLE);
}
void ADVANCE_TIM_Init(void)
{
ADVANCE_TIM_NVIC_Config();
ADVANCE_TIM_GPIO_Config();
ADVANCE_TIM_Mode_Config();
}
/*********************************************END OF FILE**********************/
bsp_AdvanceTim.h
#ifndef __BSP_ADVANCETIM_H
#define __BSP_ADVANCETIM_H
#include "stm32f10x.h"
/************高级定时器TIM参数定义,只限TIM1和TIM8************/
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意
// 这里我们使用高级控制定时器TIM1
#define ADVANCE_TIM TIM1
#define ADVANCE_TIM_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADVANCE_TIM_CLK RCC_APB2Periph_TIM1
// 输入捕获能捕获到的最小的频率为 72M/{ (ARR+1)*(PSC+1) }
/* 注意,这里的计数周期不能设置的太小,如果我们设置的计数周期 < PWM周期
那么就无法捕获PWM脉冲,一般驱动电机的PWM是10k ~ 25kHz,我们设置的
周期为1ms,对应频率为1kHz,那么就可以捕获1kHz以上的PWM 信号*/
#define ADVANCE_TIM_PERIOD (1000-1)
#define ADVANCE_TIM_PSC (72-1)
// 中断相关宏定义
#define ADVANCE_TIM_IRQ TIM1_CC_IRQn
#define ADVANCE_TIM_IRQHandler TIM1_CC_IRQHandler
// TIM1 输入捕获通道1
#define ADVANCE_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADVANCE_TIM_CH1_PORT GPIOA
#define ADVANCE_TIM_CH1_PIN GPIO_Pin_8
#define ADVANCE_TIM_IC1PWM_CHANNEL TIM_Channel_1
#define ADVANCE_TIM_IC2PWM_CHANNEL TIM_Channel_2
/**************************函数声明********************************/
void ADVANCE_TIM_Init(void);
#endif /* __BSP_ADVANCETIM_H */
然后在中断服务函数中读取CCR1 和 CCR2寄存器的值,并计算 频率 和 占空比。
__IO uint16_t IC2Value = 0;
__IO uint16_t IC1Value = 0;
__IO float DutyCycle = 0;
__IO float Frequency = 0;
/*
* 如果是第一个上升沿中断,计数器会被复位,锁存到CCR1寄存器的值是0,CCR2寄存器的值也是0
* 无法计算频率和占空比。当第二次上升沿到来的时候,CCR1和CCR2捕获到的才是有效的值。其中
* CCR1对应的是周期,CCR2对应的是占空比。
*/
void ADVANCE_TIM_IRQHandler(void)
{
/* 清除中断标志位 */
TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1);
/* 获取输入捕获值 */
IC1Value = TIM_GetCapture1(ADVANCE_TIM);
IC2Value = TIM_GetCapture2(ADVANCE_TIM);
// 注意:捕获寄存器CCR1和CCR2的值在计算占空比和频率的时候必须加1
if (IC1Value != 0)
{
/* 占空比计算 */
DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value + 1);
/* 频率计算 */
Frequency = (72000000/(ADVANCE_TIM_PSC+1))/(float)(IC1Value + 1);
printf("占空比:%0.2f%% 频率:%0.2fHz\n",DutyCycle,Frequency);
}
else
{
DutyCycle = 0;
Frequency = 0;
}
}
实际测试效果如下: