当前位置:   article > 正文

STM32——TIMER(定时器)篇_stm32 timer

stm32 timer

技术笔记!

1.   定时器概述(了解)

1.1  软件定时器原理

使用纯软件(CPU死等)的方式实现定时(延时)功能

缺点:1. 延时不准确        2. CPU死等。      

1.2  定时器定时原理

1.3  STM32定时器分类

1.4  STM32定时器特性表(F1为例)

可参考芯片数据手册或者各个开发板对应的开发指南

1.5  STM32定时器之间的区别

2.  基本定时器(掌握)

基础定时器 通用定时器 没有输入输出通道,常用作时基,即定时功能

2.1  基本定时器简介(了解)

主要特性:

16位递增计数器(计数值:0~65535)

16位预分频器(分频系数:1~65536)

可用于触发DAC

在更新事件(计数器溢出)时,会产生中断/DMA请求

2.2  基本定时器框图(熟悉)

2.3  定时器计数模式及溢出条件(熟悉)

2.4  定时器中断实验相关寄存器(了解)F1为例

1.  TIM6和TIM7控制寄存器1(TIMx_CR1)

2.  TIM6 和TIM7 DMA/中断使能寄存器(TIMx_DIER)

3.  TIM6 和TIM7 状态寄存器(TIMx_SR)

4.  TIM6 和TIM7 计数器(TIMx_CNT)

5.   TIM6 和TIM7 预分频器(TIMx_PSC)

6.  TIM6 和TIM7 自动重装载寄存器(TIMx_ARR)

2.5  定时器溢出时间计算方法(掌握)

2.6  定时器中断实验配置步骤(掌握)

2.7  基础定时器实验

使用定时器6,实现500ms定时器更新中断,在中断里翻转LED0

btim.c

  1. #include "./BSP/LED/led.h"
  2. #include "btim.h"
  3. TIM_HandleTypeDef g_timx_handle;
  4. /* 定时器中断初始化函数 */
  5. void btim_timx_int_init(uint16_t arr, uint16_t psc)
  6. {
  7. g_timx_handle.Instance = TIM6; //基地址
  8. g_timx_handle.Init.Prescaler = psc; //预分频系数
  9. g_timx_handle.Init.Period = arr; //预加载值
  10. HAL_TIM_Base_Init(&g_timx_handle); //基础时钟初始化函数
  11. HAL_TIM_Base_Start_IT(&g_timx_handle); //使能更新中断并启动计数器
  12. }
  13. /* 定时器基础MSP初始化函数 */
  14. void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
  15. {
  16. if (htim->Instance == TIM6)
  17. {
  18. __HAL_RCC_TIM6_CLK_ENABLE();
  19. HAL_NVIC_SetPriority(TIM6_IRQn, 1, 3);
  20. HAL_NVIC_EnableIRQ(TIM6_IRQn);
  21. }
  22. }
  23. /* 定时器6中断服务函数 */
  24. void TIM6_IRQHandler(void)
  25. {
  26. HAL_TIM_IRQHandler(&g_timx_handle);
  27. }
  28. /* 定时器溢出中断中断回调函数 */
  29. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  30. {
  31. if (htim->Instance == TIM6)
  32. {
  33. LED0_TOGGLE();
  34. }
  35. }

main.c

  1. #include "./SYSTEM/sys/sys.h"
  2. #include "./SYSTEM/usart/usart.h"
  3. #include "./SYSTEM/delay/delay.h"
  4. #include "./BSP/LED/led.h"
  5. #include "./BSP/BTIM/btim.h"
  6. int main(void)
  7. {
  8. HAL_Init(); /* 初始化HAL库 */
  9. sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  10. delay_init(72); /* 延时初始化 */
  11. led_init(); /* LED初始化 */
  12. btim_timx_int_init(5000-1, 7200 -1);/* 10Khz的计数频率,计数5K次为500ms */
  13. while(1)
  14. {
  15. delay_ms(500);
  16. }
  17. }

实验小结:

        首先通过自定义的中断初始化函数对基础定时器进行初始化,配置定时器句柄结构体的相关成员变量赋值,通过基础时钟初始化函数对基础时钟初始化,通过基础时钟中断并启动计数器函数进行使能该基础时钟,再通过定时器基础msp初始化函数对时钟和中断进行使能及配置,最后通过对应定时器中断服务函数进行中断处理。

3.  通用计时器(掌握)

3.1  通用计时器简介(了解)

主要特性:

16位递增、递减、中心对齐计数器(计数值:0~65535)

16位预分频器(分频系数:1~65536)

可用于触发DAC、ADC 在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求

4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式 使用外部信号控制定时器且可实现多个定时器互连的同步电路 支持编码器和霍尔传感器电路等

3.2  通用定时器框图(熟悉)

3.3  计数器时钟源(掌握)

3.4  通用定时器PWM输出实验(掌握)

脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微 处理器的数字输出来对模拟电路进行控制的一种非常有效的技术

3.4.1  通用定时器输出比较部分框图介绍(熟悉)

3.4.2  通用定时器输出PWM原理(掌握)

        让定时器产生PWM, 在计数器频率固定时,PWM 频率或者周期由自动重载寄存(TIMx_ARR)的值决定,其占空 比由捕获/比较寄存器(TIMx_CCRx)的值决定。

        当 CNT=CCRx 时,IO 输出高电平(逻辑 1);当 CNT=ARR 时,定时器溢出,CNT 的值被清零,然后继续递增,依次循环。

        在这个循环中,改 变 CCRx 的值,就可以改变 PWM 的占空比,改变 ARR 的值,就可以改变 PWM 的频率,这就 是 PWM 输出的原理。

3.4.3  PWM模式(熟悉)

3.4.4  通用定时器PWM输出实验配置步骤(掌握)

相关寄存器讲解:

1.  捕获/比较模式寄存器 1/2(TIMx_CCMR1/2)

        TIM2/TIM3/TIM4/TIM5 的捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器一般有 2 个TIMx _CCMR1 和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 CH2,而 TIMx_CCMR2 控制 CH3 和 CH4。TIMx_CCMR1 寄存器描述如图 21.3.1.1 所示:

2.  捕获/比较使能寄存器(TIMx_CCER)

        TIM2/TIM3/TIM4/TIM5 的捕获/比较使能寄存器,该寄存器控制着各个输入输出通道的开 关和极性。TIMx_CCER 寄存器描述如图 21.3.1.2 所示:

3.  捕获/比较寄存器 1/2/3/4(TIMx_CCR1/2/3/4)

        捕获/比较寄存器(TIMx_CCR1/2/3/4),该寄存器总共有 4 个,对应 4 个通道 CH1~CH4。 我们使用的是通道 2,所以来看看 TIMx_CCR2 寄存器,描述如图 21.3.1.3 所示

3.4.5  通用定时器PWM输出实验(掌握)

