stm32 定时器中断

目录

定时器分类

通用定时器框图

时钟源

内部时钟(CK_INT)

外部时钟模式 1( TI1、 TI2)

时钟信号输入引脚

滤波器

        如果来自外部的时钟信号的频率过高或者混杂有高频干扰信号的话,我们就需要使用滤波器对信号重新采样,来达到降频或者去除高频干扰的目的,也可以设置不使用滤波器

边沿检测

触发输入选择器:触发选择

从模式选择

使能计数器

外部时钟模式 2( ETR)

内部触发输入(ITRx)

控制器

时基单元

预分频器 PSC

自动重载寄存器 ARR

计数器 CNT

hal库代码

标准库代码


定时器分类

        STM32F1系列最多有8个常规定时器, 2个基本定时器( TIM6、 TIM7)、 4个通用定时器( TIM2、 TIM3、 TIM4、TIM5)、 2个高级定时器( TIM1、 TIM8)

        STM32F103C8T6定时器资源:TIM1TIM2TIM3TIM4

        

图1

图2

通用定时器框图

时钟源

内部时钟(CK_INT)

        STM32F1 系列的定时器 TIM2-7都是挂载在 APB1 总线上,时钟不是直接由 APB1 总线直接提供,而是先经过一个倍频器。当 APB1 的预分频器系数为 1 时,这个倍频器系数为 1,即定时器的时钟频率等于 APB1 总线时钟频率;当 APB1 的预分频器系数≥2 分频时,这个倍频 器系 数就 为 2, 即定 时器的 时钟 频率 等于 APB1 总 线时 钟频 率的 两倍,一般设置 APB1 总线时钟频率为 36M, APB1 总线的预分频器分频系数是 2,所以挂载在 APB1 总线的定时器时钟频率为 72Mhz。一般情况下都是使用内部时钟。当从模式控制寄存器 TIMx_SMCR 的 SMS 位等于 000 时,则使用内部时钟。

        高级定时器 TIM1 和 TIM8 是挂载在 APB2 总线上的,如果 APB2 预分频系数为 1,挂载在该总线的定时器时钟频率不变,否则频率是该总线时钟频率的 2 倍。一般设置 APB2 总线时钟频率为 72MHz, APB2 预分频器的预分频系数为1,所以 TIM1 和 TIM8 时钟源频率为 72MHz。

外部时钟模式 1( TI1、 TI2)

        顾名思义时钟信号来自芯片外部。时钟源进入定时器的流程如下:外部时钟源信号→IO→TIMx_CH1(或者 TIMx_CH2),这里需要注意的是:从 IO到 TIMx_CH1(或者 TIMx_CH2),就需要配置 IO 的复用功能,才能使 IO 和定时器通道相连通。

时钟信号输入引脚

        当使用外部时钟模式 1 的时候,时钟信号来自于定时器的输入通道,总共有 4 个,分别为 TI1/2/3/4,即 TIMx_CH1/2/3/4。具体使用哪一路信号,由 TIM_CCMRx 的位 CCxS[1:0] 配置,其中 CCMR1控制 TI1/2, CCMR2 控制 TI3/4。外部时钟模式 1 下,时钟源信号只能从 CH1 或者 CH2 输入到定时器, CH3 和 CH4 都是不可以的。这以 CH2(通道 2)为例的,时钟源信号到达 CH2 后,那么这里这个时钟源信号用 TI2 表示

滤波器
        如果来自外部的时钟信号的频率过高或者混杂有高频干扰信号的话,我们就需要使用滤波器对信号重新采样,来达到降频或者去除高频干扰的目的,也可以设置不使用滤波器
边沿检测

        边沿检测的信号来自于滤波器的输出,在成为触发信号之前,需要进行边沿检测,决定是上升沿有效还是下降沿有效

触发输入选择器:触发选择

        图中框出了 TI1F_ED、 TI1FP1 和 TI2FP2 三个触发输入信号(TRGI)。 TI1F_ED 表示来自于 CH1,并且没有经过边沿检测器过滤的信号,所以它是 CH1 的双边沿信号,即上升沿或者下降沿都是有效的。 TI1FP1 表示来自 CH1 并经过边沿检测器后的信号,可以是上升沿或者下降沿。 TI2FP2 表示来自 CH2 并经过边沿检测器后的信号,可以是上升沿或者下降沿。这里以CH2 为例,那只能选择 TI2FP2。如果是 CH1 为例,那就可以选择 TI1F_ED 或者 TI1FP1

