MCU中的定时器

第一章 定时器的应用场景

第二章 定时器的原理

2.1 定时器的计数原理

 

1. 定时器的本质是一个计数器;

2. 计数器是对输入的系统频率信号进行计数;

3. 每来一个周期的信号,计数器的cnt 加一。如果周期T表示为1s,来三个周期就表示3s时间到达;如果系统时钟频率是120MHz,那么周期为T = (1/120)us,记录120次,t = 120 * T us = 1us时间到;

2.2 定时器的计数上限

2.3 定时器的预分频器

假定预分频器的值设置为120,总线给定时器的时钟频率为120MHZ;

在定时器的预分频器(Prescaler)设置为 120 的情况下,输入时钟频率为 120 MHz 时,预分频器会对输入的时钟信号进行分频。具体分频过程如下:

  1. 预分频器的功能:它的作用是将高频的输入时钟信号降低到适合计数器工作的频率。
  2. 预分频器设置为 120:意味着预分频器需要 120 个输入脉冲 才会输出 1 个脉冲
  3. 输出到计数器的频率:输入时钟频率为 120 MHz,经过预分频器分频后,输出频率为: 

f = 120MHz / 120 = 1 MHz

因此,输入到预分频器的 120 个脉冲,会转换成输出的 1 个脉冲,然后这个脉冲才会送到定时器的计数器。

总结:

  • 预分频器 将输入的 120 MHz 时钟分频为 1 MHz。
  • 计数器 以 1 MHz 的频率进行计数。

那么输入120个脉冲到预分频器后,才产生1个脉冲给计数器

2.4 定时器的硬件结构

2.5 定时器的时钟树

2.6 通用定时器和高级定时器

  1. 最直观的区别是通道个数;

第三章 定时器输出PWM

3.1 应用举例

3.2 定时器利用输出比较产生PWM控制LED

3.2.1 GD32

  1.  如果T = 1us,周期为1us。每来一个脉冲计数器加一,并且将累计后的值CNT和输出比较值比较。如果小于输出比较值,输出高电平;如果大于输出比较值,输出低电平;
  2. 如果累加值累加到自动重装载值后,就会触发自动重载操作,将计数器中的累加值CNT置为为0,重新开始累加。
  • 定时器配置

3.2.2 stm32F4

注意:

  1. PF9口本来是一个普通的GPIO口,现在需要配置为定时器14的通道1,所以需要将IO口设置为复用功能;
  2. 配置定时器14输出100HZ的频率(周期为10ms),

3.3 定时器利用输入捕获功能测量信号的周期频率或脉冲宽度

3.3.1 输入捕获的原理一

  1. 预分配一直给计数器周期性的脉冲,并且假定周期为T = 1us;
  2. 计数器每来一个脉冲,计数器中的CNT累加1。
  3. 突然某一个时刻,GPIO口的电平发生跳变(假定是一个上升沿),此时会触发捕获事件以及和一个中断事件。
  4. 捕获事件将计数器中的值保存到输入捕获寄存器中保存着;
  5. 中断事件(中断函数中)将输入捕获寄存器中的值取出等待使用,假定为V1;
  6. 突然某一个时刻,GPIO口的电平又发生跳变(假定是一个上升沿),此时会触发捕获事件以及和一个中断事件。
  7. 捕获事件将计数器中的值保存到输入捕获寄存器中保存着;
  8. 中断事件(中断函数中)将输入捕获寄存器中的值取出,假定为V2;

中断中将V2 - V1得到一个差值V3;V3 * T = V3 * 1us的结果就是输入的周期频率

3.3.2 输入捕获的原理二

  1. 预分配一直给计数器周期性的脉冲,并且假定周期为T = 1us;
  2. 计数器每来一个脉冲,计数器中的CNT累加1。
  3. 突然某一个时刻,GPIO口的电平发生跳变(假定是一个上升沿),此时会触发捕获事件以及和一个中断事件。
  4. 捕获事件将计数器中的值保存到输入捕获寄存器中保存着;
  5. 中断事件(中断函数中)将计数器中的值第一次置位为0;
  6. 突然某一个时刻,GPIO口的电平又发生跳变(假定是一个上升沿),此时会触发捕获事件以及和一个中断事件。
  7. 捕获事件将计数器中的值保存到输入捕获寄存器中保存着;
  8. 中断事件(中断函数中)将计数器中的值读取出来为V1;

