STM32 通用定时器

一、概述

        STM32内部集成了多个定时/计数器,根据型号不同,STM32系列芯片最多包含8个定时/计数器。其中,TIM6、TIM7为基本定时器,TIM2~TIM5为通用定时器,TIM1、TIM8为高级控制定时器。

1.定时器的类型

  • 基本定时器
  • 通用定时器
  • 高级控制定时器
  • 窗口看门狗定时器
  • 独立看门狗定时器
  • 系统滴答定时器

2.计数模式

  • 向上计数模式:计数器从0计数到自动加载值(ARR),并产生向上溢出事件。

  • 向下计数模式:计数器从自动加载值(ARR)向下计数到0,并产生向下溢出事件。 

  • 中央对齐模式:计数器从0开始计数到自动加载值-1,产生向上溢出事件,然后向下计数到1,产生向下溢出事件,最后再从0开始重新计数。

3.主要功能 

  • 基本定时功能
  •  输出比较
  • 输入捕获
  • 编码器接口模式
  • 单脉冲模式
  • 死区控制和刹车功能

        注:本文将介绍前四种常见的功能。 

4.通用定时器的结构

        STM32通用定时器主要包括1个外部触发引脚(TIMx_ETR),4个输入/输出通道(TIMx_CH1、 TIMx_CH2、TIMx_CH3和TIMx_CH4),1个内部时钟1个触发控制器1个时钟单元(由预分频器PSC、自动重装载寄存器ARR和计数器CNT组成)。如图所示:

5.时钟源

        定时/计数器时钟可由下列时钟源(如上图所示)提供: 

  • 内部时钟(CK_INT)
  • 外部时钟模式1(TIMx_CH1~4)
  • 外部时钟模式2(TIMx_ETR)
  • 内部触发输入(ITR,使用一个定时器作为另一个定时器的预分频器)

        当时钟源来自内部时,可实现定时功能;当时钟源来自外部时,可实现计数功能。

6.功能寄存器

        略。

二、基本定时功能

        下面介绍TIM定时器最基础的功能:基本定时功能。这种功能常用于周期性事件的触发。使用流程和步骤如下:

  1. 选择时钟源
  2. 配置时基单元
  3. 配置NVIC
  4. 编写中断服务函数
void Timer_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟TIM_InternalClockConfig(TIM2);		//选择时钟源为内部时钟,若不调用此函数,TIM2默认也为内部时钟/*配置时基单元*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;			TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				//计数周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				//预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位,TIM_TimeBaseInit函数末尾,手动产生了更新事件,//若不清除此标志位,则开启中断后,会立刻进入一次中断,//如果不介意此问题,则不清除此标志位也可。TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//设置NVIC分组/*配置NVIC*/NVIC_InitTypeDef NVIC_InitStructure;						NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//设置抢占优先级为2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//设置响应优先级为1NVIC_Init(&NVIC_InitStructure);	TIM_Cmd(TIM2, ENABLE);			                            //使能TIM2,运行TIM2
}void TIM2_IRQHandler(void)                                      //定时器中断函数
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){    //此处编写要周期实现的功能TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

 三、输出比较功能

         当定时器的计数器值(CNT)与捕获比较寄存器(CCR)的值相等时,产生比较事件,并根据配置对输出管脚进行相应的操作,如翻转或置位。其应用场景如下:

  • PWM(脉宽调制)信号的产生:输出占空比可调的PWM信号,用于电机控制、LED调光等。
  • 定时脉冲:在特定时间产生一个脉冲信号,用于精确事件触发。
  • DAC触发:精确触发模拟信号输出。

        下面介绍产生PWM波的使用流程:

  1.  配置GPIO,用于输出PWM,根据引脚定义表配置
  2. 选择时钟源
  3. 配置时基单元
  4. 配置输出比较模式
void PWM_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟/*配置GPIO*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                 //复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);								//受外设控制的引脚,均需要配置为复用模式TIM_InternalClockConfig(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 = 36 - 1;               //预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);            /*配置输出比较模式*/ 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值,也可以不定为0,直接定为想要的占空比所需的CCR值TIM_OC3Init(TIM2, &TIM_OCInitStructure);                        TIM_Cmd(TIM2, ENABLE);			//使能TIM2,运行TIM2
}TIM_SetCompare3(TIM2, Compare);		            
//设置CCR的值,设置CCR几的值取决于使用哪个引脚,PA2对应的是CCR3
//该行代码用于改变占空比,一般放在主函数或者中断服务函数

 另外,PWM的三项重要数据的计算方法如下:

  1. 占空比:CCR/(ARR+1)
  2. 分辨率:1/(ARR+1)
  3. 频率:CK_PSC/(PSC+1)/(ARR+1),CK_PSC一般为72MHz

