你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32实战用定时器实现呼吸灯经验分享

[复制链接]
攻城狮Melo 发布时间:2023-6-14 16:20
我们这次只使用一块STM32核心板并且基于上篇中实现的工程模板进行开发即可。开始之前,先介绍一下什么是PWM。

1、什么是PWM
脉冲宽度调制(Pulse Width Modulation)是一种通过数字信号控制模拟器件的方式,它通过产生不同占空比(Duty Cycle)的矩形波来在输出端等效出不同电压值的控制效果,即对输出的脉冲宽度(占空比)进行调制,实现其对模拟器件的控制。下图展示了三种不同占空比PWM波的时间-幅度图:


微信图片_20230614161846.png


PWM的输出等效电压计算公式为:


微信图片_20230614161842.png


假设上述三种PWM波的VCC为5V,则上述三种等效输出电压分别为1.5V、2.5V、3.5V,即使用数字信号实现了模拟控制。




2、STM32定时器
从官网上或使用CubeMX查询可知STM32F103RCT6属于大容量产品,共计有8个16位定时器,其中定时器1与定时器8为高级定时器;定时器2、3、4、5为通用定时器;定时器6、7为基本定时器。高级定时器与基本定时器都各有4个通道可以用来实现输入捕获或生成PWM等功能,同时高级定时器还具有输出互补PWM、刹车控制等功能。而基本定时器可以实现定时产生中断的基本功能。
本次使用的LED连接在PA8上,查询F1的数据手册可知其可被复用为TIM1_CH1,即定时器1的输出通道1,可以使用该通道生成一个PWM。再使用定时器2生成一个周期性中断,改变PWM的占空比,生成一个占空比变化的PWM波,同时学**定时器的使用方法。

在HAL库中基本每个.C文件都对应了一个外设,里面封装了对该外设的各种操作函数,在文件中还有详细的注释描述其使用方法.例如定时器操作的源码就在stm32f1xx_hal_tim.c文件中,按照文件开始的注释中的说明,定时器或PWM生成的基本步骤为:
  • 根据用途调用HAL_TIM_Base_MspInit()或HAL_TIM_PWM_MspInit()等函数进行初始化
  • 使用__HAL_RCC_TIMx_CLK_ENABLE()使能定时器对应的时钟,设置使用的IO
  • 根据用途调用HAL_TIM_PWM_ConfigChannel()等进行进一步设置
  • 调用HAL_TIM_Base_Start()或HAL_TIM_PWM_Start()启动定时器

3、代码编写
为了方便后续管理,我将GPIO的部分与定时器的部分分开,并且开启定时器2的中断功能。1、GPIO部分负责初始化PA8端口,并设置为功能复用。gpio.c与gpio.h内容如下:
  1. // gpio.c
  2. #include "gpio.h"

  3. int GPIOInit(void)
  4. {
  5.   GPIO_InitTypeDef GPIO_InitStruct = {0};
  6.   __HAL_RCC_GPIOA_CLK_ENABLE();

  7.   GPIO_InitStruct.Pin = GPIO_PIN_8;
  8.   GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  9.   GPIO_InitStruct.Pull = GPIO_PULLUP;
  10.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  11.   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  12.   return 0;
  13. }
复制代码
  1. // gpio.h
  2. #ifndef __GPIO__H__
  3. #define __GPIO__H__

  4. #ifdef __cplusplus
  5. extern "C" {
  6. #endif

  7. #include "stm32f1xx_hal.h"

  8. int GPIOInit(void);

  9. #ifdef __cplusplus
  10. }
  11. #endif

  12. #endif
复制代码


