STM32(五):TIMER定时器 (标准库函数)

本文详细介绍了如何使用STM32单片机的TIMER定时器控制LED灯的交替闪烁,包括定时器的工作原理、时钟配置、中断处理和实操代码示例,以TIM1或TIM8为例,旨在帮助读者理解和实践相关技术。
摘要由CSDN通过智能技术生成

前言

上一篇文章已经介绍了如何用STM32单片机中的Systick嘀嗒定时器来控制LED灯的交替闪烁,实现了点灯的第四种方式。这篇文章我们来介绍一下如何用STM32单片机中的TIMER定时器来控制LED灯的交替闪烁。

一、实验原理

1.定时器的功能

STM32的定时器是一种强大的外设,具有以下几种主要功能:

  • 定时功能:可以设置定时时间,用于在特定时刻执行任务。
  • 计数功能:对输入的时钟脉冲进行计数。
  • PWM信号产生:用于控制电机速度、灯光亮度等。
  • 输入捕获:捕获并记录外部信号的时间参数。
  • 定时器中断:在计数值达到设定值时触发中断,进行相应处理。

2.定时器的类型

STM32中的定时器根据功能和复杂度分为三种类型:

  • 高级定时器:TIM1和TIM8,支持更复杂的控制需求和高精度的操作。
  • 通用定时器:TIM2、TIM3、TIM4、TIM5,适用于常规的定时和计数任务。
  • 基本定时器:TIM6和TIM7,主要用于简单的定时任务,没有高级控制功能。

3.定时器工作原理

以下是定时器的工作原理图,主要分为5个部分,分别是时钟产生、定时器、输入、输出和寄存器部分。

时钟产生来源:
1. 内部时钟     PB1(CLK_INT) (*)
2. TIMx_ETR       外部引脚输入
3. ITRx            内部触发输入,定时器集联
4. TL1FP1/2        来自于定时器外部通道
主要是内部时钟,最后输出 [ CK_PSC ]

输出比较:
比如说现在定时器是从 0-100 向上计数
如果把 [ 捕获比较寄存器 ] 的值设置为 v = 50;
如果 当前值 < v 则输出高电平, 当前值 > v 则输出低电平
那么效果就是 0-50 输出高电平, 50-100 输出低电平, 实现通道输出
通过控制 V 的值 就能改变占空比,  实现PWM。

定时器主要分为3种计数模式,向上计数、向下计数以及中央对齐模式。

1.向上计数模式

2.向下计数模式

3.中央对齐模式

 3.定时器系统框图

以下是定时器的系统框图:

主要有4块组成部分:

1.振荡器

  • HSI RC:内部高速时钟,8MHz。

  • HSE Osc:外部高速时钟,频率依赖于外部晶振。

  • LSI RC:内部低速时钟。

  • LSE Osc:外部低速时钟,通常是32.768kHz,用于实时时钟(RTC)。

2.时钟源选择和配置

  • PLL(相位锁环):可以将HSI或HSE的频率乘以一个系数(如图中所示,最高可达72MHz)。

  • SYSCLK(系统时钟):可以选择HSI、HSE或PLLCLK作为源。

  • MCO(微控制器时钟输出):可以输出不同源的时钟,如HSI、HSE或PLLCLK的一部分。

3.总线和接口时钟

  • AHB Prescaler:高性能总线预分频器,可对HCLK进行分频。

  • APB1 Prescaler和APB2 Prescaler:两个外设总线预分频器,分别对PCLK1和PCLK2进行分频。

  • ADC Prescaler:模数转换器预分频器,提供给ADC的时钟。

4.定时器

  • TIM1、TIM8(高级定时器):接收PCLK2时钟,可以选择直接时钟或通过倍增器增倍(x1或x2)。

  • TIM2、TIM3、TIM4(通用定时器):接收PCLK1时钟,同样有倍增器选择。

  • 系统时钟与这些定时器的关系,决定了定时器的计数速度和精度。

二、实验步骤

1.时钟初始化

