STM32的TIM输入捕获和PWMI详解

news2024/10/5 19:21:00

系列文章目录

STM32单片机系列专栏

C语言术语和结构总结专栏


文章目录

1. IC输入捕获

2. 频率测量

3. 主模式、从模式、触发源选择

4. 输入捕获基本结构

5. PWMI模式

6. 代码示例

6.1 PWM.c

6.2 PWM.h

6.3 IC.c

6.4 IC.h

6.5 完整工程文件


输出比较可以看下面这篇文章:

STM32定时器的OC比较和PWM

1. IC输入捕获

输入捕获(Input Capture,简称IC)是微控制器中定时器功能之一,主要用于精确地捕获外部事件(如信号的边沿)发生的时刻。在STM32中,IC功能通过定时器来实现,可以对外部输入信号的上升沿或下降沿进行时间的捕获,通过读取捕获时定时器的计数器(CNT)的值,从而知道外部事件发生的确切时刻。并且当前CNT的值将被锁存到CCR中。

输入捕获功能有以下功能:

  • 测量两个事件之间的时间间隔。
  • 测量PWM波形的频率、占空比、脉冲间隔,电平持续时间等。
  • 测量外部信号的脉宽。

每个高级定时器和通用定时器都有4个输入捕获通道。可配置为PWMI模式(PWM输入模式),同时测量频率和占空比,也可以配合主从触发模式,实现硬件全自动测量。这两个模式的组合,可以实现全自动检测,不需要中断也不影响主程序,从而减轻CPU的负担。

注意因为输入捕获和输出比较使用的为同一个寄存器,所以输出捕获和输出比较只能使用一个,不能同时使用。

2. 频率测量

测频法(适合高频信号):在闸门时间T内(通常为1s),对上升沿计次,得到N,则频率

fx = N / T  在单位时间内出现的脉冲数量越多,脉冲的频率就越高。

测周法(适合低频信号):两个上升沿内持续的时间(因为周期的倒数就是频率),使用一个已知的标准频率fc的计次时钟来驱动计数器,从第一个上升沿到下一个上升沿,得到N ,则频率

fx = = fc / N   在给定的周期内,脉冲数量保持不变时,周期越长,每个脉冲的宽度就越宽。

中界频率:测频法与测周法误差相等的频率点

fm = √(fc  / T)   这个指标是用来描述在脉冲调制中,信号的中心频率位置。

3. 主模式、从模式、触发源选择

主模式可以将定时器内部的信号映射到TRGO引脚,用于触发其他的外设。

从模式可以接收其他外设或自身外设的信号,用于控制自身定时器的运行,也就是被其他的信号控制。

触发源选择是选择从模式的触发信号源,选择一个指定的信号,得到TRGI,通过TRGI去触发从模式。从模式可以在列表中选择一项操作来自动执行。例如:如果想让TI1FP1信号自动触发CNT清零,触发源选择TI1FP1,从模式选择Reset,实现硬件全自动。

主模式:

  • 复位 - 主输出TRGO不激活。
  • 使能 - 定时器计数器使能时TRGO被激活。
  • 更新 - 每当定时器更新事件发生时,比如计数器溢出时,TRGO被激活。
  • 比较匹配 - 比如当定时器的比较匹配事件发生时,如CCR1匹配时,TRGO被激活。
  • 比较 - OC1REF的比较匹配事件激活TRGO。
  • 比较 - OC4REF的比较匹配事件激活TRGO。

从模式:

  • 关闭从模式,定时器不受外部信号控制。
  • 编码器模式1,使用内部TRGI(定时器内部触发输入)作为时钟源,通常与编码器的输出相连,用于计数编码器的脉冲。
  • 编码器模式2,同上,但使用两个内部触发输入,允许同时检测方向和速度。
  • 编码器模式3,同上,但使用两个内部触发输入,允许同时检测方向和速度,并且可以检测到位置信息。
  • 复位模式,当TRGI发生时,定时器计数器会被复位。
  • 门控模式,TRGI信号作为启动和停止计数的使能信号。
  • 触发模式,定时器仅在TRGI信号发生时更新其寄存器,并开始计数。
  • 外部时钟模式1,使用TRGI作为定时器的时钟源。

4. 输入捕获基本结构

当配置好时基单元后,启动定时器,此时CNT就会在预分频器之后的时钟驱动下,不断自增。预分频器之后的时钟频率。就是驱动CNT的标准频率fc。

