【STM32】TIM定时器定时中断与定时器外部时钟的使用

TIM定时器定时中断与定时器外部时钟的使用

  • 一、TIM定时器简介
    • 1、TIM(Timer)定时器
    • 2、定时器类型
    • 3、高级定时器
    • 4、通用定时器
    • 5、基本定时器
    • 6、定时中断基本结构
      • 代码编写:定时中断/外部时钟定时中断
    • 7、预分频器时序
    • 8、计数器时序
    • 9、计数器无预装时序(没有缓冲寄存器/影子寄存器)
    • 10、计数器有预装时序(有缓冲寄存器/影子寄存器)
    • 11、RCC时钟树

一、TIM定时器简介

1、TIM(Timer)定时器

  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断

  • 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时

    • 72M/65536/65536 = 中断频率
    • 时间= 1/中断频率 = 59.65s
  • 计数器:用来执行计数定时的一个寄存器,每来一个时钟,计数器加1

  • 预分频器:可以对计数器的时钟进行分频,让这个计数更加灵活

  • 自动重装寄存器:就是计数的目标值,就是要计多少个时钟申请中断
    以上这些寄存器构成了定时器最核心的部分,这一块电路称为时基单元

  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择输入捕获输出比较、编码器接口、主从触发模式等多种功能

  • 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

2、定时器类型

在这里插入图片描述
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4

3、高级定时器

在这里插入图片描述

4、通用定时器

时钟使用:内部与外部均可使用
计数模式:向上计数,向下计数,中央对齐计数

在这里插入图片描述
时基单元以上部分是内外时钟源选择和主从触发模式的结构
TRGI当做外部时钟来使用的时候,这一路就叫做“外部时钟模式1"

通用定时器和高级定时器的计数器支持向上计数模式,向下计数模式和中史对齐模式
向下计数模式:从重装值开始,向下自减,减到0之后,回到重装值同时申请中断。
中央对齐计数模式,就是从0开始,先向上自增,计到重装值,申请中断,然后再向下自减,减到0,再申请中断,然后继续下一轮,依次循环。

5、基本定时器

只能选择内部时钟, 计数模式只有向上计数

在这里插入图片描述
内部时钟频率72MHZ输入到时基单元:预分频器进行分频,计数器自增,计数值与自动重装寄存器的值进行比较,当两值相同时,产生中断

  • 预分频器:对输入的基准频率提前进行一个分频的操作
    寄存器 = 0时为1分频
    寄存器=1时为2分频…以此类推,即实际分频系数=预分频器值+1
    预分频器为16位,最大可分频65536
  • 计数器(向上计数):这个计数器可以对预分频后的计数时钟进行计数,计数时钟每来一个上升沿,计数器的值加1
    计数器为16位,计数器的范围为0-65535,超过65535,计数器回到0重新开始计数,所以计数器的值在计时过程中会不断地自增运行,当自增运行到目标值时,产生中断,完成了定时的任务。
  • 自动重装寄存器(16位寄存器):存储计数目标值的寄存器
    在运行的过程中,数值不断自增,自动重装值是固定的目标,当计数值等于自动重装值时,也就是计时时间到了,此时产生中断信号,并且清零计数器,计数器开始下一次的计数计时

计数值等于自动重装值产生的中断称为“更新中断”,这个更新中断之后就会通往NVIC,再配置好NVIC的定时器通道,那定时器的更新中断就能够得到CPU的响应了
图上画的一个向上的折线箭头,就代表这里会产生中断信号
向下的箭头,代表的是会产生一个事件,这里对应的事件就叫做“更新事件”,更新事件不会触发中断,但可以触发内部其他电路的工作

6、定时中断基本结构

在这里插入图片描述
中断输出控制就是一个中断输出的允许位

代码编写:定时中断/外部时钟定时中断

步骤:
1、RCC开启时钟
2、选择时基单元的时钟源—选择内部时钟源
3、配置时基单元—包括预分频器、自动重装器、计数模式等
4、配置输出中断控制,允许更新中断输出到NMIC
5、配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级
6、运行控制
7、使能计数器

// 定时器中断程序(使用内部时钟):定时器每秒计数加1,输出到OLED进行显示

// timer.c
#include "stm32f10x.h"                  // Device header

extern uint16_t Num;

void Timer_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    
    TIM_InternalClockConfig(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;// 预分频器的值
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;// 重复计数器的值(用于高级定时器)
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
    
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);  // 解决刚初始化完就进入中断的问题
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);
    
    TIM_Cmd(TIM2, ENABLE);
}

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