首先是对时钟的初始化,其中我们要知道以下计算的式子

定时器频率 =((1+TIM_Prescaler )/72M)*(1+TIM_Period )
例:如果想要设置定时器频率为 1秒,可以设置
TIM_Prescaler=35999,TIM_Period=1999 //2Khz的频率,计数到
2000
TIM_Prescaler=7199,TIM_Period=9999 //10KHz的频率,计数到
10000

参数如下所示:

  • TIM_Prescaler  用来设置分频系数
  • TIM_CounterMode  用来设置计数方式
  • TIM_Period 设置自动重载计数周期值
  • TIM_ClockDivision 用来设置时钟分频因子
void GENERAL_TIMx_Configuration(void)
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

  /* 开启TIMx_CLK,x[2,3,4,5],即内部时钟CK_INT=72M */
  GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK, ENABLE);
  
  /* 通用定时器 TIMx,x[2,3,4,5]中断优先级配置 */
  GENERAL_TIMx_NVIC_Configuration();
  
  /* 自动重装载寄存器周的值(计数值) */
  TIM_TimeBaseStructure.TIM_Period=1000;
  /* 累计 TIM_Period个频率后产生一个更新或者中断
     时钟预分频数为71,则驱动计数器的时钟CK_CNT = CK_INT / (71+1)=1M */
  TIM_TimeBaseStructure.TIM_Prescaler= 71;
  /* 时钟分频因子 ,没有用到,不用管 */
  //TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
  /* 计数器计数模式,基本定时器TIM6和TIM7只能向上计数,没有计数模式的设置,不用管 */
  TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; 
  /* 重复计数器的值,通用定时器没有,不用管 */
  //TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
  /* 初始化定时器TIMx, x[2,3,4,5] */
  TIM_TimeBaseInit(GENERAL_TIMx, &TIM_TimeBaseStructure);

  /* 清除计数器中断标志位 */
  TIM_ClearITPendingBit(GENERAL_TIMx, TIM_IT_Update);
  /* 开启计数器中断 */
  TIM_ITConfig(GENERAL_TIMx,TIM_IT_Update,ENABLE);

  /* 使能计数器: */
  TIM_Cmd(GENERAL_TIMx, ENABLE); 
}

2.时钟中断

时钟中断函数GENERAL_TIMx_NVIC_Configuration(void),这边是对中断来源以及优先级的配置,前面在Systick中有所介绍,可以看一下之前的连接:STM32(四):Systick (标准库函数)-CSDN博客