下面的GPIO输入方波信号,经过滤波器和边沿检测,选择TI1FP1为上升沿触发,之后输出选择直连的通道,分频器选择不分频。当TI1FP1出现上升沿之后,CNT(最大65535)的当前计数值会进入CCR1中。同时触发源选择。选择TI1FP1为触发信号,从模式选择复位模式,从而清零CNT。

5. PWMI模式

这部分前面和输入捕获一样,但后面使用了两个通道,可以同时测量周期和占空比,TI1FP2为下降沿触发。这样CCR1就是一整个周期的计数值,CCR2是高电平之间的计数值。此时用CCR2除以CCR1 就是占空比。

6. 代码示例

第一步:开始RCC时钟,打开将要使用的TIM外设时钟和GPIO外设时钟。

第二步:GPIO初始化,把GPIO配置为输入模式,一般为上拉输入或浮空输入模式。

第三步:配置时基单元,让CNT计数器在内部时钟的驱动下自增运行,PSC预分频器、CNT计数器和ARR自动重装载,也包括包括时钟源选择。

第四步:配置输入捕获单元,包括滤波器、极性、直连通道/交叉通道、分频器。

第五步:选择从模式的触发源(TI1FP1)

第六步:选择触发后执行的操作,这里执行Reset操作。

第七步:调用TIM_Cmd函数,开启定时器。

下面的代码实现功能为: 通过PWMI得到时钟频率和占空比。

6.1 PWM.c

要使用的库函数文件依然为:stm32f10x_tim.h,拖到最下面,在这里可以找到定时器TIM需要使用到的函数。

#include "stm32f10x.h" 

//PWM初始化
void PWM_Init(void)
{
	//开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	//GPIO重映射
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用
	
	//GPIO初始化
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA0引脚初始化为复用推挽输出	
																	//受外设控制的引脚,均需要配置为复用模式		
	
	//配置时钟源
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	//时基单元初始化
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;					//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
	
	//输出比较初始化
	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值
																	//则最好执行此函数,给结构体所有成员都赋一个默认值
																	//避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		//输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
	
	//TIM使能
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

//PWM设置CCR
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);		//设置CCR1的值
}

//PWM设置PSC
void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);		//设置PSC的值
}

RCC_APB1PeriphClockCmd

  • TIM2 代表定时器2,它是STM32的一个基础硬件定时器。在STM32的某些系列中,TIM2连接到的是APB1总线。
  • ENABLE 是一个宏定义,用来开启某项功能,这里用来开启TIM2的时钟。如果传递 DISABLE 则会关闭外设的时钟。
  • 简单来说,RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 这行代码的作用是开启连接到APB1总线的定时器2(TIM2)的时钟。只有开启了时钟,程序中关于TIM2的其他功能(如计时、计数、PWM发生等)才能正常工作。

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

  • GPIO的初始化中,选择AF_PP复用推挽输出,因为对于普通的开漏推挽输出,引脚的控制权是来自于输出数据寄存器的,如果想用定时器来控制引脚,就需要使用复用开漏/推挽输出模式。

 

6.2 PWM.h

接着是PWM.h文件,这部分引用声明一下即可

#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
void PWM_SetPrescaler(uint16_t Prescaler);

#endif

6.3 IC.c

在这段代码中,我们设置了一个STM32的定时器TIM3以输入捕获(IC)模式运行。代码的主要目的是初始化TIM3以捕获来自外部信号(从PWM波)的频率和占空比。

#include "stm32f10x.h"

//输入捕获初始化
void IC_Init(void)
{
	//开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	//GPIO初始化
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA6引脚初始化为上拉输入
	
	//配置时钟源
	TIM_InternalClockConfig(TIM3);		//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	//时基单元初始化
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;               //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
	
	//PWMI模式初始化
	TIM_ICInitTypeDef TIM_ICInitStructure;							//定义结构体变量
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				//选择配置定时器通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;		//极性,选择为上升沿触发捕获
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;			//捕获预分频,选择不分频,每次信号都触发捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;	//输入信号交叉,选择直通,不交叉
	TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);						//将结构体变量交给TIM_PWMIConfig,配置TIM3的输入捕获通道
																	//此函数同时会把另一个通道配置为相反的配置,实现PWMI模式

	//选择触发源及从模式
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					//触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);					//从模式选择复位
																	//即TI1产生上升沿时,会触发CNT归零
	
	//TIM使能
	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}