从模式选择

        选定了触发源信号后,最后需把信号连接到 TRGI 引脚,让触发信号成为外部时钟模式 1 的输入,最终等于 CK_PSC,然后驱动计数器 CNT 计数。由 ECE 位和 SMS[2:0]位来选择定时器的时钟源,外部时钟模式 1,所以 ECE 位置 0, SMS[2:0] = 111 即可。

使能计数器

        最后只需使能计数器开始计数,外部时钟模式 1 的配置就算完成。使能计数器由 TIMx_CR1 的位 CEN 配置

外部时钟模式 2( ETR)

        在外部时钟模式 2 下,定时器时钟信号首先从 ETR 引脚进来,信号只有 1 个;接着经过外部触发极性选择器,由 ETP 位来设置上升沿有效还是下降沿有效;然后经过外部触发预分频器,由 ETPS[1:0]位来设置预分频系数,频率不能超过 TIMx_CLK(72M)的 1/4;紧接着经过滤波器器,由 ETF[3:0]位来设置滤波方式,也可以设置不使用滤波器;最后经过从模式选择器,由 ECE 位和 SMS[2:0]位来选择定时器的时钟源,体的配置 TIMx_SMCR 的位 ECE 为 1 即可选择外部时
钟模式 2;最后只需使能计数器开始计数,外部时钟模式 2 的配置就完成。使能计数器由 TIMx_CR1 的位 CEN 配置

内部触发输入(ITRx)

        内部触发输入是使用一个定时器作为另一个定时器的预分频器。在硬件上高级控制定时器和通用定时器在内部连接在一起,可以实现定时器同步或级联。主模式的定时器可以对从模式定时器执行复位、启动、停止或提供时钟

假如TIM1 作为 TIM2 的预分频器:

        TIM1_CR2 寄存器的 MMS[2:0]位设置为 010,即 TIM1 的主模式选择为更新(选择更新
事件作为触发输出 (TRGO));

        TIM2_SMCR 寄存器的 TS[2:0]位设置为 000,即使用 ITR1 作为内部触发,如下图

        外部时钟模式 1 的时候 TI1F_ED、TI1FP1 和 TI2FP2,以及外部时钟模式 2 的ETRF,它们都是属于外部的,其余的都是内部触发了。TS[2:0]位设置为 000,使用 ITR0 作为内部触发,这个 ITR0 什么意思?看下图

        当从模式定时器为 TIM2 时, ITR1 表示主模式定时器就是 TIM2,TIM1 和 TIM2 的 CEN 位都要置 1,即启动计数器

控制器

        控制器包括:从模式控制器、编码器接口和触发控制器(TRGO)。从模式控制器可以控制
计数器复位、启动、递增/递减、计数。编码器接口针对编码器计数。触发控制器用来提供触发信号给别的外设,比如为其它定时器提供时钟或者为 DAC/ADC 的触发转换提供信号。

时基单元

预分频器 PSC

        预分频器 PSC,有一个输入时钟 CK_PSC 和一个输出时钟 CK_CNT。输入时钟 CK_PSC 就是上面时钟源的输出,输出 CK_CNT 则用来驱动计数器 CNT 计数。通过设置预分频器 PSC 的值可以得到不同的 CK_CNT,PSC预分频的范围为0~65535

自动重载寄存器 ARR

        自动重载寄存器 ARR 用来存放与计数器 CNT 比较的值,可以向上计数、向下计数或向上/向上双向计数。当计数值达到设定值时,会产生溢出事件,可以发出中断或DMA请求,然后再由自动装载寄存器进行重新加载或更新。通过 TIMx_CR1 寄存器的 ARPE 位控制自动重载影子寄存器功能,如果 ARPE 位置 1,自动重载影子寄存器有效,只有在事件更新时才把 TIMx_ARR 值赋给影子寄存器。如果 ARPE 位为 0,则修改 TIMx_ARR 值马上有效

