当前位置:   article > 正文

STM32学习----基本定时器应用_stm32关闭定时器

stm32关闭定时器

什么是基本定时器

就是除了定时功能,没有其他什么功能。

基本定时器的结构组成

从图中可以看出基本定时器就这么些内容

1、时钟源(CLK)

2、预分频寄存器(PSC)

3、自动重装载寄存器(ARR)

4、计数器寄存器(CNT)

需要理解几个概念,什么是时钟源?什么是分频器?什么是自动重载?什么是计数器?什么是溢出?

举个栗子:

拿一个量杯,量杯的容量是65535mL;

往量杯里面滴水,每秒滴1mL;

65535秒后,杯子就装满了。

可以这么理解:

这个水滴就是时钟源,每滴就是1秒钟。

这个量杯就可以看作是一个计数器了,看量杯上的刻度就知道经过多长时间。

量杯满了,也叫溢出了。

量杯满了,就自动把水倒掉,重新接水计时,这个过程叫自动重载。

现在觉得1秒钟滴1mL太快了,改为10秒钟滴1mL,这个过程叫分频,10分频;如果改为5秒钟滴1mL就是5分频,改变时间间隔的就是分频器。

量杯的容量65536mL是实验室里面最大的量杯了,觉得太大了,可以换个50000mL的量杯,40000ml的杯子,25000mL的杯子。。。。。。这个容量可以理解为自动重装载寄存器的值。

量杯滴满了,除了把水倒掉重新接水外,还可以提醒你去干点啥吧,这个干点啥就可以理解为溢出产生的事件了,也可以理解为中断

STM32F103的基本定时器

F103的基本定时器有2个,TIM6和TIM7

TIM6和TIM7的特点:只能向上计数

什么叫向上计数:0、 1、 2 、3 。。。。。。99、100,从小往大的数叫向上计数;

什么叫向下计数:100、 99、 98 。。。。。。1、 0,从大往小的数叫向下计数;

与基本定时器相关的寄存器

这一个大表格看着就吓人,其实基本定时器就只有8个寄存器,

人心都是浮躁的,在哪个时代都是一样的,但是要想学点东西还是要静下心来,单片机要想学好,底层的寄存器一定要理解,别看STM32CubeMX一键生成代码很爽,在复杂的工程里面出点BUG你就GG了。

还是来看看那7个寄存器吧

控制寄存器1(TIMx_CR1)

APRE:Auto -reload Preload Enable 自动重装载预装载使能

0:自动重装载寄存器没有缓冲

1:自动重装载寄存器有缓冲

OPM:One-pulse mode 单脉冲模式

0:在发生更新事件时,计数器不停止

1:在发生下次更新事件时,计算器停止计数(清除CEN位)

URS:Update request source 更新请求源

该位由软件设置和清除,以选择UEV事件的请求源

0:所有状况都可以产生更新中断

1:只有计数器上溢或者下溢才可以产生更新中断或DMA

UDIS:Updata disable 禁止更新

0:更新事件使能

1:不产生更新事件

CEN :Counter Enable计数器使能

0:关闭计数器

1:使能计数器

控制寄存器2(TIMx_CR2)

DMA/中断使能寄存器(TIMx_DIER)

UDE:Update DMA Request Enabel 更新DMA请求使能

0:禁止更新DMA请求

1:使能更新DMA请求

UIE:Updata interrupt enable 更新中断使能

0:禁止更新中断

1:使能更新中断

状态寄存器(TIMx_SR)

UIF:Update interrupt 更新中断标志

硬件在更新中断时设置该位,由软件清除

0:没有产生更新

1:产生了更新

事件产生寄存器(TIMx_EGR)

UG:Update Generation 产生更新事件

0:无作用

1:重新初始化定时器的计数器,并产生对寄存器的更新

计数器(TIMx_CNT)

分频器(TIMx_PSC)

自动重装载寄存器(TIMx_ARR)

基本定时器的使用步骤

查询使用

1、参数初始化

2、打开定时器计数

3、关闭定时器

打开定时器对应的函数

  1. //在HAL库中,使用HAL_TIM_Base_Start()函数来打开定时器
  2. HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim)
  3. //
  4. //其实HAL_TIM_Base_Start()函数就是调用__HAL_TIM_ENABLE(__HANDLE__) 函数;
  5. //而__HAL_TIM_ENABLE(__HANDLE__) 函数就是将CR1寄存器的CEN位设置为1
  6. __HAL_TIM_ENABLE(__HANDLE__)