pwm.c

  1. #include "./BSP/LED/led.h"
  2. #include "./BSP/PWM/pwm.h"
  3. TIM_HandleTypeDef g_timx_pwm_chy_handle;
  4. TIM_OC_InitTypeDef g_timx_oc_pwm_chy_handle;
  5. void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
  6. {
  7. g_timx_pwm_chy_handle.Instance = TIM3 ; /* 定时器 x */
  8. g_timx_pwm_chy_handle.Init.Prescaler = psc; /* 定时器分频 */
  9. g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;/* 递增计数模式 */
  10. g_timx_pwm_chy_handle.Init.Period = arr; /* 自动重装载值 */
  11. HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle); /* 初始化 PWM */
  12. g_timx_oc_pwm_chy_handle.OCMode = TIM_OCMODE_PWM1; /* 模式选择 PWM1 */
  13. /* 设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为 50% */
  14. g_timx_oc_pwm_chy_handle.Pulse = arr/2;
  15. g_timx_oc_pwm_chy_handle.OCPolarity = TIM_OCNPOLARITY_LOW; /* 输出比较极性为低 */
  16. HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &g_timx_oc_pwm_chy_handle, TIM_CHANNEL_2);/* 配置 TIMx 通道 y */
  17. HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, TIM_CHANNEL_2); /*开启 PWM 通道*/
  18. }
  19. void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
  20. {
  21. if(htim->Instance == TIM3)
  22. {
  23. GPIO_InitTypeDef gpio_init_struct;
  24. __HAL_RCC_GPIOB_CLK_ENABLE(); /* 开启通道 y 的 CPIO 时钟 */
  25. __HAL_RCC_TIM3_CLK_ENABLE();
  26. gpio_init_struct.Pin = GPIO_PIN_5; /* 通道 y 的 CPIO 口 */
  27. gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */
  28. gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
  29. gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
  30. HAL_GPIO_Init(GPIOB, &gpio_init_struct);
  31. GTIM_TIMX_PWM_CHY_GPIO_REMAP(); /* IO 口 REMAP 设置,设置重映射 */
  32. }
  33. }

main.c

  1. int main(void)
  2. {
  3. uint16_t ledrpwmval = 0;
  4. uint8_t dir = 1;
  5. HAL_Init(); /* 初始化HAL库 */
  6. sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  7. delay_init(72); /* 延时初始化 */
  8. led_init(); /* LED初始化 */
  9. /* 72M/72=1M 的计数频率,自动重装载为 500,那么 PWM 频率为 1M/500=2kHZ */
  10. gtim_timx_pwm_chy_init(500 - 1, 72 - 1);
  11. while(1)
  12. {
  13. delay_ms(10);
  14. if(dir)ledrpwmval++;
  15. else ledrpwmval--;
  16. if(ledrpwmval > 300)dir = 0;
  17. if(ledrpwmval == 0) dir = 1;
  18. __HAL_TIM_SET_COMPARE(&g_timx_pwm_chy_handle, TIM_CHANNEL_2,ledrpwmval);
  19. }
  20. }
3.5  通用定时器输入捕获实验(掌握)
3.5.1  通用定时器输入捕获部分框图介绍(熟悉)

3.5.2  通用定时器输入捕获脉宽测量原理(掌握)

3.5.3  通用定时器输入捕获实验配置步骤(掌握)

3.5.4  通用定时器输入捕获实验(掌握)

tim.c
  1. #include "./BSP/LED/led.h"
  2. #include "tim.h"
  3. TIM_HandleTypeDef g_timx_cap_chy_handle; /* 定时器x句柄 */
  4. /* 通用定时器通道y 输入捕获 初始化函数 */
  5. void gtim_timx_cap_chy_init(uint16_t arr, uint16_t psc)
  6. {
  7. TIM_IC_InitTypeDef timx_ic_cap_chy = {0};
  8. g_timx_cap_chy_handle.Instance = TIM5; /* 定时器5 */
  9. g_timx_cap_chy_handle.Init.Prescaler = psc; /* 定时器分频 */
  10. g_timx_cap_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
  11. g_timx_cap_chy_handle.Init.Period = arr; /* 自动重装载值 */
  12. HAL_TIM_IC_Init(&g_timx_cap_chy_handle);
  13. timx_ic_cap_chy.ICPolarity = TIM_ICPOLARITY_RISING; /* 上升沿捕获 */
  14. timx_ic_cap_chy.ICSelection = TIM_ICSELECTION_DIRECTTI; /* 映射到TI1*/
  15. timx_ic_cap_chy.ICPrescaler = TIM_ICPSC_DIV1; /* 配置输入分频,不分频 */
  16. timx_ic_cap_chy.ICFilter = 0; /* 配置输入滤波器,不滤波 */
  17. HAL_TIM_IC_ConfigChannel(&g_timx_cap_chy_handle, &timx_ic_cap_chy, TIM_CHANNEL_1); /* 配置TIM5通道1 */
  18. __HAL_TIM_ENABLE_IT(&g_timx_cap_chy_handle, TIM_IT_UPDATE); /* 使能更新中断 */
  19. HAL_TIM_IC_Start_IT(&g_timx_cap_chy_handle, TIM_CHANNEL_1); /* 开始捕获TIM5的通道1 */
  20. }
  21. /* 定时器 输入捕获 MSP初始化函数 */
  22. void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
  23. {
  24. if(htim->Instance == TIM5) /*输入通道捕获*/
  25. {
  26. GPIO_InitTypeDef gpio_init_struct;
  27. __HAL_RCC_TIM5_CLK_ENABLE(); /* 使能TIM5时钟 */
  28. __HAL_RCC_GPIOA_CLK_ENABLE(); /* 开启捕获IO的时钟 */
  29. gpio_init_struct.Pin = GPIO_PIN_0;
  30. gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */
  31. gpio_init_struct.Pull = GPIO_PULLDOWN; /* 下拉 */
  32. gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
  33. HAL_GPIO_Init(GPIOA, &gpio_init_struct);
  34. HAL_NVIC_SetPriority(TIM5_IRQn, 1, 3); /* 抢占1,子优先级3 */
  35. HAL_NVIC_EnableIRQ(TIM5_IRQn); /* 开启ITMx中断 */
  36. }
  37. }
  38. /* 输入捕获状态(g_timxchy_cap_sta)
  39. * [7] :0,没有成功的捕获;1,成功捕获到一次.
  40. * [6] :0,还没捕获到高电平;1,已经捕获到高电平了.
  41. * [5:0]:捕获高电平后溢出的次数,最多溢出63次,所以最长捕获值 = 63*65536 + 65535 = 4194303
  42. * 注意:为了通用,我们默认ARR和CCRy都是16位寄存器,对于32位的定时器(如:TIM5),也只按16位使用
  43. * 按1us的计数频率,最长溢出时间为:4194303 us, 约4.19秒
  44. *
  45. * (说明一下:正常32位定时器来说,1us计数器加1,溢出时间:4294秒)
  46. */
  47. uint8_t g_timxchy_cap_sta = 0; /* 输入捕获状态 */
  48. uint16_t g_timxchy_cap_val = 0; /* 输入捕获值 */
  49. /* 定时器5中断服务函数 */
  50. void TIM5_IRQHandler(void)
  51. {
  52. HAL_TIM_IRQHandler(&g_timx_cap_chy_handle); /* 定时器HAL库共用处理函数 */
  53. }
  54. /* 定时器输入捕获中断处理回调函数 */
  55. void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
  56. {
  57. if(htim->Instance == TIM5)
  58. {
  59. if ((g_timxchy_cap_sta & 0X80) == 0) /* 还没有成功捕获 */
  60. {
  61. if(g_timxchy_cap_sta & 0X40) /* 捕获到一个下降沿 */
  62. {
  63. g_timxchy_cap_sta |= 0X80; /* 标记成功捕获到一次高电平脉宽 */
  64. g_timxchy_cap_val = HAL_TIM_ReadCapturedValue(&g_timx_cap_chy_handle,TIM_CHANNEL_1); /* 获取当前的捕获值 */
  65. TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle,TIM_CHANNEL_1); /* 一定要先清除原来的设置 */
  66. TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle,TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); /* 配置TIM5通道1上升沿捕获 */
  67. __HAL_TIM_ENABLE(&g_timx_cap_chy_handle); /* 使能定时器5 */
  68. }
  69. }
  70. }
  71. }
  72. /* 定时器更新中断回调函数 */
  73. void HAL_TIM_PeriorElapsedCallback(TIM_HandleTypeDef *htim)
  74. {
  75. if(htim->Instance == TIM5)
  76. {
  77. if((g_timxchy_cap_sta & 0X80) == 0) /* 还未成功捕获 */
  78. {
  79. if(g_timxchy_cap_sta & 0X40) /* 已经捕获到高电平了 */
  80. {
  81. if((g_timxchy_cap_sta & 0X3F) == 0X3F) /* 高电平太长了 */
  82. {
  83. TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle,TIM_CHANNEL_1); /* 一定要先清除原来的设置 */
  84. TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle,TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); /* 配置TIM5通道1上升沿捕获 */
  85. g_timxchy_cap_sta |= 0x80; /* 标记成功捕获了一次 */
  86. g_timxchy_cap_val = 0XFFFF;
  87. }
  88. else /* 累计定时器溢出次数 */
  89. {
  90. g_timxchy_cap_sta++;
  91. }
  92. }
  93. }
  94. }
  95. }

