STM32学习笔记(3) TIM基本定时器

简介: 代码中有这样一条,有人会问函数TIM_ClearFlag()和函数TIM_ClearITPendingBit()有什么区别?其实重点在Flag和IT,前者是外设的状态标志,而后者是外设的中断标志。状态标志就是一个外设它有自身的一些标志位(Flag),来表明它处于什么状态,下图就是定时器的状态标记。中断标志就是使能外设的中断后,每次发生一次中断,它会表明发生了什么样的中断,同样中断也有相应的标记。两者分别靠函数TIM_GetFlagStatus()和函数TIM_GetITStatus()来获取

1.基本定时器


功能:定时,无PWM


f0fa3e81c9734b429d7de7eb3513cd4e.png

ad70ee0a116e48c98e3b385505c71d8e.png


● 计数器寄存器(TIMx_CNT)


● 预分频寄存器(TIMx_PSC)


● 自动重装载寄存器(TIMx_ARR)


2.基本定时器TIM的工作原理


来自内部时钟源的CK_PSC(频率=72MHz,72*10^6)进入到预分频器,预分频器PSC再对内部时钟CK_PSC分频,得到计数器时钟 CK_CNT = CK_PSC/(PSC+1)


f586b902590c483e8076bb44a3612016.png


当CNT_EN使能为1后,计数器CNT在CK_CNT时钟信号下,从0开始计数,电压周期性(计数一次的时间 1/CK_CNT) 的变为1/0,电压每变为1就记作是一次,当CNT的值与ARR的值相等时,就自动生成事件,且CNT自动清0,再重新开始计数


2.1范例:定时1ms的计算


PSC=72-1,
72MHz=1/(72*10^6)us
CNT=72MHz/(72-1+1)=1MHz=1us;即每过1us,有一次脉冲
ARR=1000-1,从0计数到999,所以就记了1000次
ARR*CNT=1ms


3.程序流程:


先配置中断,再配置TIM


3.1配置时基初始化结构体


 TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
 RCC_APB1PeriphClockCmd(TIM_CLK,ENABLE);
 TIM_BaseInitStructure.TIM_Prescaler=TIM_PRE-1;
 TIM_BaseInitStructure.TIM_Period=TIM_ARR-1;


首先肯定要开启时钟线啦,至于开哪条呢?下面我也展示出来了


3.1.1时钟线的选择


APB1外设时钟使能寄存器(RCC_APB1ENR)


60dbfb07ad7741ca99da70b8dcebbb0d.png


【28】PWR_EN是电源


APB2 外设时钟使能寄存器(RCC_APB2ENR)


5295d5e9ab95463db50beeb914274567.png


我们可以观察到,APB1往往跟四大通讯方式,基本定时器,通用定时器(TIM2~TIM7)有关


而APB2往往与GPIO,EXTI(AFIO),高级定时器(TIM1,TIM8),注意还有一个USART1


总而言之呢,APB2在我们项目中比较常用的一些模块


3.1.2开启定时器更新中断


TIM_ClearFlag(BASICTIM,TIM_FLAG_Update);//清除更新中断标志位
TIM_ITConfig(BASICTIM,TIM_IT_Update,ENABLE);//TIM_IT_Update,开启更新中断


3.1.3TIM_ClearFlag()和函数TIM_ClearITPendingBit()区别


代码中有这样一条,有人会问函数TIM_ClearFlag()和函数TIM_ClearITPendingBit()有什么区别?其实重点在Flag和IT,前者是外设的状态标志,而后者是外设的中断标志。状态标志就是一个外设它有自身的一些标志位(Flag),来表明它处于什么状态,下图就是定时器的状态标记。中断标志就是使能外设的中断后,每次发生一次中断,它会表明发生了什么样的中断,同样中断也有相应的标记。两者分别靠函数TIM_GetFlagStatus()和函数TIM_GetITStatus()来获取


void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG)
{  
  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx));
  assert_param(IS_TIM_CLEAR_FLAG(TIM_FLAG));
  /* Clear the flags */
  TIMx->SR = (uint16_t)~TIM_FLAG;
}


void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
{
  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx));
  assert_param(IS_TIM_IT(TIM_IT));
  /* Clear the IT pending Bit */
  TIMx->SR = (uint16_t)~TIM_IT;
}


可以看到,两个函数基本一样,只是对TIM_FLAG或TIM_IT操作不同


3.1.4允许更新中断函数及其寄存器DIER


对于允许更新中断函数


void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
{  
  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx));
  assert_param(IS_TIM_IT(TIM_IT));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    /* Enable the Interrupt sources */
    TIMx->DIER |= TIM_IT;
  }
  else
  {
    /* Disable the Interrupt sources */
    TIMx->DIER &= (uint16_t)~TIM_IT;
  }
}


a2d68f27924b4273b370072ab8a0d396.png



