4.STM32CubeMX学习笔记四:STM32的定时器开发基础

一、定时器的基本概述


通过 滴漏漏沙瓶这两个例子简单理解定时器的基本工作原理。滴漏是向上计数,漏沙瓶为向下计数。
STM32的常见的定时器资源: 系统嘀嗒定时器SysTick、看门狗定时器WatchDog、实时时钟RTC、基本定时器、通用定时器、高级定时器。

系统嘀嗒定时器SysTick :
这是一个集成在Cortex M3内核当中的定时器,它并不属于芯片厂商的外设,也就是说使用ARM内核的不同厂商,都拥有基本结构相同的系统定时器。主要目的是给RTOS提供时钟节拍做时间基准。
基本定时器:TIM6、TIM7。
通用定时器:TIM2、TIM3、TIM4、TIM5:
在基本定时器的基础上,实现输出比较、输入捕获、PWM生成、单脉冲模式输出等功能。这类定时器最具代表性,使用也最广泛。
高级定时器:TIM1、TIM8。

二、STM32通用定时器的重要知识点


通用定时器的基本结构组成:
STM32的通用定时器,是一个通过可编程预分频器(Prescaler)驱动的16位自动重装主计数器(Counter Period)构成。可以对内部时钟或触发源以及外部时钟或触发源进行计数。

通用定时器的基本工作原理:
首先,定时器时钟信号送入16位可编程预分配器(Prescaler),该预分配器系数为0~65535之间的任意数值。预分配器溢出后,会向16位的主计数器(Counter Period)发出一个脉冲信号。
预分频器,本质上是一个加法计数器,预分频系数实际上就是加计数的溢出值。

定时器发生中断时间的计算方法:
定时时间

三、STM32CubeMX中关于TIM的配置


例:时钟信号32MHz,每隔500ms翻转一次PB5的输出电平。
【1】设置 Clock Source时钟源。
【2】设置 PrescalerCounter Period参数。(如何算着方便是重点和难点)
【3】设置 NVIC嵌套向量中断控制器。

计算:32000*500/32000000=0.5s=500ms
即Prescaler参数=500-1=499
Counter Period参数=32000-1=31999

和外部中断一样,定时器中断也需要对中断服务函数进行编写,这一部分会在接下来的练习中进行详细说明。
需要注意的是,在main()函数中启动相应的定时器,注意函数是否带中断使能

四、练习:外部中断信号控制LED灯开关


在STM32F103ZET6开发板上,利用STM32CubeMX和Keil5协同开发,完成以下的功能:
【1】利用 TIM2实现间隔定时,每隔0.2秒将 LED1的开关状态翻转。
【2】利用 TIM3实现间隔定时,每隔1秒将 LED0的开关状态翻转。
【3】修改 TIM2的初始化代码,改为每隔0.5秒将 LED1的开关状态翻转。

第一部分:配置STM32CubeMX
配置STM32CubeMX的详细步骤见基于HAL库开发的STM32学习笔记一:STM32的GPIO开发基础。这里说明配置定时器中断时需要注意的细节。
【1】由于使用了定时器中断,时钟树的设置尤为重要,时钟树的频率设置直接决定了后面定时的时长。我们在这里保证后面的时钟都为32MHz。
时钟树的设定
【2】定时器中断的配置。我们在练习中使用到了TIM2和TIM3中断,使用的是内部时钟,在左侧的Timers的选项卡中,找到TIM2和TIM3,将时钟源设置为内部时钟。
时钟源的设置
【3】两中断源参数的设置。根据第三部分关于TIM的配置这一部分的计算过程,以及TIM2和TIM3两个定时器需要设定的时间,可以得出:

定时器TIM2:32000*200/32000000=0.2s=200ms
即Prescaler参数=200-1=199
Counter Period参数=32000-1=31999

定时器TIM3:32000*1000/32000000=1s=1000ms
即Prescaler参数=1000-1=999
Counter Period参数=32000-1=31999
计数模式均设置为向上计数。
TIM2参数设置
TIM3参数设置
【4】使能相关的NVIC通道。与外部中断一样,定时器中断也需要使能相关NVIC通道,在左侧的Timers的选项卡中,找到TIM2和TIM3,Configuration选项卡中有NVIC Settings中的选项卡,进入后勾选对应定时器中断即可。
使能相关NVIC通道
第二部分:Keil5中代码的编写
【1】定时器参数配置函数的剖析
可以在项目的tim.c文件中看到,该文件中对TIM2和TIM3两个定时器的参数进行了配置,这些都是我们在STM32CubeMX中进行设定的,在后续配置中也可以通过修改此处代码来改变参数。练习的第三步修改TIM2代码,修改定时时间,就在此处进行处理。

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 31999;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 199;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

【2】定时器中断服务函数的编写
我们在stm32f1xx_hal_tim.c文件中可以找到定时器溢出回调函数:

__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(htim);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_TIM_PeriodElapsedCallback could be implemented in the user file
   */
}

用户对定时器溢出函数进行重写即可,来实现具体的中断逻辑处理。

/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM2)                //处理TIM2定时中断
	{
		HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
	}
	
	if(htim->Instance == TIM3)                //处理TIM3定时中断
	{
		HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
	}
}
/* USER CODE END 0 */

在main函数中启用定时器:

/* USER CODE BEGIN 2 */
    HAL_TIM_Base_Start_IT(&htim2);        //启动定时器TIM2
    HAL_TIM_Base_Start_IT(&htim3);        //启动定时器TIM3
  /* USER CODE END 2 */

注意事项:
(1)在stm32f1xx_hal_tim.c文件中,有两个定时器启动函数,需要注意的是,函数名称带IT的,带有中断使能;不带IT的,没有中断使能操作,在main函数中启用定时器不要出错。

HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim);      //不含中断使能操作
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);   //带有中断使能操作

(2)在main函数中启用定时器时,可以发现在中断服务函数编写时,使用的是指针类型的参数,我们在调用的时候,要注意取htim变量的地址,需要加取地址符号。

点击此处即可下载练习例程

以下是几篇自己学习过程中发现的比较好的文章,有助于对C语言中的指针与定时器中断的理解和使用,学习时可以参考:

C/C++中和&的用法
C语言中的和&符号
C语言指针详解(经典,非常详细)
C语言指针详解
STM32 定时器详细篇(基于HAL库)
STM32 定时器的几种基本使用
STM32基础定时器详解
STM32定时器详解(定时器中断实验)

以上内容为本人学习b站小蜜蜂老师基于STM32CubeMX的嵌入式开发基础教程所做的笔记,其中有一些为个人的理解与感悟,如有疏漏之处,敬请大佬指正。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
/* TIM3 init function */ 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}; TIM_OC_InitTypeDef sConfigOC = {0}; /* USER CODE BEGIN TIM3_Init 1 */ /* USER CODE END TIM3_Init 1 */ htim3.Instance = TIM3; htim3.Init.Prescaler = 71; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 9999; htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; 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(); } if (HAL_TIM_PWM_Init(&htim3) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_4) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM3_Init 2 */ /* USER CODE END TIM3_Init 2 */ HAL_TIM_MspPostInit(&htim3); }
07-13

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值