main.c

  1. extern uint8_t g_timxchy_cap_sta; /* 输入捕获状态 */
  2. extern uint16_t g_timxchy_cap_val; /* 输入捕获值 */
  3. int main(void)
  4. {
  5. uint32_t temp = 0;
  6. uint8_t t = 0;
  7. HAL_Init(); /* 初始化HAL库 */
  8. sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  9. delay_init(72); /* 延时初始化 */
  10. led_init(); /* LED初始化 */
  11. gtim_timx_cap_chy_init(0XFFFF, 72 - 1); /*1Mhz的频率计数 捕获 */
  12. while(1)
  13. {
  14. if (g_timxchy_cap_sta & 0X80) /* 成功捕获到了一次高电平 */
  15. {
  16. temp = g_timxchy_cap_sta & 0X3F;
  17. temp *= 65536; /* 溢出时间总和 */
  18. temp += g_timxchy_cap_val; /* 得到总的高电平时间 */
  19. printf("HIGH:%d us\r\n", temp); /* 打印总的高点平时间 */
  20. g_timxchy_cap_sta = 0; /* 开启下一次捕获*/
  21. }
  22. t++;
  23. if (t > 20) /* 200ms进入一次 */
  24. {
  25. t = 0;
  26. LED0_TOGGLE(); /* LED0闪烁 ,提示程序运行 */
  27. }
  28. delay_ms(10);
  29. }
  30. }
3.6  通用定时器脉冲计数实验(掌握)
3.6.1  脉冲计数实验原理(熟悉)

3.6.2  通用定时器脉冲计数实验配置步骤(掌握)

3.6.3  通用定时器脉冲计数实验(掌握)

tim.c

  1. #include "./BSP/LED/led.h"
  2. #include "tim.h"
  3. TIM_HandleTypeDef g_timx_cnt_chy_handle; /* 定时器x句柄 */
  4. /* 通用定时器通道y 脉冲计数 初始化函数 */
  5. void gtim_timx_cnt_chy_init(uint16_t psc)
  6. {
  7. TIM_SlaveConfigTypeDef tim_salve_config = {0};
  8. g_timx_cnt_chy_handle.Instance = TIM2;
  9. g_timx_cnt_chy_handle.Init.Prescaler = psc;
  10. g_timx_cnt_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
  11. g_timx_cnt_chy_handle.Init.Period = 65535;
  12. HAL_TIM_IC_Init(&g_timx_cnt_chy_handle);
  13. tim_salve_config.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
  14. tim_salve_config.InputTrigger = TIM_TS_TI1F_ED;
  15. tim_salve_config.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
  16. tim_salve_config.TriggerFilter = 0;
  17. HAL_TIM_SlaveConfigSynchro(&g_timx_cnt_chy_handle, &tim_salve_config);
  18. HAL_TIM_IC_Start(&g_timx_cnt_chy_handle, TIM_CHANNEL_1);
  19. }
  20. /* 定时器 输入捕获 MSP初始化函数 */
  21. void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
  22. {
  23. if(htim->Instance == TIM2)
  24. {
  25. GPIO_InitTypeDef gpio_init_struct;
  26. __HAL_RCC_GPIOA_CLK_ENABLE();
  27. __HAL_RCC_TIM2_CLK_ENABLE();
  28. gpio_init_struct.Pin = GPIO_PIN_0;
  29. gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 推挽式复用功能 */
  30. gpio_init_struct.Pull = GPIO_PULLDOWN; /* 下拉 */
  31. gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
  32. HAL_GPIO_Init(GPIOA, &gpio_init_struct);
  33. }
  34. }

key.c

  1. #include "key.h"
  2. #include "./SYSTEM/delay/delay.h"
  3. /*按键初始化函数*/
  4. void key_init(void)
  5. {
  6. GPIO_InitTypeDef gpio_init_struct;
  7. KEY0_GPIO_CLK_ENABLE(); /* KEY0时钟使能 */
  8. KEY1_GPIO_CLK_ENABLE(); /* KEY1时钟使能 */
  9. WKUP_GPIO_CLK_ENABLE(); /* WKUP时钟使能 */
  10. gpio_init_struct.Pin = KEY0_GPIO_PIN; /* KEY0引脚 */
  11. gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
  12. gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
  13. gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
  14. HAL_GPIO_Init(KEY0_GPIO_PORT, &gpio_init_struct); /* KEY0引脚模式设置,上拉输入 */
  15. gpio_init_struct.Pin = KEY1_GPIO_PIN; /* KEY1引脚 */
  16. gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
  17. gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
  18. gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
  19. HAL_GPIO_Init(KEY1_GPIO_PORT, &gpio_init_struct); /* KEY1引脚模式设置,上拉输入 */
  20. gpio_init_struct.Pin = WKUP_GPIO_PIN; /* WKUP引脚 */
  21. gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
  22. gpio_init_struct.Pull = GPIO_PULLDOWN; /* 下拉 */
  23. gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
  24. HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP引脚模式设置,下拉输入 */
  25. }
  26. /**
  27. * @brief 按键扫描函数
  28. * @note 该函数有响应优先级(同时按下多个按键): WK_UP > KEY1 > KEY0!!
  29. * @param mode:0 / 1, 具体含义如下:
  30. * @arg 0, 不支持连续按(当按键按下不放时, 只有第一次调用会返回键值,
  31. * 必须松开以后, 再次按下才会返回其他键值)
  32. * @arg 1, 支持连续按(当按键按下不放时, 每次调用该函数都会返回键值)
  33. * @retval 键值, 定义如下:
  34. * KEY0_PRES, 1, KEY0按下
  35. * KEY1_PRES, 2, KEY1按下
  36. * WKUP_PRES, 4, WKUP按下
  37. */
  38. uint8_t key_scan(uint8_t mode)
  39. {
  40. static uint8_t key_up = 1; /* 按键按松开标志 */
  41. uint8_t keyval = 0;
  42. if (mode) key_up = 1; /* 支持连按 */
  43. if (key_up && (KEY0 == 0 || KEY1 == 0 || WK_UP == 1)) /* 按键松开标志为1, 且有任意一个按键按下了 */
  44. {
  45. delay_ms(10); /* 去抖动 */
  46. key_up = 0;
  47. if (KEY0 == 0) keyval = KEY0_PRES;
  48. if (KEY1 == 0) keyval = KEY1_PRES;
  49. if (WK_UP == 1) keyval = WKUP_PRES;
  50. }
  51. else if (KEY0 == 1 && KEY1 == 1 && WK_UP == 0) /* 没有任何按键按下, 标记按键松开 */
  52. {
  53. key_up = 1;
  54. }
  55. return keyval; /* 返回键值 */
  56. }

