STM32定时器学习 hal库PWM输入捕获实验

2023-11-03

实验原理

输入捕获实验目的是为了获得脉宽和频率,首先是脉宽的获得。
在实际捕获实验中,有两个通道,一个负责捕获上升沿,一个负责下降沿,
在这里插入图片描述
从上图可以看出,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);
  }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32定时器学习 hal库PWM输入捕获实验 的相关文章

  • Python——元组的基本语法(创建、访问、修改、删除)

    Python 元组的使用 Python 的元组与列表类似 不同之处在于元组的元素不能修改 元组使用小括号 列表使用方括号 元组创建很简单 只需要在括号中添加元素 并使用逗号隔开即可 gt gt gt tup1 Google Runoob 1
  • 相关滤波之开篇Mosse原理及代码详解

    相关滤波之开篇Mosse原理及代码详解 相关滤波 Correlation Filter 介绍 代码解读 程序框图 本文主要介绍相关滤波算法开篇 mosse具体原理及其python代码实现流程 相关滤波 Correlation Filter
  • LeetCode-字母大小写全排列

    类似于二叉树的前序遍历 左分支代表不转换字母的大小写 右分支代表转换字母的大小写 如果遇到数字就跳过 继续遍历下一个位置 循环的终止条件是扫描到最后一个元素的下一个位置 如果扫描到最后一个元素就停止的话 那么最后一个元素可能还没有进行大小写
  • 【OpenVINO】将TensorFlow模型转成IR文件,并部署到NCS2上运行

    TensorFlow模型 这里以本人用TensorFlow实现的AlexNet模型为例 详见文章用TensorFlow实现AlexNet 且下面的过程都在激活OpenVINO环境后打开的jupyter notebook中完成 模型转换 编写
  • Django模型和数据库操作

    文本参考菜鸟教程 创建模型和数据库 并对mysql数据库进行操作 1 创建数据库 在mysql命令行 输入创建数据库语句 create database runoob default charset utf8 2 修改settings py
  • 部署Phabricator的记录

    前传 开始的时候 试图通过已有的docker来集中部署 发现虽然docker hub里面有一些相关的docker镜像 但是还需要另外运行mysql mariadb的docker 后来多次尝试后 基本卡在两个地方 设置连接db的信息 打开we

随机推荐