STM32——定时器

一、定时器简介:


1.最大59.65s定时:
因为预分频器和自动重装寄存器的最大存储值都是65536,当预分频器设置为65536时,就是当有65536个波形输入到预分频器时,预分频器才会输出一个波形到计数器,然后跟自动重装寄存器的值进行比对,只有当计数器也计到65536时才会产生一个更新事件,因此最大定时时间 = 72MHz / 65536 / 65536 = 59.65s

2.预分频器的值为0时 = 不分频/1分频

二、定时器类型:

(1)基本定时器:

触发信号:

在嵌入式系统、微控制器(如STM32)以及许多其他电子系统中,触发信号(Trigger Signal)通常用于启动某个事件、中断、操作或转换。这些信号可以是内部的,由微控制器自身产生,也可以是外部的,由其他硬件组件(如传感器、开关或其他微控制器)提供。

触发输出TRGO介绍:

**触发输出TRGO是定时器模块中的一个输出信号,当定时器满足特定条件时,这个信号会被激活并输出到微控制器的总线上。其他外设或定时器可以通过监听这个总线来接收TRGO信号,并根据需要执行相应的操作。

**

主模式触发DAC功能:

(1)普通情况:
如果我们需要DAC输出一段波形,那么就需要每隔一段时间触发一次DAC让他输出下一个电压点,那么就是先设置一个定时器产生中断,每隔一段时间在中断程序中调用代码手动触发一次DAC转换然后DAC输出,这样会频繁进入中断程序,影响主程序的运行和其他中断程序的响应。
(2)主模式触发:
把定时器的更新事件映射到触发输出TRGO的位置,然后TRGO接带DAC的触发转换引脚上,那么就不会产生中断导致频繁进到中断程序。

(2)通用定时器:

ETRF作为外部时钟(外部时钟模式2):


根据引脚定义可以知道,这个TINX_EXT引脚接在PA0处,因此可以在PA0引脚接一个外部方波时钟,然后在配置极性和、边沿检测和预分频器再经过滤波电路就可以兵分两路:

1.上面一路ETRF进入触发控制器然后就可以被选择作为时基单元的时钟了(外部时钟模式2)。

2.下面一路可以作为触发输入的时钟来源,两者可以看作是等价的,只是下面一路会占用触发输入通道而已。

TRGI触发输入作为外部时钟(时钟模式1):

触发输入的信号来源有很多,其中之一就是前面说到的ETR外部时钟。

此外,还有ITR信号,这个信号来自其他定时器,从结构图的右上角部分可以看到,主模式的触发输出TRGO是可以通向其他定时器的,然后作为其他定时器的时钟信号输入,这时候就是将TRGO接到ITR0...1...2...3上。

主从模式概述:

定义: 主从模式涉及至少两个定时器,其中一个作为主定时器(Master Timer),另一个或多个作为从定时器(Slave Timer)。主定时器在满足特定条件时,会发送信号给从定时器,触发从定时器的某些功能。

应用场景: 适用于需要多个定时器同时或协同工作的场景,如PWM信号的生成、精确的时间间隔控制等。

通过这个模式就可以实现定时器的级联:
1.先初始化TIM3,然后使用主模式把它的更新事件映射到TRGO上(TIM3为主模式)。

2.然后再初始化TIM2,选择ITR2,从表中可以知道,TIM2的ITR2是接在TIM3上的,那么ITR2对应的就是TIM3的触发输出TRGO,然后再选择外部时钟模式1(模式2对应的是ETR大的信号输入),那么当TIM3产生更新事件通过触发输出TRGO输出一段波形就会经过ITR2作为TIM2的外部时钟信号输入到TIM2(从模式)的时基单元,从而实现定时器的级联,就是定时器3控制定时器2产生更新中断或者更新事件的时间。

触发控制器:STM32的触发控制器在定时器模块中扮演着关键角色,它主要负责处理触发信号

(1)触发控制器的功能部分
1.输出TRGO信号:

TRGO信号是定时器输出给其它定时器、DAC/ADC等外设的触发信号。
当定时器工作在主模式时,TRGO信号用于触发其他从模式的定时器或外设。

2.从模式控制器:

通过接收到的ETRF(外部触发帧)、TRGI(触发输入)等信号来实现对自身计数器的控制。

控制操作包括复位、使能、向上/向下控制、计数等。
3.编码器接口:

用于实现与增量编码器的接口,接收编码器的信号并据此进行计数或位置检测。

(2)触发控制器的输入
1.内部时钟CK_INT:

最常用的方法,用于计数,通常与系统时钟同步。

2.外部输入ETRF:

可以作为定时器时钟进行计数,用于同步多个定时器。

也可以输出到TRGI对定时器进行复位、门控、触发等控制。

3.外部输入TI1FP1、TI2FP2(CH1和CH2引脚):

可以作为触发输入信号,连接到从模式控制器。