main.c

  1. extern TIM_HandleTypeDef g_timx_cnt_chy_handle; /* 定时器x句柄 */
  2. int main(void)
  3. {
  4. uint16_t curcnt;
  5. uint16_t oldcnt;
  6. uint8_t key;
  7. uint8_t t = 0;
  8. HAL_Init(); /* 初始化HAL库 */
  9. sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  10. delay_init(72); /* 延时初始化 */
  11. usart_init(115200); /* 串口初始化为115200 */
  12. led_init(); /* LED初始化 */
  13. key_init(); /* 初始化按键 */
  14. gtim_timx_cnt_chy_init(0);
  15. while(1)
  16. {
  17. key = key_scan(0);
  18. if(key == KEY0_PRES)
  19. {
  20. __HAL_TIM_SET_COUNTER(&g_timx_cnt_chy_handle, 0);
  21. }
  22. curcnt = __HAL_TIM_GET_COUNTER(&g_timx_cnt_chy_handle);
  23. if(oldcnt != curcnt)
  24. {
  25. oldcnt = curcnt;
  26. printf("CNT:%d\r\n", oldcnt);
  27. }
  28. t++;
  29. if(t > 20)
  30. {
  31. t = 0;
  32. LED0_TOGGLE();
  33. }
  34. delay_ms(10);
  35. }
  36. }

4.  高级定时器 (掌握)

4.1  高级定时器简介(了解)

主要特性:

16位递增、递减、中心对齐计数器(计数值:0~65535)
16位预分频器(分频系数:1~65536)
可用于触发DAC、ADC
在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求
4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式
使用外部信号控制定时器且可实现多个定时器互连的同步电路
支持编码器和霍尔传感器电路等
重复计数器
死区时间带可编程的互补输出
断路输入,用于将定时器的输出信号置于用户可选的安全配置中

4.2  高级定时器框图(熟悉)

4.3  高级定时器输出指定个数PWM实验(掌握)
4.3.1  重复计数器特性(熟悉)

4.3.2  高级定时器输出指定个数PWM实验原理(掌握)

4.3.3  高级定时器输出指定个数PWM实验配置步骤(掌握)

相关寄存器:

1.  控制寄存器 1(TIMx_CR1)

2.  捕获/比较模式寄存器 1/2(TIMx_CCMR1/2)

3.  捕获/比较使能寄存器(TIMx_ CCER)

4.  事件产生寄存器(TIMx_ EGR)

5.  重复计数器寄存器(TIMx_ RCR)

重复计数器寄存器用于设置重复计数器值,因为它具有影子寄存器,所以它本身只是起缓 冲作用。

6.  断路和死区寄存器(TIMx_ BDTR)

7.  捕获/比较寄存器 1/2/3/4(TIMx_CCR1/2/3/4)

4.3.4  实战

gtim.c

  1. #include "./BSP/LED/led.h"
  2. #include "tim.h"
  3. TIM_HandleTypeDef g_timx_npwm_chy_handle; /* 定时器x句柄 */
  4. static uint8_t g_npwm_remain = 0;
  5. /* 高级定时器TIMX 通道Y 输出指定个数PWM 初始化函数 */
  6. void atim_timx_npwm_chy_init(uint16_t arr, uint16_t psc)
  7. {
  8. TIM_OC_InitTypeDef timx_oc_npwm_chy = {0};
  9. g_timx_npwm_chy_handle.Instance = TIM8; /* 定时器x */
  10. g_timx_npwm_chy_handle.Init.Prescaler = psc; /* 定时器分频 */
  11. g_timx_npwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
  12. g_timx_npwm_chy_handle.Init.Period = arr; /* 自动重装载值 */
  13. g_timx_npwm_chy_handle.Init.RepetitionCounter = 0; /* 重复计数器初始值 */
  14. HAL_TIM_PWM_Init(&g_timx_npwm_chy_handle); /* 初始化PWM */
  15. timx_oc_npwm_chy.OCMode = TIM_OCMODE_PWM1; /* 模式选择PWM 1*/
  16. timx_oc_npwm_chy.Pulse = arr / 2; /* 设置比较值,此值用来确定占空比 */
  17. /* 这里默认设置比较值为自动重装载值的一半,即占空比为50% */
  18. timx_oc_npwm_chy.OCPolarity = TIM_OCPOLARITY_HIGH; /* 输出比较极性为高 */
  19. HAL_TIM_PWM_ConfigChannel(&g_timx_npwm_chy_handle, &timx_oc_npwm_chy, TIM_CHANNEL_1);
  20. __HAL_TIM_ENABLE_IT(&g_timx_npwm_chy_handle, TIM_IT_UPDATE);
  21. HAL_TIM_PWM_Start(&g_timx_npwm_chy_handle, TIM_CHANNEL_1);
  22. }
  23. /* 定时器 PWM输出 MSP初始化函数 */
  24. void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
  25. {
  26. if (htim->Instance == TIM8)
  27. {
  28. GPIO_InitTypeDef gpio_init_struct;
  29. __HAL_RCC_GPIOC_CLK_ENABLE();
  30. __HAL_RCC_TIM8_CLK_ENABLE();
  31. gpio_init_struct.Pin = GPIO_PIN_6;
  32. gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 推挽式复用功能 */
  33. gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
  34. gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
  35. HAL_GPIO_Init(GPIOC, &gpio_init_struct);
  36. HAL_NVIC_SetPriority(TIM8_UP_IRQn, 1, 3);
  37. HAL_NVIC_EnableIRQ(TIM8_UP_IRQn);
  38. }
  39. }
  40. /* 高级定时器TIMX NPWM设置PWM个数函数 */
  41. void atim_timx_npwm_chy_set(uint8_t npwm)
  42. {
  43. if(npwm == 0) return;
  44. g_npwm_remain = npwm;
  45. HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE);
  46. __HAL_TIM_ENABLE(&g_timx_npwm_chy_handle);
  47. }
  48. /* 定时器8中断服务函数 */
  49. void TIM8_UP_IRQHandler(void)
  50. {
  51. HAL_TIM_IRQHandler(&g_timx_npwm_chy_handle);
  52. }
  53. /* 定时器更新中断回调函数 */
  54. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  55. {
  56. if (htim->Instance == TIM8)
  57. {
  58. if(g_npwm_remain)
  59. {
  60. TIM8->RCR = g_npwm_remain - 1;
  61. HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE);
  62. __HAL_TIM_ENABLE(&g_timx_npwm_chy_handle);
  63. g_npwm_remain = 0;
  64. }
  65. else
  66. {
  67. TIM8->CR1 &= ~(1 << 0);
  68. }
  69. }
  70. }