//获取输入捕获的频率
uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);		//测周法得到频率fx = fc / N,这里不执行+1的操作也可以
}

//获取输入捕获的占空比
uint32_t IC_GetDuty(void)
{
	return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);	//占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可以
}

6.4 IC.h

同样这部分引用声明一下即可

#ifndef __IC_H
#define __IC_H

void IC_Init(void);
uint32_t IC_GetFreq(void);
uint32_t IC_GetDuty(void);

#endif

6.5 完整工程文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1640981.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Covalent Network(CQT)为 Arbitrum 生态提供 250 万美元的资助,以促进 Web3 的创新与发展

Covalent Network(CQT)作为 Web3 领先的“数据可用性”层,宣布将提供 250 万美元的资金以支持 Arbitrum 生态项目,包括 Arbitrum One、Nova、Orbit 或 Stylus。此举旨在通过提供资源和帮助,推动利用 Arbitrum 网络上 C…

华为机考入门python3--(20)牛客20- 密码验证合格程序

分类&#xff1a;字符串 知识点&#xff1a; 遍历字符串的每个字符 for char in my_str: 可以直接比较字符范围 a < char < z 列表统计元素个数 my_list.count(elem) 寻找子串 my_str.find(sub_str) 题目来自【牛客】 import re import sysdef check_…

信息泄露.

一&#xff0c;遍历目录 目录遍历&#xff1a;没有过滤目录相关的跳转符号&#xff08;例如&#xff1a;../&#xff09;&#xff0c;我们可以利用这个目录找到服务器中的每一个文件&#xff0c;也就是遍历。 tipe&#xff1a;依次点击文件就可以找到flag 二&#xff0c;phpi…

AI视频教程下载:零代码创建AI智能体、AI Agents和ChatGPT的Gpts

这门课程专注于提示工程的掌握&#xff0c;教你以精确的方式引导GPT&#xff0c;利用它们的生成能力产生卓越的AI驱动结果。一步一步地&#xff0c;你将学会创建多样化的GPT军团——每个都设计来满足特定的专业需求。 从提供个性化职业变更指导的职业教练AI&#xff0c;到以惊…

uniapp 监听APP切换前台、后台插件 Ba-Lifecycle

监听APP切换前台、后台 Ba-Lifecycle 简介&#xff08;下载地址&#xff09; Ba-Lifecycle 是一款uniapp监听APP切换前台、后台的插件&#xff0c;简单易用。 截图展示 也可关注博客&#xff0c;实时更新最新插件&#xff1a; uniapp 常用原生插件大全 使用方法 在 script…

可视化大屏应用场景:智慧安防,保驾护航

hello&#xff0c;我是大千UI工场&#xff0c;本篇分享智慧安防的大屏设计&#xff0c;关注我们&#xff0c;学习N多UI干货&#xff0c;有设计需求&#xff0c;我们也可以接单。 实时监控与预警 可视化大屏可以将安防系统中的监控画面、报警信息、传感器数据等实时展示在大屏上…

k8s笔记 | Ingress

安装Ingress 添加helm创库 Installation Guide - Ingress-Nginx Controller Ingress | Kubernetes 下载包 将 文件helm 放到 /usr/local/bin/ 并给到执行权限 # 添加可执行权限 chmod ux helm # 测试是否能运行 helm version# 结果 version.BuildInfo{Version:"v3.14…

快速批量重命名文件(夹)

首先&#xff0c;需要用到的这个工具&#xff1a; 度娘网盘 提取码&#xff1a;qwu2 蓝奏云 提取码&#xff1a;2r1z 我这里处理这4个文本&#xff0c;实际可以处理任意数量的文本和文件夹 1、打开工具&#xff0c;进入文件批量复制版块 2、点击“重命名” 3、把要重命名的…

从0开始学习制作一个微信小程序 学习部分(6)组件与事件绑定

系列文章目录 学习篇第一篇我们讲了编译器下载&#xff0c;项目、环境建立、文件说明与简单操作&#xff1a;第一篇链接 第二、三篇分析了几个重要的配置json文件&#xff0c;是用于对小程序进行的切换页面、改变图标、控制是否能被搜索到等的操作第二篇链接、第三篇链接 第四…

【算法】数字接龙 走迷宫问题的一般处理思路

