35.1 初学者重要提示
学习本章节前,务必优先学习第32章,HAL库的几个常用API均作了讲解和举例。
STM32H7支持TIM1-TIM8,TIM12-TIM17共14个定时器,而中间的TIM9,TIM10,TIM11是不存在的,这点要注意。
在不需要任何补偿的情况下,误差可以做到正负1微秒以内。
TIM2和TIM5是32位定时器,而TIM3和TIM4是16位定时器。
35.2 定时器单次延迟驱动设计
单次定时器要实现1us的精度,可以直接将定时器时钟设置为1MHz,这样定时器每计数1次就是1us。对于16位定时器最大值就是0xFFFF微秒,而32位定时器就是0xFFFFFFFF微秒。
剩下的问题就是单次延迟时间到了可以及时执行相应功能,那么就可以开启一个CC捕获比较中断。而延迟时间可以直接通过设置CCR比较捕获寄存器实现。比如当前定时器的计数值是1000,我们要实现10us的单次延迟,我们就可以直接设置CCR的数值为1000 + 10 =1010即可,等1010的计数值到了,就会触发CC捕获比较中断。
35.2.1 定时器单次延迟宏定义
单次延迟支持TIM2,TIM3,TIM4和TIM5,其中TIM2和TIM5是32位定时器,而TIM3和TIM4是16位定时器。每个定时器都有4个通道,可以独立配置使用,互不影响。
1. /*
2. 定义用于硬件定时器的TIM, 可以使 TIM2 - TIM5
3. */
4. #define USE_TIM2
5. //#define USE_TIM3
6. //#define USE_TIM4
7. //#define USE_TIM5
8.
9. #ifdef USE_TIM2
10. #define TIM_HARD TIM2
11. #define RCC_TIM_HARD_CLK_ENABLE() __HAL_RCC_TIM2_CLK_ENABLE()
12. #define TIM_HARD_IRQn TIM2_IRQn
13. #define TIM_HARD_IRQHandler TIM2_IRQHandler
14. #endif
15.
16. #ifdef USE_TIM3
17. #define TIM_HARD TIM3
18. #define RCC_TIM_HARD_CLK_ENABLE() __HAL_RCC_TIM3_CLK_ENABLE()
19. #define TIM_HARD_IRQn TIM3_IRQn
20. #define TIM_HARD_IRQHandler TIM3_IRQHandler
21. #endif
22.
23. #ifdef USE_TIM4
24. #define TIM_HARD TIM4
25. #define RCC_TIM_HARD_CLK_ENABLE() __HAL_RCC_TIM4_CLK_ENABLE()
26. #define TIM_HARD_IRQn TIM4_IRQn
27. #define TIM_HARD_IRQHandler TIM4_IRQHandler
28. #endif
29.
30. #ifdef USE_TIM5
31. #define TIM_HARD TIM5
32. #define RCC_TIM_HARD_CLK_ENABLE() __HAL_RCC_TIM5_CLK_ENABLE()
33. #define TIM_HARD_IRQn TIM5_IRQn
34. #define TIM_HARD_IRQHandler TIM5_IRQHandler
35. #endif
36.
37. /* 保存 TIM定时中断到后执行的回调函数指针 */
38. static void (*s_TIM_CallBack1)(void);
39. static void (*s_TIM_CallBack2)(void);
40. static void (*s_TIM_CallBack3)(void);
41. static void (*s_TIM_CallBack4)(void);
这里把几个关键的地方阐释下:
第4- 7行,用于选择要使用的定时器,使用哪个定时器,使能那个宏定义即可。
第9 - 14行,用于配置定时器的四个宏定义,这里是配置的TIM2,后面TIM3,TIM4,TIM5的配置同理。
第38 – 40行,定义4个函数指针,用于保存定时器CC比较捕获中断执行后的回调函数指针。
35.2.2 定时器单次延迟初始化
单次定时器的初始化代码如下:
1. /*
2. ******************************************************************************************************
3. * 函 数 名: bsp_InitHardTimer
4. * 功能说明: 配置 TIMx,用于us级别硬件定时。TIMx将自由运行,永不停止.
5. * TIMx可以用TIM2 - TIM5 之间的TIM, 这些TIM有4个通道, 挂在 APB1 上,输入时钟
6. * =SystemCoreClock / 2
7. * 形 参: 无
8. * 返 回 值: 无
9. ******************************************************************************************************
10. */
11. void bsp_InitHardTimer(void)
12. {
13. TIM_HandleTypeDef TimHandle = {0};
14. uint32_t usPeriod;
15. uint16_t usPrescaler;
16. uint32_t uiTIMxCLK;
17. TIM_TypeDef* TIMx = TIM_HARD;
18.
19. RCC_TIM_HARD_CLK_ENABLE(); /* 使能TIM时钟 */
20.
21. /*-----------------------------------------------------------------------
22. bsp.c 文件中 void SystemClock_Config(void) 函数对时钟的配置如下:
23.
24. System Clock source = PLL (HSE)
25. SYSCLK(Hz) = 400000000 (CPU Clock)
26. HCLK(Hz) = 200000000 (AXI and AHBs Clock)
27. AHB Prescaler = 2
28. D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)
29. D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)
30. D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)
31. D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)
32.
33. 因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz;
34. 因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;
35. APB4上面的TIMxCLK没有分频,所以就是100MHz;
36.
37. APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
38. APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17
39.
40. APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5
41.
42. ----------------------------------------------------------------------- */
43. if ((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM15) || (TIMx == TIM16) || (TIMx == TIM17))
44. {
45. /* APB2 定时器时钟 = 200M */
46. uiTIMxCLK = SystemCoreClock / 2;
47. }
48. else
49. {
50. /* APB1 定时器 = 200M */
51. uiTIMxCLK = SystemCoreClock / 2;
52. }
53.
54. usPrescaler = uiTIMxCLK / 1000000 - 1; /* 分频比 = 1 */
55.
56. if (TIMx == TIM2 || TIMx == TIM5)
57. {
58. usPeriod = 0xFFFFFFFF;
59. }
60. else
61. {
62. usPeriod = 0xFFFF;
63. }
64.
65. /*
66. 设置分频为usPrescaler后,那么定时器计数器计1次就是1us
67. 而参数usPeriod的值是决定了最大计数:
68. usPeriod = 0xFFFF 表示最大0xFFFF微妙。
69. usPeriod = 0xFFFFFFFF 表示最大0xFFFFFFFF微妙。
70. */
71. TimHandle.Instance = TIMx;
72. TimHandle.Init.Prescaler = usPrescaler;
73. TimHandle.Init.Period = usPeriod;
74. TimHandle.Init.ClockDivision = 0;
75. TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
76. TimHandle.Init.RepetitionCounter = 0;
77. TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
78.
79. if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
80. {
81. Error_Handler(__FILE__, __LINE__);
82. }
83.
84. /* 配置定时器中断,给CC捕获比较中断使用 */
85. {
86. HAL_NVIC_SetPriority(TIM_HARD_IRQn, 0, 2);
87. HAL_NVIC_EnableIRQ(TIM_HARD_IRQn);
88. }
89.
90. /* 启动定时器 */
91. HAL_TIM_Base_Start(&TimHandle);
92. }
这里把几个关键的地方阐释下:
第13行,HAL库的这个结构体变量要初始化为0,此问题在第32章的的4.1小节有专门说明。
第43 – 52行,获取定时器的时钟频率,TIM2,TIM3,TIM4和TIM5都是用的APB1,因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz。
第54行,设置分频参数,定时器分频的频率是1MHz。
第71 - 82行,设置分频为usPrescaler后,那么定时器计数器计1次就是1us,而参数usPeriod的值是决定了最大计数: usPeriod = 0xFFFF 表示最大0xFFFF微秒。 usPeriod = 0xFFFFFFFF 表示最大0xFFFFFFFF微秒。
第86 – 87行,这里要特别注意,此处是开启定时器的NVIC是供CC捕获比较中断使用,而不是更新中断。
第91行,启动定时器。
35.2.3 定时器单次延迟启动
下面是定时器的启动代码,使用TIM2-5做单次定时器使用, 定时时间到后执行回调函数。可以同时启动4个定时器,互不干扰。
1. /*