手把手教你开发stm32——定时器(上)(基于hal库)

这篇具有很好参考价值的文章主要介绍了手把手教你开发stm32——定时器(上)(基于hal库)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.stm32定时器介绍

1.1.stm32f103定时器介绍

  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。
  • 16位计数器、预分频器、自动重装载寄存器的时基单元。
  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能。
  • 根据复杂度和引用场景分为了高级定时器、通用定时器、基本定时器三种类型。
    stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
    以上是各类定时器的主要功能

1.2.定时器计数模式

stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件

  1. 向上计数模式
    计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始技术并且产生一个计数器溢出事件。
  2. 向下计数模式
    计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
  3. 中央对齐模式(向上/向下计数)
    计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出四件;然后再从0开始重新计数。

1.3.定时器的时钟

stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
上图是stm32的系统结构,我们可以从上图中看到,TIM1和TIM8是挂载到APB2总线上的,而TIM2-TIM7是挂载到APB1总线上的。
在stm32f103系列的MCU中,虽然定时器挂载的总线是不同的,但是每个定时器的时钟频率都是相同的。而其他系列的MCU,比如说stm32f407系列的MCU,如果挂载到不同的总线上,那么定时器的时钟频率可能是不同的。从下图配置的cubemx图中我们可以看到每个定时器上具体的时钟。
stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
我们从上面这个图中可以看到,挂载到APB1和APB2总线上的定时器的时钟都是72MHZ。

2.stm32时钟的工作方式

我们这个地方主要就是讲一下定时器基本的定时计数的功能,定时器基本的定时计数功能主要是通过以下的框图来进行的。
stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件

  • 时钟源:定时器时钟TIMxCLK,即内部时钟CK_INT,经过APB预分频器分频后提供,就是从APB总线上引出的时钟来源。
  • 计数器时钟:计数器时钟是经过②PSC预分频器后产生的,就是由于不需要APB总线上这么高频率的时钟,所以在时钟输入以后,还需要进行预分频以后,才能产生真正驱动CNT计数器的时钟,每个定时器的时钟的预分频器PSC都是最高16位的,所以不能超过预分频器的范围。
  • 计数器CNT:计数器CNT是进行计数的单元,当接收到CK_CNT来的时钟信号以后,每经过一个时钟频率,CNT就会进行向上计数或者向下计数,当向上计数到自动重装载寄存器的值(ARR)或者从ARR减到0以后,就会产生中断或者事件更新。计数器CNT是一个16位/32位的计数器,每个定时器都是不同的,所以我们需要根据实际的情况来进行设置。
  • 自动重装载寄存器(ARR):这里面装着计数器能计数的最大数值,当计数到这个值的时候,如果使能了中断的话,定时器就会产生溢出中断。
  • 计时中断时间:1/(TIMxCLK/(PSC+1)*(ARR+1))

3.定时器中断具体实现

我们需要实现的目标是利用基本定时器实现定时1s中断,并在中断处理函数中控制LED灯的亮灭。

3.1.cubemx的具体配置

首先配置TIM2的相关参数
stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
其次配置TIM2的相关中断
stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件

3.2.具体代码的实现

stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
我们设置了定时器中断,同样需要在这个中断C文件中去寻找相关的TIM2的中断,我们找到这个函数以后追进去。
stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
我们找到定时器更新事件,不出意外需要重写回调函数。
stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
进行回调函数的重写,写的规则是if(htim->Instance == TIM2) {HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_1);},这样就可以在每一秒钟,使得PA1口的LED灯进行亮灭的操作。
其中htim->Instance 是指htim是一个结构体指针,然后指向他的成员变量intance(这个是寄存器基地址),如果这个寄存器的基地址等于TIM2的基地址,则证明这两者相同,也就是说是TIM2产生的事件更新中断,然后再进行下一步的操作。

写到这个地方以后,我们还需要进行定时器中断时基单元的开启,这样,定时器才能开始计数。
stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
我们在main函数中开启TIM2的时基单元,这样就能实现最终的效果了。

4.通用定时器功能分析