前言 其实走迷宫就是一个普普通通的深搜回溯嘛&#xff0c;但是我之前做的很多题都是在一个二维的地图上&#xff0c;只能上下左右四个方向走迷宫&#xff0c;在做数字接龙这道题的时候&#xff0c;相当于可以往8个方向走&#xff0c;虽然逻辑上不变&#xff0c;但按照我之前的…

可视化大屏应用(20):智慧水务、水利、防汛

hello&#xff0c;我是大千UI工场&#xff0c;本篇分享智智慧水务的大屏设计&#xff0c;关注我们&#xff0c;学习N多UI干货&#xff0c;有设计需求&#xff0c;我们也可以接单。 在智慧水务领域&#xff0c;可视化大屏具有以下几个方面的价值&#xff1a; 数据展示和监控 可…

wpf转换器

WPF&#xff08;Windows Presentation Foundation&#xff09;中的转换器主要是指IValueConverter接口的实现&#xff0c;它用于在数据绑定过程中转换源数据和目标数据的类型或表示形式。这种机制使得开发者能够灵活地处理数据&#xff0c;特别是在用户界面&#xff08;UI&…

【开源物联网平台】window环境下搭建调试监控设备环境

&#x1f308; 个人主页&#xff1a;帐篷Li &#x1f525; 系列专栏&#xff1a;FastBee物联网开源项目 &#x1f4aa;&#x1f3fb; 专注于简单&#xff0c;易用&#xff0c;可拓展&#xff0c;低成本商业化的AIOT物联网解决方案 目录 一、使用docker脚本部署zlmediakit 1.1 …

简述前后端分离架构案例

Hello , 这里是小恒不会java 。今晚1点写写关于RESTful接口的使用案例&#xff0c;本文会通过django原生js前后端分离的案例简单讲解。本文带你认识一下简化版的前后端分离架构 代码 本文案例代码在GitHub上 https://github.com/lmliheng/fontend前后端分离 先说说什么是前后…

Java | Spring框架 | 核心概念

控制反转&#xff08;IoC&#xff09;与依赖注入&#xff08;DI&#xff09;&#xff1a;轻松管理对象依赖 一、理解IoC和DI 控制反转&#xff08;IoC&#xff09;是一种设计原则&#xff0c;它通过将控制权从程序代码转移到外部容器来降低计算机代码之间的耦合关系。在传统的…

1. 傅里叶变换原理

1. 频率域的引入 1.1 时域角度 1.2. 频域角度 不同的角度表达的是同一件事情&#xff0c;从时间域和空间域来进行表达同一间事情 。时间域是都动态的&#xff0c;频率域是静止的 1.3. 时域角度和频域角度 1.4 相位 2 函数的时域角度 2.1 时间域 2.2 频率域 2.3 例子 2.3…

使用 TensorFlow 和 Keras 构建 U-Net

原文地址&#xff1a;building-a-u-net-with-tensorflow-and-keras 2024 年 4 月 11 日 计算机视觉有几个子学科&#xff0c;图像分割就是其中之一。如果您要分割图像&#xff0c;则需要在像素级别决定图像中可见的内容&#xff08;执行分类时&#xff09;&#xff0c;或者从像…

《Fundamentals of Power Electronics》——升压隔离型变换器、SEPIC隔离型变换器

以下是升压型隔离变换器的相关知识点&#xff1a; 升压型隔离变换器可以通过互换降压型隔离变换器的电源与负载的位置得到。升压型隔离变换器有许多种结构&#xff0c;此处简短的讨论两种情况。这些转换器主要使用在高压电源和低谐波整流器中。 图6.36所示是一种全桥型电路结…

MyBatis 使用 XML 文件映射

在MyBatis中 我们可以使用各种注解来配置我们Mapper 类中的方法 我们为什么要使用XML文件呢&#xff1f; 如果我们是一条非常长的SQL 语句 使用 注解配置的话&#xff0c; 会非常不利于阅读 如下 所以&#xff0c;就需要使用到一个XML文件来对SQL语句进行映射&#xff0c;那么 …

【linuxC语言】空洞文件

文章目录 前言一、空洞文件1.1 空洞文件的介绍1.2 用途 二、示例代码总结 前言 在 Linux 系统编程中&#xff0c;空洞文件是一种特殊类型的文件&#xff0c;它包含了逻辑上的空洞&#xff0c;也就是说文件中的某些部分并没有实际写入数据。尽管文件在逻辑上可能非常大&#xf…