计数器 CNT

        高级控制定时器的计数器有三种计数模式,分别为递增计数模式、递减计数模式和递增/递减 (中心对齐) 计数模式,CNT计数器的范围为0~65535。

        纵轴表示计数器的计数值,横轴表示时间, ARR 表示自动重载寄存器的值,小红点就是更新事件发生的时间点。例如:递增计数模式下,当计数值等于 ARR 时,计数器的值被复位为 0,定时器溢出,并伴随着更新事件的发生,后面继续递增计数
        定时器的定时时间主要取决于预分频系数和定时周期,计算公式为:

 当前系统时钟频率为72MHz, APB1二分频为36MHz, TIMxCLK则为72MHz。预分频系数任意取一值,假设为PSC=10000-1,自动装载器值假设为ARR=7200-1,则此时定时器定时为:

 即,定时器每间隔1s,将产生一次溢出事件,产生中断

hal库代码

/*1毫秒后会触发中断,然后执行中断服务函数操作。*/
TIM_HandleTypeDef g_timer;

/*定时器中断初始化函数*/
void general_timer_init()
{
    TIM_ClockConfigTypeDef tim_config = {0};
    
    g_timer.Instance = TIM2;/*选择定时器*/
    /*72MHz经过72分频后,定时器时钟为1MHz,即定时器计数1次的时间,刚好为1us;*/
    g_timer.Init.Prescaler = 72-1;/*设置预分频系数*/
    g_timer.Init.CounterMode = TIM_COUNTERMODE_UP;/*递增计数模式*/
    g_timer.Init.Period = 0;/*自动重载值ARR,任意,后面代码再修改*/
    g_timer.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; /*时钟分频*/
    g_timer.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; /*不自动重载*/
    
    HAL_TIM_Base_Init(&g_timer);
    
    tim_config.ClockSource = TIM_CLOCKSOURCE_INTERNAL;/*用内部时钟作为定时器时钟源*/
    HAL_TIM_ConfigClockSource(&g_timer , &tim_config);
    HAL_TIM_Base_Start_IT(&g_timer);/*使能定时器中断*/
    
}
/*定时器MSP初始化函数*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        __HAL_RCC_TIM2_CLK_ENABLE();/*使能时钟*/
        HAL_NVIC_SetPriority(TIM2_IRQn, 1, 1);/*设置中断抢占和响应优先级*/
        HAL_NVIC_EnableIRQ(TIM2_IRQn);/*使能中断*/
    }
}

/*定时器实现的延时函数,延时时间为 t us , 最多能延时65535us*/
void timer_us_delay(uint16_t t)
{
    uint16_t counter = 0;
    __HAL_TIM_SET_AUTORELOAD(&g_timer, t);/*直接设置ARR寄存器的值*/
    __HAL_TIM_SET_COUNTER(&g_timer, counter);/*直接设置CNT计数器的值,保证定时器从0开始计数;*/
    HAL_TIM_Base_Start(&g_timer);/*启动定时器*/
    while( counter != t)
    {
        counter = __HAL_TIM_GET_COUNTER(&g_timer);/*获取定时器当前计数*/
    }/*直到定时器计数从 0 计数到 t 结束循环,刚好 t us*/
    HAL_TIM_Base_Stop(&g_timer);/*停止定时器*/
}

/*定时器实现的延时函数,延时时间为 t ms, 最多65ms多点*/
void timer_ms_delay(uint16_t t)
{
    int i = 0;
    for(; i<t; i++)
        timer_us_delay(1000);/*设置定时器的自动重载值(ARR)为1000*/
}

#if 1
/*中断服务函数:配置了使能中断,当计数器的值达到1000时,会触发中断*/
void TIM2_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_timer);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim ->Instance == TIM2)
    {
        
        /*中断触发*/
    }
    
}
#endif

标准库代码