四、输入捕获功能 

        输入捕获模式用于测量外部信号的时间特性,例如周期、频率、脉宽等。它通过将外部输入信号的某个边沿(上升沿或下降沿)捕获并保存计数器的值,从而实现时间测量。 

        下面介绍通过输入捕获功能实现频率测量的步骤:

  1.  配置GPIO,用于接收需要测频率的信号,根据引脚定义表配置
  2. 选择时钟源
  3. 配置时基单元
  4. 配置输入捕获功能
  5. 编写频率计算函数
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);			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_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_ICInit(TIM3, &TIM_ICInitStructure);	/*选择触发源及从模式*/TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					//触发源选择TI1FP1TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);					//从模式选择复位,即TI1产生上升沿时,会触发CNT归零TIM_Cmd(TIM3, ENABLE);			//使能TIM3,运行TIM3
}uint32_t IC_GetFreq(void)
{return 1000000 / (TIM_GetCapture1(TIM3) + 1);		//测周法得到频率fx = fc / N,这里不执行+1的操作也可
}

         频率测量方法有两种,一种是适用于测量高频信号的测频法,一种是适用于测量低频信号的测周法。其原理如下图所示:

 五、编码器模式

        编码器接口模式用于解码旋转编码器的信号。它可以直接连接增量型旋转编码器的A相和B相信号,并解码出编码器的旋转方向和位置。 每个高级定时器和通用定时器都拥有1个编码器接口,两个输入引脚借用了输入捕获的通道1和通道2。

        下面介绍使用编码器模式来测量电机转速的步骤,硬件上将带编码器的电机的编码输出连接到STM32的PA6和PA7,具体如下:

  1. 配置GPIO,用于接收正交编码,根据引脚定义表配置
  2. 配置时基单元
  3. 配置输入捕获模式
  4. 配置编码器模式
  5. 配置另一个定时器,编写速度计算函数
void Encoder_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_IN_FLOATING;           //浮空输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;          //配置PA6和PA7引脚GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);							/*配置时基单元*/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 = 1 - 1;                //预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //使用TIM3/*配置输入捕获模式*/TIM_ICInitTypeDef TIM_ICInitStructure;							TIM_ICStructInit(&TIM_ICInitStructure);																																		TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				TIM_ICInitStructure.TIM_ICFilter = 0x10;							//输入滤波器参数,可以过滤信号抖动TIM_ICInit(TIM3, &TIM_ICInitStructure);	TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;				TIM_ICInitStructure.TIM_ICFilter = 0x10;							//输入滤波器参数,可以过滤信号抖动TIM_ICInit(TIM3, &TIM_ICInitStructure);							TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Rising);//配置编码器模式以及两个输入通道是否反相TIM_ClearFlag(TIM3, TIM_FLAG_Update);TIM_SetCounter(TIM3, 0);TIM_Cmd(TIM3, ENABLE);			                                //使能TIM3
}//使用二、基本定时功能,在中断服务函数中编写计算速度的代码。
//这里需要另外配置一个定时器,相关代码参考第二点,这里不再赘述。
//先计算电机转一圈,STM32收到的n个编码;这取决于电机本身的参数。
//再每隔T时间输出STM32一共接收到的N个编码;则N/n即这段时间T里电机转过的圈数。
//最后用N/n/T即可求出转速。其中:int n=xxx                     //根据电机参数计算int N=TIM_GetCounter(TIM3);   //STM32接收到的编码数TIM_SetCounter(TIM3, 0);      //拿到T时间内的编码数后,计数清零,重新计数float Speed=N/n/T;            //T为定时周期

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

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

相关文章

实战案例:结合大模型与爬虫技术实现12306智能查票系统

大语言模型,例如 GPT-4,拥有强大的知识储备和语言理解能力,能够进行流畅的对话、创作精彩的故事,甚至编写代码。然而,它们也面临着一些难以克服的困境,就像一个空有知识却无法行动的巨人 信息滞后&#xf…

Linux 之 安装软件、GCC编译器、Linux 操作系统基础

安装软件、GCC编译器、Linux 操作系统基础 学习任务: 安装 Vmware虚拟机、掌握Ubuntu 系统的使用认识 Ubuntu 操作系统的终端和 Shell掌握软件安装、文件系统、掌握磁盘管理与解压缩掌握 VIM 编辑器、Makefile 基本语法熟悉 Linux 常见指令操作 安装好开发软件&…

[Go语言快速上手]初识Go语言

目录 一、什么是Go语言 二、第一段Go程序 1、Go语言结构 注意 2、Go基础语法 关键字 运算符优先级 三、Go语言数据类型 示例 小结 一、什么是Go语言 Go语言,通常被称为Golang,是一种静态类型、编译型的计算机编程语言。它由Google的Robert Gr…

用HTML5+CSS+JavaScript庆祝国庆

用HTML5CSSJavaScript庆祝国庆 中华人民共和国的国庆日是每年的10月1日。 1949年10月1日,中华人民共和国中央人民政府成立,在首都北京天安门广场举行了开国大典,中央人民政府主席毛泽东庄严宣告中华人民共和国成立,并亲手升起了…

Vue3 中Ref的最佳实践

在vue3中如果我们需要获取一个响应式的变量,可以使用ref来定义一个变量。 const name ref( "" );name.value "test" 定义好后,就可以实现修改状态,更新UI的效果了。 在这个基础上,本文主要讨论跨组件时如何…