void GENERAL_TIMx_NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure; 
  /* 设置中断组为0 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		
  /* 设置中断来源 */
  NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM_IRQ;	
  /* 设置主优先级为 0 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	 
  /* 设置抢占优先级为3 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	 /*定时器使能 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

但到这里我们的中断配置还没结束!!重点!!易踩坑!!

我们还需要在stm32f10x_it.c的文件里面加上新的中断服务函数 GENERAL_TIM_INT_FUN (),记得加上头文件,例如:

#include "bsp/GeneralTIM/bsp_GeneralTIM.h"
extern __IO uint16_t timer_count;

void  GENERAL_TIM_INT_FUN (void)
{
	if ( TIM_GetITStatus(GENERAL_TIMx,TIM_IT_Update) != RESET ) 
	{	
		timer_count++;
		TIM_ClearITPendingBit(GENERAL_TIMx , TIM_IT_Update);  		 
	}		 	
}

三、实操代码

程序分为3个文件:bsp_GeneralTIM.c、bsp_GeneralTIM.h、main.c

1.bsp_GeneralTIM.c


/* 包含头文件 ----------------------------------------------------------------*/
#include "bsp/GeneralTIM/bsp_GeneralTIM.h" 
/**
  * 函数功能: 通用定时器 TIMx,x[2,3,4,5]中断优先级配置
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
void GENERAL_TIMx_NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure; 
  /* 设置中断组为0 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		
  /* 设置中断来源 */
  NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM_IRQ;	
  /* 设置主优先级为 0 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	 
  /* 设置抢占优先级为3 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	 /*定时器使能 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}


void GENERAL_TIMx_Configuration(void)
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

  /* 开启TIMx_CLK,x[2,3,4,5],即内部时钟CK_INT=72M */
  GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK, ENABLE);
  
  /* 通用定时器 TIMx,x[2,3,4,5]中断优先级配置 */
  GENERAL_TIMx_NVIC_Configuration();
  
  /* 自动重装载寄存器周的值(计数值) */
  TIM_TimeBaseStructure.TIM_Period=1000;
  /* 累计 TIM_Period个频率后产生一个更新或者中断
     时钟预分频数为71,则驱动计数器的时钟CK_CNT = CK_INT / (71+1)=1M */
  TIM_TimeBaseStructure.TIM_Prescaler= 71;
  /* 时钟分频因子 ,没有用到,不用管 */
  //TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
  /* 计数器计数模式,基本定时器TIM6和TIM7只能向上计数,没有计数模式的设置,不用管 */
  TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; 
  /* 重复计数器的值,通用定时器没有,不用管 */
  //TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
  /* 初始化定时器TIMx, x[2,3,4,5] */
  TIM_TimeBaseInit(GENERAL_TIMx, &TIM_TimeBaseStructure);

  /* 清除计数器中断标志位 */
  TIM_ClearITPendingBit(GENERAL_TIMx, TIM_IT_Update);
  /* 开启计数器中断 */
  TIM_ITConfig(GENERAL_TIMx,TIM_IT_Update,ENABLE);

  /* 使能计数器: */
  TIM_Cmd(GENERAL_TIMx, ENABLE); 
}

2.bsp_GeneralTIM.h

#ifndef __GENERAL_TIM_H__
#define __GENERAL_TIM_H__

/* 包含头文件 ----------------------------------------------------------------*/
#include <stm32f10x.h>

/* 类型定义 ------------------------------------------------------------------*/
/* 宏定义 --------------------------------------------------------------------*/
/********************通用定时器TIM参数定义,只限TIM2 & TIM3 & TIM4 & TIM5************/
#define GENERAL_TIMx                 TIM2
#define GENERAL_TIM_APBxClock_FUN    RCC_APB1PeriphClockCmd
#define GENERAL_TIM_CLK              RCC_APB1Periph_TIM2
#define GENERAL_TIM_IRQ              TIM2_IRQn
#define GENERAL_TIM_INT_FUN          TIM2_IRQHandler

//#define GENERAL_TIMx                 TIM3
//#define GENERAL_TIM_APBxClock_FUN    RCC_APB1PeriphClockCmd
//#define GENERAL_TIM_CLK              RCC_APB1Periph_TIM3
//#define GENERAL_TIM_IRQ              TIM3_IRQn
//#define GENERAL_TIM_INT_FUN          TIM3_IRQHandler

//#define GENERAL_TIMx                 TIM4
//#define GENERAL_TIM_APBxClock_FUN    RCC_APB1PeriphClockCmd
//#define GENERAL_TIM_CLK              RCC_APB1Periph_TIM4
//#define GENERAL_TIM_IRQ              TIM4_IRQn
//#define GENERAL_TIM_INT_FUN          TIM4_IRQHandler

//#define GENERAL_TIMx                 TIM5
//#define GENERAL_TIM_APBxClock_FUN    RCC_APB1PeriphClockCmd
//#define GENERAL_TIM_CLK              RCC_APB1Periph_TIM5
//#define GENERAL_TIM_IRQ              TIM5_IRQn
//#define GENERAL_TIM_INT_FUN          TIM5_IRQHandler

/* 扩展变量 ------------------------------------------------------------------*/
/* 函数声明 ------------------------------------------------------------------*/
void GENERAL_TIMx_Configuration(void);

#endif	/* __GENERAL_TIM_H__ */

3.main.c