void tim_init(void)
{
    /*开启时钟*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    /*使用内部时钟*/
    TIM_InternalClockConfig(TIM2);
    /*初始化定时器*/

    /*计数器溢出频率= 72Mhz/(TIM_Prescaler+1)/(TIM_Period+1),这里定1秒 */

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;/*设置时钟分频因子为1,即没有分频*/
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;/*向上计数*/
    TIM_TimeBaseInitStruct.TIM_Period = 10000-1;/*自动重装值*/
    TIM_TimeBaseInitStruct.TIM_Prescaler = 7200-1;/*预分频值*/
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;/*重复计数器,高级定时器才有,这里给0*/
    TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
    /*清除更新中断标志位,避免系统初始化(或复位)后立刻更新进入中断*/
    TIM_ClearFlag(TIM2,TIM_IT_Update);
    /*使能中断*/
    /*TIM_IT_Update表示更新中断,也就是定时器计数溢出时产生的中断*/
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    /*配置NVIC*/
    /*分组*/
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;/*中断通道*/
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;/* 使能中断 */
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; /* 抢断优先级*/
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;  /* 子优先级 */
    NVIC_Init(&NVIC_InitStruct);
    /*开启Tim*/
    TIM_Cmd(TIM2,ENABLE);
}

/*中断服务函数*/
void TIM2_IRQHandler(void)
{
    /*检查中断标志*/
    if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
    {
        /*中断触发*/
        /*清除标志位*/
        TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    }
}

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

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

相关文章

linux编译boost库并执行程序

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、--prefix命令 二、安装过程 1、shell脚本&#xff1a; 2、gcc编译环境 执行过程 三、linux下执行cpp程序 总结 前言 提示&#xff1a;这里可以添加本文…

iOS报错命名空间“std”中的“unary_function”

刚刚将我的 Xcode 升级到 15.0&#xff0c;突然它开始在 RCT_Folly 中出现以下错误 No template named unary_function in namespace std; did you mean __unary_function?我尝试删除缓存数据和派生数据并清理构建。也尝试删除 pod 和 node_modules。但没有任何帮助。 于是我…

深入理解udp

1.再谈端口号 1.1复习 我们上一篇谈了很久的应用层的http&#xff0c;并在此前我们使用socket编程写了一个能相互通信的客户端与服务端&#xff0c;但是我们也只是粗略的理解了一下tcp和udp在编程过程中所形成的差异性&#xff0c;并没有实质去了解一下其详细内容&#xff0c;…

Mybatis延迟加载(缓存)

延迟加载 分步查询的优点&#xff1a;可以实现延迟加载&#xff0c;但是必须在核心配置文件中设置全局配置信息&#xff1a;lazyLoadingEnabled&#xff1a;延迟加载的全局开关。当开启时&#xff0c;所有关联对象都会延迟加载 aggressiveLazyLoading&#xff1a;当开启时&…

基于【逻辑回归】的评分卡模型金融借贷风控项目实战

背景知识&#xff1a; 在银行借贷过程中&#xff0c;评分卡是一种以分数形式来衡量一个客户的信用风险大小的手段。今天我们来复现一个评分A卡的模型。完整的模型开发所需流程包括&#xff1a;获取数据&#xff0c;数据清洗和特征工程&#xff0c;模型开发&#xff0c…

OPENCV 闭运算实验示例代码morphologyEx()函数

