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);
	}
}
相关推荐
zmd-zk1 小时前
flink学习(2)——wordcount案例
大数据·开发语言·学习·flink
Chef_Chen1 小时前
从0开始学习机器学习--Day33--机器学习阶段总结
人工智能·学习·机器学习
hopetomorrow2 小时前
学习路之压力测试--jmeter安装教程
学习·jmeter·压力测试
hopetomorrow2 小时前
学习路之PHP--使用GROUP BY 发生错误 SELECT list is not in GROUP BY clause .......... 解决
开发语言·学习·php
/**书香门第*/2 小时前
Cocos creator 3.8 支持的动画 7
学习·游戏·游戏引擎·游戏程序·cocos2d
美式小田2 小时前
单片机学习笔记 9. 8×8LED点阵屏
笔记·单片机·嵌入式硬件·学习
兰_博3 小时前
51单片机-独立按键与数码管联动
单片机·嵌入式硬件·51单片机
猫爪笔记3 小时前
前端:HTML (学习笔记)【2】
前端·笔记·学习·html
_不会dp不改名_3 小时前
HCIA笔记3--TCP-UDP-交换机工作原理
笔记·tcp/ip·udp
时光の尘3 小时前
C语言菜鸟入门·关键字·float以及double的用法
运维·服务器·c语言·开发语言·stm32·单片机·c