main.c

  1. extern TIM_HandleTypeDef g_timx_cnt_chy_handle; /* 定时器x句柄 */
  2. int main(void)
  3. {
  4. uint8_t key;
  5. uint8_t t = 0;
  6. GPIO_InitTypeDef gpio_init_struct;
  7. HAL_Init(); /* 初始化HAL库 */
  8. sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  9. delay_init(72); /* 延时初始化 */
  10. usart_init(115200); /* 串口初始化为115200 */
  11. led_init(); /* LED初始化 */
  12. key_init(); /* 初始化按键 */
  13. /* 把PE5设置为输入,避免与PC6冲突 */
  14. __HAL_RCC_GPIOE_CLK_ENABLE();
  15. gpio_init_struct.Pin = GPIO_PIN_5;
  16. gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
  17. gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
  18. gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
  19. HAL_GPIO_Init(GPIOE, &gpio_init_struct);
  20. atim_timx_npwm_chy_init(5000 - 1, 7200 - 1);
  21. atim_timx_npwm_chy_set(5);
  22. while (1)
  23. {
  24. key = key_scan(0);
  25. if(key == KEY0_PRES)
  26. {
  27. atim_timx_npwm_chy_set(6);
  28. }
  29. t++;
  30. delay_ms(10);
  31. if (t > 50) /* 控制LED0闪烁, 提示程序运行状态 */
  32. {
  33. t = 0;
  34. LED0_TOGGLE();
  35. }
  36. }
  37. }
4.4  高级定时器输出比较模式实验(掌握)
4.4.1  高级定时器输出比较模式实验原理(掌握)

总结:PWM波周期或频率由ARR决定,占空比固定50%,相位由CCRx决定

4.4.2  高级定时器输出比较模式实验配置步骤(掌握)

4.4.3  ,编程实战:高级定时器输出比较模式实验(掌握)

tim.c

  1. #include "./BSP/LED/led.h"
  2. #include "tim.h"
  3. /**
  4. * @brief 高级定时器 TIMX 输出比较模式 初始化函数(使用输出比较模式)
  5. * @note
  6. * 配置高级定时器 TIMX 4 路输出比较模式 PWM 输出,实现 50%占空比,不同相位控制
  7. * 注意,本例程输出比较模式,每 2 个计数周期才能完成一个 PWM 输出,因此输出频率减半
  8. * 另外,我们还可以开启中断在中断里面修改 CCRx,从而实现不同频率/不同相位的控制
  9. * 但是我们不推荐这么使用,因为这可能导致非常频繁的中断,从而占用大量 CPU 资源
  10. *
  11. * 高级定时器的时钟来自 APB2, 而 PCLK2 = 72Mhz, 我们设置 PPRE2 不分频, 因此
  12. * 高级定时器时钟 = 72Mhz
  13. * 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
  14. * Ft=定时器工作频率,单位:Mhz
  15. * @param arr: 自动重装值。
  16. * @param psc: 时钟预分频数
  17. * @retval 无
  18. */
  19. TIM_HandleTypeDef g_timx_comp_pwm_handle;
  20. void atim_timx_comp_pwm_init(uint16_t arr, uint16_t psc)
  21. {
  22. TIM_OC_InitTypeDef timx_oc_comp_pwm = {0};
  23. TIM_OC_InitTypeDef tim_oc_handle = {0};
  24. g_timx_comp_pwm_handle.Instance = ATIM_TIMX_COMP; /* 定时器 x */
  25. g_timx_comp_pwm_handle.Init.Prescaler = psc ; /* 定时器分频 */
  26. g_timx_comp_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP;/* 递增计数 */
  27. g_timx_comp_pwm_handle.Init.Period = arr; /* 自动重装载值 */
  28. g_timx_comp_pwm_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; /* 使能影子寄存器 TIMx_ARR */
  29. HAL_TIM_OC_Init(&g_timx_comp_pwm_handle); /* 输出比较模式初始化 */
  30. timx_oc_comp_pwm.OCMode = TIM_OCMODE_TOGGLE; /* 比较输出模式翻转功能 */
  31. timx_oc_comp_pwm.Pulse = 250 - 1; /* 设置输出比较寄存器的值 */
  32. timx_oc_comp_pwm.OCPolarity = TIM_OCPOLARITY_HIGH;/* 输出比较极性为高 */
  33. HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &timx_oc_comp_pwm, \
  34. TIM_CHANNEL_1); /* 初始化定时器的输出比较通道 1 */
  35. /* CCR1 寄存器预装载使能 */
  36. __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_1);
  37. tim_oc_handle.Pulse = 500;
  38. HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &tim_oc_handle, \
  39. TIM_CHANNEL_2); /* 初始化定时器的输出比较通道 2 */
  40. /* CCR2 寄存器预装载使能 */
  41. __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_2);
  42. tim_oc_handle.Pulse = 750;
  43. HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &tim_oc_handle,TIM_CHANNEL_3); /* 初始化定时器的输出比较通道 3 */
  44. /* CCR3 寄存器预装载使能 */
  45. __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_3);
  46. tim_oc_handle.Pulse = 1000;
  47. HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &tim_oc_handle,
  48. TIM_CHANNEL_4); /* 初始化定时器的输出比较通道 4 */
  49. /* CCR4 寄存器预装载使能 */
  50. __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_4);
  51. HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_1);
  52. HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_2);
  53. HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_3);
  54. HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_4);
  55. }
  56. /**
  57. * @brief 定时器底层驱动,时钟使能,引脚配置
  58. 此函数会被 HAL_TIM_OC_Init()调用
  59. * @param htim:定时器句柄
  60. * @retval 无
  61. */
  62. void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
  63. {
  64. if (htim->Instance == ATIM_TIMX_COMP)
  65. {
  66. GPIO_InitTypeDef gpio_init_struct;
  67. ATIM_TIMX_COMP_CLK_ENABLE();
  68. ATIM_TIMX_COMP_CH1_GPIO_CLK_ENABLE();
  69. ATIM_TIMX_COMP_CH2_GPIO_CLK_ENABLE();
  70. ATIM_TIMX_COMP_CH3_GPIO_CLK_ENABLE();
  71. ATIM_TIMX_COMP_CH4_GPIO_CLK_ENABLE();
  72. gpio_init_struct.Pin = ATIM_TIMX_COMP_CH1_GPIO_PIN;
  73. gpio_init_struct.Mode = GPIO_MODE_AF_PP;
  74. gpio_init_struct.Pull = GPIO_NOPULL;
  75. gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  76. HAL_GPIO_Init(ATIM_TIMX_COMP_CH1_GPIO_PORT, &gpio_init_struct);
  77. gpio_init_struct.Pin = ATIM_TIMX_COMP_CH2_GPIO_PIN;
  78. HAL_GPIO_Init(ATIM_TIMX_COMP_CH2_GPIO_PORT, &gpio_init_struct);
  79. gpio_init_struct.Pin = ATIM_TIMX_COMP_CH3_GPIO_PIN;
  80. HAL_GPIO_Init(ATIM_TIMX_COMP_CH3_GPIO_PORT, &gpio_init_struct);
  81. gpio_init_struct.Pin = ATIM_TIMX_COMP_CH4_GPIO_PIN;
  82. HAL_GPIO_Init(ATIM_TIMX_COMP_CH4_GPIO_PORT, &gpio_init_struct);
  83. }
  84. }

