当前位置: 首页 > news >正文

基本定时器的寄存器介绍及案例实践

引言

前面我们介绍了基本定时器的功能以及基本工作原理,了解了如何计算定时时间,接下来,我们继续写聊聊与之相关的寄存器,并结合案例基于寄存器进行练习,并学习基于HAL库的基本定时器的使用方法。

一、基本定时器相关寄存器

       首先,我们来熟悉一下基本定时器相关的寄存器,关于寄存器的介绍都在STM32的参考手册中,我们先总览一下,看看基本定时器的寄存器映射,如下图所示

       由上图可知,基本定时器相关的寄存器有8个,当然我们平时使用的话也不会都用,最多也就使用其中的几个位进行一下配置。主要使用的是最后三个寄存器TIMx_CNT、TIMx_PSC、TIMx_ARR,看名字也能猜到,这三个寄存器是与前面介绍的功能框图上的几个部分一一对应的,分别是计数器寄存器、预分频寄存器以及自动重装载寄存器,后面的案例中我们着重配置的就是预分频以及自动重装载这俩部分。

       接下来,我们就先整体过一遍所有的寄存器,然后重点看看常用的部分。

1.1 TIMx_CR1寄存器

       TIMx_CR1是控制寄存器1,如上图。其中一共有五个可配置的位,主要是用于控制使能的位。其中常用的是第7位和第0位,也就是ARPE位和CEN位,分别代表的是预装载使能位和计数器使能位,如下图所示。

       ARPE位,就是用来确定是否进行预装载的位,前面介绍自动重装载寄存器的时候介绍过其可以分成重装载寄存器和影子寄存器两个,置1则是有缓冲,即进行预装载,然后计数过程中修改的重装载值会在本轮计数溢出后更新到影子寄存器起作用;置0就没有缓冲,即不进行预装载,修改的重装载值会直接更新到影子寄存器中作为本轮计数溢出时判断的数值。

       CEN位,也就是计数器使能位,用于开启或关闭计数器。置1则开启计数器,然后定时器就会开始计数;置0就是关闭计数器,定时器会停止计数。

       然后其他的位就用的没那么多,简单了解一下就行,如下图所示。

       OPM位,就是设置一种叫做单脉冲的模式,决定计数器在产生更新事件时是否停止计数的;

       URS位,更新请求源,可以选择更新事件的内容吧,比如置0就会在三种情况下的任一情况发生时产生一个更新中断或DMA请求,置1就只有计数器溢出这个情况会产生更新中断或DMA请求了;

       UDIS位,禁止更新,就是用来使能或者禁止UEV事件的产生,手册描述还比较详细,就不再赘述了。

       总结一下,状态寄存器CR1主要是用于开启或关闭计数器以及是否进行预装载的时候。

1.2 TIMx_CR2寄存器

       TIMx_CR2也是一个控制寄存器,其中只有一个控制位,包含三位,其介绍如下图所示。

       MMS位,主要是用于主模式选择的,就是前面我们解释功能框图时,在触发控制器部分中其中一个走向就是用来触发外设输出的,该位就是与之相关的内容,不过目前我们还用不着,所以不再赘述。

1.3 TIMx_DIER寄存器

       然后是TIMx_DIER寄存器(DMA Interrupt Enable Register),也就是DMA/中断使能寄存器,顾名思义就是用来使能DMA请求或者更新中断的寄存器。从上图中可以看见也只有两个位,可以猜到应该就是这两种情况的使能位了,详细介绍如下图所示。

       UDE位,也就是更新DMA请求使能位,置1则使能更新DMA请求,置0就不使能,即禁止;

       UIE位,即更新中断使能位,置1则使能更新中断,置0就是禁止更新中断。

       TIMx_DIER寄存器主要在使用定时器中断或进行DMA请求时常常需要配置。

1.4 TIMx_SR寄存器

TIMx_SR寄存器是一个状态寄存器,其中只有一位,不过他挺重要的。详细介绍如下图所示。

       UIF位,也就是更新中断标志位,这是记录更新中断是否产生的标志位,当产生更新中断时会被硬件自动置1,。值得注意的是,需要由软件将该标志位清除,也就是置0

       因此,该寄存器在产生更新中断的时候时务必要使用的,一般在产生更新中断后进入中断处理函数以后要首先注意清除更新中断标志位UIF,避免一直进入中断。

1.5 TIMx_EGR寄存器

       TIMx_EGR寄存器是一个事件产生寄存器,显然应该是产生更新事件的,其中也只有一个位使用,即UG,详细介绍如下图所示。

       如图中描述,UG位用来产生更新事件。该位置1以后,会重新初始化计数器,而且更新相关的寄存器,同时预分频器也会更新。这意味着更新事件所做的事情就是给我们更新定时器的寄存器配置以及计数器初始化、预分频器中的内容,这也是预分频器和自动重装载寄存器存在影子寄存器或者缓冲时会在产生更新事件后才进行更新的原因。

        因此,TIMx_EGR寄存器通常在希望初始化定时器的计数以及寄存器内容对UG位进行置位操作,清除是硬件自动进行,无需软件干预。