用于对定时器进行复位、门控、触发等控制。

也可以实现与编码器接口。

4.TI1F_ED:(CH1引脚的边沿)

与或门输入,用于实现与霍尔传感器接口。

主要由通道1的输入信号经过上升沿、下降沿双沿检测后生成的脉冲信号。

5.内部输入ITR:

可以作为其他定时器的触发输出信号输入,连接到本定时器的内部触发输入端ITRx(x可能是0~3)。

允许常规定时器内部最多有4路内部输入选择端。

归纳

触发控制器在STM32定时器中起着桥梁和枢纽的作用,它连接着外部信号源和定时器的内部逻辑。通过合理配置触发控制器,可以实现定时器与外部设备的精确同步和协同工作,为各种应用提供强大的定时和触发功能。

(3)高级定时器:


2部分:
高级定时器的1部分是和通用定时器相同的,而2部分在中断时是能够实现几个技术周期才产生更新中断或者更新事件,而通用定时器是一个计数周期就产生一次更新中断或者更新事件,因此高级定时器的计数时间就有原来的59.65s变为59.65 × 65536s

3部分:
这个部分可以用于产生PWM波形、控制三相电机等等。

三、定时器内部时序:

(1)预分频器时序图:

预分频控制寄存器和预分频缓冲寄存器:

预分频控制寄存器和预分频缓冲寄存器在数字电路和微控制器(如STM32)的定时器系统中扮演着关键角色。以下是关于这两个寄存器的详细解释:

(1)预分频控制寄存器:

  • 功能:
    预分频控制寄存器主要用于读写操作,但它并不直接决定分频系数。它存储的是用户设置或期望的分频值。
  • 特点:
    寄存器的值表示用户期望的分频系数。
    更改寄存器的值并不会立即影响定时器的时钟频率。
    当寄存器的值发生变化时(例如从0变为1),这种变化并不会立即反映到实际的分频操作中。
  • 与预分频缓冲寄存器的关系:
    预分频控制寄存器的值会在特定条件下(如计数周期结束并产生更新事件后)被传递到预分频缓冲寄存器。

(2)预分频缓冲寄存器:

  • 功能:

    预分频缓冲寄存器是真正决定分频系数的寄存器。它根据预分频控制寄存器的值来执行实际的分频操作。

  • 特点:

    寄存器的值直接控制定时器的时钟频率。

    它的值在接收到预分频控制寄存器的更新后才会改变。

    在接收到更新之前,它会继续使用之前的分频系数。

    与预分频控制寄存器的关系:当预分频控制寄存器的值发生变化并满足更新条件时,这个新的值会被传递到预分频缓冲寄存器,从而改变定时器的时钟频率。

  • 总结:

  • 预分频控制寄存器和预分频缓冲寄存器在定时器系统中协同工作,以实现精确的时钟频率控制。

  • 预分频控制寄存器用于存储用户设置的分频值,但并不直接控制定时器的时钟频率。

  • 预分频缓冲寄存器根据预分频控制寄存器的值来执行实际的分频操作,并直接控制定时器的时钟频率。

  • 当预分频控制寄存器的值发生变化时,这种变化并不会立即反映到定时器的时钟频率上,而是需要等待满足更新条件后才会被传递到预分频缓冲寄存器并执行相应的分频操作。

(2)计数器时序图:

计数器无预装时序:


如果没有预装的影子寄存器,那么当我们突然更改自动加载寄存器,如果计数器的寄存器还没有达到我更改的数值还好,不会有什么问题,但是如果已经超过我更改的数值,那么计数器就会一直计数到最大值自动清0再从0达到我的更改值才产生更新时间或更新中断。

计数器有预装时序:


有预装之后,计数器实际上是与自动加载影子寄存器的值进行对比,如果我突然更改自动加载寄存器的值,自动加载影子寄存器的值并不会马上更改,而是在经过原来的一个计数周期之后才更改。

四、时钟树:

左边1部分是时钟产生电路
右边2部分是时钟分配电路

系统时钟产生:系统时钟由内部RC振荡器和外部高速晶振提供

外部晶振比RC振荡器更稳定。

  • 1.一开始启动内部时钟,单片机系统时钟SYSCLK暂时由内部时钟RC振荡器提供 = 8MHz
  • 2.之后再配置外部时钟,配置外部时钟走红色通路经过锁相环进行9倍频后得到72MHz,等到锁相环输出稳定后选择锁相环输出为系统时钟 = 72MHz频率

锁相环:

锁相环结构:


压控振荡器:

  • 压控振荡器本身就能够产生几十GHZ的频率,输入频率与反馈频率通过鉴相器进行比较,使压控振荡器输出的频率非常大,但是和输入频率的相位是相同的,因此这是锁相环能够实现倍频的关键