mian.c

  1. int main(void)
  2. {
  3. uint8_t t = 0;
  4. HAL_Init(); /* 初始化HAL库 */
  5. sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  6. delay_init(72); /* 延时初始化 */
  7. usart_init(115200); /* 串口初始化为115200 */
  8. led_init(); /* LED初始化 */
  9. atim_timx_comp_pwm_init(1000 - 1, 72 - 1); /* 1Mhz 的计数频率 1Khz 的周期. */
  10. ATIM_TIMX_COMP_CH1_CCRX = 250 - 1; /* 通道 1 相位 25% */
  11. ATIM_TIMX_COMP_CH2_CCRX = 500 - 1; /* 通道 2 相位 50% */
  12. ATIM_TIMX_COMP_CH3_CCRX = 750 - 1; /* 通道 3 相位 75% */
  13. ATIM_TIMX_COMP_CH4_CCRX = 1000 - 1; /* 通道 4 相位 100% */
  14. while (1)
  15. {
  16. delay_ms(10);
  17. t++;
  18. if (t >= 20)
  19. {
  20. LED0_TOGGLE(); /* LED0(RED)闪烁 */
  21. t = 0;
  22. }
  23. }
  24. }
4.5  高级定时器互补输出带死区控制实验(掌握)
4.5.1  互补输出,还带死区控制,什么意思?(了解)

上图中,CH1 输出黄色的 PWM,它的互补通道 CH1N 输出绿色的 PWM。通过对比,可以 知道这两个 PWM 刚好是反过来的,CH1 的 PWM 为高电平期间,CH1N 的 PWM 则是低电平, 反之亦然,这就是互补输出。

4.5.2  带死区控制的互补输出应用之H桥(了解)

4.5.3  捕获/比较通道的输出部分(通道1至3)(熟悉)

4.5.4  死区时间计算(掌握)

4.5.5  刹车(断路)功能(熟悉)

4.5.6  高级定时器互补输出带死区控制实验配置步骤(掌握)

4.5.7  编程实战:高级定时器互补输出带死区控制实验(掌握)

tim.c

  1. #include "./BSP/LED/led.h"
  2. #include "tim.h"
  3. /* @brief 高级定时器 TIMX 互补输出 初始化函数(使用 PWM 模式 1
  4. * @note
  5. * 配置高级定时器 TIMX 互补输出, 一路 OCy 一路 OCyN, 并且可以设置死区时间
  6. *
  7. * 高级定时器的时钟来自 APB2, 而 PCLK2 = 72Mhz, 我们设置 PPRE2 不分频, 因此
  8. * 高级定时器时钟 = 72Mhz
  9. * 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
  10. * Ft=定时器工作频率,单位:Mhz
  11. *
  12. * @param arr: 自动重装值。
  13. * @param psc: 时钟预分频数
  14. * @retval 无
  15. */
  16. TIM_OC_InitTypeDef tim_oc_cplm_pwm;
  17. TIM_HandleTypeDef g_timx_cplm_pwm_handle;
  18. TIM_BreakDeadTimeConfigTypeDef g_sbreak_dead_time_config;
  19. void atim_timx_cplm_pwm_init(uint16_t arr, uint16_t psc)
  20. {
  21. GPIO_InitTypeDef gpio_init_struct = {0};
  22. TIM_OC_InitTypeDef tim_oc_cplm_pwm = {0};
  23. ATIM_TIMX_CPLM_CLK_ENABLE(); /* TIMx 时钟使能 */
  24. ATIM_TIMX_CPLM_CHY_GPIO_CLK_ENABLE(); /* 通道 X 对应 IO 口时钟使能 */
  25. ATIM_TIMX_CPLM_CHYN_GPIO_CLK_ENABLE(); /* 通道 X 互补通道对应 IO 口时钟使能 */
  26. ATIM_TIMX_CPLM_BKIN_GPIO_CLK_ENABLE(); /* 通道 X 刹车输入对应 IO 口时钟使能 */
  27. gpio_init_struct.Pin = ATIM_TIMX_CPLM_CHY_GPIO_PIN;
  28. gpio_init_struct.Mode = GPIO_MODE_AF_PP;
  29. gpio_init_struct.Pull = GPIO_PULLUP;
  30. gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH ;
  31. HAL_GPIO_Init(ATIM_TIMX_CPLM_CHY_GPIO_PORT, &gpio_init_struct);
  32. gpio_init_struct.Pin = ATIM_TIMX_CPLM_CHYN_GPIO_PIN;
  33. HAL_GPIO_Init(ATIM_TIMX_CPLM_CHYN_GPIO_PORT, &gpio_init_struct);
  34. gpio_init_struct.Pin = ATIM_TIMX_CPLM_BKIN_GPIO_PIN;
  35. HAL_GPIO_Init(ATIM_TIMX_CPLM_BKIN_GPIO_PORT, &gpio_init_struct);
  36. /* 重映射定时器 IO */
  37. __HAL_RCC_AFIO_CLK_ENABLE();
  38. __HAL_AFIO_REMAP_TIM1_ENABLE();
  39. g_timx_cplm_pwm_handle.Instance = ATIM_TIMX_CPLM; /* 定时器 x */
  40. g_timx_cplm_pwm_handle.Init.Prescaler = psc; /* 定时器预分频系数 */
  41. g_timx_cplm_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP;/* 递增计数 */
  42. g_timx_cplm_pwm_handle.Init.Period = arr; /* 自动重装载值 */
  43. /* CKD[1:0] = 10, tDTS = 4 * tCK_INT = Ft / 4 = 18Mhz */
  44. g_timx_cplm_pwm_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4;
  45. g_timx_cplm_pwm_handle.Init.AutoReloadPreload =\
  46. TIM_AUTORELOAD_PRELOAD_ENABLE; /* 使能影子寄存器 TIMx_ARR */
  47. HAL_TIM_PWM_Init(&g_timx_cplm_pwm_handle);
  48. tim_oc_cplm_pwm.OCMode = TIM_OCMODE_PWM1; /* PWM 模式 1 */
  49. tim_oc_cplm_pwm.OCPolarity = TIM_OCPOLARITY_LOW; /* OCy 低电平有效 */
  50. tim_oc_cplm_pwm.OCNPolarity = TIM_OCNPOLARITY_LOW; /* OCyN 低电平有效 */
  51. tim_oc_cplm_pwm.OCIdleState = TIM_OCIDLESTATE_SET; /* 当 MOE=0,OCx=1 */
  52. tim_oc_cplm_pwm.OCNIdleState = TIM_OCNIDLESTATE_SET;/* 当 MOE=0,OCxN=1 */
  53. HAL_TIM_PWM_ConfigChannel(&g_timx_cplm_pwm_handle, &tim_oc_cplm_pwm,
  54. ATIM_TIMX_CPLM_CHY);
  55. /* 设置死区参数,开启死区中断 */
  56. /* 运行模式的关闭输出状态 */
  57. g_sbreak_dead_time_config.OffStateRunMode = TIM_OSSR_DISABLE;
  58. /* 空闲模式的关闭输出状态 */
  59. g_sbreak_dead_time_config.OffStateIDLEMode = TIM_OSSI_DISABLE;
  60. g_sbreak_dead_time_config.LockLevel = TIM_LOCKLEVEL_OFF;/* 不用寄存器锁功能 */
  61. g_sbreak_dead_time_config.BreakState = TIM_BREAK_ENABLE;/* 使能刹车输入 */
  62. /* 刹车输入有效信号极性为高 */
  63. g_sbreak_dead_time_config.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  64. /* 使能 AOE 位,允许刹车结束后自动恢复输出 */
  65. g_sbreak_dead_time_config.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE;
  66. HAL_TIMEx_ConfigBreakDeadTime(&g_timx_cplm_pwm_handle,
  67. &g_sbreak_dead_time_config);
  68. /* 使能 OCy 输出 */
  69. HAL_TIM_PWM_Start(&g_timx_cplm_pwm_handle, ATIM_TIMX_CPLM_CHY);
  70. /* 使能 OCyN 输出 */
  71. HAL_TIMEx_PWMN_Start(&g_timx_cplm_pwm_handle, ATIM_TIMX_CPLM_CHY);
  72. }
  73. /**
  74. * @brief 定时器 TIMX 设置输出比较值 & 死区时间
  75. * @param ccr: 输出比较值
  76. * @param dtg: 死区时间
  77. * @arg dtg[7:5]=0xx 时, 死区时间 = dtg[7:0] * tDTS
  78. * @arg dtg[7:5]=10x 时, 死区时间 = (64 + dtg[6:0]) * 2 * tDTS
  79. * @arg dtg[7:5]=110 时, 死区时间 = (32 + dtg[5:0]) * 8 * tDTS
  80. * @arg dtg[7:5]=111 时, 死区时间 = (32 + dtg[5:0]) * 16 * tDTS
  81. * @note tDTS = 1 / (Ft / CKD[1:0]) = 1 / 18M = 55.56ns
  82. * @retval 无
  83. */
  84. void atim_timx_cplm_pwm_set(uint16_t ccr, uint8_t dtg)
  85. {
  86. g_sbreak_dead_time_config.DeadTime = dtg; /* 死区时间设置 */
  87. HAL_TIMEx_ConfigBreakDeadTime(&g_timx_cplm_pwm_handle,
  88. &g_sbreak_dead_time_config); /*重设死区时间*/
  89. __HAL_TIM_MOE_ENABLE(&g_timx_cplm_pwm_handle); /* MOE=1,使能主输出 */
  90. ATIM_TIMX_CPLM_CHY_CCRY = ccr; /* 设置比较寄存器 */
  91. }