// main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;

int main(void)
{
    OLED_Init();
    Timer_Init();
    
    OLED_ShowString(1, 1, "Num:");
    
    while (1)
    {
        OLED_ShowNum(1, 5, Num, 5);
    }
}

// 定时器外部时钟:
\quad 使用对射式红外传感器的DO引脚接PA0,配置为上拉输入,配置定时器外部时钟模式
\quad 使用挡光片遮挡一次,次数加1,OLED显示计数值

// timer.c
#include "stm32f10x.h"                  // Device header

 extern uint16_t Num;
  
void Timer_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    //配置外部时钟模式--注意参数的填写
    TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;
    TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
    
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);
    
    TIM_Cmd(TIM2, ENABLE);
}

uint16_t Timer_GetCounter(void)
{
    return TIM_GetCounter(TIM2);
}

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


// main.c 
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;

int main(void)
{
    OLED_Init();
    Timer_Init();
    
    OLED_ShowString(1, 1, "Num:");
    OLED_ShowString(2, 1, "CNT:");
    
    while (1)
    {
        OLED_ShowNum(1, 5, Num, 5);
        OLED_ShowNum(2, 5, Timer_GetCounter(), 5);
    }
}

浮空输入使用场景:外部的输入信号功率很小,内部的这个上拉电阻可能会影响到这个输入信号,使用浮空输入,防止影响外部输入的电平

7、预分频器时序

在这里插入图片描述

  • 一个计数周期的工作流程:

\quad CK PSC,预分频器的输入时钟,选内部时钟的话一般是72MHZ,时钟在不断地运行,CNT_EN,计数器使能,高电平计数器正常运行,低电平计数器停止,CK_CNT,计数器时钟,它既是预分频器的时钟输出,也是计数器的时钟输入(开始时,计数器未使能,计数器时钟不运行,然后使能后,前半段,预分频器系数为1,计数器的时钟等于预分频器前的时钟,后半段,预分频器系数变为2,计数器的时钟就也变为预分频器前时钟的一半),在计数器时钟的驱动下,下面的计数器寄存器也跟随时钟的上升沿不断自增,在中间的这个位置FC之后,计数值变为0,可以推断出ARR自动重装值就是FC,当计数值计到和重装值相等,并且下一个时钟来临时,计数值才清零,同时,产生一个更新事件。
\quad 下方三行时序,描述的是这个预分频寄存器的一种缓冲机制,也就是这个预分频寄存器实际上是有两个,一个是预分频控制寄存器,供我们读写用的,它并不直接决定分频系数,另外还有一个缓冲寄存器或者说是影子寄存器,这个缓冲寄存器才是真正起作用的寄存器(比如我们在某个时刻,把预分频寄存器由0改成了1,如果在此时立刻改变时钟的分频系数,那么就会导致在一个计数周期内,前半部分和后半部分的频率不一样,计数计到一半,计数频率突然改变,一般不会有什么问题,但还是设计缓冲寄存器,当计数计到一半的时候改变了分频值,这个变化并不会立刻生效,而是会等到本次计数周期结束时,产生了更新事件,预分频寄存器的值才会被传递到缓冲寄存器里面去,才会生效),预分频器内部实际上也是靠计数来分频的,当预分频值为0时,计数器就一直为0,直接输出原频率,当预分频值为1时,计数器就0、1、0、1、0、1、0、1计数,在回到0的时候,输出一个脉冲,这样输出频率就是输入频率的2分频,预分频器的值和实际的分频系数之间有一个数的偏移。
计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)
如 1分频计算频率 :72M/(1+1) = 36M

8、计数器时序

分频因子为2即分频系数为2
在这里插入图片描述

  • 计数器的工作流程:

\quad 第一行是内部时钟72MHZ,第二行是时钟使能,高电平启动,第三行是计数器时钟,因为分频系数为2,所以这个频率是72M除2,然后计数器在这个时钟每个上升沿自增,当增到0036的时候,发生溢出,那计到36之后,再来一个上升沿,计数器清零,计数器溢出,产生一个更新事件脉冲,另外还会置一个更新中断标志位UIF,这个标志位只要置1了,就会去申请中断,然后中断响应后,需要在中断程序中手动清零。
计数器溢出频率:
CK_CNT_OV = CK_CNT / (ARR + 1)
= CK_PSC / (PSC + 1) / (ARR + 1)