void CrelaxMyFriendDlg::OnBnClickedOk() {hdc this->GetDC()->GetSafeHdc();// TODO: 在此添加控件通知处理程序代码string imAddr "c:/Users/actorsun/Pictures/";string imAddr1 imAddr"rice.png";Mat relax, positive;relax imread(imAddr1…

SPSS卡方检验

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件请点击此链接下…

浅谈开口互感器在越南美的工业云系统中的应用

摘 要&#xff1a;分析低压开口式电流互感器的原理&#xff0c;结合工程实例分析开口电流互感器在低压配电系统中&#xff0c;主要是改造项目中的应用及施工细节&#xff0c;为用户快速实现智能配电提供解决方案&#xff0c;该方案具有成本低、投资少、安装接线简便等优点&…

自学SLAM(6)相机与图像实践:OpenCV处理图像与图像拼接(点云)

前言 如果写过SLAM14讲第一次的作业&#xff0c;或者看过我之前的运行ORB_SLAM2教程应该都安装过OpenCV了&#xff0c;如果没有安装&#xff0c;没关系&#xff0c;可以看我之前的博客&#xff0c;里面有如何安装OpenCV。 链接: 运行ORB-SLAM2&#xff08;含OpenCV的安装&…

234. 回文链表、Leetcode的Python实现

博客主页&#xff1a;&#x1f3c6;看看是李XX还是李歘歘 &#x1f3c6; &#x1f33a;每天分享一些包括但不限于计算机基础、算法等相关的知识点&#x1f33a; &#x1f497;点关注不迷路&#xff0c;总有一些&#x1f4d6;知识点&#x1f4d6;是你想要的&#x1f497; ⛽️今…

跨境商城源码价格

在当今数字商务的时代&#xff0c;跨境电商已经成为了越来越多企业的选择。然而&#xff0c;要建立一个高效、便捷、全球化的跨境商城并不是一件简单的事情。所幸&#xff0c;现在有一个开源的解决方案&#xff0c;给企业提供了无限的可能性。跨境商城源码价格合乎实际&#xf…

html获取网络数据,列表展示 一

html获取网络数据&#xff0c;列表展示 js遍历json数组中的json对象 image.png || - 判断数据是否为空&#xff0c;为空就显示 - <!DOCTYPE html> <html><head><meta charset"utf-8"><title>网页列表</title></head><b…

Web渗透编程语言基础

Web渗透初学者JavaScript专栏汇总-CSDN博客 Web渗透Java初学者文章汇总-CSDN博客 一 Web渗透PHP语言基础 PHP 教程 | 菜鸟教程 (runoob.com) 一 PHP 语言的介绍 PHP是一种开源的服务器端脚本语言,它被广泛用于Web开发领域。PHP可以与HTML结合使用,创建动态网页。 PHP的特…

微信小程序-授权登录(手机号码)

1、WXBizDataCrypt.js-下载地址 2、UNIAPP代码 <template> <view class"work-container"> <view class"login"> <view class"content"> <button class"button_wx&q…

修复国产电脑麒麟系统开机出现initramfs 问题

目录预览 一、问题描述二、原因分析三、解决方案四、知识点呀initramfsBusyBox 五、参考链接 一、问题描述 国产麒麟系统出现 initramfs 模式 二、原因分析 一般在拷贝卡顿过程【强制关机】或者电【脑异常断电】的情况下概率性导致系统分区损坏&#xff0c;重启后大概率就会进…

Java实现Hive UDF详细步骤 (Hive 3.x版本,IDEA开发)

这里写目录标题 前言1. 新建项目2.配置maven依赖3.编写代码4.打jar包5.上传服务器6.代码中引用 前言 老版本编写UDF时&#xff0c;需要继承 org.apache.hadoop.hive.ql.exec.UDF类&#xff0c;然后直接实现evaluate()方法即可。 由于公司hive版本比较高&#xff08;3.x&#x…

goquery库来编写爬虫程序

使用goquery库来编写一个爬虫程序&#xff0c;以下是代码&#xff1a; package main ​ import ("fmt""net/http""log""time""github.com/PuerkitoBio/goquery" ) ​ func main() {// 设置服务器dialer : &net.Dialer{…

day01_Java概述丶环境搭建

前置知识 什么是计算机语言&#xff1f; 计算机语言就是人与计算机之间进行信息交流沟通的一种特殊语言。所谓计算机编程语言&#xff0c;就是人们可以使用编程语言对计算机下达命令&#xff0c;让计算机完成人们需要的功能。 Java语言概述 是美国Sun公司&#xff08;Stanf…

LabVIEW对多个同一类型控件进行操作

LabVIEW对多个同一类型控件进行操作 有时候LabVIEW要多多个同一类的控件进行操作&#xff0c;如对tab中某个page中所有String控件设为dissable。就可以用如下的方式。className是获取不同类型的控件。通过类型选择&#xff0c;可以选择所有的String控件&#xff0c;并可对特定…

1. eulerAngles函数

对线性代数库Eigen3中eulerAngles函数的理解_qingtian11112的博客-CSDN博客作用&#xff1a; 将旋转矩阵转换为欧拉角 Vector3f ea mat.eulerAngles(2, 0, 2); // 等价于 mat AngleAxisf(ea[0], Vector3f::UnitZ())* AngleAxisf(ea[1], Vector3f::UnitX())* AngleAxisf(ea[…