STM32定时器timer的对比理解和案例代码详解

阅读前须知:本文章没有涵盖所有可能的定时器使用方法,遵循本人的一贯原则,只有见过、理解过并且测试过的才会用自己的话写出来,因此,多余部分不常用的就不到处copy凑字啦!

如果后面工程用到,会不定时更新的。

本文使用STM32F103C8T6作为测试芯片,配合cubemax使用。

研究目的

由于使用cubemax生成的代码修改过后再用一次cubemax就会覆盖掉很多东西,不方便重新生成。

对比使用cubemax生成的base code在不同模式下的区别,以便于手动修改。

理解不同模式的定时器对于程序功能的影响。

给出相关案例供参考。

配置界面简介

当我们使用cubemax配置一个定时器的时候,点击左侧的Timers,选择好定时器之后出现如下界面

Channel1-4只是引脚不同,咱们就看一个,Channel4.

Internal Clock

第一种情况,选择internal clock

接着可以选择是否开启中断

Output Compare

第二种情况,选择某一个通道(这里是Timer2-Channel4)

同样可以选择是否开启中断 。

下面来对比开启中断和不开启中断:

两种情况下的开不开启NVIC的区别是一样的,就拿internal clock的举例。

没有开启NVIC的情况下,代码如下:

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM2)
  {
    __HAL_RCC_TIM2_CLK_ENABLE();
  }
}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM2)
  {
    __HAL_RCC_TIM2_CLK_DISABLE();
  }
}

 开启之后的代码为:

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM2)
  {
    __HAL_RCC_TIM2_CLK_ENABLE();

    HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);
  }
}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM2)
  {
    __HAL_RCC_TIM2_CLK_DISABLE();
		
    HAL_NVIC_DisableIRQ(TIM2_IRQn);
  }
}

可以看到,区别就是设置中断优先级和使能中断(关闭中断)。

那么可以说明,如果你配置cubemax的时候,忘记开中断了,那么就需要加上这三句

当然,需要加在正确的位置!

HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
HAL_NVIC_DisableIRQ(TIM2_IRQn);

不过这还没完,你还需要找到stm32f1xx_it.c文件

在其中加入:

extern TIM_HandleTypeDef htim2;
void TIM2_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&htim2);
}

 OK,然后你就拥有了带有中断的定时器!

Internal Clock、Output Compare、PWM Generation、开启NVIC模式的区别

别慌,不是什么大事儿,听我慢慢道来。

从字面上看呢,第一个内部时钟生成模块(Internal Clock)就是最普通的嘛,你单片机运行那么快,总不能叫我工程师自己计数吧,这也太不高级了。

所以就需要一个工具人,但是这个工具人吧脑子简单,只会数数,按照不同速度数数,仅此而已。

至于数到几,速度是多少,数完有什么用,不知道。

我们通常就用这个来进行后台计数,不会抢占当前程序正在执行的函数的优先级,一边运行我一边计数,计数完不一定给谁用,但是你看,哎,我还能计数呢。

然后再看Output Compare和PWM Generation,这俩其实有时候可以混用。

最终的目的就是在刚刚那个工具人的基础上进行升级,你不是只会数数吗,好的,我告诉你数到某一个数(比如100)的时候,你帮我做一件事情。而这件事情在硬件上也就是对电平进行一些操作罢了。

至于更具体是什么,大伙可以去看看这几种模式的内涵:

#define TIM_OCMODE_TIMING                                                           
#define TIM_OCMODE_ACTIVE                                                          
#define TIM_OCMODE_INACTIVE                 
#define TIM_OCMODE_TOGGLE                  
#define TIM_OCMODE_PWM1                     
#define TIM_OCMODE_PWM2                                 
#define TIM_OCMODE_FORCED_ACTIVE            
#define TIM_OCMODE_FORCED_INACTIVE

也就是下面这一句控制的部分:

sConfigOC.OCMode = TIM_OCMODE_TIMING;

我们知道,一个单片机不可能只干一个事情,你比如我就拿一个stm32就计数,那有啥用,肯定要压榨干净剩余价值。

这样的话就牵扯到一个问题了,不同的事件谁先干啊?

这个活就光荣的分配给中断(NVIC)来做了。

中断优先级高的先做,并且如果当前正在做的事情优先级不如我高,那我就可以打断它,你先把我的事情干完再去接着干别的。

中断搭配定时器来用,好处就是刚刚那个工具人好不容易数到100了,那就接着可以去做我刚刚分配给他的任务了,不用等。

如果不开NVIC呢?

就会一直完成不了任务,表现在硬件上就是定时器关联的触发任务没有反应,那可不就失去价值了,工具人直接失业。

补充

最后做一个经验总结:

函数HAL_TIM_OC_Start_IT和HAL_TIM_Base_Start_IT怎么用?

是不是Output Compare就一定要用HAL_TIM_OC_Start_IT?

并不是的,具体用什么函数来打开定时器主要取决于你的目的。比如:

HAL_TIM_OC_Start_IT用于比较输出,就是每次计数到位置就操作引脚,想要输出PWM波形等等的话可以用。

但如果你想用HAL_TIM_PeriodElapsedCallback函数,就得用HAL_TIM_Base_Start_IT了,因为这个函数是到时间就调用CallBack函数的。

应用

用定时器实现按键检测,长按、短按、一次触发、连续按触发。

Stm32主频72MHz,我们设置Period = 999, Prescaler = 71,就得到了周期为1ms的定时器

72M/72/1000=1000Hz=0.001s=1ms

然后我们在HAL_TIM_PeriodElapsedCallback里面写入:

if (htim->Instance == TIM2) {
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==KEY_ON)
    	{
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==KEY_ON&&key1_cnt<press_down)key1_cnt++;
			else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==KEY_OFF)key1_cnt--;
			if(key1_cnt>=press_down)flag=1;//flag=1控制run运行,否则不运行
		}
		else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==KEY_OFF)
		{
			key1_cnt=0;
			//flag=0;
			/*
			如果想要实现按住转动,不按不转,就需要解开注释,
			并且在run函数里面增加判断flag,flag=0立即退出
			否则每次松手都会运行完整整个程序不能立刻停下
			*/
		}
 }

如果想要更改长按短按,只需要改变press_down的值即可。 

物联沃分享整理
物联沃-IOTWORD物联网 » STM32定时器timer的对比理解和案例代码详解

发表评论