tim.h

  1. #ifndef __TIM_H__
  2. #define __TIM_H__
  3. #include "./SYSTEM/sys/sys.h"
  4. #include "stm32f1xx_hal_gpio_ex.h"
  5. /* TIMX 互补输出模式 定义
  6. * 这里设置互补输出相关硬件配置, CHY 即正常输出, CHYN 即互补输出
  7. * 修改 CCRx 可以修改占空比.
  8. * 默认是针对 TIM1
  9. * 注意: 通过修改这些宏定义,可以支持 TIM1/TIM8 定时器, 任意一个 IO 口输出互补 PWM(前提是必须有
  10. 互补输出功能)
  11. */
  12. /* 输出通道引脚 */
  13. #define ATIM_TIMX_CPLM_CHY_GPIO_PORT GPIOE
  14. #define ATIM_TIMX_CPLM_CHY_GPIO_PIN GPIO_PIN_9
  15. #define ATIM_TIMX_CPLM_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); \
  16. }while(0) /* PE 口时钟使能 */
  17. /* 互补输出通道引脚 */
  18. #define ATIM_TIMX_CPLM_CHYN_GPIO_PORT GPIOE
  19. #define ATIM_TIMX_CPLM_CHYN_GPIO_PIN GPIO_PIN_8
  20. #define ATIM_TIMX_CPLM_CHYN_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE();\
  21. }while(0) /* PE 口时钟使能 */
  22. /* 刹车输入引脚 */
  23. #define ATIM_TIMX_CPLM_BKIN_GPIO_PORT GPIOE
  24. #define ATIM_TIMX_CPLM_BKIN_GPIO_PIN GPIO_PIN_15
  25. #define ATIM_TIMX_CPLM_BKIN_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE();\
  26. }while(0) /* PE 口时钟使能 */
  27. /* TIMX REMAP 设置
  28. * 因为 PE8/PE9/PE15, 默认并不是 TIM1 的复用功能脚, 必须开启完全重映射, 才可以将:
  29. TIM1_CH1->PE9; TIM1_CH1N->PE8; TIM1_BKIN->PE15;
  30. * 这样, PE8/PE9/PE15, 才能用作 TIM1CH1N/CH1/BKIN 功能.
  31. * 因此必须实现 ATIM_TIMX_CPLM_CHYN_GPIO_REMAP, 通过 sys_gpio_remap_set 函数设置重映射
  32. * 如果我们使用默认的复用功能输出, 则不用设置重映射, 是可以不需要该函数的! 根据具体需要来实现.
  33. */
  34. /* 互补输出使用的定时器 */
  35. #define ATIM_TIMX_CPLM TIM1
  36. #define ATIM_TIMX_CPLM_CHY TIM_CHANNEL_1
  37. #define ATIM_TIMX_CPLM_CHY_CCRY ATIM_TIMX_CPLM->CCR1
  38. #define ATIM_TIMX_CPLM_CLK_ENABLE() do{ __HAL_RCC_TIM1_CLK_ENABLE(); }while(0)
  39. void atim_timx_cplm_pwm_set(uint16_t ccr, uint8_t dtg);
  40. void atim_timx_cplm_pwm_init(uint16_t arr, uint16_t psc);
  41. #endif

main.c

  1. int main(void)
  2. {
  3. uint8_t t = 0;
  4. HAL_Init(); /* 初始化HAL库 */
  5. sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  6. delay_init(72); /* 延时初始化 */
  7. usart_init(115200); /* 串口初始化为115200 */
  8. led_init(); /* LED初始化 */
  9. atim_timx_cplm_pwm_init(1000 - 1, 72 - 1); /* 1Mhz 的计数频率 1Khz 的周期. */
  10. atim_timx_cplm_pwm_set(300, 100); /* 占空比:7:3, 死区时间 100*tDTS */
  11. while (1)
  12. {
  13. delay_ms(10);
  14. t++;
  15. if (t >= 20)
  16. {
  17. LED0_TOGGLE(); /* LED0(RED)闪烁 */
  18. t = 0;
  19. }
  20. }
  21. }
4.6  高级定时器PWM输入模式实验(掌握)
4.6.1  PWM输入模式工作原理(熟悉)

4.6.2  PWM输入模式时序(熟悉)

4.6.3  高级定时器PWM输入模式实验配置步骤(掌握)

4.6.4  编程实战:高级定时器PWM输入模式实验(掌握)