1.6 TIMx_CNT寄存器

       TIMx_CNT寄存器就是定时器的计数器寄存器,它既简单又尤为重要。其中就是存放定时器的计数值的寄存器,我们通常以读取计数器数值的方式使用

1.7 TIMx_PSC寄存器

       TIMx_PSC寄存器是预分频器寄存器,最大记录数值范围是16位,也就是0-65535。其主要是用来设置预分频数值的,然后会给到预分频器去对输入的内部时钟进行分频操作。我们通常会使用该寄存器进行预分频数值的配置,进而实现希望的时钟分频操作。

       比如要进行72分频,则这里设置的预分频数值为实际预分频系数减1,也就是设置为71,原因前面详细介绍过,这里不再赘述。

1.8 TIMx_ARR寄存器

       TIMx_ARR寄存器是自动重装载寄存器(Auto Reload Register),用于设置合适的自动重装载值,然后给到实际的自动重装载寄存器中,用来作为计数器溢出时机的参考值。ARR的数值最大16位,也就是0-65535

       比如计数5次,则这里设置的值为实际计数值-1,也就是设置为4,原因前面已经介绍过,这里不再赘述。

1.9 常用寄存器总结

       按照基本定时器功能框图的走向,除了内部时钟外,我们配置的主要是其中的时基单元,按顺序应该是设置预分频系数、设置自动重装载值、是否使能更新中断以及使能基本定时器外设,最后在更新中断后注意清零定时器更新中断标志位

       那么其中涉及到的寄存器也就是预分频寄存器、自动重装载值寄存器、DMA/中断使能寄存器、控制寄存器1以及状态寄存器这几个了。


二、基本定时器案例实践

       接下来就进入实操环节,编写代码,本次案例与前面学习的系统定时器实际上一样的内容,即控制LED闪烁

2.1 LED灯闪烁

2.1.1 需求描述

       利用基本定时器定时中断功能,实现LED-2每1s闪烁一次。

2.1.2 硬件电路设计

       由图可知,LED-2连接在GPIOA的PA1上,同时LED-2另一边接在3V3的高电平上,因此只需要PA1输出低电平即可点亮LED-2了,且控制闪烁就是一会输出高电平、一会输出低电平就能实现

2.1.3 软件设计(寄存器实现)

       关于这个需求的分析,与前面系统定时器案例是一样的,所以如果不太明白的话可以跳转过去看看其中的需求分析部分,这里就不再赘述了。

系统定时器SysTick的功能与用法详解-CSDN博客https://blog.csdn.net/2301_79475128/article/details/152412683?spm=1001.2014.3001.5501       那么我们直接开始一步一步编写代码。很明显,首先我们是要对本次使用的定时器进行初始化操作,那么我这次就使用TIM6来实现该案例吧。

       本次案例工程目录结构如下:

2.1.3.1 基本定时器的初始化

       初始化TIM6定时器,首先这也是个片上外设,肯定是有时钟的,而且前面在时钟树上也看见了,挂载在APB1低速外设总线上,因此首先我们需要开启时钟

    // 1. 开启时钟RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;

注:由于定时器是一个片上外设,所以并不涉及复用功能AF的时钟,且不算是外部中断,因此EXTI也不用,所以这里只需要开启定时器相应的时钟即可。

       然后就是定时器本身的配置,按照功能框图的顺序来,就是设置预分频系数、设置自动重装载值、使能更新中断、开启计数器,当然了,由于基本定时器也是一个外设,其NVIC需要进行一下配置(即中断优先级组、优先级等)。

       因此整个流程应该是设置预分频数值、设置自动重装载值、使能更新中断、配置NVIC、使能TIM6计数器。我们一步一步来。

       本次需求是每1s闪烁一次,也就是说尽可能实现1s产生一次更新中断即可,为了方便计算,我们预分频系数希望为7200,也就是将内部时钟72MHz进行7200分频,即计数频率为10KHz,然后计数次数设置为10000即可实现每1s产生一次溢出,并生成更新中断了。

       因此,对于预分频数值,我们设置为7200-1即可实现7200分频;自动重装载值设置为10000-1即可实现计数10000次,这样就能实现需要的定时1s了。代码参考如下

// 2. 设置预分频值
TIM6->PSC = 7200 - 1;
// 3. 设置自动重装载值
TIM6->ARR = 10000 - 1;

       然后开启中断使能,也就是DIER寄存器的操作UIE位,置1即可