中断中将V1 * T = V1 * 1us的结果就是输入捕获的周期信号的频率。

3.3.3 代码示例;

project/

├── main.c // 主函数入口

├── tim_capture.c // 定时器输入捕获功能实现

├── tim_capture.h // 定时器输入捕获功能声明

 1. 头文件 (tim_capture.h)

#ifndef TIM_CAPTURE_H
#define TIM_CAPTURE_H

#include "stm32f10x.h"

// 捕获功能初始化函数
void TIM2_InputCapture_Init(void);

// 获取捕获的周期(单位: 微秒)
uint32_t TIM2_GetPeriod(void);

#endif // TIM_CAPTURE_H

2. 源文件 (tim_capture.c)

#include "tim_capture.h"

// 全局变量,存储捕获结果
static volatile uint16_t capture1 = 0, capture2 = 0; // 捕获值
static volatile uint32_t period = 0;                // 信号周期
static volatile uint8_t captureFlag = 0;            // 捕获完成标志

// 定时器输入捕获初始化
void TIM2_InputCapture_Init(void) {
    // 1. 启用时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // TIM2 时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // GPIOA 时钟

    // 2. 配置 GPIO
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;             // PA0: TIM2_CH1
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 3. 配置定时器
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
    TIM_TimeBaseStruct.TIM_Period = 0xFFFF; // 最大计数值
    TIM_TimeBaseStruct.TIM_Prescaler = 72 - 1; // 定时器分频为 72MHz / 72 = 1MHz
    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);

    // 4. 配置输入捕获
    TIM_ICInitTypeDef TIM_ICInitStruct;
    TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;         // 捕获通道 1
    TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; // 捕获上升沿
    TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; // 直接输入
    TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;    // 无分频
    TIM_ICInitStruct.TIM_ICFilter = 0x0;                 // 无滤波
    TIM_ICInit(TIM2, &TIM_ICInitStruct);

    // 5. 使能中断
    TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE); // 使能捕获比较中断
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    // 6. 启动定时器
    TIM_Cmd(TIM2, ENABLE);
}

// 获取捕获周期
uint32_t TIM2_GetPeriod(void) {
    return period; // 返回测量的信号周期
}

// 定时器中断处理函数
void TIM2_IRQHandler(void) {
    if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) {
        TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); // 清除中断标志

        if (captureFlag == 0) {
            capture1 = TIM_GetCapture1(TIM2); // 第一次捕获
            captureFlag = 1;
        } else if (captureFlag == 1) {
            capture2 = TIM_GetCapture1(TIM2); // 第二次捕获
            if (capture2 > capture1) {
                period = capture2 - capture1; // 正常情况
            } else {
                period = (0xFFFF - capture1 + capture2 + 1); // 计数器溢出情况
            }
            captureFlag = 0; // 重置标志
        }
    }
}

3. 主函数 (main.c)

#include "stm32f10x.h"
#include "tim_capture.h"

int main(void) {
    SystemInit();                // 初始化系统时钟
    TIM2_InputCapture_Init();    // 初始化 TIM2 输入捕获功能

    while (1) {
        // 检查是否已经测量到信号周期
        uint32_t signalPeriod = TIM2_GetPeriod();
        if (signalPeriod != 0) {
            // 计算信号频率 (Hz)
            float frequency = 1000000.0f / signalPeriod; // 1 MHz 时钟基准
            // 用户可添加进一步处理,如显示频率或其他逻辑
        }
    }
}

触发中断的条件

根据代码和 STM32 的定时器捕获功能,以下条件会触发这个中断:

  1. TIM2 的输入捕获通道 1(CC1)检测到信号边沿跳变

    • 在定时器配置时,将 CC1 通道配置为输入捕获模式,并设置边沿触发类型(上升沿、下降沿或双边沿)。
    • 例如,信号从低到高(上升沿)或高到低(下降沿)跳变时,捕获触发。
  2. 中断使能

    • 必须启用 TIM2 的 CC1 通道中断:

      TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);

  3. 中断标志位被置位

    • 捕获事件发生时,定时器的 CC1 中断标志位(TIM_IT_CC1)会被置位,随后触发 TIM2_IRQHandler