系统时钟分配:

  • 通用定时器和基本定时器是接在APB1总线上的,在APB1总线连接其他外设的线路中有一条支路专门用来连接定时器,而这个支路上的电路将APB1总线的信号频率进行了加工,如果APB1总线的频率经过分频后变成了36MHz,这个电路会把输入的频率进行×2 = 72MHz再输出给定时器。

五、内部时钟定时中断程序实现:

总代码:

c 复制代码
#include "stm32f10x.h"                  // Device header

extern uint16_t Num;//自己查找一下
void Timer_Init(void)
{
	//使能定时器2的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	//选择内部时钟为TIM2时基单元的时钟源
	TIM_InternalClockConfig(TIM2);
	//配置时基单元
	//初始化定时器2时基单元的结构体
	TIM_TimeBaseInitTypeDef TIM_TIM2_InitStructure;
	/*
	TIM_ClockDivision 是这个结构体中的一个字段,用于设置定时器的时钟分频。
	具体来说,它决定了定时器时钟(TIMxCLK)的频率与内部时钟(CK_INT)之间的关系。
	TIM_CKD_DIV1 表示不进行分频,即 CK_INT = TIMxCLK。
	换句话说,定时器的内部时钟频率与输入的定时器时钟频率相同。
	*/
	TIM_TIM2_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	//计数器模式:选择向上计数模式
	TIM_TIM2_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	//ARR自动重装器的值
	TIM_TIM2_InitStructure.TIM_Period = 10000 - 1;
	//PSC预分频器的值
	TIM_TIM2_InitStructure.TIM_Prescaler = 7200 - 1;
	//重复计数器的值,高级计数器才有
	TIM_TIM2_InitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2,&TIM_TIM2_InitStructure);
	
	//使能中断,开启了更新中断到NVIC的通路
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	
	//配置NVIC,配置优先级
	//先选择分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//
	NVIC_InitTypeDef NVIC_TIM2_InitStructure;
	NVIC_TIM2_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_TIM2_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_TIM2_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_TIM2_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_TIM2_InitStructure);
	
	//启动定时器
	TIM_Cmd(TIM2,ENABLE);
}

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
	{
		Num++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

需要了解的函数:

步骤实现根据下图:

第一步:RCC打开时钟

示例使用定时器2,它是APB1总线上的外设:

c 复制代码
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

第二步:给时基单元选择时钟源

c 复制代码
//选择内部时钟为TIM2时基单元的时钟源
	TIM_InternalClockConfig(TIM2);

第三步:配置时基单元

注意:

基本定时器只有向上计数模式,通用和高级定时器有向上、向下、中央对齐模式

c 复制代码
//配置时基单元
	//初始化定时器2时基单元的结构体
	TIM_TimeBaseInitTypeDef TIM_TIM2_InitStructure;
	/*
	TIM_ClockDivision 是这个结构体中的一个字段,用于设置定时器的时钟分频。
	具体来说,它决定了定时器时钟(TIMxCLK)的频率与内部时钟(CK_INT)之间的关系。
	TIM_CKD_DIV1 表示不进行分频,即 CK_INT = TIMxCLK。
	换句话说,定时器的内部时钟频率与输入的定时器时钟频率相同。
	*/
	TIM_TIM2_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	//计数器模式:选择向上计数模式
	TIM_TIM2_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	//ARR自动重装器的值
	TIM_TIM2_InitStructure.TIM_Period = 10000 - 1;
	//PSC预分频器的值
	TIM_TIM2_InitStructure.TIM_Prescaler = 7200 - 1;
	//重复计数器的值,高级计数器才有
	TIM_TIM2_InitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2,&TIM_TIM2_InitStructure);

第四步:配置输出中断控制,允许更新中断输出到NVIC

c 复制代码
//使能中断,开启了更新中断到NVIC的通路
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);

第五步:配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级并启动定时器

c 复制代码
//配置NVIC,配置优先级
	//先选择分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//
	NVIC_InitTypeDef NVIC_TIM2_InitStructure;
	NVIC_TIM2_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_TIM2_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_TIM2_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_TIM2_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_TIM2_InitStructure);
	//启动定时器
	TIM_Cmd(TIM2,ENABLE);

第六步:到启动文件Startup查找TIM2对应的中断函数名,编写中断程序:

注意检查中断标志位并在最后清除标志位

c 复制代码
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
	{
		Num++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}
相关推荐
yutian06062 小时前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
sanguine__5 小时前
Web APIs学习 (操作DOM BOM)
学习
冷眼看人间恩怨5 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
析木不会编程5 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
数据的世界017 小时前
.NET开发人员学习书籍推荐
学习·.net
四口鲸鱼爱吃盐7 小时前
CVPR2024 | 通过集成渐近正态分布学习实现强可迁移对抗攻击
学习
枯无穷肉9 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
OopspoO9 小时前
qcow2镜像大小压缩
学习·性能优化
不过四级不改名6779 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件