stm32 PWM输入捕获
2022-04-05 15:10 jym蒟蒻 阅读(848) 评论(0) 编辑 收藏 举报普通的输入捕获,可使用定时器的四个通道,一路捕获占用一个捕获寄存器.
PWM输入,只能使用两个通道,通道1和通道2。
一路PWM输入占用两个捕获寄存器,一个捕获周期,一个捕获占空比。
这里,用通用定时器产生一路PWM信号,用高级定时器的通道1或通道2捕获。
通用定时器TIM3的通道1,PA6,用于输出PWM信号。
高级控制定时器TIM1的通道1,PA8,用于PWM输入捕获。
bsp_ AdvanceTim.c文件,高级定时器PWM输入捕获驱动程序。
bsp_ GeneralTim.c文件,通用定时器PWM信号输出驱动程序。
通用定时器产生PWM配置
高级定时器PWM输入配置
中断服务程序,计算测量的频率和占空比。
关键,PWM信号输出,PWM信号输入捕获。
通用定时器宏定义:
PWM 输出,就是对外输出脉宽(即占空比)可调的方波信号,信号频率由自动重装寄存器ARR的值决定,占空比由比较寄存器CCR的值决定。可看之前写的高级定时器基础知识。
可算出PWM信号的频率F:72M/( 10*72 )=100KHZ。
PWM 信号的周期 T = (ARR+1) * (1/CLK_cnt) = (ARR+1) * (PSC+1) / 72M
注释:
定时器时钟经过PSC预分频器后,即CK_CNT,用来驱动计数器计数。
PSC是16位的预分频器,可以对定时器时钟TIMxCLK进行1~65536之间的任何一个数进行分频。CK_CNT=TIMxCLK/(PSC+1)。
定时器时钟TIMxCLK,即内部时钟CK_INT,经APB1预分频器分频提供。
APB1预分频系数等于1,频率不变。
库函数中APB1预分频的系数是2,即PCLK1=36M,所以定时器时钟TIMxCLK=36*2=72M。
计一个数的时间乘上一个波形的计数次数(ARR+1),就是整个波形的所需时间。
时间取倒数就是PWM信号的频率。
以CNT工作在递增模式为例,上图,ARR=8,CCR=4,CNT从0开始计数.
当CNT<CCR,OCxREF为有效的高电平,同时,比较中断寄存器CCxIF置位。
CCR<=CNT<=ARR,OCxREF为无效的低电平。CNT又从0开始计数,并生成计数器上溢事件,循环往复。
占空比:GENERAL_TIM_CCR1/(GENERAL_TIM_PERIOD+1)= 50%
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_PERIOD;
#define GENERAL_TIM_PERIOD 0XFFFF
/************通用定时器TIM参数定义,只限TIM2、3、4、5************/
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意
// 这里默认使用TIM3
#define GENERAL_TIM TIM3
#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIM_CLK RCC_APB1Periph_TIM3
// 输出PWM的频率为 72M/{ (ARR+1)*(PSC+1) }
#define GENERAL_TIM_PERIOD (10-1)
#define GENERAL_TIM_PSC (72-1)
#define GENERAL_TIM_CCR1 5
#define GENERAL_TIM_CCR2 4
#define GENERAL_TIM_CCR3 3
#define GENERAL_TIM_CCR4 2
// TIM3 输出比较通道1
#define GENERAL_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define GENERAL_TIM_CH1_PORT GPIOA
#define GENERAL_TIM_CH1_PIN GPIO_Pin_6
通用定时器引脚初始化,初始化了通用定时器PWM输出用到的GPIO,使用不同的GPIO时,只需修改头文件里的宏定义。
/**
* @brief 通用定时器PWM输出用到的GPIO初始化
* @param 无
* @retval 无
*/
static void GENERAL_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 输出比较通道1 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
// 输出比较通道2 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH2_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH2_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH2_PORT, &GPIO_InitStructure);
// 输出比较通道3 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH3_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH3_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH3_PORT, &GPIO_InitStructure);
// 输出比较通道4 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH4_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH3_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH3_PORT, &GPIO_InitStructure);
}
通用定时器PWM输出初始化。
GENERAL_TIM_Mode_Config()函数中初始化了两个结构体,时基结构体和输出比较结构体。
若修改PWM的周期和占空比,需修改头文件里面的GENERAL_TIM_PERIOD、GENERAL_TIM_PSC和GENERAL_TIM_CCR1这三个宏。
PWM信号的频率的计算公式为:F=TIM_CLK/{(ARR+1)*(PSC+1)},TIM_CLK=72MHZ,ARR是自动重装载寄存器的值,对应GENERAL_TIM_PERIOD,PSC是计数器时钟的分频因子,对应GENERAL_TIM_PSC。
/**
* @brief 通用定时器PWM输出初始化
* @param 无
* @retval 无
* @note
*/
static void GENERAL_TIM_Mode_Config(void)
{
// 开启定时器时钟,即内部时钟CK_INT=72M
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
// 配置周期,这里配置为100K
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_PERIOD;
// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_PSC;
// 时钟分频因子 ,配置死区时间时需要用到
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
/*--------------------输出比较结构体初始化-------------------*/
TIM_OCInitTypeDef TIM_OCInitStructure;
// 配置为PWM模式1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
// 输出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
// 输出通道电平极性配置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
// 输出比较通道 1
TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR1;
TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 输出比较通道 2
TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR2;
TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 输出比较通道 3
TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR3;
TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 输出比较通道 4
TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR4;
TIM_OC4Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 使能计数器
TIM_Cmd(GENERAL_TIM, ENABLE);
}
调用函数GENERAL_TIM_Init()后,相应引脚就输出PWM信号。
/**
* @brief 通用定时器PWM输出用到的GPIO和PWM模式初始化
* @param 无
* @retval 无
*/
void GENERAL_TIM_Init(void)
{
GENERAL_TIM_GPIO_Config();
GENERAL_TIM_Mode_Config();
}
输入到高级定时器捕获引脚的PWM信号,来自通用定时器的输出。
宏定义里面,可以算出计数器的计数周期为T=72M/(1000*72)=1MS。
这是定时器在不溢出的情况下的最大计数周期,也就是说,周期小于1ms的PWM信号都可以被捕获到。转换成频率就是能捕获到的最小的频率为1KHZ。
要根据捕获的PWM信号,调节ADVANCE_TIM_PERIOD和ADVANCE_TIM_PSC这两个宏。
#ifndef __BSP_ADVANCETIME_H
#define __BSP_ADVANCETIME_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) }
#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_ADVANCETIME_H */
高级定时器PWM输入模式:
ADVANCE_TIM_Mode_Config()函数中初始化了两个结构体。
TIM_TimeBaseInitTypeDef,时基结构体,用于定时器基础参数设置。
TIM_OCInitTypeDef,输出比较结构体,用于输出比较模式。
PWM输入模式,只能使用通道1和通道2。
使用通道1,即TI1,输入的PWM信号被分成两路,分别是TI1FP1和TI1FP2,两路都可以是触发信号。
选择TI1FP1为触发信号,IC1捕获到的是PWM信号的周期,IC2捕获到的是占空比。
这种输入通道TI和捕获通道IC的映射关系叫直连,输入捕获结构体TIM_ICSelection配置为TIM_ICSelection_DirectTI。
选择TI1FP2为触发信号,则IC2捕获到的是周期,IC1捕获到的是占空比。
这种输入通道TI和捕获通道IC的映射关系叫非直连,输入捕获结构体的TIM_ICSelection要配置为TIM_ICSelection_IndirectTI。
输入通道TI和捕获通道IC的具体映射关系有直连和非直连两种。
/**
* @brief 高级定时器PWM输入初始化和用到的GPIO初始化
* @param 无
* @retval 无
*/
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);
}
高级定时器中断优先级:只有一个中断源,优先级可以随便配置
// 中断相关宏定义
#define ADVANCE_TIM_IRQ TIM1_CC_IRQn
#define ADVANCE_TIM_IRQHandler TIM1_CC_IRQHandler
/**
* @brief 高级控制定时器 TIMx,x[1,8]中断优先级配置
* @param 无
* @retval 无
*/
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);
}
高级定时器中断服务函数:
捕获到PWM信号的第一个上升沿时,产生中断,计数器被复位,锁存到捕获寄存器IC1和IC2的值都为0。
下降沿到来时,IC2会捕获,对应的是占空比,会产生中断。
当捕获到第二个上升沿时,IC1会捕获,对应的是周期,再次进入中断。
可以根据IC1和IC2的值计算出频率和占空比。
中断复位函数中,获取输入捕获寄存器CCR1和CCR2寄存器中的值。
CCR1的值不为0,说明有效捕获到了一个周期,然后计算出频率和占空比。
计算时,CCR1和CCR2的值都必须加1,因为计数器从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;
}
}
main
通用定时器初始化完之后,输出PWM信号,高级定时器初始化完之后,捕获通用定时器输出的PWM信号。
通用定时器TIM3的通道1,PA6,用于输出PWM信号。
高级控制定时器TIM1的通道1,PA8,用于PWM输入捕获。
用杜邦线短接PA6和PA8,用USB线连电脑,打开串口调试助手,可以看到捕获到的PWM信号的频率和占空比。
同时,可以通过仿真模拟的方法看到PA6,输出的信号波形。
// TIM—高级定时器-PWM输入捕获应用,通用定时器产生PWM波,高级定时器则捕获这个PWM,并测量周期和占空比
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_AdvanceTim.h"
#include "bsp_GeneralTim.h"
#include "bsp_usart.h"
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
/* 串口初始化 */
USART_Config();
/* 通用定时器初始化,用于生成PWM信号 */
GENERAL_TIM_Init();
/* 高级定时器初始化 ,用户捕获PWM信号*/
ADVANCE_TIM_Init();
while(1)
{
}
}