2、定时器部分负责初始化定时器1与定时器2,设定的频率分别为1kHz与100Hz,并且设置定时器1输出PWM的占空比。timer.c与timer.h内容如下:
  1. // timer.c
  2. #include "timer.h"

  3. TimerDef timer1, timer2;

  4. int TimerInit(void)
  5. {
  6.     TimerDef *tmpTim = &timer1;
  7.     /* 1kHz */
  8.     tmpTim->TIM_Handle.Instance = TIM1;
  9.     tmpTim->TIM_Handle.Init.Prescaler = 72 - 1;
  10.     tmpTim->TIM_Handle.Init.Period = 1000;

  11.     __HAL_RCC_TIM1_CLK_ENABLE();
  12.     HAL_TIM_PWM_Init(&tmpTim->TIM_Handle);

  13.     tmpTim = &timer2;
  14.     /* 100Hz */
  15.     tmpTim->TIM_Handle.Instance = TIM2;
  16.     tmpTim->TIM_Handle.Init.Prescaler = 72 - 1;
  17.     tmpTim->TIM_Handle.Init.Period = 10000;

  18.     __HAL_RCC_TIM2_CLK_ENABLE();

  19.     HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
  20.     HAL_NVIC_EnableIRQ(TIM2_IRQn);

  21.     HAL_TIM_Base_Init(&tmpTim->TIM_Handle);
  22.     HAL_TIM_Base_Start_IT(&tmpTim->TIM_Handle);

  23.     return 0;
  24. }

  25. /**
  26. * \brief Set the PWM config and starts the PWM output.
  27. * \param handle Timer handle
  28. * \param channel the TIM Channel
  29. * \param duty the duty of PWM
  30. * \return status
  31. */
  32. int TimerSetPulse(TimerDef *handle, uint32_t channel, float duty)
  33. {
  34.     int t = handle->TIM_Handle.Init.Period * duty;
  35.     handle->TIM_OC_Handle.Pulse = t;
  36.     handle->TIM_OC_Handle.OCMode = TIM_OCMODE_PWM1;

  37.     HAL_TIM_PWM_ConfigChannel(&handle->TIM_Handle, &handle->TIM_OC_Handle, channel);
  38.     HAL_TIM_PWM_Start(&handle->TIM_Handle, channel);
  39.     return 0;
  40. }
复制代码
  1. // timer.h
  2. #ifndef __TIMER__H__
  3. #define __TIMER__H__

  4. #ifdef __cplusplus
  5. extern "C" {
  6. #endif

  7. #include "stm32f1xx_hal.h"

  8. typedef struct TimerDef
  9. {
  10.     TIM_HandleTypeDef TIM_Handle;
  11.     TIM_OC_InitTypeDef TIM_OC_Handle;

  12. }TimerDef;

  13. int TimerInit(void);
  14. int TimerSetPulse(TimerDef *handle, uint32_t channel, float duty);

  15. extern TimerDef timer1, timer2;

  16. #ifdef __cplusplus
  17. }
  18. #endif

  19. #endif
复制代码


3、定时器2中断函数:因为上面开启了定时器2中断,所以需要自行实现中断函数。这里在中断函数里每次增加PWM的1%占空比,增加到100%后变为每次减小1%,减小到0后再增加,如此往复。
  1. void TIM2_IRQHandler(void)
  2. {
  3.     static int duty = 0;
  4.     static int inc = 1;

  5.     /* HAL库通用定时器中断处理函数 */
  6.     HAL_TIM_IRQHandler(&timer2.TIM_Handle);

  7.     duty += inc;
  8.     if((duty >= 100) || (duty <= 0))
  9.     {   /* 当占空比到100或0时改变变化方向 */
  10.         inc = -inc;
  11.     }

  12.     TimerSetPulse(&timer1, TIM_CHANNEL_1, duty / 100.0);
  13. }
复制代码


4、总结
至此我们就实现了呼吸灯代码的编写,我们使用了TIM1产生PWM驱动LED,使用TIM2产生中断修改PWM的占空比,让LED的亮度发生变化。

微信图片_20230614161837.gif

通过示波器可以直观的看到该PWM的占空比是时刻在变化的,本期代码可以在文后网址中下载。下期我们来聊聊串口的使用,扫一扫下方二维码关注我,我们下期再见。

微信图片_20230614161636.gif



转载自: 嵌入式技术栈
如有侵权请联系删除


收藏 评论0 发布时间:2023-6-14 16:20

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版