Discord:报错:A fatal Javascript error occured(解决办法)

按 Windows 键 R 并输入 %appdata% 选择 discord 文件夹并将其删除。 再次按 Windows 键 R 并输入 %LocalAppData% 选择 discord 文件夹并再次将其删除。 附加: 如果还不行,就通过官网下载吧,这个问题通过epic下载可能会有

Python并发编程挑战与解决方案

Python并发编程挑战与解决方案 并发编程是现代软件开发中的一项核心能力,它允许多个任务同时运行,提高程序的性能和响应速度。Python因其易用性和灵活性而广受欢迎,但其全局解释器锁(GIL)以及其他特性给并发编程带来了…

Docker面试-24年

1、Docker 是什么? Docker一个开源的应用容器引擎,是实现容器技术的一种工具,让开发者可以打包他们的应用以及环境到一个镜像中,可以快速的发布到任何流行的操作系统上。 2、Docker的三大核心是什么? 镜像:Docker的…

网络威胁情报技术的进步

网络威胁形势不断演变,必然导致防御者和攻击者之间持续展开军备竞赛。幸运的是,网络威胁情报 (CTI) 技术的进步为安全专业人员提供了强大的工具,使他们能够保持领先地位。 本指南深入探讨了 CTI 的最新进展,让您了解这些技术如何…

【学习笔记】手写一个简单的 Spring MVC

目录 一、什么是Spring MVC ? Spring 和 Spring MVC 的区别? Spring MVC 的运行流程? 二、实现步骤 1. DispatcherServlet 1. 创建一个中央分发器 拦截所有请求 测试 2. 接管 IOC 容器 1. 创建配置文件 2. 修改 web.xml 配置文件 …

1分钟搞懂K8S中的NodeSelector

文章目录 NodeSelector是什么?为什么使用NodeSelector?怎么用NodeSelector?POD配置示例yaml配置示例 如何知道K8S上面有哪些节点,每个节点都有什么信息呢?1. 使用kubectl命令行工具查看所有节点及其标签2. 使用kubectl…

算法【Java】—— 二叉树的深搜

深搜 深搜简单来说就是一直递归到底,然后返回,以二叉树为例,就是从根节点出发一直搜索到叶子节点,然后想上返回。 这里简单说明一下:深搜的英文缩写是 dfs,下面定义深搜函数名我直接命名为 dfs 实战演练 …

内存占用估算方法

优质博文:IT-BLOG-CN 通过掌握每种数据类型的大小,就可以更准确地预测对象和数据的内存消耗。 一、基础数据类型 Java基础数据类型结构,在64位系统开启指针压缩情况下的内存占用字节数: booleanbytecharshortintlongfloatdoub…

PYTHON实现HTTP request的一些有用的函数

前言 我们知道,当需要设计一个程序和服务器进行交互时,往往会用到HTTP的request,即服务器有一个对外接口REST API,因此当向服务器发送符合格式要求的HTTP request时,服务器会给出响应,甚至执行一些任务。如…

码随想录算法训练营第62天|卡码网:97. 小明逛公园、127. 骑士的攻击

1. 卡码网 97. 小明逛公园 题目链接:https://kamacoder.com/problempage.php?pid1155 文章链接:https://www.programmercarl.com/kamacoder/0097.小明逛公园.html 思路: 使用Floyd 算法,目的是解决多源最短路问题,即 …

如何编写一个优雅的commit message

在Git中,git commit 命令扮演着至关重要的角色。它的主要作用是将暂存区(staging area)里的改动内容提交到本地仓库(repository)中,形成一个新的版本或提交(commit)。这个过程是 Git…

基于Node2Vec的图嵌入实现过程

目录 一、引言二、Node2Vec(原理)2.1 随机游走(Random Walk)2.2 嵌入学习2.3 Node2Vec 的优势 三、使用 Node2Vec 进行图嵌入(实践)3.1 读取和转换 JSON 文件为 Graph 对象3.2 训练 Node2Vec 模型3.3 二维嵌…

10款好用的开源 HarmonyOS 工具库

大家好,我是 V 哥,今天给大家分享10款好用的 HarmonyOS的工具库,在开发鸿蒙应用时可以用下,好用的工具可以简化代码,让你写出优雅的应用来。废话不多说,马上开整。 1. efTool efTool是一个功能丰富且易用…

Kotlin:2.0.20 的新特性

一、概述 Kotlin 2.0.20英文版官方文档 Kotlin 2.0.20发布了!这个版本包括对Kotlin 2.0.0的性能改进和bug修复,我们在其中宣布Kotlin K2编译器为Stable。以下是本次发布的一些亮点: 数据类复制函数将具有与构造函数相同的可见性来自默认目标层次结构的源集的静态访…

Python批量下载PPT模块并实现自动解压

日常工作中,我们总是找不到合适的PPT模板而烦恼。即使有免费的网站可以下载,但是一个一个地去下载,然后再批量解压进行查看也非常的麻烦,有没有更好方法呢? 今天,我们利用Python来爬取一个网站上的PPT&…