-
2021-06-30 12:06:04
高级定时器-输入捕获应用(PWM输入捕获)
前面我们讲了用输入捕获测量了信号的脉宽,这一节我们讲输入捕获的一个特例—PWM输入。
普通的输入捕获可以使用定时器的四个通道,一路捕获占用一个捕获寄存器,而PWM输入则只能使用两个通道,即通道1和通道2,且一路PWM输入要占用两个捕获寄存器,一个用于捕获周期,一个用于捕获占空比。
在本实验中,我们用通用定时器产生的一路PWM信号,然后用高级定时器的通道1和通道2来捕获。
硬件设计
实验中用到两个引脚,一个是通用定时器TIM3的通道1,即PA6,用于输出PWM信号,另一个是高级定时器TIM1的通道1,即PA8,用来PWM输入捕获,实验中,用一根杜邦线短接PA6和PA8即可,同时可用示波器监控PA6的波形,看看捕获的数据是否正确。
软件设计
AdvancedTime.c文件
#include "./advancedtime/bsp_advancedtime.h" static void AdvancedTime_NVIC_Init(void) { NVIC_InitTypeDef NVIC_InitStructure = {0}; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = AdvancedTimeX_IRQn;//定时器1的捕获中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } static void AdvancedTime_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure = {0}; //开启GPIO的时钟 AdvancedTimeX_CH1_RCC_APB_CLKCMD(AdvancedTimeX_CH1_RCC_CLK_Periph,ENABLE); GPIO_InitStructure.GPIO_Pin = AdvancedTimeX_CH1_RCC_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(AdvancedTimeX_CH1_RCC_GPIO_PORT,&GPIO_InitStructure); } static void AdvancedTime_Mode_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure = {0}; TIM_ICInitTypeDef TIM_ICInitStructure = {0}; //开启定时器时钟 AdvancedTimeX_RCC_APB_CLKCMD(AdvancedTimeX_RCC_CLK_Periph,ENABLE); //时基配置 TIM_TimeBaseInitStructure.TIM_Period = AdvancedTimeX_Period; TIM_TimeBaseInitStructure.TIM_Prescaler = AdvancedTimeX_PSC; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(AdvancedTimeX,&TIM_TimeBaseInitStructure); //输入捕获配置 TIM_ICInitStructure.TIM_Channel = AdvancedTimeX_Channel; TIM_ICInitStructure.TIM_ICFilter = 0; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_PWMIConfig(AdvancedTimeX,&TIM_ICInitStructure); //配置输入触发信号 TIM_SelectInputTrigger(AdvancedTimeX,TIM_TS_TI1FP1); //选择从模式:复位模式.PWM输入模式时,从模式必须工作在复位模式,当捕获开始时,计数器CNT会被复位 TIM_SelectSlaveMode(AdvancedTimeX, TIM_SlaveMode_Reset); TIM_SelectMasterSlaveMode(AdvancedTimeX,TIM_MasterSlaveMode_Enable); //清除中断标志位 TIM_ClearITPendingBit(AdvancedTimeX,TIM_IT_CC1); //使能捕获中断,这个中断针对的是主捕获通道(测量周期这个) TIM_ITConfig(AdvancedTimeX,TIM_IT_CC1,ENABLE); //使能高级定时器,计数器开始计数 TIM_Cmd(AdvancedTimeX,ENABLE); } void AdvancedTime_Init(void) { AdvancedTime_NVIC_Init(); AdvancedTime_GPIO_Init(); AdvancedTime_Mode_Init(); }
AdvancedTime.h文件
#ifndef __BSP_ADVANCEDTIME_H #define __BSP_ADVANCEDTIME_H #include "stm32f10x.h" #define AdvancedTimeX TIM1 #define AdvancedTimeX_RCC_APB_CLKCMD RCC_APB2PeriphClockCmd #define AdvancedTimeX_RCC_CLK_Periph RCC_APB2Periph_TIM1 #define AdvancedTimeX_CH1_RCC_APB_CLKCMD RCC_APB2PeriphClockCmd #define AdvancedTimeX_CH1_RCC_CLK_Periph RCC_APB2Periph_GPIOA #define AdvancedTimeX_CH1_RCC_GPIO_PORT GPIOA #define AdvancedTimeX_CH1_RCC_GPIO_PIN GPIO_Pin_8 #define AdvancedTimeX_IRQn TIM1_CC_IRQn #define AdvancedTimeX_IRQHandler TIM1_CC_IRQHandler #define AdvancedTimeX_Period (1000-1) #define AdvancedTimeX_PSC (72-1) #define AdvancedTimeX_Channel TIM_Channel_1 extern void AdvancedTime_Init(void); #endif
GeneralTime.c文件
#include "./generaltime/bsp_generaltime.h" //定时器的GPIO配置 static void GeneralTime_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure = {0}; //开启时钟 GeneralTimX_CH1_RCC_CLKCMD(GeneralTimeX_CH1_RCC_CLK_Periph,ENABLE); GeneralTimX_CH2_RCC_CLKCMD(GeneralTimeX_CH2_RCC_CLK_Periph,ENABLE); GeneralTimX_CH3_RCC_CLKCMD(GeneralTimeX_CH3_RCC_CLK_Periph,ENABLE); GeneralTimX_CH4_RCC_CLKCMD(GeneralTimeX_CH4_RCC_CLK_Periph,ENABLE); //GPIO配置 GPIO_InitStructure.GPIO_Pin = GeneralTimX_CH1_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GeneralTimX_CH1_GPIO_PORT,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GeneralTimX_CH2_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GeneralTimX_CH2_GPIO_PORT,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GeneralTimX_CH3_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GeneralTimX_CH3_GPIO_PORT,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GeneralTimX_CH4_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GeneralTimX_CH4_GPIO_PORT,&GPIO_InitStructure); } //定时器的Mode配置 static void GeneralTime_Mode_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure = {0}; TIM_OCInitTypeDef TIM_OCInitStructure = {0}; //开启定时器时钟 GeneralTimeX_RCC_CLKCMD(GeneralTimeX_RCC_CLK_Periph,ENABLE); //时基配置 TIM_TimeBaseInitStructure.TIM_Period = GeneralTimeX_Period; TIM_TimeBaseInitStructure.TIM_Prescaler = GeneralTimeX_PSC; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(GeneralTimeX,&TIM_TimeBaseInitStructure); //输出比较配置 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_Pulse = GeneralTimeX_CCR1; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OC1Init(GeneralTimeX,&TIM_OCInitStructure); TIM_OC1PreloadConfig(GeneralTimeX,TIM_OCPreload_Enable); //输入捕获通道2 TIM_OCInitStructure.TIM_Pulse = GeneralTimeX_CCR2; TIM_OC2Init(GeneralTimeX,&TIM_OCInitStructure); TIM_OC2PreloadConfig(GeneralTimeX,TIM_OCPreload_Enable); //输入捕获通道3 TIM_OCInitStructure.TIM_Pulse = GeneralTimeX_CCR3; TIM_OC2Init(GeneralTimeX,&TIM_OCInitStructure); TIM_OC3PreloadConfig(GeneralTimeX,TIM_OCPreload_Enable); //输入捕获通道4 TIM_OCInitStructure.TIM_Pulse = GeneralTimeX_CCR4; TIM_OC2Init(GeneralTimeX,&TIM_OCInitStructure); TIM_OC4PreloadConfig(GeneralTimeX,TIM_OCPreload_Enable); //使能定时器 TIM_Cmd(GeneralTimeX,ENABLE); } void GeneralTime_Init(void) { GeneralTime_GPIO_Init(); GeneralTime_Mode_Init(); }
GeneralTime.h文件
#ifndef __BSP_GENERALTIME_H #define __BSP_GENERALTIME_H #include "stm32f10x.h" #define GeneralTimeX TIM3 #define GeneralTimeX_RCC_CLKCMD RCC_APB1PeriphClockCmd #define GeneralTimeX_RCC_CLK_Periph RCC_APB1Periph_TIM3 #define GeneralTimeX_Period (10-1) #define GeneralTimeX_PSC (72-1) #define GeneralTimeX_CCR1 5 #define GeneralTimeX_CCR2 4 #define GeneralTimeX_CCR3 3 #define GeneralTimeX_CCR4 2 #define GeneralTimX_CH1_RCC_CLKCMD RCC_APB2PeriphClockCmd #define GeneralTimeX_CH1_RCC_CLK_Periph RCC_APB2Periph_GPIOA #define GeneralTimX_CH1_GPIO_PORT GPIOA #define GeneralTimX_CH1_GPIO_PIN GPIO_Pin_6 #define GeneralTimX_CH2_RCC_CLKCMD RCC_APB2PeriphClockCmd #define GeneralTimeX_CH2_RCC_CLK_Periph RCC_APB2Periph_GPIOA #define GeneralTimX_CH2_GPIO_PORT GPIOA #define GeneralTimX_CH2_GPIO_PIN GPIO_Pin_7 #define GeneralTimX_CH3_RCC_CLKCMD RCC_APB2PeriphClockCmd #define GeneralTimeX_CH3_RCC_CLK_Periph RCC_APB2Periph_GPIOB #define GeneralTimX_CH3_GPIO_PORT GPIOB #define GeneralTimX_CH3_GPIO_PIN GPIO_Pin_0 #define GeneralTimX_CH4_RCC_CLKCMD RCC_APB2PeriphClockCmd #define GeneralTimeX_CH4_RCC_CLK_Periph RCC_APB2Periph_GPIOB #define GeneralTimX_CH4_GPIO_PORT GPIOB #define GeneralTimX_CH4_GPIO_PIN GPIO_Pin_1 extern void GeneralTime_Init(void); #endif
中断服务函数文件
__IO uint16_t IC2Value = 0; __IO uint16_t IC1Value = 0; __IO float DutyCycle = 0; __IO float Frequency = 0; void SysTick_Handler(void) { } void AdvancedTimeX_IRQHandler(void) { /* 清除中断标志位 */ TIM_ClearITPendingBit(AdvancedTimeX, TIM_IT_CC1); //获取输入捕获值 IC1Value = TIM_GetCapture1(AdvancedTimeX); IC2Value = TIM_GetCapture2(AdvancedTimeX); // 注意:捕获寄存器CCR1和CCR2的值在计算占空比和频率的时候必须加1 if (IC1Value != 0) { /* 占空比计算 */ DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1); /* 频率计算 */ Frequency = (72000000/(AdvancedTimeX_PSC+1))/(float)(IC1Value+1); printf("占空比:%0.2f%% 频率:%0.2fHz\n",DutyCycle,Frequency); } else { DutyCycle = 0; Frequency = 0; } }
main.c文件
#include "stm32f10x.h" #include "bsp_led.h" #include "./generaltime/bsp_generaltime.h" #include "./usart/bsp_usart.h" #include "./advancedtime/bsp_advancedtime.h" int main(void) { GeneralTime_Init(); USART_Config(); AdvancedTime_Init(); while (1) { } }
学有所成,以图强国!
更多相关内容 -
【STM32】HAL库-PWM输入捕获-复位模式demo
2022-05-17 16:42:03PA0为定时器输入捕获通道1(CH1),上升沿捕获,CH1连接IC1 根据实际需求PA0配置PA0的模式 输入模式 上/下拉电阻 IC2连接CH1,下降沿捕获 复位模式,选择TRGI触发源选择TI1PF1 PC13控制LED灯,用杜邦线连接PA0与PC13,... -
PWM输入捕获(测量频率及占空比).zip
2020-07-26 18:54:59利用STM32的PWM输入模式测量PWM波的占空比及频率,实现PWM波的准确捕获,在某些情况下,频繁的高低电平转换即为PWM波,此模式可用于转速的测量。 -
STM32F407定时器TIM4PWM输入捕获.zip
2021-08-18 10:52:00STM32F407定时器TIM4PWM输入捕获 -
STM32F103利用PWM输入捕获模式测量PWM波形频率和占空比
2019-09-09 14:09:34在STM32F103单片机上给PA6口输入PWM波,利用STM32单片机的PWM输入捕获模式测量输入PWM波的频率和站空比。 -
pwm输入捕获
2019-02-21 16:18:41pwm输入捕获 -
stm32PWM输入捕获模式详解
2020-07-21 07:42:36本文是关于stm32PWM输入捕获模式详解。 -
stm32 PWM输入捕获
2022-03-03 12:07:06普通的输入捕获,可使用定时器的四个通道,一路捕获占用一个...高级控制定时器TIM1的通道1,PA8,用于PWM输入捕获。 bsp_ AdvanceTim.c文件,高级定时器PWM输入捕获驱动程序。 bsp_ GeneralTim.c文件,通用定时器PWM信普通的输入捕获,可使用定时器的四个通道,一路捕获占用一个捕获寄存器.
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信号输出
通用定时器宏定义:
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输入捕获
输入到高级定时器捕获引脚的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) { } }
-
STM32定时器学习 hal库PWM输入捕获实验
2022-03-05 22:30:00PWM输入捕获实验原理
输入捕获实验目的是为了获得脉宽和频率,首先是脉宽的获得。
在实际捕获实验中,有两个通道,一个负责捕获上升沿,一个负责下降沿,
从上图可以看出,t1-t2 时间就是我们需要测量的高电平时间,假如定时器工作在向上计数模式,测量方法是:首先设置定时器通道 x 为上升沿捕获,这样在 t1 时刻,就会捕获到当前的 CNT 值,然后立即清零 CNT,并设置通道 x 为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。根据定时器的计数频率,我们就可以算出 t1-t2 的时间,从而得到高电平脉宽。我们来看代码部分。代码讲解
最关键的部分是输入捕获模式的初始化
static void TIM_PWMINPUT_Config(void) { TIM_IC_InitTypeDef TIM_ICInitStructure; TIM_SlaveConfigTypeDef TIM_SlaveConfigStructure; TIM_MasterConfigTypeDef TIM_MasterConfigStructure; // 开启TIMx_CLK,x[1,8] ADVANCE_TIM_CLK_ENABLE(); /* 定义定时器的句柄即确定定时器寄存器的基地址*/ TIM_PWMINPUT_Handle.Instance = ADVANCE_TIM; // 这里设置为周期最大,为65535,也就是65535*1us,为什么老师也没讲, 个人理解是因为这里的输出的pwm波周期是500*1us,输入捕获周期设置为最大能避免自身更新产生中断,有正确的理解的老哥可以和我讲一讲 TIM_PWMINPUT_Handle.Init.Period = 0xFFFF; // 高级控制定时器时钟源TIMxCLK = HCLK=168MHz // 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=1MHz,每次计数时间是1us TIM_PWMINPUT_Handle.Init.Prescaler = 168-1; // 采样时钟分频,不分频 TIM_PWMINPUT_Handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; // 计数方式,向上计数 TIM_PWMINPUT_Handle.Init.CounterMode=TIM_COUNTERMODE_UP; // 初始化定时器TIMx, x[1,8] HAL_TIM_IC_Init(&TIM_PWMINPUT_Handle); /* IC1捕获:上升沿触发 TI1FP1 */ TIM_ICInitStructure.ICPolarity = TIM_ICPOLARITY_RISING; //在系统结构中IC1和IC2是两个通道,但是他们又彼此可以连接,我们设定IC1直接通道,IC2间接就使得两个对一个引脚进行采样 TIM_ICInitStructure.ICSelection = TIM_ICSELECTION_DIRECTTI; //设定捕获几次发生中断,每次都发生中断就选1,这里分频的目的是进行数字滤波防止出实现抖动带来的误差,如果有需求再去手册了解 TIM_ICInitStructure.ICPrescaler = TIM_ICPSC_DIV1; //配合上条指令,详细见手册 TIM_ICInitStructure.ICFilter = 0x0; HAL_TIM_IC_ConfigChannel(&TIM_PWMINPUT_Handle,&TIM_ICInitStructure,ADVANCE_IC1PWM_CHANNEL); /* IC2捕获:下降沿触发 TI1FP2 */ TIM_ICInitStructure.ICPolarity = TIM_ICPOLARITY_FALLING; TIM_ICInitStructure.ICSelection = TIM_ICSELECTION_INDIRECTTI; TIM_ICInitStructure.ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.ICFilter = 0x0; HAL_TIM_IC_ConfigChannel(&TIM_PWMINPUT_Handle,&TIM_ICInitStructure,ADVANCE_IC2PWM_CHANNEL); /* 选择从模式: 复位模式 */ 复位模式的存在作用是让IC1和IC2的计数器在检测到上升沿时被寄存器捕获。之后清零重新计数,并且产生中断,中断再处理捕获的数值,这样我们就会得到单纯的一次周期的数值。 TIM_SlaveConfigStructure.SlaveMode = TIM_SLAVEMODE_RESET; /* 选择定时器输入触发: TI1FP1 */选择IC1通道的进行触发 TIM_SlaveConfigStructure.InputTrigger = TIM_TS_TI1FP1; HAL_TIM_SlaveConfigSynchronization(&TIM_PWMINPUT_Handle,&TIM_SlaveConfigStructure); /* 使能捕获/比较2中断请求 */ HAL_TIM_IC_Start_IT(&TIM_PWMINPUT_Handle,TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(&TIM_PWMINPUT_Handle,TIM_CHANNEL_2); }
第二个比较重要的就是一个频率和占空比的计算
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) { /* 获取输入捕获值 */ IC1Value = HAL_TIM_ReadCapturedValue(&TIM_PWMINPUT_Handle,ADVANCE_IC1PWM_CHANNEL); IC2Value = HAL_TIM_ReadCapturedValue(&TIM_PWMINPUT_Handle,ADVANCE_IC2PWM_CHANNEL); if (IC1Value != 0) { /* 占空比计算 ,这里乘以100是为了转换成百分比形式*/ DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1); /* 频率计算 ,频率等于1s内的次数,现IC1等于1us乘以n次,乘以10的6次方就是一秒多少次,就是频率*/ Frequency = 168000000/168/(float)(IC1Value+1); } else { DutyCycle = 0; Frequency = 0; } } }
其余就是之前讲过的输出pwm初始化,引脚初始化和中断初始化
//初始化引脚,中断,输出模式,输入模式 void TIMx_Configuration(void) { TIMx_GPIO_Config(); TIMx_NVIC_Configuration(); TIM_PWMOUTPUT_Config(); TIM_PWMINPUT_Config(); }
//引脚初始化 static void TIMx_GPIO_Config(void) { /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStructure; /*开启定时器相关的GPIO外设时钟*/ GENERAL_OCPWM_GPIO_CLK_ENABLE(); ADVANCE_ICPWM_GPIO_CLK_ENABLE(); /* 定时器功能引脚初始化 */ /* 通用定时器PWM输出引脚 */ GPIO_InitStructure.Pin = GENERAL_OCPWM_PIN; GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; GPIO_InitStructure.Pull = GPIO_NOPULL; GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; GPIO_InitStructure.Alternate = GENERAL_OCPWM_AF; HAL_GPIO_Init(GENERAL_OCPWM_GPIO_PORT, &GPIO_InitStructure); /* 高级定时器输入捕获引脚 */ GPIO_InitStructure.Pin = ADVANCE_ICPWM_PIN; GPIO_InitStructure.Alternate = ADVANCE_ICPWM_AF; HAL_GPIO_Init(ADVANCE_ICPWM_GPIO_PORT, &GPIO_InitStructure); } / /高级控制定时器 TIMx,x[1,8]中断优先级配置 static void TIMx_NVIC_Configuration(void) { //设置抢占优先级,子优先级 HAL_NVIC_SetPriority(ADVANCE_TIM_IRQn, 0, 3); // 设置中断来源 HAL_NVIC_EnableIRQ(ADVANCE_TIM_IRQn); }
static void TIM_PWMOUTPUT_Config(void) { TIM_OC_InitTypeDef TIM_OCInitStructure; // 开启TIMx_CLK,x[2,3,4,5,12,13,14] GENERAL_TIM_CLK_ENABLE(); /* 定义定时器的句柄即确定定时器寄存器的基地址*/ TIM_PWMOUTPUT_Handle.Instance = GENERAL_TIM; /* 累计 TIM_Period个后产生一个更新或者中断*/ //当定时器从0计数到9999,即为10000次,为一个定时周期 TIM_PWMOUTPUT_Handle.Init.Period = 500-1; // 高级控制定时器时钟源TIMxCLK = HCLK=84MHz // 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=1mHz TIM_PWMOUTPUT_Handle.Init.Prescaler = 84-1; // 采样时钟分频 TIM_PWMOUTPUT_Handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; // 计数方式 TIM_PWMOUTPUT_Handle.Init.CounterMode=TIM_COUNTERMODE_UP; // 重复计数器 TIM_PWMOUTPUT_Handle.Init.RepetitionCounter=0; // 初始化定时器TIMx, x[1,8] HAL_TIM_PWM_Init(&TIM_PWMOUTPUT_Handle); /*PWM模式配置*/ //配置为PWM模式1 TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1; TIM_OCInitStructure.Pulse = 250; TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH; TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH; TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET; TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET; //初始化通道3输出PWM HAL_TIM_PWM_ConfigChannel(&TIM_PWMOUTPUT_Handle,&TIM_OCInitStructure,TIM_CHANNEL_3); /* 定时器通道3输出PWM */ HAL_TIM_PWM_Start(&TIM_PWMOUTPUT_Handle,TIM_CHANNEL_3); }
定时器.h文件中各个引脚的定义。
#ifndef __ADVANCE_TIM_H #define __ADVANCE_TIM_H #include "stm32f4xx.h" /* 通用定时器 */ #define GENERAL_TIM TIM4 #define GENERAL_TIM_CLK_ENABLE() __TIM4_CLK_ENABLE() /* 通用定时器PWM输出 */ /* PWM输出引脚 */ #define GENERAL_OCPWM_PIN GPIO_PIN_14 #define GENERAL_OCPWM_GPIO_PORT GPIOD #define GENERAL_OCPWM_GPIO_CLK_ENABLE() __GPIOD_CLK_ENABLE() #define GENERAL_OCPWM_AF GPIO_AF2_TIM4 /* 高级控制定时器 */ #define ADVANCE_TIM TIM1 #define ADVANCE_TIM_CLK_ENABLE() __TIM1_CLK_ENABLE() /* 捕获/比较中断 */ #define ADVANCE_TIM_IRQn TIM1_CC_IRQn #define ADVANCE_TIM_IRQHandler TIM1_CC_IRQHandler /* 高级控制定时器PWM输入捕获 */ /* PWM输入捕获引脚 */ #define ADVANCE_ICPWM_PIN GPIO_PIN_9 #define ADVANCE_ICPWM_GPIO_PORT GPIOE #define ADVANCE_ICPWM_GPIO_CLK_ENABLE() __GPIOE_CLK_ENABLE() #define ADVANCE_ICPWM_AF GPIO_AF1_TIM1 #define ADVANCE_IC1PWM_CHANNEL TIM_CHANNEL_1 #define ADVANCE_IC2PWM_CHANNEL TIM_CHANNEL_2 extern TIM_HandleTypeDef TIM_PWMOUTPUT_Handle; extern TIM_HandleTypeDef TIM_PWMINPUT_Handle; void TIMx_Configuration(void); #endif /* __ADVANCE_TIM_H */
mian.c函数,记得初始化串口通信printf
int main(void) { /* 初始化系统时钟为168MHz */ SystemClock_Config(); /* 初始化串口 */ DEBUG_USART_Config(); /* 初始化基本定时器定时,1s产生一次中断 */ TIMx_Configuration(); while(1) { HAL_Delay(500); printf("IC1Value = %d IC2Value = %d ", IC1Value, IC2Value); printf("占空比:%0.2f%% 频率:%0.2fHz\n", DutyCycle, Frequency); } }
-
pwm与脉冲捕获_STM32F103_stm32输入捕获_
2021-09-29 14:44:36stm32输入捕获,可以捕获PWM波,可以检测占空和频率等。。。。。。。。 -
stm32时钟(2)输入捕获以及PWM输入捕获
2021-12-17 20:36:29输入捕获 过程 在输入捕获模式下,信号从TIx输入,一共有四个输入口,以TI1为例,经过滤波器和边沿检测器后输出TI1FPx两路信号,该信号可以直接从IC1从输入,也可以从IC2输入,此处以IC1为例,当ICx信号检测到...输入捕获
过程
在输入捕获模式下,信号从TIx输入,一共有四个输入口,以TI1为例,经过滤波器和边沿检测器后输出TI1FPx两路信号,该信号可以直接从IC1从输入,也可以从IC2输入,此处以IC1为例,当ICx信号检测到跳变沿时,会锁存计数器的值,并把SR寄存器中的捕获比较中断位置1,并可发出中断或DMA请求,当读取CCR1的值后,相应的SR中断捕获比较中断位会自动清零,当下一次捕获事件发生时,如果上一次的CCR1值还未被读取,则会将重复捕获寄存器(SR中的CC1OF)置1,可通过软件置0。
还是以从TI1输入并在出现上升沿时将值捕获到CCR1。
-
选择有效输入,如上图,从IC1输入的信号对应着CCR1寄存器,要将值捕获到CCR1寄存器就得把信号作为IC1信号输入,由图知,TI1FP1和TI2FP1可以作为IC1信号输入,但我们的目的是从TI1输入,故只能选择TI1FP1信号,即需要配置从TI1输入。
具体需要将CCMR1(捕获比较模式寄存器1,控制1,2通道,对应的CCMR2控制3,4通道)寄存器中的CC1S(捕获比较1选择,即控制1通道信号输入的位)寄存器写入01,将会将通道配置成输入,并将IC1映射到TI1上。
-
根据连接到定时器的信号,对所需的输入滤波时间进行编程。这个我平时也没怎么用,
-
选择有效边沿。配置了了从哪个通道传入数据,也要配置了在什么时候捕获数据,这需要对CCER寄存器中的CC1NP与CC1P位进行配置,有四种选择分别是上升沿捕获(00),下降沿捕获(01),上下沿均触发(11),保留状态(10),此处我们选择上升沿即(00)。
-
禁止CCRx预装载寄存器。使可以随时向CCR1写入数据,写入后立刻生效,需要配置CCMR1寄存器的IC1PSC位为(00)。
-
捕获使能。就好似配置一个GPIO口一样,要先配置他的速度,端口等等,最后一定要将GPIO使能。本处也是一样,为了允许将计数器的值捕获到捕获寄存器中,配置完捕获之后要将捕获使能,即将CCER寄存器的CCIE位(捕获比较1输出使能)置1。
-
生成中断/DMA请求(可选)。这可以将DIER寄存器中的CC1IE位置1来使能中断请求,将CC1DE位置来使能DMA请求。如果要使用中断的话还需要初始化NVIC结构体,编写中断服务函数等,此处不再介绍,要注意一点就是,TIM1产生的捕获比较中断就只有TIM1_CC_IRQn一个,通道1,2,3,4产生的都是这个中断,要区分各个中断必须得在中断服务程序中通过SR(状态寄存器)中的中断标志位进行判断。
PWM输入模式
PWM学名为脉冲宽度调制,一般可以用来调节电机转速以及控制LED等亮暗等等。
PWM输入模式是普通输入模式的特例,区别在于,对于PWM信号,一般都要测量他的周期和占空比,而这不是仅仅通过一个通道就可以测量的,所以说一般都要使用两个通道对PWM信号进行测量,而且这两个通道一个是在上升沿进行捕获而另一个是在下降沿进行捕获,如下图,IC2在下降沿捕获,而IC1在上升沿捕获。在下图中,IC1捕获的值加1乘以时钟周期即为PWM信号的周期,IC2捕获的值加一与IC1捕获的值加一的比值即为占空比。
另外,对于PWM输入模式,我们只能使用TIMx_CH1/TIMx_CH2信号,因为只有TI1FP1和TI2FP2连接到了从模式控制器,而后续过程中我们是需要使用从模式控制器的,故只能从这两个通道输入信号。如下图所示
下面主要介绍一下配置过程,以测量从TI1输入的的PWM信号为例。
-
选择有效输入,类似于普通输入捕获的配置,对于CCR1寄存器,向CCMR1寄存器的CC1S写入(01),选择TI1;对于CCR2寄存器,向CCMR1寄存器的CC2S写入(01),选择TI1。
-
确定有效边沿,向CCER寄存器中断CC1P与CC1NP写入0,表示上升沿有效,相应地,向CCER寄存器中断CC2P与CC2NP写入1,表示下降沿有效。
-
选择有效触发输入,我们一般把用于测量周期的信号配置为触发输入,此处我们使用TI1FP1用于周期测量,故将SMCR寄存器的TS位配置为101(即为TI1FP1)。
-
将从模式控制器配置为复位模式,即将SMCR寄存器的SMS位配置为100(复位模式),即表示当我们所选的触发信号(结合步骤3)出现上升沿时,会重新初始化计数器并生成一个寄存器更新事件,从此时,计时器从0开始计数,当再次出现上升沿、下降沿时,相应的定时器值被捕获到CCR1,CCR2寄存器,通过这两个寄存器的值,既可以计算PWM信号的周期与占空比。
-
使能捕获,如普通捕获输入一样,配置的最后要使能捕获,即将CCER寄存器的CC1E、CC2E置1。
具体代码可参考如下
GPIO_InitTypeDef GPIO_InitStructure; GPIO_PinAFConfig(ttj_GPIO_PORT, ttj_GPIO_SOU, ttj_GPIO_AF); //基本的GPIO配置, GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Pin = ttj_GPIO_PIN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed; GPIO_Init(ttj_GPIO_PORT, &GPIO_InitStructure); //配置时基-基本的时钟配置,和配置普通的定时器一样 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_ICInitTypeDef TIM_ICInitStructure; ttj_TIM_ClockCmd(ttj_TIM_Clock, ENABLE);//开启时钟 //初始化时基单元 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = ttj_TIM_PERIOD; TIM_TimeBaseInitStructure.TIM_Prescaler = ttj_TIM_PSC; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(ttj_TIM, &TIM_TimeBaseInitStructure); //初始化输入捕获比较结构体 配置了IC1的,IC2的可通过程序自动配置。 TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //通过一通道输入数据 TIM_ICInitStructure.TIM_ICFilter = 0X00; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获,IC2相反为falling,这配置的就是上述配置pwm流程的第二步 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //一般都不分频,不滤波 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//直连,映射到IC1,IC2相反,为非直连 TIM_PWMIConfig(ttj_TIM, &TIM_ICInitStructure);//使用PWM捕获时才使用此语句,使用该语句后,只用配置一个输入捕获,另一个自动配置, //选择有效输入端(PWM模式可用) 选择的即为周期,另一个即为高电平时间,对应的是上述配置pwm流程的第三步, TIM_SelectInputTrigger(ttj_TIM, TIM_TS_TI1FP1); //配置为主从复位模式(PWM模式可用),在有效输入端出现上升沿时让定时器归零并生成更新事件,记住,还要生成更新事件!!也会产生更新中断。对应的是上述配置pwm流程的第四步, TIM_SelectSlaveMode(ttj_TIM, TIM_SlaveMode_Reset); TIM_SelectMasterSlaveMode(ttj_TIM, TIM_MasterSlaveMode_Enable); //这步有什么用具体也没看懂 //开启中断 TIM_ITConfig(ttj_TIM, TIM_IT_Update|TIM_IT_CC1 | TIM_IT_CC2, ENABLE); TIM_ClearFlag(ttj_TIM, TIM_FLAG_Update|TIM_FLAG_CC1 |TIM_FLAG_CC2); //使能定时器 TIM_Cmd(ttj_TIM, ENABLE);
至此,时钟配置完成。
-
-
STM32PWM输入捕获模式源码
2018-10-31 15:39:50STM32PWM输入捕获模式源码,采用的是STM32F103系列的单片机 -
【蓝桥杯嵌入式备赛】7.一路PWM输入捕获和两路输入捕获驱动快速编写及常见问题
2021-05-25 09:04:57一般要求一路的话直接就直接用标准库的PWM输入捕获例程了,可以把它看成一种特殊的输入捕获。两路的话就需要使用输入捕获在例程的基础上修改一下(标准库例程只能计算一路频率),不过思路是一样的。 一般捕获都需要... -
stm32 timer pwm输入捕获理解
2021-10-13 10:57:39stm32 timer输入捕获计算公式: IC2Value=TIM_GetCapture2(TIM2);//获得CCR2的值 pulse=TIM_GetCapture1(TIM2);//获得CCR1的值 DutyCycle=pulse/IC2Value; Frequency=72000000/IC2Value; duty=(u32)(DutyCycle*100);... -
STM32F1PWM输入捕获
2020-09-05 10:42:23于是我尝试着使用STM32的定时器捕获功能来测量PWM波。 配置PWM波 由于要测量PWM波形,那么首先要产生一个PWM波。 PWM配置代码如下: //PWM输出初始化 //arr:自动重装值 //psc:时钟预分频数 void TIM3_PWM_... -
STM32定时器使用,PWM输入捕获模式计算PWM波占空比和频率
2022-08-02 09:49:45使用CubeMX配置生成,底层采用HAL库,可以使用易于上手 -
【WB32库开发】第12章(上)TIM1高级定时器——PWM输入捕获
2021-12-31 08:52:42本章要学习的PWM输入捕获是定时器又一重要应用,使用PWM输入捕获可以测量输入PWM的频率和占空比。 PWM输入只能使用定时器的两个通道:通道1和通道2,且一路PWM输入要占用两个捕获寄存器,一个用于捕获周期,一个用于... -
STM32F1 定时器 PWM输入捕获两路
2021-03-04 11:22:44//通道3捕获到高电平的时刻 __IO u32 TIM4CH3_CAPTURE_DOWNVAL = 0;//通道3捕获到低电平的时刻 __IO u32 TIM4CH4_CAPTURE_UPVAL = 0;//通道4捕获到高电平的时刻 __IO u32 TIM4CH4_CAPTURE_DOWNVAL = 0;//通道4捕获... -
STM32 HAL库系列(一)定时器PWM输入捕获模式
2021-06-04 15:30:35前言:STM32 为了方便用户使用,设置了专门的PWM输入捕获模式,在这种模式下,输入捕获更加快捷,而且相比于普通的输入捕获模式,该模式的代码量更小,能够快速实现对PWM周期,占空比,频率的测量。 定时器的PWM输入... -
STM32 PWM 输入捕获的计算.pdf
2021-08-22 20:59:07Cube MX配置,使用HAL 库 -
PWM输入捕获(只使用一路定时器通道)
2020-09-05 12:16:29前言 上一篇文章介绍了使用《STM32参考手册》上 PWM 输入模式捕获的 PWM 波,这种方法简单且准确,但是它占用的资源太多了,因为它...本文介绍了另一种捕获 PWM 的方法,只使用任何一路定时器的输入捕获,就可以测 PW -
定时器PWM输入捕获DMA模式下的异常记录
2021-10-21 16:06:56问题描述:使用定时器PWM输入捕获+DMA传输数据,共3路输入捕获,仅其中有1路出现问题。具体表现为,占空比部分的数据能通过DMA存入数组,但是周期部分的数据全为0,且使用DMA过半中断时,仿真不能准确的及时暂停,等... -
【WB32库开发】第12章(中)PWM输入捕获——串口打印数据
2021-12-31 11:07:22上节我们讲述了如何配置TIM的PWM输入捕获功能,本节内容将与串口内容结合,将捕获到的信息通过上位机显示出来。 12.3 串口配置 12.3.1 串口基础配置 我们在第八章的内容中已经讲过串口是如何配置和使用的。那么如何... -
【STM32】标准库-通用定时器-PWM输入捕获
2022-06-22 15:35:43采用STM32F429IGT6单片机,KeilMDK5.32版本 使用SysTick系统滴答定时器进行延时 LED_R、LED_G、LED_B分别为...IC1上升沿捕获,IC2下降沿捕获,开启更新与IC1捕获中断 用杜邦线连接PA0与PA5 KEIL5下载配置有FLASH与SRAM -
使用STM32cubemx进行定时器单多路pwm输入捕获
2021-03-24 16:24:44提示:文章写完后,目录可以自动...本文主要内容是STM32的定时器输入捕获,基于MDK_ARM,STM32 CUBEMX,keil软件和STM32F407VGT6最小系统板,实现一路PWM的输出与捕获,并通过串口输出捕获信息。 一、使用步骤 1.STM32 -
TIM—高级定时器PWM输入捕获.rar
2021-09-04 14:34:18使用STM32的高级控制定时器PWM输入捕获模式,测量PWM波形频率和周期。 使用高级控制定时器输入捕获功能,以及通用定时器PWM输出功能。 -
【蓝桥杯嵌入式拓展板】—PWM输入捕获 详解(官网程序优化)(附程序源码)
2021-02-26 19:49:31} } } 其实相比较官网例程,主要增加了初始化时基结构体,周期很重要,既然是输入捕获,所以直接设成最大 0xFFFF 还有就是计算周期和占空比的时候,应进行 加一操作。 实测 稳的一批! 欢迎交流探讨 -
【STM32】关于PWM输入捕获占空比显示误差和低频显示出错的解决及经验总结
2021-04-09 17:09:37在比赛提供的官方固件库的基础上进行修改,暂且用了两块板子,板子A烧录的之前写的输出比较输出两路频率和占空比可变的PWM作为信号发生源供烧入捕获的板子B检测。 板子A一路是1Khz作为基频的频率可调,另一路是10Khz...