// 4. 开启定时器中断
TIM6->DIER |= TIM_DIER_UIE;

       接着就是配置NVIC,主要是设置一下优先级组、中断优先级,最后使能NVIC就好了。优先级组一般就是全部设为抢占优先级,也就是优先级组设置为3,中断优先级在本案例中都可以,因为只有这一个中断。参考代码如下:

// 5. NVIC配置
// 5.1 设置优先级组分配
NVIC_SetPriorityGrouping(3);
// 5.2 设置各优先级值
NVIC_SetPriority(TIM6_IRQn, 2);
// 5.3 使能NVIC
NVIC_EnableIRQ(TIM6_IRQn);

       最后,开启TIM6计数器即可。

// 6. 使能定时器
TIM6->CR1 |= TIM_CR1_CEN;
2.3.1.2 重写中断处理程序

       初始化定时器后,就直接重写一下中断处理程序即可。同样其中断处理函数名可以去STM32的汇编文件的中断向量表里面找到,如下图所示。

       在中断处理程序中,首先我们不要忘了清除TIM6的更新中断标志位UIF,否则会一直进行中断。然后就直接执行LED状态翻转函数即可。

参考代码如下

// 中断处理程序
void TIM6_IRQHandler(void)
{// 清除中断标志位TIM6->SR &= ~TIM_SR_UIF;// 翻转LED2状态LED_Toggle(LED2);
}
2.3.1.3 main.c

       最后,在main.c中完成LED以及TIM6的初始化函数的调用即可。

/** @Description: 利用TIM6定时器中断实现每秒LED-2闪烁* @version: * @Author: JaRyon* @Date: 2025-03-01 20:49:32* @LastEditors: BreezeJuvenile* @LastEditTime: 2025-03-02 17:34:47*/
#include "LED.h"
#include "tim6.h"int main(void)
{// 1. 初始化LED_Init();TIM6_Init();// 2. 死循环保持状态while(1){		}
}

       至此,基于寄存器的实现就完成了。最后编译运行烧录一下即可看见LED-2闪烁的实验效果了。


2.3.2 软件设计(HAL库实现)

       接下来,我们再使用HAL库的方式实现一下这个案例。

2.3.2.1 STM32CubeMX配置

       首先打开STM32CubeMX,点击【ACCESS TO MCU SELECTOR】进行芯片选择。

       然后,搜索自己使用的芯片(我使用的是STM32F103ZET6),双击芯片创建工程。

       工程创建好后,会进入该工程的图形化配置界面。然后先进行系统配置,单击【System Core】,选择【SYS】,将其中的调试器【Debug】选择【Serial Wire】串行单线调试模式,也就是使用STlink使用的调试模式,时基源默认系统定时器,如下图所示。

       然后配置一下时钟。选择【RCC】,然后选择HSE和LSE均为石英晶体震荡【Crystal/Ceramic Resonator】。如下图所示。

       接着点击【Clock Configuration】配置一下时钟树,选择HSE,9倍频,然后APB1部分进行2分频,如下图所示。

       这里我们也可以发现,图形化配置中的时钟树自动将输入定时器的时候找那个频率x2给到了定时器2~7了。

       接着,配置定时器。选择【Timers】,我使用TIM6定时器,所以点击TIM6,然后勾选【Activated】,接着在下方【Parameter Settings】部分配置定时器6,如下图,显然就是对TIM6进行初始化的内容,依然预分频数值7199、重装载值9999、不进行预装载、不做触发。

       接着,进入【NVIC Settings】,给TIM6开启全局中断,勾选【Enabled】即可。

       然后我们就可以在NVIC部分看见我们开启的TIM6中断NVIC配置了,如下图所示。

       最后,我们要驱动LED-2,所以在配置一下GPIOA的PA1端口。

       我们的LED1连接在PA1上,所以在图形化32芯片上找到PA1,然后单击选择GPIO Output,然后配置一下相关模式,设置默认输出电平为高电平、通用推挽输出模式、无上下拉、最高速率、给一个标签LED2即可。

       最后我们配置一下工程管理,点击进入【Project Manager】,在【Project】中配置一下,如下图所示。

       然后进入【Code Generator】,勾选内容如下图所示。

       最后点击生成代码即可。

       然后就会自动在keil中打开工程了。接着我们对keil中的Debug做简单配置就好了。

2.3.2.2 Keil中的配置

       keil中主要是简单设置一下debug的内容。进入【魔法棒】,然后选择【Debug】,进入【Settings】,然后选择【Flash Download】,勾选上【Reset and Run】;然后进入【Pack】,取消勾选【Enable】即可。

       最后不要直接叉掉,点击确认/Ok后退出。