关闭定时器对应的函数

  1. //在HAL库中,使用HAL_TIM_Base_Stop()函数来关闭定时器
  2. HAL_StatusTypeDef HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim)
  3. //
  4. //其实HAL_TIM_Base_Stop()函数就是调用__HAL_TIM_DISABLE(__HANDLE__)函数;
  5. //而__HAL_TIM_DISABLE(__HANDLE__)函数就是将CR1寄存器的CEN位设置为0
  6. __HAL_TIM_DISABLE(__HANDLE__);

设置与读取计数器寄存器的初值,

默认复位之后,计数器寄存器的初值就是0,也可以自己设置个初值

  1. //设置计数器寄存器的初值
  2. __HAL_TIM_SET_COUNTER(__HANDLE__, __COUNTER__)
  3. //读取计数器寄存器的值
  4. __HAL_TIM_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNT)

设置自动重装载寄存器的值

定时器就是从计数器的初值,计到自动重装载的值之后,又从新计数;

一般计数器的初值是0,那这个自动重装的值就是周期,就是使用定时器经常看到的那个Preriod;

  1. //__HAL_TIM_SET_AUTORELOAD()函数就是设置ARR寄存器
  2. #define __HAL_TIM_SET_AUTORELOAD(__HANDLE__, __AUTORELOAD__) \
  3. do{ \
  4. (__HANDLE__)->Instance->ARR = (__AUTORELOAD__); \
  5. (__HANDLE__)->Init.Period = (__AUTORELOAD__); \
  6. } while(0)

用基本定时器TIM6来做一个准确的延时

基本定时器只能使用系统内部时钟,看自己设置的是多少,一般设置为72MHz,72分频之后就是1MHz,一个时钟周期就是1us

使用STM3CubeMX自动生成了一些配置代码

tim源文件代码如下

  1. #include "tim.h"
  2. TIM_HandleTypeDef htim6;
  3. void MX_TIM6_Init(void)
  4. {
  5. htim6.Instance = TIM6; //使用基本定时器TIM6
  6. htim6.Init.Prescaler = 72-1; //72分频,72MHz 72分频之后就是1MHz
  7. htim6.Init.CounterMode = TIM_COUNTERMODE_UP; //向上计数
  8. htim6.Init.Period = 0; //自动重装载的值先不设定,在延时函数里面设置
  9. htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //自动预装载不使能,自动装载值马上更新
  10. if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
  11. {
  12. Error_Handler();
  13. }
  14. }
  15. void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
  16. {
  17. if(tim_baseHandle->Instance==TIM6)
  18. {
  19. __HAL_RCC_TIM6_CLK_ENABLE();
  20. }
  21. }
  22. void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
  23. {
  24. if(tim_baseHandle->Instance==TIM6)
  25. {
  26. __HAL_RCC_TIM6_CLK_DISABLE();
  27. }
  28. }
  29. /* USER CODE BEGIN 1 */
  30. //
  31. /* 函数名 Delay_us
  32. * us级别的延时,t表示t us;
  33. * 前提是将定时器的时钟分频到1MHz,系统时钟72MHz的话,72分频即可
  34. */
  35. void Delay_us(uint16_t t)
  36. {
  37. uint16_t count = 0;
  38. __HAL_TIM_SET_AUTORELOAD(&htim6, t); //写入自动重装载寄存器的值,就是计数周期
  39. __HAL_TIM_SET_COUNTER(&htim6, 0); //将计数器初值设置为0
  40. __HAL_TIM_ENABLE(&htim6); //打开定时器开始计数
  41. while(count != t) //没有计到自动装载的值,就一直等着
  42. {
  43. count = __HAL_TIM_GET_COUNTER(&htim6); //读取计数器里面的值,赋值给count
  44. }
  45. __HAL_TIM_MOE_DISABLE(&htim6); //关闭定时器
  46. }
  47. /*
  48. *
  49. *ms级的延时就是1000个us级的延时
  50. *
  51. **/
  52. void Delay_ms(uint16_t t)
  53. {
  54. uint16_t cnt;
  55. for(cnt=0; cnt<t; cnt++)
  56. {
  57. Delay_us(1000);
  58. }
  59. }