所以输入信号的周期为:

T = capture2 - capture1; // 周期的单位是微秒

所以输入信号的频率frequency (Hz)        f = (1 / T ) * 10^6 Hz

3.4 定时器输出定时功能

  1. 假定总线给定时器的时钟频率为120MHz时,并且设置预分频器的预分频值为120;那么产生的时钟频率f = 1MHz。
  2. T = 1us,如果设置自动重装载值为600,并且设置定时器为向下计数。那么当从600递减为0时,定时器会产生一个(1us * 600)的600us中断,表示600us时间到达。

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

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

相关文章

类和对象——static 成员,匿名对象(C++)

1.static成员 a)⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进行初始化。 b)静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。 …

POD-Transformer多变量回归预测(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现POD-Transformer多变量回归预测,本征正交分解数据降维融合Transformer多变量回归预测,使用SVD进行POD分解(本征正交分解); 2.运行环境Matlab20…

C#中的二维数组的应用:探索物理含义与数据结构的奇妙融合

在C#编程中,二维数组(或矩阵)是一种重要的数据结构,它不仅能够高效地存储和组织数据,还能通过其行、列和交叉点(备注:此处相交处通常称为“元素”或“单元格”,代表二维数组中的一个…

【网络安全 | 漏洞挖掘】通过密码重置污染实现账户接管

未经许可,不得转载。 文章目录 密码重置污染攻击漏洞挖掘的过程目标选择与初步测试绕过 Cloudflare 的尝试发现两个域名利用 Origin 头部污染实现账户接管攻击流程总结在今天的文章中,我们将深入探讨一种 账户接管 漏洞,并详细分析如何绕过 Cloudflare 的保护机制,利用密码…

uniapp 相关的swiper的一些注意事项

先推荐一个一个对标pc端swiper的uniapp版本 zebra-swiper 缺点是自定义分页器不是很好处理 不知道怎么弄 优点:可以进行高度自适应 &#xff08;这个uniapp原生swiper没有 只能动态修改 采用js 或者只有几种固定高度时采用变量修改&#xff09; <swiperref"lifeMiddle…

机器学习笔记——聚类算法(Kmeans、GMM-使用EM优化)

本笔记介绍机器学习中常见的聚类算法&#xff08;Kmeans、GMM-使用EM优化&#xff09;。 文章目录 聚类K-Means工作原理特点 K-Medoids工作原理特点 Mini-Batch K-Means工作原理特点 K-Means&#xff08;重要&#xff09;工作原理特点 总结K的选值1. 肘部法则&#xff08;Elbow…

SpringBoot项目升级到3.*,并由JDK8升级到JDK21

文章目录 技术选型说明JDK21的Demo项目下载升级过程出现的问题及解决1、程序包javax.servlet.http不存在1.1、java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter1.2、javax.validation包替换为jakarta.validation1.3、jakarta的名字由来 2、mybatis-plus升级3…

谈一谈QThread::CurrentThread和this->thread

QThread::CurrentThread是指的当前函数调用者者所在的线程 this->thread是指的当前对象所在的线程&#xff08;对象创建出来的时候所在的线程&#xff09; Qt文档说明 CurrentThread返回一个指向管理当前执行线程的QThread的指针 thread返回对象所在的线程 这两个函数所…

每日论文23-24ESSERC 6.4-16.1Ghz混合并联-串联谐振器

《A 6.4-to-16.1GHz Hybrid Parallel-Series Resonator Mode-Switching Oscillator with 206.6dBc/Hz FoMT at 1MHz Offset in 40nm CMOS》 24ESSERC 首先这篇文章有个地方我其实没太明白&#xff0c;它在title和行文的时候都写的是“ hybrid parallel-series resonator mode-…

数据结构C语言描述3(图文结合)--双链表、循环链表、约瑟夫环问题

前言 这个专栏将会用纯C实现常用的数据结构和简单的算法&#xff1b;有C基础即可跟着学习&#xff0c;代码均可运行&#xff1b;准备考研的也可跟着写&#xff0c;个人感觉&#xff0c;如果时间充裕&#xff0c;手写一遍比看书、刷题管用很多&#xff0c;这也是本人采用纯C语言…

深入理解Flutter生命周期函数之StatefulWidget(一)

目录 前言 1.为什么需要生命周期函数 2.开发过程中常用的生命周期函数 1.initState() 2.didChangeDependencies() 3.build() 4.didUpdateWidget() 5.setState() 6.deactivate() 7.dispose() 3.Flutter生命周期总结 1.调用顺序 2.函数调用时机以及主要作用 4.生…

uniapp vue3小程序报错Cannot read property ‘__route__‘ of undefined

在App.vue里有监听应用的生命周期 <script>// 只能在App.vue里监听应用的生命周期export default {onError: function(err) {console.log(AppOnError:, err); // 当 uni-app 报错时触发}} </script>在控制台打印里无意发现 Cannot read property ‘__route__‘ of …

ESP32移植Openharmony外设篇(5)aht20温湿度传感器

模块简介 产品概述 AHT20&#xff0c;新一代温湿度传感器在尺寸与智能方面建立了新的标准&#xff1a;它嵌入了适于回流焊的双列扁平无引脚SMD封装&#xff0c;底面 3 x 3mm &#xff0c;高度1.0mm。传感器输出经过标定的数字信号&#xff0c;标准 I2 C 格式。 AHT20 配有一个…

量子计算来袭:如何保护未来的数字世界

目录 前言 一、量子计算安全的学习方向 1. 量子物理学基础 2. 量子计算原理与技术 3. 传统网络安全知识 4. 量子密码学 5. 量子计算安全政策与法规 二、量子计算的漏洞风险 1. 加密算法被破解风险 2. 区块链安全风险 3. 量子密钥分发风险 4. 量子计算系统自身风险 …

Git入门图文教程 -- 深入浅出 ( 保姆级 )

01、认识一下Git&#xff01;—简介 Git是当前最先进、最主流的分布式版本控制系统&#xff0c;免费、开源&#xff01;核心能力就是版本控制。再具体一点&#xff0c;就是面向代码文件的版本控制&#xff0c;代码的任何修改历史都会被记录管理起来&#xff0c;意味着可以恢复…

C++之异常

1.异常的概念及其使用 1.1 异常的概念 异常是一种用于处理错误的机制&#xff0c;它允许程序在检查到错误条件时&#xff0c;能够从一个代码块转到另一个代码块&#xff0c;以处理改错误&#xff0c;而不是直接崩溃返回不确定的结果。 C的异常处理机制依赖于三个关键字&#x…

Golang语言整合jwt+gin框架实现token

1.下载jwt go get -u github.com/dgrijalva/jwt-go2.新建生成token和解析token文件 2.1 新建common文件夹和jwtConfig文件夹 新建jwtconfig.go文件 2.2 jwtconfig.go文件代码 /* Time : 2021/8/2 下午3:03 Author : mrxuexi File : main Software: GoLand */ package jwtC…

河工oj(1101-1113)

1101 求组合数&#xff08;函数专题&#xff09; 代码 #include<bits/stdc.h> using namespace std;int fact(int n) {int res 1;while(n) {res * n--;}return res; } int main() {int m, k;cin >> m >> k;cout << fact(m)/fact(k)/fact(m-k) <&l…

07架构面试题

目录 一、关于合生元的面试题的架构分析的问题 1. 陈述两种方案的优劣 2. 在那些条件下&#xff0c;会选择哪一个方案 3. 你倾向那一种&#xff1f; 4. 如果要实施方案二的&#xff0c;准备步骤和流程 一、关于合生元的面试题的架构分析的问题 1. 陈述两种方案的优劣 方案…

递归:编程世界的奇妙魔法之旅

模块一&#xff1a;递归的神秘面纱 —— 初窥魔法之门 1. 递归的概念&#xff1a;神秘的魔法回响 &#x1f60e;嘿&#xff01;各位编程大侠们&#xff0c;今天咱们要来聊聊一个超级神奇的玩意儿 —— 递归。这递归啊&#xff0c;简直就是编程世界里神秘得不能再神秘的魔法回…