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

细说MCU定时器中断的实现方法

目录

一、硬件及工程 

二、STM32G4系列MCU的定时器

三、定时器中断的实现过程

1、配置新工程.ioc

2、代码修改

(1)时钟初始化函数MX_TIM3_Init()

(2)使能定时器中断

(3)定时器中断服务函数

(4)重定义定时器回调函数

3、下载并运行 

4、修改定时器参数


一、硬件及工程 

        文章依赖的硬件及工程配置同本文作者的其他文章:细说ARM MCU的串口接收数据的实现过程-CSDN博客 https://wenchm.blog.csdn.net/article/details/139541112

二、STM32G4系列MCU的定时器

         STM32G4系列MCU的定时器功能比较强大,有下面几种定时器:

  • 1个高精度定时器(high-resolution timer);
  • 3个高级控制定时器(advanced-control timer);
  • 7个通用定时器(general-purpose timer);
  • 2个基本定时器(basic timer);
  • 2个看门狗定时器(watch-dog timer);
  • 1个SysTick定时器。

  

定时器

计数器
精度

计数器类型

预分频因子

DMA
请求产生

捕捉/

比较通道

互补输出

高精度
定时器

HRTIM

16

Up

1/2/4(x2,x4,
x8,x16,x32,
DLL)

12

高级
控制

TIM1TIM8TIM20

16

UpDown
Up/Down

165536
之间的整数

4

4

通用

TIM2TIM5

32

UpDown
Up/Down

165536
之间的整数

4

通用

TIM3TIM4

16

UpDown
Up/Down

165536
之间的整数

4

通用

TIM15

16

Up

165536
之间的整数

2

1

通用

TIM16TIM17

16

Up

165536
之间的整数

1

1

基本

TIM6TIM7

16

Up

165536
之间的整数

0

        定时器最基本的功能是起到定时的作用,其中有一个关键模块:计数器(counter)。该计数器可以循环往复计数,计数的模式有三种类型:升、降和升/降。Up模式是从0到最大值递增计数,计到最大值后再从0重新开始计。

        除了TIM2和TIM5以外,其余的定时器中,计数器都是16位,相应的计数最大值为65535。除了计数器的参数以外,定时器中的另一个比较重要的参数是预分频因子(prescaler factor),这个参数关系到两次计数之间的计时间隔(具体数值,还要结合定时器的时钟频率来计算)。此外,定时器还可用于输入捕捉,以及产生PWM波形(互补)输出;当然对这两个功能,不同的定时器是有差别的。

三、定时器中断的实现过程

        本文利用 STM32G474RE上的通用定时器,以定时器中断的方式控制NUCLEO - G474RE板上的发光二极管LD2以不同的频率闪烁(该功能也可通过延时函数的方式实现)。

1、配置新工程.ioc

  • 配置GPIO:配置PA5为输出,用PA5控制开发板上的LD2。Level=High,mode=PP,Pull_up,speed=High,Label=LED;
  • 外部时钟,Serial Wire;
  • 配置定时器:打开TIM3的配置界面,在模式(Mode)区,将时钟源(Clock Source)选择为Internal Clock;然后,在配置区中,将参数设置(Parameter Settings)选项卡中的预分频因子(Prescaler)和计数器周期(Counter Period)分别设置为999和16999。这两个参数从0开始计数,分频因子为999,实际为分频999+1倍;计数器周期的计算与此相同。这里的计数周期实际就是计数器计数时的最大值,在时钟频率确定的情况下,预分频因子决定着两次计数之间的时间间隔。所以,根据这两个参数以及定时器的时钟频率,就可以计算出定时器计数的周期。此外,把计数模式(Counter Mode)设置为升模式(Up),并使能自动重载(auto-reload preload)。
  • 配置中断:使能TIM3的全局中断。将它的抢占式优先级设为1,响应优先级设为0。
  • 配置系统时钟:将系统时钟(SYSCLK)频率配置为170 MHz。定时器的时钟来自高级外设总线(APB,Advanced Peripheral Bus),APB的时钟也有自己的预分频因子,如果该因子为1,则定时器的时钟频率就与APB时钟相同,也与系统时钟相同,都是170 MHz。

2、代码修改

        至此,硬件配置便完成了。保存,启动代码生成过程,系统会将刚才配置硬件的信息自动转换成代码。

(1)时钟初始化函数MX_TIM3_Init()

         该函数自动生成。

/*** @brief TIM3 Initialization Function* @param None* @retval None*/
static void MX_TIM3_Init(void)
{/* USER CODE BEGIN TIM3_Init 0 *//* USER CODE END TIM3_Init 0 */TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};/* USER CODE BEGIN TIM3_Init 1 *//* USER CODE END TIM3_Init 1 */htim3.Instance = TIM3;htim3.Init.Prescaler = 999;htim3.Init.CounterMode = TIM_COUNTERMODE_UP;htim3.Init.Period = 16999;htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&htim3) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM3_Init 2 *//* USER CODE END TIM3_Init 2 */}

        MX_TIM3_Init()函数主要完成对TIM3的模式和参数配置,如预分频因子、计数模式、计数周期等参数。在MX_TIM3_Init()函数的定义中,用到了一个结构体变量htim3,该结构体变量也被称为定时器句柄。这个变量是在自动代码生成过程中自动生成的,位于main.c文件的最前面:

