STM32
直播中

周煌煦

7年用户 1018经验值
私信 关注
[问答]

怎样去使用STM32F1的基本定时器呢

STM32F1中分为哪几类定时器呢?

怎样去使用STM32F1的基本定时器呢?有哪些使用流程?


回帖(1)

况冶

2021-11-24 15:05:33
  一、定时器简介
  STM32F1 系列中,除了互联型的产品,共有 8 个定时器,分为基本定时器,通用定时器和高级定时器。
  基本定时器 TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器,只能定时,没有外部 IO。
  通用定时器 TIM2/3/4/5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。
  高级定时器 TIM1/8是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有 8 个外部 IO。
  
  1.1 基本定时器功能
  
  ①时钟源
  定时器时钟 TIMxCLK,即内部时钟 CK_INT,经 APB1 预分频器后分频提供,如果APB1 预分频系数等于 1,则频率不变,否则频率乘以 2,库函数中 APB1 预分频的系数是 2,即 PCLK1=36M,所以定时器时钟 TIMxCLK=36*2=72M。
  ②计数器时钟
  定时器时钟经过 PSC 预分频器之后,即 CK_CNT,用来驱动计数器计数。PSC 是一个16 位的预分频器,可以对定时器时钟 TIMxCLK 进行 1~65536 之间的任何一个数进行分频。具体计算方式为:CK_CNT=TIMxCLK/(PSC+1)。
  ③计数器
  计数器 CNT 是一个 16 位的计数器,只能往上计数,最大计数值为 65535。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。
  ④自动重装载寄存器
  自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。
  1.2 基本定时器时基
  基本定时器的核心是时基,不仅基本定时器有,通用定时器和高级定时器也有。基本定时器与通用定时器的时基有3个(TIMx_CNT、TIMx_PSC、TIMx_ARR)。高级定时器的时基有4个(TIMx_CNT、TIMx_PSC、TIMx_ARR、TIMx_RCR)。
  二、基本定时器使用流程
  2.1 NVIC 设置
  /**
  @brief NVIC初始化(使用TIM6基本定时器)
  @param 无
  @return 无
  */
  void BASIC_TIM_NVIC_Config(void)
  {
  NVIC_InitTypeDef NVIC_InitStructure;
  // 设置中断组为 0
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  // 设置中断来源
  NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
  // 设置主优先级为 0
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  // 设置抢占优先级为 3
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  }
  2.2 定时器中断配置
  
  /**
  @brief 定时器中断配置(使用TIM6基本定时器)
  @param 无
  @return 无
  */
  void BASIC_TIM_Config(void)
  {
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  /*
  可以从上图看出基本定时器和通用定时器使用APB1总线,
  高级定时器使用APB2总线。
  */
  // 开启定时器时钟,即内部时钟 CK_INT=72M
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
  /*
  预分频将输入时钟频率按1~65536之间的值任意分频,分频值决定了计数频率。
  计数值为计数的个数,当计数寄存器的值达到计数值时,产生溢出,发生中断。
  如系统时钟为72MHz,预分频 TIM_Prescaler = 71,
  计数值 TIM_Period = 1000,
  则 TIM_Period * (TIM_Prescaler + 1) / 72000000 = 0.001,
  即每1ms产生一次中断。
  */
  // 自动重装载寄存器周的值(计数值)
  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;
  // 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
  //TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
  // 重复计数器的值,基本定时器没有,不用管
  //TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
  /*
  完成时基设置
  */
  // 初始化定时器
  TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
  /*
  为了避免在设置时进入中断,这里需要清除中断标志位。
  如果是向上计数模式(基本定时器采用向上计数),
  则采用函数 TIM_ClearFlag(TIM6, TIM_FLAG_Update),
  清除向上溢出中断标志。
  */
  // 清除计数器中断标志位
  TIM_ClearFlag(TIM6, TIM_FLAG_Update);
  // 使能计数器
  TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);
  // 开启计数器
  TIM_Cmd(TIM6, ENABLE);
  // 暂时关闭定时器的时钟,等待使用
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, DISABLE);
  }
  2.3 中断服务程序
  #define BASIC_TIM_IRQHandler TIM6_IRQHandler
  // 1ms发生一次中断,time 记录中断次数
  uint16_t time;
  void BASIC_TIM_IRQHandler(void)
  {
  if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
  {
  time++;
  TIM_ClearITPendingBit(TIM6, TIM_FLAG_Update);
  }
  }
  三、设计实例
  board_timer.h
  #ifndef BOARD_TIMER_H
  #define BOARD_TIMER_H
  /**************************************************
  * INCLUDES
  */
  #include “stm32f10x.h”
  /**************************************************
  * DEFINITIONS
  */
  #define BASIC_TIM_IRQHandler TIM6_IRQHandler
  /**************************************************
  * API FUNCTIONS
  */
  void BASIC_TIM_Config(void);
  void BASIC_TIM_NVIC_Config(void);
  #endif
  board_timer.c
  #include “board_timer.h”
  /**************************************************
  * GLOBAL VALUE
  */
  uint16_t time;
  /**************************************************
  * PUBLIC FUNCTIONS
  */
  /**
  @brief NVIC初始化(使用TIM6基本定时器)
  @param 无
  @return 无
  */
  void BASIC_TIM_NVIC_Config(void)
  {
  NVIC_InitTypeDef NVIC_InitStructure;
  // 设置中断组为 0
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  // 设置中断来源
  NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
  // 设置主优先级为 0
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  // 设置抢占优先级为 3
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  }
  /**
  @brief 定时器中断配置(使用TIM6基本定时器)
  @param 无
  @return 无
  */
  void BASIC_TIM_Config(void)
  {
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  /*
  可以从上图看出基本定时器和通用定时器使用APB1总线,
  高级定时器使用APB2总线。
  */
  // 开启定时器时钟,即内部时钟 CK_INT=72M
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
  /*
  预分频将输入时钟频率按1~65536之间的值任意分频,分频值决定了计数频率。
  计数值为计数的个数,当计数寄存器的值达到计数值时,产生溢出,发生中断。
  如系统时钟为72MHz,预分频 TIM_Prescaler = 71,
  计数值 TIM_Period = 1000,
  则 TIM_Period * (TIM_Prescaler + 1) / 72000000 = 0.001,
  即每1ms产生一次中断。
  */
  // 自动重装载寄存器周的值(计数值)
  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;
  // 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
  //TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
  // 重复计数器的值,基本定时器没有,不用管
  //TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
  /*
  完成时基设置
  */
  // 初始化定时器
  TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
  /*
  为了避免在设置时进入中断,这里需要清除中断标志位。
  如果是向上计数模式(基本定时器采用向上计数),
  则采用函数 TIM_ClearFlag(TIM6, TIM_FLAG_Update),
  清除向上溢出中断标志。
  */
  // 清除计数器中断标志位
  TIM_ClearFlag(TIM6, TIM_FLAG_Update);
  // 使能计数器
  TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);
  // 开启计数器
  TIM_Cmd(TIM6, ENABLE);
  // 暂时关闭定时器的时钟,等待使用
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, DISABLE);
  }
  main.c
  #include “board_timer.h”
  extern uint16_t time;
  int main()
  {
  /* 基本定时器 TIMx,x[6,7] 定时配置 */
  BASIC_TIM_Config();
  /* 配置基本定时器 TIMx,x[6,7]的中断优先级 */
  BASIC_TIM_NVIC_Config();
  /* 基本定时器 TIMx,x[6,7] 重新开时钟,开始计时 */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
  while(1)
  {
  if(time == 1000)
  {
  /* 1000 * 1 ms = 1s 时间到 */
  time = 0;
  /* LED1 取反 */
  static uint8_t tmp = 0;
  /*LED1 反转*/
  if(tmp == 0)
  {
  // 该函数在GPIO输出章节中
  UseLibSetOutput(GPIOB, GPIO_Pin_0,0);
  tmp = 1;
  }
  else
  {
  // 该函数在GPIO输出章节中
  UseLibSetOutput(GPIOB, GPIO_Pin_0,1);
  tmp = 0;
  }
  }
  }
  return 0;
  }
举报

更多回帖

发帖
×
20
完善资料,
赚取积分