stm32 学习 定时器定时中断(最详细的Timer定时器介绍)

TIM定时器

TIM概念

e9366c6f7c4b439eb555b372b146c1f0.png

16位计数器:仓库的概念每来一个时钟信号 ,计数器➕1

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

自动重装寄存器:设定触发,让计数达到一定数值的时候,进行中断的申请

总结:三部分统称为时基单元,并且都是16位的寄存器,那么最大数值2的16次方,也就是所有数值最大设置,就能实现59.65秒的定时 72MHz / 65536 / 65536 得到中断频率,取倒数就是最大定时时间

最大定时 = (最大计数器)*(最大自动重装数)/72/Mhz

定时器类型

b2da010f11e84f04a5860dfa33868b4d.png

基本定时器

859ed895c4ef406a84b64ddbc47e993f.png

主从模式触发DAC 的功能

DAC即数模转换器(Digital to Analog Converter)

当需要用DAC 输出一段波形的时候(例如PWM),那么就需要每隔一段事件来触发一次DAC,让他输出下一个电压点,如果用正常的更新中断来手动触发DAC 转换的话,就会极大占用资源,影响主程序的运行和其他中断的相应,而在使用主模式下,可以将触发事件映射到触发输出TROG(Trigger Out)的位置然后TRGO就直接接到DAC 这样定时器的更新就不需要经过NVIC 中断响应来触发DAC转换了,过程不需要软件的参与,实现了硬件的自动化。

注:D是数字两 A是模拟量

a0f2216f28004bf8a28671f4cf439b5c.png

通用定时器

03e3afd088ea4572aa87bad0a5b8fdd4.png

向上计数: 计数器从0开始向上自增,达到设定值,清零同时申请中断,依次循环

通用/高级定时器除此还支持向下计数以及中央对齐计数模式;

edac6ec28e974663ab1c0273deb1a763.png

情况1

f255f8810c384bcdbb9dc14d4840e528.png

情况2

d227e9da1fe643bb855ce3aec0f2622c.png

情况3

c332d4227d294e8f94079dd00f9d8c61.png

总结,外部时钟模式1 下,输入可以是ETR引脚。其他定时器,CH1 引脚的边沿,CH1 和CH2 引脚,一般情况下,使用ETR 为外部时钟输入

 

输入捕获电路/输出比较电路 (之后会详细介绍)

78a30d8c5c6b4afb814098908a6e8233.png

输出比较电路有四个通道分别对应CH1 到 CH4的引脚,可以用于输出PWM 波形

输入捕获电路也有四个通道,可以用于输入方波的频率测量等

中间的是输入捕获跟输出比较电路寄存器,两个电路共用四根引脚,不能同时进行

高级定时器

bacd79f1128b409ba2b4d901279e8f4f.png

相比于通用计时器,这里首先在申请中断的地方 增加了一个重复次数计数器,可以实现每隔几个计数周期,才发生一次更新事件和更新中断,这就相当于对于输出的更新信号,又做了一次分频,这里可以实现级联计时器的效果。

 

DTG (Dead Time Generate)死区生成电路 防止桥臂直通现象

 

BRK 刹车输入功能 如果外部BKIN产生刹车信号,或者内部时钟信号产生问题,那么就会自动切断电机的驱动,防止意外的发生

 

定时器中断基本结构

45ac0865686843158e8a99af13a40b31.png

运行控制:控制寄存器位的,比如向上计数,向下计数,启动停止等等

中断输出控制:中断输出允许位,如果需要中断,就允许

bc8095a34fce409c8704ee458140a3c6.png

预分频器时序

83b901151e5d49a192a9138600b4eb05.png

  CK_PSC 时钟源; CNT_EN 计数器使能; CK_CNT 计数器时钟,他即是预分频器的时钟输出,也是计数器的时钟输入

  缓冲寄存器(影子寄存器):在计数过程中,如果突然改变分频数值,只有等到更新事件后,改变后的分频数值才会真正生效

  计数器计数频率 = 时钟源频率/(分频器数值+1);

 

  计数器时序

83465629195e49c6b843d87beccba6a7.png

计数器溢出频率=时钟频率/(设定定时阈值+1) | =定时器时钟源 / (分频系数+1)/(自动重转器系数+1)

 

注: CK_CNT 跟 CK_PSC 虽然同为时钟,但是CK_PSC 为时钟源,SK_CNT 为预分配器的输出,也为CNT 计数器的输入,是经过处理后的时钟,

 

定义:计数器溢出频率是指一段时间内,有多少个计数周期,与其呈反比,计数周期越短,溢出频率(触发频率)也就越大 溢出时间为溢出频率的倒数,例如溢出频率为10MHz 表示1秒内,会发生10次的溢出,那么每次就为1/10

 

计数器预装时序

f7214275bed4472a87cd7b02eb442eda.png936b9576c3bc46c5b81843026acd5425.png

让数值的更改与更新事件同步发生,防止运行途中出现错误,通过第ARPE进行设置就可以开启/关闭

 

RCC时钟树

主要分为时钟的产生电路与时钟的分配电路两大部分

543b098e000f4c03a0d442576444c0a1.png

时钟产生电路

在时钟的产生部分,有四个震荡源,高速晶振提供系统时钟AHB APB2 APB1 都是来源这两个高速晶振,外部晶振比RC 内部振荡器更稳定准确

内部的8MHz高速RC振荡器

外部的4-16MHz石英晶体振荡器,也就是晶振,一般都是接8MHz

外部的32.768Khz低速晶振,一般是给RTC提供时钟的 RTC(Real-Time Clock)即实时时钟