通用定时器除了最基本的定时功能以外,还有输入捕获和输出比较的功能,具体的功能我们可以通过下面的结构框图来进行了解。
stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
我们一块一块来分析

  1. 第1块是整个定时器的时钟源,用来进行时钟源的选择,时钟源有以下几个选择:

    • 内部时钟(CK_INT):内部时钟源就是通过stm32的内部时钟来进行时钟的输入的,通过内部时钟总来作为定时器的时钟来源
    • 外部时钟模式1:外部输入引脚Tlx(x=1,2,3,4),采用定时器的外部通道引脚的输入信号(TI1,TI2)作为定时器的时钟源。TI1(TI2)通过滤波和边缘检测,得到信号TI1FP2(TI2FP2)作为计数器的触发信号。值得注意的是,只有TI1FP2和TI2FP2可以作为时钟源,也就是说想要使用外部时钟模式1,时钟信号只能通过通道1或者通道2输入。
    • 外部时钟模式2:外部触发输入ETR,采用定时器的ETR引脚作为外部时钟信号源,通过边沿检测和滤波来作为定时器的时钟源。
    • 外部触发输入(ITRx):使用一个定时器作为另一定时器的预分频器,将主定时器的TRGO信号作为从定时器的ITRx输入作为时钟源。
      一般而言,我们都是使用内部时钟作为定时器的时钟来源
  2. 第2块是通用定时器的控制器,主要包括触发控制器、从模式控制器以及编码器接口。触发控制器用来针对片内外设输出触发信号,比如为其他定时器提供时钟和触发DAC/ADC转换。编码器接口专门针对编码器计数来使用;从模式控制器可以控制计数器复位、启动、递增/递减、计数等功能。控制器其实说白了就是时钟系统的配置单元,一个外设中需要使能相应的功能那么就需要进行控制器的配置,比如说我们需要配置定时器的中断、定时器主从级联、定时器发送信号等功能就需要进行控制器的配置,由控制器来控制整个定时器的功能。

  3. 第3块是时基单元,定时器产生的定时计数就是从这个地方产生的。通用定时器的时基单元主要由预分频器PSC、计数器CNT、自动冲转载寄存器ARR组成的。时钟单元我们在第二章stm32时钟工作方式讲过,这个地方就不再赘述。

  4. 第4块是输入捕获的部分,输入捕获部分我们主要就是用来捕获外部输入信号的频率或者脉宽,我们肯定是需要去将外部的信号去连接我们的定时器通道。

    • 当一个信号输入进来以后,首先我们需要对输入进来的信号进行一个滤波,因为输入进来的信号可能不是很稳定的信号,所以我们首先需要进行滤波的操作;然后我们还需要进行一个边沿的检测,当我们需要去检测一个外部信号的脉宽或者频率的时候,我们首先要想的就是如何去检测这个信号,我们肯定是要用边沿来检测这样一个信号,如果要去检测脉宽,那么就需要测量一个信号的上升沿/下降沿和下降沿/上升沿,如果需要去检测一个信号的频率,那么我们就需要去检测一个信号的两次边沿,这样就能测量出来这个信号的频率,这就是我们需要使用一个输入滤波和边沿检测器的原因了。
    • 采集到信号以后,我们需要进行预分频器的设置,我们可以去设置这个预分频器,也可以不去设置这个预分频器的值,如果设置这个预分频器,比如说我们将预分频器的值设置为2,那么这个意思就是,我们在采集信号频率的时候,并不是采集到两次上升沿或者两次下降沿就去报告我们采集到这个信号的频率,我们需要去采集四次上升沿和下降沿再去汇报这个信号的频率,这样做的目的就是我们可以进行信号的一次采集滤波,更好地去测试信号的频率。
  5. 第5块是捕获/比较寄存器,输入捕获和输出比较的时候都需要用到这个寄存器,在我们使用输入捕获的功能的时候,我们可以用这个寄存器来存放我们输入捕获到的值,这就是我们在输入捕获过程中需要使用这个寄存器的方法;而输出比较的时候,我们需要通过这个寄存器来设置CCR的值,通过这个CCR的值和ARR的值的比较,我们就可以输出一个PWM的波形,这样就可以用作电机PWM调速或者产生呼吸灯的效果,具体的输出比较功能我们放在第6块来讲解。

  6. 输出比较的功能就是用来产生PWM波形,下面这个图就是显示PWM的原理。
    我们可以看到在0-t1的过程中,CCR的值小于ARR的值,这样就可以产生一个低电平;在t1-t2的过程中,ARR的值大于CCR,这样就可以产生一个高电平;我们知道,PWM的全称是脉冲宽度调制,当高低电平在一个周期中有不同的时候,这样就可以产生不同的电压。比如说我们知道高电平是3.3V,当低电平时间在一个周期里面占40%,那么高电平时间占60%的时间,那么这样在一个周期里面的平均电平就是1.98V,这样就是PWM的原理,通过输出比较的功能,我们就可以看到这样就可以产生一个PWM的信号用来驱动电机或者产生呼吸灯的效果。
    stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件