2.3.2.3 Vscode完善代码

       然后,我们就可以使用VScode打开工程,进行代码分析并且补充一下代码了。

       那么,我们先看看我们的定时器的代码,显然就是在tim.c中,直接去看

       显然,HAL库中是使用了一个TIM_HandleTypeDef结构体封装了定时器的各个参数,然后依次进行配置,和我们寄存器实现的时候的初始化差不多的,但是我们发现好像还是缺了点啥,没有使能定时器吧?是的,HAL库中把定时器的开启单独定义成了函数,因此我们首先要去在mian函数中调用开启定时器6的函数。如下图所示

       也就是调用一下HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)这个函数就可以,传入的参数就是相应定时器的结构体指针了。

       然后可以发现main.c中也进行了初始化调用,如下图所示。

       因此,我们最后需要补充的就是TIM6的中断处理程序部分了,因此核心在于stm32f1xx_it.c中,所以移步进入中断文件,翻到最下面找到我们的TIM6的中断处理程序的函数,如下图所示。

       可见这也是在里面调用了一个好像定时器中断总处理函数一样的函数,传入了前面初始化的TIM6配置的结构体指针。那我们进去看看这个HAL_TIM_IRQHnadler函数里面是什么,如下图所示。

       一点进来,一脸懵,这么多条件语句,一下肯定肯定看不明白,不过我们往下划一划可以看见里面就很多callback的函数,也可以确信这个函数确实是一个处理三类定时器中断的统一调用的函数了。那么我们需要处理TIM6的中断的话要怎么做呢?实际上确实是去找到对应的回调函数,然后重写,将中断处理逻辑写进去就好了。

       但是怎么找也是个问题,所以我们可以翻到这个文件最上面看看注释,如下图所示

        可以看见,这么多回调,其中我们本次要使用的是上图框出来的这个,这个名字的意思大概是周期消逝回调,也就是一个计数器溢出产生的更新中断的回调函数。

       然后我们直接复制了在这个文件里面搜一下对应的函数原型,如下图所示。

       __weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)就是这个函数,可见这是弱实现函数,因此我们可以重写它。同时其中传入的参数是TIM定时器的结构体指针,实际上这个函数也是一个统一使用的回调函数,所以传入对应定时器结构体指针才能知道我要执行的是哪一个定时器的中断,因此重写时还要判断一下如果执行的中断是TIM6的,那就执行其中的逻辑

       所以直接完整复制然后回到中断文件中最底下对其进行重写,若传入的结构体指针中的Instance是对应的TIM6,那么执行LED翻转的函数。

参考代码如下

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM6){HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);}
}

好了,最后编译运行,烧录一下就能看见实验效果了。

       至此,关于基本定时器的内容就结束了。


以上便是本次文章的所有内容,欢迎各位朋友在评论区讨论,本人也是一名初学小白,愿大家共同努力,一起进步吧!

鉴于笔者能力有限,难免出现一些纰漏和不足,望大家在评论区批评指正,谢谢!

http://www.dtcms.com/a/439381.html

相关文章:

  • pyhton基础【31】装饰器
  • 陕西网站建设价格网站死链
  • JDK21新特性全解析:重塑Java开发体验的五大突破
  • 合肥企业网站建设工access数据库网站
  • cuda版本和torch版本要对应才能使用
  • 如何解决顽固的盘符修改失败问题
  • Qwen3-Omni
  • 【C语言数据结构】第2章:线性表(1)--定义ADT
  • 做英文网站有用吗wordpress过滤html标签了
  • 瑞幸麦当劳代下单项目
  • 公司做网站找谁公司做网站找谁微信网站与响应式网站
  • ubuntu系统引导重置
  • 2017做电商做什么网站金华市建设局网站职称
  • 模糊视频图像如何处理?
  • 做外汇网站卖判刑多少年做图表的网站
  • 家具网站开发目的wordpress 父页面跳转
  • 江门免费模板建站o2o网站设计方案
  • 快速开发项目软件:一套可以企业级部署的激光切割系统上位机软件
  • 文案交流网站儿童网站 源码
  • 【Docker】基于Ubuntu的Docker的日常使用方法
  • 网站建设考核网站制作策划建设大纲
  • 建网站软件下载西部中大建设集团网站
  • 外贸网站外链网上哪里有卖嗅探器
  • 携程网的网站推广方式提升排名
  • Day02_刷题niuke20251003
  • 简化多智能体系统:基于双Agent的通用工具操作框架
  • DAY 39 图像数据与显存-2025.10.3
  • 如何开网站详细步骤深圳旅游公司网站
  • 自助建站免费自助建站网站icp备案号怎么查询
  • HTB Mailing 靶机渗透记录:利用 CVE-2024-21413 捕获 NTLM Hash