9、计数器无预装时序(没有缓冲寄存器/影子寄存器)

在这里插入图片描述

  • 无预装时序情况:

\quad 计数器正在进行自增计数,突然更改了自动加载寄存器,就是自动重装寄存器,由FF改成了36,那计数值的目标值就由FF变成了36,所以这里计到36之后,就直接更新,开始下一轮计数

10、计数器有预装时序(有缓冲寄存器/影子寄存器)

通过设置ARPE位,就可以选择是否使用预装功能
在这里插入图片描述
\quad 在计数的中途,突然把计数目标由F5改成了36,可以看到下面有个影子寄存器,这个影子寄存器才是真正起作用的,它还是F5,所以现在计数的目标还是计到F5,产生更新事件,同时,要更改的36才被传递到影子寄存器,在下一个计数周期这个更改的36才有效。
\quad 所以可以看出,引入这个影子寄存器的目的实际上是为了同步,就是让值的变化和更新事件同步发生,防止在运行途中更改造成错误。无预装时序中不使用影子寄存器的话,F5改到36立刻生效,但此时计数值已经到了F1,已经超过36了,F1只能一直加到FFFF后回到0,再加到36才能产生更新,此时就会产生一些问题。

11、RCC时钟树

在这里插入图片描述
\quad 时钟树是STM32中用来产生和配置时钟,并且把配置好的时钟发送到各个外设的系统,时钟是所有外设运行的基础,所以时钟也是最先需要配置的,程序中执行主函数之前还会执行一个SystemInit函数,这个函数就是用来配置时钟树的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Arthas dashboard(当前系统的实时数据面板)

文章目录 二、命令列表2.1 jvm相关命令2.1.1 dashboard(当前系统的实时数据面板) 二、命令列表 2.1 jvm相关命令 2.1.1 dashboard(当前系统的实时数据面板) 使用场景: 在 Arthas 中,dashboard 命令用于提…

旋转机械故障诊断 震动故障分析与诊断

旋转机械故障诊断 机理资料整理 电气故障,机械故障(不平衡,不对中,松动,轴承,共振,流体振动,皮带松动),低速与高速机器故障诊断等 旋转机械故障诊断:机理资料整理 目录…

音视频入门基础:AAC专题(10)——FFmpeg源码中计算AAC裸流每个packet的pts、dts、pts_time、dts_time的实现

音视频入门基础:AAC专题系列文章: 音视频入门基础:AAC专题(1)——AAC官方文档下载 音视频入门基础:AAC专题(2)——使用FFmpeg命令生成AAC裸流文件 音视频入门基础:AAC…

移动硬盘‘需格式化‘困境:原因剖析、恢复策略与预防之道

困境直击:移动硬盘为何需格式化才能访问? 在数字化时代,移动硬盘作为数据存储与传输的重要工具,其稳定性与可靠性直接关系到用户数据的安全。然而,不少用户在使用过程中遭遇了“移动硬盘需要格式化才能打开”的尴尬境…

Stable Diffusion 优秀博客转载

初版论文地址:https://arxiv.org/pdf/2112.10752 主要流程图: Latent Diffusion Models(LDMs) DDPM是"Denoising Diffusion Probabilistic Models"的缩写, 去噪扩散概率模型 博客: 【论文阅读…

【LeetCode】146. LRU缓存

1.题目 2.思想 3.代码 3.1 代码1 下面这是一版错误的代码。错误的原因在于逻辑不正确导致最后的代码也是不正确的。 class LRUCache:def __init__(self, capacity: int):self.time 0 # 用于全局记录访问的时间self.num2time {} # 数字到时间的映射self.key2val {} # 数字…

OpenCV特征检测(8)检测图像中圆形的函数HoughCircles()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在灰度图像中使用霍夫变换查找圆形。 该函数使用霍夫变换的一种修改版本在灰度图像中查找圆形。 例子&#xff1a; #include <opencv2/imgp…

对抗攻击的详细解析:原理、方法与挑战

对抗攻击的详细解析&#xff1a;原理、方法与挑战 对抗攻击&#xff08;Adversarial Attack&#xff09;是现代机器学习模型&#xff0c;尤其是深度学习模型中的一个关键安全问题。其本质在于&#xff0c;通过对输入数据添加精微的扰动&#xff0c;人类难以察觉这些扰动&#…

计算机网络:概述 --- 体系结构