5.高级定时器功能分析

stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
高级定时器和通用定时器的主要区别就是在第3块和第5块的地方
第3块主要的区别就是,高级定时器比通用定时器多了一个重复次数计数器,重复次数计数器的作用就是,当CNT计数器产生溢出的时候,并不会直接产生中断,而是会将溢出传递到重复次数计数器,重复次数计数器也会存一个数值,每次CNT计数器产生溢出以后,重复次数计数器数值就会减一,当重复次数计数器减到0以后,才会产生溢出。
第5块主要区别就是高级定时器增加了可编程的死区互补输出功能,主要应用在工业电机控制方面。但是目前来讲,我没有用到过这个功能,因此我也就不在这里班门弄斧了。

6.输入捕获实验

我们上面介绍过,定时器有一个非常好用的功能就是输入捕获,很多的示波器的原理就是利用时钟的输入捕获的功能,本章节就来具体讲一个输入捕获的实验。
实验目标:利用定时器4的输入捕获功能测量按键按下后低电平持续的时间,并用LED灯亮的时间来显示捕获的时间。

6.1.理论知识

输入捕获功能框图如下图所示
stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
我们可以从每个定时器的通道中来进行输入捕获,具体的结构分析我们在上面的理论知识中已经讲过了,这里就不再赘述了。

6.2.cubemx配置

stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
其他配置都和原来的配置相同,将调试端口、时钟配置、文件名配置完成后就可以生成对应的文件了。

6.3.具体代码实现

实验目标:利用定时器4的输入捕获功能测量按下按键后低电平持续的时间,并通过PA3口LED灯亮的时间来显示。

  1. 首先输入捕获模式,可以用来测量频率或者电平保持的时间,如下图所示:
    stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
    我们如果需要捕获低电平的时间,就需要从value2处触发一次捕获中断,然后再从value3处触发一次捕获中断,通过这两次捕获中断的差值就可以来计算出低电平持续的时间了。但是我们一开始设置的是下降沿捕获,而第二次捕获需要进行上升沿的捕获,所以中间就会涉及到一次边沿捕获方式的转变。
  2. 然后我们需要考虑捕获的时间,由于我们低电平持续的时间可能会大于定时器ARR的值,导致中途溢出,如下图所示:
    stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
    所以我们还需要考虑每次中途溢出的时间,我们可以用一个标志位来记录每次按键按下以后,低电平持续时间中,定时器中断溢出了几次,这样我就可以使用标志位*ARR的值来计算溢出的时间,然后通过最后捕获的时间减去初始时间就能得到最后精确的值了。

以下是我的代码的具体实现方式:

  1. 我们首先找到TIM4的中断函数
    stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
  2. 追进去以后需要找到两个回调函数,第一个就是定时器中断溢出的回调函数,第二个就是输入捕获的回调函数,我们找到这两个回调函数以后,需要对这两个回调函数进行重写,来实现我们的功能。

stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
3. 两个回调函数的重写
我的两个回调函数是在time.c中重写并实现具体的功能的,我的回调函数都有写注释,然后具体可以看我写的注释。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM4)
	{
		HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_3);  //当按键按下以后,每过1s钟,PA3上的LED灯就翻转一次
		tim_value+=10000;  //按键按下后,当到达溢出以后,tim_value的值就加上ARR设置的值,ARR设置的值为10000
	}
}