内部的40KHz低速RC振荡器,这个可以给看门狗提供时钟

dd4d28c45b0e49a399e2899fb3878038.png

CSS (Clock Security System) 时钟安全系统

一旦外部时钟失效,就会让内部RC振荡器作为时钟

 

时钟分配电路

所有定时器时钟频率都为72MHz

3f7319ce7cb5492faeeef8708dc03496.png

定时器定时中断(代码函数部分)

硬件连接

6ccacd8223804f9ba91ef88a0fcb5194.png

根据定时中断基本结构进行配置

 

b9d7f7e2f8d1483b87666184832aa93b.png

相关配置函数列表

//时钟源选择
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                             uint16_t ExtTRGFilter);
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,

//时基单元配置
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

//中断输出控制
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

//运行控制
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);

//设置NVIC
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

 

用于中途单独更改设定数值的函数:

 
// 配置定时器的预分频器
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);

// 配置定时器的计数模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);

// 配置定时器自动重装载寄存器预装载使能
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);

// 设置定时器的计数器值
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);

// 设置定时器的自动重装载值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);

// 获取定时器的当前计数器值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);

// 获取定时器的预分频值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);

// 获取定时器特定标志位的状态
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);

// 清除定时器特定标志位
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);

// 获取定时器中断状态
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);

// 清除定时器中断挂起位
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

初始化函数部分

#include "stm32f10x.h"                  // Device header

// 定时器初始化函数
void Timer_Init(void)
{
    // 使能 TIM2 时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    // 配置 TIM2 使用内部时钟源
    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;
    TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
    // 高级定时器才有的重复计数器,这里设置为 0
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

    // 使能 TIM2 更新中断
    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);

    // 使能 TIM2
    TIM_Cmd(TIM2, ENABLE);
}

// TIM2 中断服务函数
void TIM2_IRQHandler(void)
{
    // 判断是否为 TIM2 更新中断
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {
        // 清除 TIM2 更新中断标志位
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

主函数main

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Timer.h"

uint16_t Num;

// 主函数
int main(void)
{
    // 初始化 OLED 显示屏
    OLED_Init();
    // 在 OLED 显示屏第一行第一列显示字符串"Num:"
    OLED_ShowString(1, 1, "Num:");
    // 初始化定时器
    Timer_Init();
    while (1)
    {
            OLED_ShowSignedNum(1,5,Num,5);
        // 无限循环,等待定时器中断
    }
}

// TIM2 中断服务函数
void TIM2_IRQHandler(void)
{
    // 判断 TIM2 是否产生更新中断
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {
        // 变量 Num 自增
        Num++;
        // 清除 TIM2 的更新中断标志位
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

代码效果,实现Num变量每秒自增+1

 

但是这里有一个现象不知道大家发现没有,就是stm32每次复位,Num的数值都会直接变成1 ,按理来说,应该以Num 的初始值0开始的,这就表明,在初始化的时候,程序就进入了一次中断,要解决其实也很简单,这题就出现在了这里

6586eb841d334f248a920b540da72c97.png

90d9902630c04b5ea2d1f65a0950ed99.png

这个的意思是生成一个更新事件以立即重新加载预分频器和重复计数器的值,要知道,我们的预分屏器是有缓冲寄存器的,我们写的值只有在更新事件的时候,才会有效,这里为了让数值在初始化的时候生效就生成了一个更新事件,缺点就是更新事件与更新中断会同时发生,导致以上电就会从1 开始计时,想要解决其实也很简单

d9a633835be1400f92c53fc8b0100f02.png

现在就可以正常显示啦!还可以在主循环里添加查看计数器数值的代码,可以发现,从0到重装值10000后,才会发生一次更新中断

c8f3098a83f84db1b70bd7f81b97d305.png

bd5cff072d0f408092a1cc4cfc8d7548.png

 

 

以下是stm32高级定时器TIM1的定时中断配置代码示例: ``` // 定时器配置 TIM_HandleTypeDef htim1; TIM_OC_InitTypeDef sConfigOC; htim1.Instance = TIM1; htim1.Init.Prescaler = 0; htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 999; htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0; if (HAL_TIM_Base_Init(&htim1) != HAL_OK) { Error_Handler(); } // 定时中断配置 HAL_NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn); HAL_TIM_Base_Start_IT(&htim1); // 中断处理函数 void TIM1_UP_TIM10_IRQHandler(void) { HAL_TIM_IRQHandler(&htim1); } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM1) { // 定时器中断处理代码 } } ``` 这个示例代码中,我们使用STM32 HAL库进行定时器配置和定时中断配置。其中,`htim1`是一个`TIM_HandleTypeDef`结构体,用于存储定时器的相关配置信息。`sConfigOC`是一个`TIM_OC_InitTypeDef`结构体,用于存储定时器输出比较(PWM)的相关配置信息。 在定时中断配置部分,我们使用了HAL库提供的函数`HAL_NVIC_SetPriority()`和`HAL_NVIC_EnableIRQ()`来配置定时器中断优先级和使能定时中断。同时,我们也使用了HAL库提供的函数`HAL_TIM_Base_Start_IT()`来启动定时器,并且使能定时中断。 在中断处理函数`TIM1_UP_TIM10_IRQHandler()`中,我们调用了HAL库提供的函数`HAL_TIM_IRQHandler()`来处理定时器中断。在`HAL_TIM_PeriodElapsedCallback()`回调函数中,我们可以编写定时器中断处理代码。 需要注意的是,这个示例代码中的定时器时钟源为APB2总线,如果需要使用其他时钟源或者其他定时器,请根据具体情况进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值