STM32的TIM输入捕获和PWMI详解

系列文章目录

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模式1TIM_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_Hvoid 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;				//选择配置定时器通道1TIM_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);					//触发源选择TI1FP1TIM_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_Hvoid IC_Init(void);
uint32_t IC_GetFreq(void);
uint32_t IC_GetDuty(void);#endif

6.5 完整工程文件

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

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

相关文章

numpy+matplotlib绘制阿基米德螺线

【第10次课]实验十一数据可视化及应用】 声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 绘制阿基米德螺线,运行效果如图所示。 参数方程为: x icosi yisini 其中半径i和圆心角i变化一致,取值范围为…

#你觉得未来的智慧工地是什么样子的#

#你觉得未来的智慧工地是什么样子的# 有人说“现阶段的智慧工地都是噱头,实际用处不大”;也有人将智慧工地吹嘘上天。那么,随着技术的发展,你觉得未来的智慧工地会是什么样子呢? 随着大数据时代的到来,未来…

Three.js杂记(十五)—— 汽车展览(下)

在上一篇文章Three.js杂记(十四)—— 汽车展览上 - 掘金 (juejin.cn)中主要对切换相机不同位置和鼠标拖拽移动相机焦点做了简单的应用。 那么现在聊聊该如何实现汽车模型自带的三种动画展示了,实际上可以是两种汽车前后盖打开和汽车4车门打开…

抑郁后的症状表现——XWX-QP大小鼠强迫游泳桶硬件

简单介绍: 大小鼠强迫游泳桶硬件主要用于抗抑郁的研究。适用于大鼠、小鼠或其他实验室动物,通过将实验动物置于一个局限的环境中,动物在该环境中拼命挣扎试图逃跑又无法逃脱,从而提供了一个无可回避的压迫环境,动物的…

如何提取二维码文本信息?文本二维码提取内容的方法

如何分解出二维码中的文本信息呢?很多商家在做活动时会给每个用户生成一个单独的二维码,每一个二维码中有单独的编号信息,那么当我们收集到用户的二维码时,如何操作才能够提取二维码中的编号信息呢?想要解决这个问题可…

双目深度估计原理立体视觉

双目深度估计原理&立体视觉 0. 写在前面1. 双目估计的大致步骤2. 理想双目系统的深度估计公式推导3. 双目标定公式推导4. 极线校正理论推导 0. 写在前面 双目深度估计是通过两个相机的对同一个点的视差来得到给该点的深度。 标准系统的双目深度估计的公式推导需要满足:1)两…

Vue3+ts(day04:watch、watchEffect)

学习源码可以看我的个人前端学习笔记 (github.com):qdxzw/frontlearningNotes 觉得有帮助的同学,可以点心心支持一下哈(笔记是根据b站上学习的尚硅谷的前端视频【张天禹老师】,记录一下学习笔记,用于自己复盘,有需要学…

树莓派5用docker运行Ollama3

书接上回,树莓派5使用1panel安装 Ollama 点击终端就可以进入容器 输入以下代码 ollama run llama3Llama3 是市场推崇的版本。您的 树莓派5上必须至少有 4.7GB 的可用空间,因此用树莓派玩机器学习就必须配置大容量的固态硬盘。用1panel部署网络下载速度…

Python | Leetcode Python题解之第58题最后一个单词的长度

题目: 题解: class Solution:def lengthOfLastWord(self, s: str) -> int:ls[]for i in s.split():ls.append(i)return len(ls[-1])

跟TED演讲学英文:The future will be shaped by optimists by Kevin Kelly

The future will be shaped by optimists Link: https://www.ted.com/talks/kevin_kelly_the_future_will_be_shaped_by_optimists Speaker: Kevin Kelly Date: August 2021 文章目录 The future will be shaped by optimistsIntroductionVocabularyTranscriptSummary后记 In…

Verilog基础语法——状态机(类型、写法、状态编码方式)

Verilog基础语法——状态机(类型、写法、状态编码方式) 写在前面一、状态机类型二、状态机写法2.1 一段式2.2 两段式2.3 三段式 三、状态机状态编码方式写在后面 写在前面 在FPGA设计过程,经常会设计状态机用于控制整个硬件电路的工作进程&am…

基于Vue3的Axios异步请求

基于Vue3的Axios异步请求 1. Axios安装与应用2. Axios网络请求封装3. axios网络请求跨域前端解决方案server.proxy 1. Axios安装与应用 Axios是一个基于promise的网络请求库,Axios.js.中文文档:https://axios.js.cn/ 安装:npm install --sa…

有没有一种可能性,你不投递简历,让HR主动联系你

你是否觉得自己得主动给某个公司投递了简历,他们才会联系你,亦或者是自己得主动在招聘APP上联系那个BOSS,他才会反过来跟你说话,又或者是你千方百计的跟他打招呼了,还是没有回应,这一节有可能让你明白,有时候是可以,你不主动,他也会主动联系你的。 目录 1 简历是如何…

QT:小项目:登录界面 (下一个连接数据库)

一、效果图 登录后&#xff1a; 二、项目工程结构 三、登录界面UI设计 四主界面 四、源码设计 login.h #ifndef LOGIN_H #define LOGIN_H#include <QDialog>namespace Ui { class login; }class login : public QDialog {Q_OBJECTpublic:explicit login(QWidge…

Spark原理之Cache Table的工作原理及实现自动缓存重复表的思考

CACHE TABLE的能力 使用此语法&#xff0c;可以由用户自定义要缓存的结果集&#xff0c;实际上就是一个临时表&#xff0c;不过数据存储在Spark集群内部&#xff0c;由Application所分配的executors管理。 一旦定义了一个缓存表&#xff0c;就可以在SQL脚本中随处引用这个表名…

Ansible自动化运维工具主机清单配置

作者主页&#xff1a;点击&#xff01; Ansible专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月24日12点21分 Ansible主机清单文件用于定义要管理的主机及其相关信息。它是Ansible的核心配置文件之一&#xff0c;用于Ansible识别目标主机并与其建立连接。 …

小猫咪邮件在线发送系统源码v1.1,支持添加附件

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 小猫咪邮件在线发送系统源码v1.1&#xff0c;支持添加附件 一款免登录发送邮件&#xff0c;支持发送附件&#xff0c;后台可添加邮箱,前台可选择发送邮箱 网站数据采取本地保存&…

Java将文件目录转成树结构

在实际开发中经常会遇到返回树形结构的场景&#xff0c;特别是在处理文件系统或者是文件管理系统中。下面就介绍一下怎么将文件路径转成需要的树形结构。 在Java中&#xff0c;将List<String>转换成树状结构&#xff0c;需要定义一个树节点类&#xff08;TreeNode&#…

分享一个网站实现永久免费HTTPS访问的方法

免费SSL证书作为一种基础的网络安全工具&#xff0c;以其零成本的优势吸引了不少网站管理员的青睐。要实现免费HTTPS访问&#xff0c;您可以按照以下步骤操作&#xff1a; 一、 选择免费SSL证书提供商 选择一个提供免费SSL证书的服务商。如JoySSL&#xff0c;他们是国内为数不…

排序算法大总结

引言 排序算法&#xff08;sorting algorithm&#xff09;是用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用&#xff0c;因为有序数据通常能够被更高效地查找、分析和处理。 如图 1-1 所示&#xff0c;排序算法中的数据类型可以是整数、浮点数、字符或字符串等…