void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)  //输入捕获中断的回调函数
{
	if(htim->Instance==TIM4)  //检查是否为TIM4触发的中断
	{
		if(toggle_flag==0) //检查标志位
		{
			HAL_Delay(20); //消抖
			if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6)==GPIO_PIN_RESET)
			{
				toggle_flag=1;  //进入中断后首先翻转标志位
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET); //PA2的LED灯亮200ms,显示进入输入捕获函数
				HAL_Delay(200);
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
				HAL_TIM_Base_Start_IT(htim); //开启溢出中断,开始进行计时
				
				__HAL_TIM_DISABLE(htim);  //关闭TIM4
				
				__HAL_TIM_SET_COUNTER(htim,0); //设置时钟计数值为0
				TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);  //清除TIM4通道1的原始设置
				TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);//将触发输入捕获中断源设置为上升沿捕获
				
				__HAL_TIM_ENABLE(htim);//使能TIM4
			} 
		}
		else if(toggle_flag==1)//检查标志位
		{
			HAL_Delay(20);//消抖
			if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6)==GPIO_PIN_SET)//检查是否为上升沿
			{
				toggle_flag=0; 
				HAL_TIM_Base_Stop_IT(htim);//关闭溢出中断
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);//若是上升沿触发输入捕获中断,LED灯亮200ms
				HAL_Delay(200);
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
				
				tim_value=(tim_value+HAL_TIM_ReadCapturedValue(&htim4,TIM_CHANNEL_1 ))/10+20;//计算捕获的总时间
				
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET); //计算出总时间后,使得PA2口的LED灯亮的时间和总时间相同,以显示输入捕获效果
				HAL_Delay(tim_value);
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
				
				__HAL_TIM_DISABLE(htim);
				
				TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);
				TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING);
				
				__HAL_TIM_ENABLE(htim);
			}
		}
	}
}
  1. 其他工作
    我们用到了定时器的相关内容,就需要使能这些内容。
  • 首先我们需要开启定时器的输入捕获功能。
    stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
  • 当然我们还需要开启定时器溢出中断的功能,但是这部分功能我在重写输入捕获的回调函数的时候才进行操作,具体可以看我上面的代码
    stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件
  • 定义相关变量
    我们在重写定时器溢出回调函数和定时器输入捕获回调函数的时候用到了一些变量,我们需要在time.c中定义这些变量
    stm32裸机开发定时器,手把手教你学stm32,stm32,单片机,嵌入式硬件

下面是我的time.c和main.c中的具体代码
main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	
	
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM4_Init();
  /* USER CODE BEGIN 2 */
	
	HAL_TIM_IC_Start_IT(&htim4,TIM_CHANNEL_1);
//	extern uint32_t tim_value;
//	extern uint8_t send_flag;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
//	  if(send_flag==1)
//	  {
//		  tim_value/=1000;
//		  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
//		  HAL_Delay(tim_value);
//		  tim_value=0;
//	  }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */



/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

time.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    tim.c
  * @brief   This file provides code for the configuration
  *          of the TIM instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "tim.h"

/* USER CODE BEGIN 0 */
uint32_t tim_value=0;
uint8_t toggle_flag=0;
//uint8_t send_flag=0;
/* USER CODE END 0 */

TIM_HandleTypeDef htim4;

