【STM32H7教程】第35章 STM32H7的定时器应用之高精度单次延迟实现(支持TIM2,3,4和5)

2023-04-13  

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.    /*

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。