目录 一. 体系结构总览 1.1 OSI七层协议体系结构 1.2 TCP/IP四层(或五层)模型结构 二. 数据传输过程 2.1 同网段传输 2.2 跨网段传输 三. 体系结构相关概念 3.1 实体 3.2 协议 3.3 服务 这里我们专门来讲一下计算机网络中的体系结构。其实我们之前…

前端组件库Element UI 的使用

一、准备工作 1.确保安装了开发软件 VS Code&#xff08;此处可查阅安装 VS Code教程&#xff09;&#xff0c;确保相关插件安装成功 2.安装Node.js 和创建Vue项目&#xff08;此处可查阅安装创建教程&#xff09; 3.成功在VS Code运行一个Vue项目&#xff08;此处可查阅运行…

Pybullet 安装过程

Pybullet 安装过程&#xff08;windows&#xff09; 1. 安装C编译工具2. 安装Pybullet 1. 安装C编译工具 pybullet 需要C编译套件&#xff0c;直接装之前检查下&#xff0c;要不会报缺少某版本MVSC的error&#xff0c;最好的方式是直接下载visual studio&#xff0c;直接按默认…

信息安全工程师(8)网络新安全目标与功能

前言 网络新安全目标与功能在当前的互联网环境中显得尤为重要&#xff0c;它们不仅反映了网络安全领域的最新发展趋势&#xff0c;也体现了对网络信息系统保护的不断加强。 一、网络新安全目标 全面防护与动态应对&#xff1a; 目标&#xff1a;建立多层次、全方位的网络安全防…

【渗透测试】-vulnhub源码框架漏洞-Os-hackNos-1

vulnhub源码框架漏洞中的CVE-2018-7600-Drupal 7.57 文章目录  前言 1.靶场搭建&#xff1a; 2.信息搜集&#xff1a; 主机探测&#xff1a; 端口扫描&#xff1a; 目录扫描&#xff1a; 3.分析&#xff1a; 4.步骤&#xff1a; 1.下载CVE-2018-7600的exp 2.执行exp: 3.写入木…

【C++篇】引领C++模板初体验:泛型编程的力量与妙用

文章目录 C模板编程前言第一章: 初始模板与函数模版1.1 什么是泛型编程&#xff1f;1.1.1 为什么要有泛型编程&#xff1f;1.1.1 泛型编程的优势 1.2 函数模板的基础1.2.1 什么是函数模板&#xff1f;1.2.2 函数模板的定义格式1.2.3 示例&#xff1a;通用的交换函数输出示例&am…

Redis面试真题总结(四)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ AOF 持久化&#xff1f; AOF&#xff08;Append Only File&#x…

ETCD学习使用

一、介绍 etcd&#xff08;分布式键值存储&#xff09;是一个开源的分布式系统工具&#xff0c;用于可靠地存储和提供键值对数据。etcd 通常通过 HTTP 或 gRPC 提供 API&#xff0c;允许应用程序通过简单的接口与其交互。由于其可靠性和稳定性&#xff0c;etcd 在构建可扩展、分…

【OpenAI o1背后技术】Sef-play RL:LLM通过博弈实现进化

【OpenAI o1背后技术】Sef-play RL&#xff1a;LLM通过博弈实现进化 OpenAI o1是经过强化学习训练来执行复杂推理任务的新型语言模型。特点就是&#xff0c;o1在回答之前会思考——它可以在响应用户之前产生一个很长的内部思维链。也就是该模型在作出反应之前&#xff0c;需要…

k8s中pod的创建过程和阶段状态

管理k8s集群 kubectl k8s中有两种用户 一种是登录的 一种是/sbin/nologin linux可以用密码登录&#xff0c;也可以用证书登录 k8s只能用证书登录 谁拿到这个证书&#xff0c;谁就可以管理集群 在k8s中&#xff0c;所有节点都被网络组件calico设置了路由和通信 所以pod的ip是可以…

WebRTC编译后替换libwebrtc.aar时提示找不到libjingle_peerconnection_so.so库

Loading native library: jingle_peerconnection_so 问题原因&#xff1a;编译的时候只编译了armeabi-v7a的版本&#xff0c;但是应用程序是arm64-v8a&#xff0c;所以无法运行 解决方法&#xff1a;更新编译脚本&#xff0c;加上arm64-v8a进行编译 ./tools_webrtc/android/bu…

【漏洞复现】用友 NC-Cloud queryStaffByName Sql注入漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…