TIM_HandleTypeDef htim3;

        在MX_TIM3_Init()函数的定义中,把设置的参数赋给了结构体变量htim3。其中一条if语句,在其条件表达式中调用了一个函数:HAL_TIM_Base_Init(&htim3);结构体变量htim3通过调用HAL_ TIM_Base_Init(&htim3)实现与实际硬件关联的。该函数只有一个参数。调用时,把刚配置的结构体变量htim3传递了过来。实际上,真正与硬件关联的,还不是HAL_TIM_Base_Init()函数,而是在HAL_TIM_Base_Init()函数中调用的TIM_Base_SetConfig()函数。正是通过TIM_Base_SetConfig()函数,才真正地把设置的参数传递给了相关寄存器。在库函数文件stm32g4xx_hal_tim.c中有对TIM_Base_Set-Config()函数的定义。

(2)使能定时器中断

        虽然配置了TIM3的中断功能,但在默认情况下,中断不是开启的。所以,在使用时,还要开启该中断。开启定时器中断可以使用如下库函数:

HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);

        该函数也只有一个参数,并且该参数也是一个结构体变量。对于TIM3来说,其实就可以用前面提到的htim3。开启定时器中断可以使用如下代码:

/*USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim3);    //手动添加
/*USER CODE END 2*/

        上述代码位于while(1)循环前面的注释对中。不过,需要将它放到TIM3初始化函数MX_TIM3_ Init()的后面。

(3)定时器中断服务函数

        开启TIM3的中断后,当条件满足时,就会执行定时器中断服务函数TIM3_IRQHandler()。该函数是自动生成的,位置在stm32g4xx_it.c文件中有该函数的定义:

/*** @brief This function handles TIM3 global interrupt.*/
void TIM3_IRQHandler(void)
{/* USER CODE BEGIN TIM3_IRQn 0 *//* USER CODE END TIM3_IRQn 0 */HAL_TIM_IRQHandler(&htim3);/* USER CODE BEGIN TIM3_IRQn 1 *//* USER CODE END TIM3_IRQn 1 */
}

        TIM3_IRQHandler()函数的定义中又调用了HAL_TIM_IRQHandler()函数,此函数的定义在stm32g4xxhal_tim.c中。实际上,在HAL_TIM_IRQHandler()函数中,还会调用TIM中断的回调函数HAL_TIM_PeriodElapsedCallback()。这个函数的定义也是在stm32g4xxhal_tim.c中,不过,被定义为一个弱函数。这种方式与串口中断接收过程是类似的。就像对串口接收中断回调函数的处理一样,在定时器中断的使用中,需要做的是在main.c中重新定义TIM中断的回调函数。

(4)重定义定时器回调函数

        在main.c中重新定义回调函数HAL_TIM_PeriodElapsedCallback()。在其中让PA5的输出状态翻转。具体实现如下:

/*USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
}
/*USER CODE END 4*/

        本例中,定时器的预分频因子(Prescaler)和计数器周期(Counter Period)分别置为999和16999,定时器的时钟频率为170 MHz,最终TIM3中断的周期为

(999+1)/(170×10⁶)×(16999+1)=0.1(s),即频率为10 Hz。

3、下载并运行 

        编译工程并下载到硬件中运行,会看到LD2灯以5 Hz的频率闪烁。为什么是5 Hz因为控制PA5用的是Toggle。

4、修改定时器参数

        修改定时器的预分频因子(Prescaler)和计数器周期(CounPeriod),改变LD2灯的闪烁频率为1 Hz、0.5 Hz等。

(19999+1)/(170×10⁶)×(16999+1)=2(s),即频率为2 Hz。
(9999+1)/(170×10⁶)×(16999+1)=1(s),即频率为1 Hz。

相关文章:

  • Java18新特性(极简)
  • 在Linux/Ubuntu/Debian中使用SSH连接远程服务器VPS
  • curl命令使用
  • 活出属于自己的人生
  • VScode基本使用
  • 使用 Netty 自定义解码器处理粘包和拆包问题详解
  • 【HiveSQL】join关联on和where的区别及效率对比
  • 【总线】AXI4第三课时:握手机制
  • 磁力搜索器,解读新一代的搜索引擎方式,磁力王、磁力猫等引擎的异同及原理
  • Java学习 (四) 面向对象--类与方法
  • 工业 UI 风格,展现独特魅力
  • 从零开始:使用ChatGPT快速创作引人入胜的博客内容
  • 前端安全——最新:lodash原型漏洞从发现到修复全过程
  • 计算机网络知识点汇总(二)
  • Linux环境编程基础学习2
  • angular2开源库收集
  • centos安装java运行环境jdk+tomcat
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • flutter的key在widget list的作用以及必要性
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • Mocha测试初探
  • ReactNativeweexDeviceOne对比
  • socket.io+express实现聊天室的思考(三)
  • SpiderData 2019年2月23日 DApp数据排行榜
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • spring学习第二天
  • TCP拥塞控制
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 创建一个Struts2项目maven 方式
  • 高程读书笔记 第六章 面向对象程序设计
  • 基于webpack 的 vue 多页架构
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 如何学习JavaEE,项目又该如何做?
  • 入门级的git使用指北
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 温故知新之javascript面向对象
  • Spring第一个helloWorld
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • (2)空速传感器
  • (C)一些题4
  • (C语言)fread与fwrite详解
  • (Redis使用系列) SpringBoot 中对应2.0.x版本的Redis配置 一
  • (一)Neo4j下载安装以及初次使用
  • (已解决)Bootstrap精美弹出框模态框modal,实现js向modal传递数据
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • (转)Sublime Text3配置Lua运行环境
  • (转)编辑寄语:因为爱心,所以美丽
  • (转)甲方乙方——赵民谈找工作
  • (转)项目管理杂谈-我所期望的新人
  • (自用)仿写程序
  • .apk文件,IIS不支持下载解决
  • .htaccess 强制https 单独排除某个目录