可以简单看到,这个函数就是对中断使能寄存器【DIER】至0或至1操作


3.2配置中断优先级


NVIC_InitStructure.NVIC_IRQChannel=TIM_IRQN;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=NVIC_HIGHT_PRIORITY;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=NVIC_LOW_PRIORITY;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;


我们可以看一下 NVIC_InitTypeDef 这个结构体里面都有什么

NVIC_IRQChannel


这一个是定义你的中断源,中断源可以在启动文件中找到


648760d9f1bb4070b1f0f6be7622e675.png


NVIC_IRQChannelPreemptionPriority


这一个是设置主优先级的结构体,由于我们是


NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);


所以范围只能选(0~3)


NVIC_IRQChannelSubPriority


设置从优先级,同理


那下面这句话有什么用呢


NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;


我们不妨先来看看这段NVIC_Init的函数


void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
{
  uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
  /* Check the parameters */
  assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd));
  assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority));  
  assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority));
  if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
  {
    /* Compute the Corresponding IRQ Priority --------------------------------*/    
    tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
    tmppre = (0x4 - tmppriority);
    tmpsub = tmpsub >> tmppriority;
    tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
    tmppriority |=  NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
    tmppriority = tmppriority << 0x04;
    NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;
    /* Enable the Selected IRQ Channels --------------------------------------*/
    NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
      (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
  }
  else
  {
    /* Disable the Selected IRQ Channels -------------------------------------*/
    NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
      (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
  }
}


NVIC_InitStruct->NVIC_IRQChannelCmd会读取你是否是ENABLE,然后把配置好的主从优先级,中断源写进相关寄存器中


tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
tmppre = (0x4 - tmppriority);
tmpsub = tmpsub >> tmppriority;
tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
tmppriority |=  NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
tmppriority = tmppriority << 0x04;


这一段代码,主要是先判断选择了哪一组的中断分组,然后把主从优先级依次写进AIRCR寄存器中,因为AIRCR只有10-8位才是控制优先级的,所以我们需要移位


38afb612f6c0437ca353b809142c120d.png


3.2.1移位


关于移位,我用C写了一小段代码,当作是复习


9c8e23575889495e81dc42c55faebc18.png

63f22d4c6a0840eb8330da7e5319a3b2.png


3.3使能定时器


TIM_Cmd(BASICTIM,ENABLE);//打开定时器


void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    /* Enable the TIM Counter */
    TIMx->CR1 |= TIM_CR1_CEN;
  }
  else
  {
    /* Disable the TIM Counter */
    TIMx->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
  }
}

09d63c039aef4ebd86477119975b2ed3.png



3.4编写中断服务函数


void TIM6_IRQHandler()
{
    if(TIM_GetFlagStatus(TIM6,TIM_FLAG_Update)!=RESET)
    {
        N++;
    }
   TIM_ClearITPendingBit(TIM6,TIM_IT_Update);
}


3.5Main函数


//对基本定时器初始化
BasicTIM_Init();


4.代码示例


基本定时器示例


代码的移植性很高,里面也已经有大量的注释

相关文章
|
6月前
使用STM32F103标准库实现定时器控制LED点亮和关闭
通过这篇博客,我们学习了如何使用STM32F103标准库,通过定时器来控制LED的点亮和关闭。我们配置了定时器中断,并在中断处理函数中实现了LED状态的切换。这是一个基础且实用的例子,适合初学者了解STM32定时器和中断的使用。 希望这篇博客对你有所帮助。如果有任何问题或建议,欢迎在评论区留言。
484 2
|
5月前
|
传感器
stm32f407探索者开发板(二十二)——通用定时器基本原理讲解
stm32f407探索者开发板(二十二)——通用定时器基本原理讲解
508 0
|
5月前
|
芯片
stm32f407探索者开发板(十二)——Systick滴答定时器-延时函数讲解
stm32f407探索者开发板(十二)——Systick滴答定时器-延时函数讲解
311 0
|
6月前
|
芯片
【STM32】STM32简述定时器
【STM32】STM32简述定时器
|
5月前
STM32CubeMX 定时器
STM32CubeMX 定时器
202 0
|
5月前
stm32f407探索者开发板(二十三)——定时器中断实验
stm32f407探索者开发板(二十三)——定时器中断实验
533 0
|
6月前
|
移动开发
技术好文:stm32寄存器版学习笔记06输入捕获(ETR脉冲计数)
技术好文:stm32寄存器版学习笔记06输入捕获(ETR脉冲计数)
298 0
|
7月前
|
传感器
STM32标准库外部中断和定时器知识点总结-2
STM32标准库外部中断和定时器知识点总结
|
7月前
|
传感器
STM32标准库外部中断和定时器知识点总结-1
STM32标准库外部中断和定时器知识点总结