tim.c

  1. #include "./BSP/TIMER/atim.h"
  2. TIM_HandleTypeDef g_timx_pwmin_chy_handle; /* 定时器x句柄 */
  3. /* PWM输入状态(g_timxchy_cap_sta)
  4. * 0,没有成功捕获.
  5. * 1,已经成功捕获了
  6. */
  7. uint8_t g_timxchy_pwmin_sta = 0; /* PWM输入状态 */
  8. uint16_t g_timxchy_pwmin_psc = 0; /* PWM输入分频系数 */
  9. uint32_t g_timxchy_pwmin_hval = 0; /* PWM的高电平脉宽 */
  10. uint32_t g_timxchy_pwmin_cval = 0; /* PWM的周期宽度 */
  11. /* PWM输入模式 初始化函数,采样时钟频率为72Mhz,精度约13.8ns */
  12. void atim_timx_pwmin_chy_init(void)
  13. {
  14. TIM_SlaveConfigTypeDef slave_config = {0};
  15. TIM_IC_InitTypeDef tim_ic_pwmin_chy = {0};
  16. g_timx_pwmin_chy_handle.Instance = TIM8; /* 定时器8 */
  17. g_timx_pwmin_chy_handle.Init.Prescaler = 0; /* 定时器预分频系数 */
  18. g_timx_pwmin_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
  19. g_timx_pwmin_chy_handle.Init.Period = 65535; /* 自动重装载值 */
  20. HAL_TIM_IC_Init(&g_timx_pwmin_chy_handle);
  21. /* 从模式配置,IT1触发更新 */
  22. slave_config.SlaveMode = TIM_SLAVEMODE_RESET; /* 从模式:复位模式 */
  23. slave_config.InputTrigger = TIM_TS_TI1FP1; /* 定时器输入触发源:TI1FP1 */
  24. slave_config.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING; /* 上升沿检测 */
  25. slave_config.TriggerFilter = 0; /* 不滤波 */
  26. HAL_TIM_SlaveConfigSynchro(&g_timx_pwmin_chy_handle, &slave_config);
  27. /* IC1捕获:上升沿触发TI1FP1 */
  28. tim_ic_pwmin_chy.ICPolarity = TIM_ICPOLARITY_RISING; /* 上升沿检测 */
  29. tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_DIRECTTI; /* 选择输入端IC1映射到TI1 */
  30. tim_ic_pwmin_chy.ICPrescaler = TIM_ICPSC_DIV1; /* 不分频 */
  31. tim_ic_pwmin_chy.ICFilter = 0; /* 不滤波 */
  32. HAL_TIM_IC_ConfigChannel(&g_timx_pwmin_chy_handle, &tim_ic_pwmin_chy, TIM_CHANNEL_1);
  33. /* IC2捕获:下降沿触发TI1FP2 */
  34. tim_ic_pwmin_chy.ICPolarity = TIM_ICPOLARITY_FALLING; /* 下降沿检测 */
  35. tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_INDIRECTTI; /* 选择输入端IC2映射到TI1 */
  36. HAL_TIM_IC_ConfigChannel(&g_timx_pwmin_chy_handle, &tim_ic_pwmin_chy, TIM_CHANNEL_2);
  37. HAL_TIM_IC_Start_IT(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1);
  38. HAL_TIM_IC_Start(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2);
  39. }
  40. /* 定时器 输入捕获 MSP初始化函数 */
  41. void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
  42. {
  43. if(htim->Instance == TIM8)
  44. {
  45. GPIO_InitTypeDef gpio_init_struct = {0};
  46. __HAL_RCC_TIM8_CLK_ENABLE();
  47. __HAL_RCC_GPIOC_CLK_ENABLE();
  48. gpio_init_struct.Pin = GPIO_PIN_6;
  49. gpio_init_struct.Mode = GPIO_MODE_AF_PP;
  50. gpio_init_struct.Pull = GPIO_PULLDOWN;
  51. gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  52. HAL_GPIO_Init(GPIOC, &gpio_init_struct);
  53. /* TIM1/TIM8有独立的输入捕获中断服务函数 */
  54. HAL_NVIC_SetPriority(TIM8_CC_IRQn, 1, 3);
  55. HAL_NVIC_EnableIRQ(TIM8_CC_IRQn);
  56. }
  57. }
  58. /* 定时器8 输入捕获 中断服务函数,仅TIM1/TIM8有这个函数,其他普通定时器没有这个中断服务函数! */
  59. void TIM8_CC_IRQHandler(void)
  60. {
  61. HAL_TIM_IRQHandler(&g_timx_pwmin_chy_handle); /* 定时器共用处理函数 */
  62. }
  63. /* PWM输入模式 重新启动捕获 */
  64. void atim_timx_pwmin_chy_restart(void)
  65. {
  66. sys_intx_disable(); /* 关闭中断 */
  67. g_timxchy_pwmin_sta = 0; /* 清零状态,重新开始检测 */
  68. g_timxchy_pwmin_hval=0;
  69. g_timxchy_pwmin_cval=0;
  70. sys_intx_enable(); /* 打开中断 */
  71. }
  72. /* 定时器输入捕获中断处理回调函数 */
  73. void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
  74. {
  75. if(htim->Instance == TIM8)
  76. {
  77. if(g_timxchy_pwmin_sta == 0)
  78. {
  79. if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
  80. {
  81. g_timxchy_pwmin_hval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2) + 1 + 1;
  82. g_timxchy_pwmin_cval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1) + 1 + 1;
  83. g_timxchy_pwmin_sta = 1;
  84. }
  85. }
  86. }
  87. }

main.c

  1. #include "./SYSTEM/sys/sys.h"
  2. #include "./SYSTEM/usart/usart.h"
  3. #include "./SYSTEM/delay/delay.h"
  4. #include "./BSP/LED/led.h"
  5. #include "./BSP/TIMER/atim.h"
  6. #include "./BSP/TIMER/gtim.h"
  7. extern uint16_t g_timxchy_pwmin_sta; /* PWM输入状态 */
  8. extern uint16_t g_timxchy_pwmin_psc; /* PWM输入分频系数 */
  9. extern uint32_t g_timxchy_pwmin_hval; /* PWM的高电平脉宽 */
  10. extern uint32_t g_timxchy_pwmin_cval; /* PWM的周期宽度 */
  11. int main(void)
  12. {
  13. uint8_t t = 0;
  14. double ht, ct, f, tpsc;
  15. HAL_Init(); /* 初始化HAL库 */
  16. sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  17. delay_init(72); /* 延时初始化 */
  18. usart_init(115200); /* 串口初始化为115200 */
  19. led_init(); /* 初始化LED */
  20. gtim_timx_pwm_chy_init(10 - 1, 72 - 1);
  21. TIM3->CCR2 = 3;
  22. atim_timx_pwmin_chy_init();
  23. while (1)
  24. {
  25. delay_ms(10);
  26. t++;
  27. if (t >= 20) /*200ms输出一次结果,并闪烁LED0,提示程序运行 */
  28. {
  29. if (g_timxchy_pwmin_sta) /* 捕获了一次数据 */
  30. {
  31. printf("\r\n"); /* 输出空,另起一行 */
  32. printf("PWM PSC :%d\r\n", g_timxchy_pwmin_psc); /* 打印分频系数 */
  33. printf("PWM Hight:%d\r\n", g_timxchy_pwmin_hval); /* 打印高电平脉宽 */
  34. printf("PWM Cycle:%d\r\n", g_timxchy_pwmin_cval); /* 打印周期 */
  35. tpsc = ((double)g_timxchy_pwmin_psc + 1) / 72; /* 得到PWM采样时钟周期时间 */
  36. ht = g_timxchy_pwmin_hval * tpsc; /* 计算高电平时间 */
  37. ct = g_timxchy_pwmin_cval * tpsc; /* 计算周期长度 */
  38. f = (1 / ct) * 1000000; /* 计算频率 */
  39. printf("PWM Hight time:%.3fus\r\n", ht); /* 打印高电平脉宽长度 */
  40. printf("PWM Cycle time:%.3fus\r\n", ct); /* 打印周期时间长度 */
  41. printf("PWM Frequency :%.3fHz\r\n", f); /* 打印频率 */
  42. atim_timx_pwmin_chy_restart(); /* 重启PWM输入检测 */
  43. }
  44. LED1_TOGGLE(); /* LED1(GREEN)闪烁 */
  45. t = 0;
  46. }
  47. }
  48. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/767933
推荐阅读
相关标签
  

闽ICP备14008679号