/* TIM4 init function */
void MX_TIM4_Init(void)
{

  /* USER CODE BEGIN TIM4_Init 0 */

  /* USER CODE END TIM4_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_IC_InitTypeDef sConfigIC = {0};

  /* USER CODE BEGIN TIM4_Init 1 */

  /* USER CODE END TIM4_Init 1 */
  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 7200;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 10000;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_IC_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
  sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
  sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  sConfigIC.ICFilter = 0;
  if (HAL_TIM_IC_ConfigChannel(&htim4, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM4_Init 2 */

  /* USER CODE END TIM4_Init 2 */

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(tim_baseHandle->Instance==TIM4)
  {
  /* USER CODE BEGIN TIM4_MspInit 0 */

  /* USER CODE END TIM4_MspInit 0 */
    /* TIM4 clock enable */
    __HAL_RCC_TIM4_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**TIM4 GPIO Configuration
    PB6     ------> TIM4_CH1
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* TIM4 interrupt Init */
    HAL_NVIC_SetPriority(TIM4_IRQn, 1, 1);
    HAL_NVIC_EnableIRQ(TIM4_IRQn);
  /* USER CODE BEGIN TIM4_MspInit 1 */

  /* USER CODE END TIM4_MspInit 1 */
  }
}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM4)
  {
  /* USER CODE BEGIN TIM4_MspDeInit 0 */

  /* USER CODE END TIM4_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM4_CLK_DISABLE();

    /**TIM4 GPIO Configuration
    PB6     ------> TIM4_CH1
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6);

    /* TIM4 interrupt Deinit */
    HAL_NVIC_DisableIRQ(TIM4_IRQn);
  /* USER CODE BEGIN TIM4_MspDeInit 1 */

  /* USER CODE END TIM4_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM4)
	{
		HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_3);  //当按键按下以后,每过1s钟,PA3上的LED灯就翻转一次
		tim_value+=10000;  //按键按下后,当到达溢出以后,tim_value的值就加上ARR设置的值,ARR设置的值为10000
	}
}


void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)  //输入捕获中断的回调函数
{
	if(htim->Instance==TIM4)  //检查是否为TIM4触发的中断
	{
		if(toggle_flag==0) //检查标志位
		{
			HAL_Delay(20); //消抖
			if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6)==GPIO_PIN_RESET)
			{
				toggle_flag=1;  //进入中断后首先翻转标志位
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET); //PA2的LED灯亮200ms,显示进入输入捕获函数
				HAL_Delay(200);
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
				HAL_TIM_Base_Start_IT(htim); //开启溢出中断,开始进行计时
				
				__HAL_TIM_DISABLE(htim);  //关闭TIM4
				
				__HAL_TIM_SET_COUNTER(htim,0); //设置时钟计数值为0
				TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);  //清除TIM4通道1的原始设置
				TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);//将触发输入捕获中断源设置为上升沿捕获
				
				__HAL_TIM_ENABLE(htim);//使能TIM4
			} 
		}
		else if(toggle_flag==1)//检查标志位
		{
			HAL_Delay(20);//消抖
			if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6)==GPIO_PIN_SET)//检查是否为上升沿
			{
				toggle_flag=0; 
				HAL_TIM_Base_Stop_IT(htim);//关闭溢出中断
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);//若是上升沿触发输入捕获中断,LED灯亮200ms
				HAL_Delay(200);
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
				
				tim_value=(tim_value+HAL_TIM_ReadCapturedValue(&htim4,TIM_CHANNEL_1 ))/10+20;//计算捕获的总时间
				
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET); //计算出总时间后,使得PA2口的LED灯亮的时间和总时间相同,以显示输入捕获效果
				HAL_Delay(tim_value);
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
				
				__HAL_TIM_DISABLE(htim);
				
				TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);
				TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING);
				
				__HAL_TIM_ENABLE(htim);
			}
		}
	}
}


/* USER CODE END 1 */

以上就是定时器(上)的相关内容,我们了解了定时器的相关结构、定时器的工作原理、定时器中断实验和输入捕获实验,欢迎各位大佬进行批评指正!文章来源地址https://www.toymoban.com/news/detail-775430.html

到了这里,关于手把手教你开发stm32——定时器(上)(基于hal库)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 手把手教你编写跑马灯——STM32

    新建一个文件夹 ,打开KeiL,projiece-----new vision projection  给文件命名(随便取)  根据自己开发板的信号选择对应的型号  在刚才创建的文件夹里面 新建一个main.c文件  双击source group 1,点击main.c,点击add  添加头文件 led.c main.c led就可以全亮然后全灭一直循环

    2024年02月08日
    浏览(38)
  • 手把手教你写stm32f103智能风扇

    本系统可以分为两个模式来进行运行,分别为手动模式和自动模式,同时,在上电进入系统后,还会有一个模式选择的界面产生。 模式选择:在此界面中,可以通过按键K1来控制模式选择,两个模式分别为手动模式和自动模式;通过按键K2可以进入模式。 手动模式:在手动模

    2023年04月17日
    浏览(44)
  • 动手实践丨手把手教你用STM32做一个智能鱼缸

    摘要: 本文基于STM32单片机设计了一款基于物联网的智能鱼缸。 本文分享自华为云社区《基于STM32+华为云IOT设计的物联网鱼缸【玩转华为云】》,作者: DS小龙哥 。 为了缓解学习、生活、工作带来的压力,提升生活品质,许多人喜欢在家中、办公室等场所养鱼。为节省鱼友

    2024年01月16日
    浏览(39)
  • FPGA之手把手教你写串口协议解析(STM32与FPGA数据互传)

    最近趁热打铁做了一个关于STM32与FPGA通信并且控制高速DA模块产生不同频率信号的正弦波、方波、三角波和锯齿波的项目,从中收获到了很多东西,也踩了一些雷和坑,将分为几篇文章将整个过程分享出来。 这一次准备分享的是对串口数据的解析和赋值。解析的数据由STM32发

    2024年02月06日
    浏览(33)
  • 手把手教你,通过HAL库实现STM32的超声波测距--以SR-04为例

    目录 0、SR-04基本原理 1、准备工作 2、连线  3、STM32CUBEMX设置 3.1新建工程 3.2芯片通用设置 3.3定时器捕获设置 ​3.4其他设置 3.5生成工程  4、程序完善 4.1完善打印输出函数  4.2完善tim.c 4.3完善gpio.c  4.4完善main函数   5、总结 声波遇到障碍物会反射,而声波的速度已知,所以

    2024年02月14日
    浏览(40)
  • 手把手教你使用USB的CDC+MSC复合设备(基于stm32f407)

      最近对usb有点兴趣,感觉挺好玩的,于是买了本圈圈大神的经典著作-圈圈教你玩USB,里面使用51单片机+usb芯片对usb的基本知识潺潺道来,做了十个左右的常用案例实验,很有趣,建议大家看看。   趁热打铁,拿身边的开发板来练练手,探索一下复合设备的好玩方便的

    2024年02月13日
    浏览(47)
  • STM32系列——手把手教你将SYN6288语音播报模块的标准库程序转为hal库使用

    目录 前言 1. 原理 2. Cubmx配置 3. keil5编写代码 3.1 main.c 3.2 syn6288.c 3.3 syn6288.h 本教程基于 stm32f103c8t6 最小系统板, hal库 开发。 操作简单,讲解直接清楚,旨在让大家少走弯路。 SYN6288就是用到一个串口资源即可,用STM32开发起来不难。 配置串口3为异步通信模式 ,注意波特率

    2024年02月07日
    浏览(64)
  • 基于STM32F103RCT6之手把手教你写智能家居项目(2)

            上一节我们简述了智能家居项目,实现了点灯的相关代码编写,还有WIFI模块的固件烧录。 连接什么平台:         我们想要远程控制家具的开关和获取家中的状态,少不了一个可以传输数据的云平台。我认为易监控是一个简单好用的云平台。 怎么连接平台:

    2024年02月20日
    浏览(40)
  • STM32系列——手把手教你蓝牙模块HC05、HC06的使用,重在“用起来”(HAL库)

    不论是HC05还是HC06,我们用到的都是蓝牙模块的透传功能,只需要用到4个引脚:RXD、TXD、VCC(5V)、GND。 1、HC-05有6个引脚,但是我们只用到4个。 2、HC-05正面有一个按键。与HC-06的区别是, 它上电之前必须要按住此按键再插入电脑中,才能够进入AT模式 。HC-06没有按键,直接

    2024年02月05日
    浏览(59)
  • FPGA之手把手教你做多路信号发生器(STM32与FPGA数据互传控制波形生成)

    最近趁热打铁做了一个关于STM32与FPGA通信并且控制高速DA模块产生不同频率信号的正弦波、方波、三角波和锯齿波的项目,从中收获到了很多东西,也踩了一些雷和坑,将分为几篇文章将整个过程分享出来。 这一次准备分享的是将串口解析的出来的波形频率数据以及波形类型

    2024年02月15日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包