main源文件

  1. #include "main.h"
  2. #include "tim.h"
  3. #include "gpio.h"
  4. void SystemClock_Config(void);
  5. int main(void)
  6. {
  7. HAL_Init();
  8. SystemClock_Config();
  9. MX_GPIO_Init();
  10. MX_TIM6_Init();
  11. while (1)
  12. {
  13. //让LED间隔1s闪缩
  14. HAL_GPIO_TogglePin(TEST_LED_GPIO_Port, TEST_LED_Pin);
  15. Delay_ms(1000);
  16. }
  17. }
  18. void SystemClock_Config(void)
  19. {
  20. RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  21. RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  22. RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  23. RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  24. RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  25. RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  26. RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  27. RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  28. RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  29. if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  30. {
  31. Error_Handler();
  32. }
  33. RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  34. |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  35. RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  36. RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  37. RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  38. RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  39. if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  40. {
  41. Error_Handler();
  42. }
  43. }
  44. void Error_Handler(void)
  45. {
  46. __disable_irq();
  47. while (1)
  48. {
  49. }
  50. }

中断使用

1、参数初始化

1、设置定时器的预分频系数

2、设置计数模式

3、设置自动重装载寄存器的值

4、预装载是否使能

2、使能定时器及其溢出中断

3、编写中断服务函数

1、检查更新中断标志位

2、检查对应的中断是不是使能了

3、清空中断标志位

4、做想做的事情,该做的事情

使用基本定时器的中断来实现LED灯闪缩

定时器tim.c源文件

  1. #include "tim.h"
  2. TIM_HandleTypeDef htim6;
  3. /* TIM6 init function */
  4. void MX_TIM6_Init(void)
  5. {
  6. htim6.Instance = TIM6; //使用定时器6
  7. htim6.Init.Prescaler = 72-1; //72分频
  8. htim6.Init.CounterMode = TIM_COUNTERMODE_UP; //向上计数
  9. htim6.Init.Period = 1000-1; //自动重装载值设置为1000
  10. htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //禁止预装载
  11. if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
  12. {
  13. Error_Handler();
  14. }
  15. HAL_TIM_Base_Start_IT(&htim6); //开启定时器以及中断
  16. }
  17. void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
  18. {
  19. if(tim_baseHandle->Instance==TIM6)
  20. {
  21. __HAL_RCC_TIM6_CLK_ENABLE();
  22. /* TIM6 interrupt Init */
  23. HAL_NVIC_SetPriority(TIM6_IRQn, 0, 0);
  24. HAL_NVIC_EnableIRQ(TIM6_IRQn);
  25. }
  26. }
  27. void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
  28. {
  29. if(tim_baseHandle->Instance==TIM6)
  30. {
  31. __HAL_RCC_TIM6_CLK_DISABLE();
  32. /* TIM6 interrupt Deinit */
  33. HAL_NVIC_DisableIRQ(TIM6_IRQn);
  34. }
  35. }

在中断源文件中编写回调函数

  1. void TIM6_IRQHandler(void)
  2. {
  3. HAL_TIM_IRQHandler(&htim6);
  4. }
  5. uint16_t count = 0;
  6. //设置的自动重装载值为1000,相当于1000us,也就是1ms中断一次,要1s后执行中断任务,就需要等1000次中断
  7. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  8. {
  9. count++;
  10. if(count == 1000)
  11. {
  12. count = 0;
  13. HAL_GPIO_TogglePin(TEST_LED_GPIO_Port,TEST_LED_Pin);
  14. HAL_TIM_Base_Start_IT(&htim6); //在中断回调函数中再次使能中断
  15. }
  16. }

HAL库里面的很多函数实在是很冗长,用起来是很方便,但是最好自己还是多研究一下底层的寄存器。

用寄存器操作结合HAL库,效率会高很多,代码看起来也会精简很多,后面有机会用简化的HAL库函数来实现定时器的中断。。。。。。

STM32的定时器功能非常强大,也很复杂,看使用手册就知道,100多页,比任何其他的外设都多,在实际的应用中,定时器也是用的非常频繁,尤其在电机控制方面,所以基本定时器的应用只是个开始。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号