/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f10x.h"
#include "bsp/led/bsp_led.h"
#include "bsp/key/bsp_key.h"
#include "bsp/delay/delay.h"
#include "bsp/systick/bsp_SysTick.h"
#include "bsp/GeneralTIM/bsp_GeneralTIM.h" 
/* 函数体 --------------------------------------------------------------------*/

__IO uint16_t timer_count=0;
/**
  * 函数功能: 主函数.
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 无
  */
int main(void)
{
	LED_GPIO_Init();
 //KEY_GPIO_Init();
//	KEY1_EXIT_Config();
//	KEY2_EXIT_Config();
//	KEY3_EXIT_Config();
	/* 初始化系统滴答定时器 */  
//  SysTick_Init();
	
	
  /* 通用定时器初始化:1ms中断一次  */
  GENERAL_TIMx_Configuration();  
  while (1)
  {
    if(timer_count==500)
    {
      LED1_ON;
	  LED2_OFF;
	  LED3_OFF;
    }
	 if(timer_count==1000)
    {
      LED1_OFF;
	  LED2_ON;
      LED3_OFF;
    }
   if(timer_count==1500)
    {
      timer_count=0;
      LED1_OFF;
	  LED2_OFF;
	  LED3_ON;
    }
    
  }

}

四、实验效果

Timer定时器点灯

结束语

本文以STM32VET6为例讲解了用TIMER定时器控制LED灯的交替闪烁的实现方法,并指出其中的易坑点。希望对大家有所帮助!如果还有什么问题,欢迎评论区留言,谢谢!

  • 61
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,对于这个问题,我可以给你一些指导。 使用 STM32F405 的定时器可以更精确地读取磁编码器的位置,因为定时器可以提供更高的计数精度。在 STM32F405 中,每个定时器都有一个计数器,可以用来计数定时器的时钟周期数。你可以将定时器连接到磁编码器的输出信号上,当定时器的计数器达到一定的值时,就可以触发一个中断来读取磁编码器的位置。 下面是一个使用 STM32F405 标准库函数的示例代码,可以读取 TLB5012B 磁编码器的位置: ```c #include "stm32f4xx.h" // 定义 GPIO 接口和引脚号 #define ENCODER_PORT GPIOA #define ENCODER_PIN GPIO_Pin_0 // 定义定时器和中断号 #define TIMER TIM2 #define IRQ TIM2_IRQn // 定义计数器 static int32_t counter = 0; // 初始化 GPIO 接口和定时器 void encoder_init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 配置 GPIO 为输入模式 GPIO_InitStructure.GPIO_Pin = ENCODER_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(ENCODER_PORT, &GPIO_InitStructure); // 连接 GPIO 到定时器 GPIO_PinAFConfig(ENCODER_PORT, GPIO_PinSource0, GPIO_AF_TIM2); // 使能定时器时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 配置定时器为计数模式 TIM_InitStructure.TIM_Period = 0xFFFF; TIM_InitStructure.TIM_Prescaler = 0; TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIMER, &TIM_InitStructure); // 配置定时器输入捕获模式 TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter = 0; TIM_ICInit(TIMER, &TIM_ICInitStructure); // 使能定时器输入捕获中断 NVIC_InitStructure.NVIC_IRQChannel = IRQ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 初始化计数器 counter = 0; // 启动定时器 TIM_Cmd(TIMER, ENABLE); } // 中断处理函数 void IRQHandler(void) { // 读取定时器计数器的值 uint16_t value = TIM_GetCapture1(TIMER); // 计数器加上脉冲数量 if (value >= 0x8000) { counter--; } else { counter++; } // 清除中断标志 TIM_ClearITPendingBit(TIMER, TIM_IT_CC1); } // 读取磁编码器的位置 int32_t encoder_get_position(void) { // 返回计数器的值 return counter; } ``` 这段代码使用了 STM32F405 的定时器 TIM2 和输入捕获模式来读取磁编码器的位置。你可以使用 `encoder_get_position()` 函数来获取磁编码器的位置。请注意,这段代码只是一个参考实现,实际的实现方法可能会因应用场景的不同而有所不同。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值