STM32 通用定时器

server/2024/10/20 10:13:57/

一、概述

        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.ppmy.cn/server/127389.html

相关文章

以串口接口为例介绍关于BSP底层架构开发的迭代过程

以串口接口为例介绍关于BSP底层架构开发的迭代过程 文章目录 以串口接口为例介绍关于BSP底层架构开发的迭代过程架构概述初代BSP二代BSP:三代BSP:四代BSP:架构概述 单片机开发有四个阶段: 阶段一:单一单片机的功能实现阶段 此阶段你开始熟悉STM32F1系列的单片机,并利用…

探索Python的工业通信之光:pymodbus的奇妙之旅

文章目录 探索Python的工业通信之光:pymodbus的奇妙之旅背景:为何选择pymodbus?pymodbus是什么?如何安装pymodbus?5个简单的库函数使用方法3个场景使用示例常见bug及解决方案总结 探索Python的工业通信之光&#xff1a…

Linux下Socket编程

1. Socket简介 Socket是什么? Socket是一种进程间通信的机制,通过它应用程序可以通过网络进行数据传输。Socket提供了一种跨平台的接口,使得同样的代码可以在不同的操作系统上运行。Socket类型 流式套接字(SOCK_STREAM&#xff0…

Linux中的 `vi` 与 `vim` 使用详解

文章目录 Linux中的 vi 与 vim 使用详解1. vi 编辑器1.1 什么是 vi1.2 vi 的基本用法1.2.1 启动 vi1.2.2 模式1.2.3 基本操作1.2.4 常用命令 1.3 vi 的特点 2. vim 编辑器2.1 什么是 vim2.2 vim 的基本用法2.2.1 启动 vim2.2.2 模式2.2.3 vim 的增强功能2.2.4 vim 的基本操作 2…

Oracle 配置恢复目录catalog

一.介绍 Oracle中使用RMAN备份的数据我们分为两类 RMAN知识库数据库的数据块 Oracle默认把 RMAN知识库 放在目标数据库的控制文件中,在以后进行恢复的时候 我们要先读知识库的信息然后才能恢复。 但这样就产生了一个问题,知识库放在了控制文件上&#xf…

国庆更新|芒果YOLOv8改进181:即插即用,最新注意力机制EMA:具有跨空间学习的高效多尺度注意力模块,ICCASSP论文

💡本篇内容:芒果YOLOv8改进135:最新注意力机制EMA:即插即用,具有跨空间学习的高效多尺度注意力模块,ICCASSP 论文 **EMA|具有跨空间学习的高效多尺度注意力模块 | 即插即用 该模块通常包括多个并行的注意力子模块,每个子模块关注于输入数据的不同尺度或分辨率。这些子模块…

2017~2018博文汇总目录

2018 设置IDEA支持JS ES6语法_idea 社区版 js语法-CSDN博客 Slf4j 不起作用的问题_slf4j不起作用-CSDN博客 GC垃圾回收器:CMS收集器和G1收集器优缺点_g1gc的缺点-CSDN博客 Java并发之AQS详解-CSDN博客 计数排序、桶排序、基数排序_if (array.length 0) return …

检查cuda和显卡的可用性

检查cuda和显卡的可用性 import torch device_gpu torch.device(cuda if torch.cuda.is_available() else cpu) print(device_gpu) print(torch.cuda.is_available())