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)
cpp 复制代码
#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
  1. 源文件 (tim_capture.c)
cpp 复制代码
#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; // 重置标志
        }
    }
}
  1. 主函数 (main.c)
cpp 复制代码
#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时间到达。
相关推荐
7yewh4 小时前
Linux驱动开发 IIC I2C驱动 编写APP访问EEPROM AT24C02
linux·arm开发·驱动开发·嵌入式硬件·嵌入式
上海易硅智能科技局有限公司4 小时前
AG32 MCU 的电机控制方案
单片机·嵌入式硬件
程序员JerrySUN5 小时前
Yocto 项目 - 共享状态缓存 (Shared State Cache) 机制
linux·嵌入式硬件·物联网·缓存·系统架构
嵌入式小强工作室6 小时前
stm32能跑人工智能么
人工智能·stm32·嵌入式硬件
MikelSun7 小时前
电压控制环与电流控制环
单片机·嵌入式硬件·物联网
陌夏微秋7 小时前
STM32单片机芯片与内部45 UART 不定长度接收 标志位结束 定时器超时 串口空闲中断
stm32·单片机·嵌入式硬件·信息与通信·智能硬件
挥剑决浮云 -8 小时前
STM32学习之 按键/光敏电阻 控制 LED/蜂鸣器
c语言·经验分享·stm32·单片机·嵌入式硬件·学习
Whappy0019 小时前
第13部分 1.STM32之PWR电源控制-----4个实验
stm32·单片机·嵌入式硬件
冲,干,闯10 小时前
单片机里不想阻塞系统的延时
单片机·嵌入式硬件
小菜鸟学代码··11 小时前
STM32中断详解
stm32·单片机·嵌入式硬件