STM32使用定时器、PWM波

一、定时器

  • 在 STM32 中,定时器(Timer) 是一种硬件外设,常用于生成精确的时间延迟、测量时间间隔、PWM(脉宽调制)输出、捕获输入信号的时间、生成定时中断等。
  • STM32 的定时器类型通常包括基础定时器(如 TIM6 和 TIM7)、通用定时器(如 TIM1、TIM2)、高级定时器等。
  • RCC(Reset and Clock Control,复位与时钟控制)是 STM32 微控制器中的一个重要模块,它负责管理和控制系统的时钟和复位功能。
  1. Prescaler(预分频器): 用于降低定时器的输入时钟频率。
  2. Counter Period(计数周期):这是定时器计数器的自动重装载值(ARR)。定时器每次从 0 计数到该值时会产生一次溢出事件,从而触发中断或其他定时功能。
  3. 据此1,2,可以算出多少时间产生溢出事件T = (Prescaler+1)* (Period +1)/TIMClk
设APB1 时钟(定时器 TIM5 的时钟)频率为 64 MHz,现在我们来看看为什么选择了这两个值:
Prescaler = 64000 - 1(STM32 的定时器会从 0 开始,故减1)
这个设置的意思是将 64 MHz 的时钟频率预分频 64000 次。
预分频后的定时器时钟频率为:
定时器时钟频率=64MHz/64000=1kHz
也就是说,现在定时器每秒计数 1000 次,每次计数的时间间隔为 1 毫秒。


Counter Period = 1000 - 1(定时器会从 0 计数到这个值)
计数周期设置为 1000,也就是说,定时器会从 0 计数到 999(总共 1000 个计数),然后产生一次溢出事件。
由于定时器时钟频率现在为 1 kHz,每次计数的时间间隔是 1 毫秒,因此计数 1000 次的总时间为:
1000×1ms=1秒
这意味着,定时器每经过 1 秒会产生一次溢出中断。
  1. 使用方法(TIM5)
    1). 使用TIM5的内部时钟源。
    2). 并设置Prescaler 为64000-1和Counter Period为1000-1
    3). 在NIVC Interrupt Table中允许TIM5 global Interrupt 中断使能
    4).RCC的High-Speed Clock的模式选择Crystal/Ceramic pesaonator,可以提供比内部振荡器更精确、稳定的时钟信号。
    5. 并时钟配置如下图
    ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/86a4002aa9b0450780d32932c973872f.png
    6. 配置LED的管脚
    7. 在main函数中启动定时器HAL_TIM_Base_Start_IT(&htim5);
    8. 然后在HAL_TIM_PeriodElapsedCallback处理你想要做的事情。
  2. 代码示例
控制灯一闪一闪的
#include "main.h"  // 包含主头文件,包含STM32 HAL库的声明

TIM_HandleTypeDef htim5;  // 声明TIM5句柄

void SystemClock_Config(void);  // 声明系统时钟配置函数
static void MX_TIM5_Init(void);  // 声明TIM5初始化函数
static void MX_GPIO_Init(void);  // 声明GPIO初始化函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_6|GPIO_PIN_7);  // 定时器中断回调,切换GPIOC引脚6和7的电平
}

int main(void)
{
  HAL_Init();  // 初始化HAL库
  SystemClock_Config();  // 配置系统时钟
  MX_TIM5_Init();  // 初始化TIM5定时器
  MX_GPIO_Init();  // 初始化GPIO引脚
  HAL_TIM_Base_Start_IT(&htim5);  // 启动TIM5定时器并使能中断

  while (1)  // 无限循环
  {
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};  // RCC时钟结构体,用于配置振荡器
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};  // RCC时钟结构体,用于配置总线时钟

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;  // 配置使用HSE外部高频振荡器
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;  // 打开HSE振荡器
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;  // HSE预分频设置为1
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;  // 启用内部高频振荡器HSI
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;  // 启用PLL(锁相环)模块
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;  // 设置PLL时钟源为HSE
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL8;  // PLL倍频系数设置为8
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)  // 配置时钟并检查是否成功
  {
    Error_Handler();  // 如果配置失败,进入错误处理
  }

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;  // 配置HCLK、SYSCLK、PCLK1和PCLK2
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;  // 使用PLL输出作为系统时钟源
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;  // AHB时钟设置为不分频
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;  // APB1时钟分频设置为2
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;  // APB2时钟设置为不分频

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)  // 配置时钟并检查是否成功
  {
    Error_Handler();  // 如果配置失败,进入错误处理
  }
}

static void MX_TIM5_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};  // 时钟源配置结构体
  TIM_MasterConfigTypeDef sMasterConfig = {0};  // 主从模式配置结构体

  htim5.Instance = TIM5;  // 指定使用TIM5定时器
  htim5.Init.Prescaler = 64000-1;  // 预分频器设置为64000-1,即每64MHz时钟周期计数一次
  htim5.Init.CounterMode = TIM_COUNTERMODE_UP;  // 设置计数模式为向上计数
  htim5.Init.Period = 1000-1;  // 自动重装载值为1000-1,计数达到该值时溢出
  htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;  // 时钟分频设置为1
  htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;  // 禁用自动重装载预加载
  if (HAL_TIM_Base_Init(&htim5) != HAL_OK)  // 初始化TIM5并检查是否成功
  {
    Error_Handler();  // 如果初始化失败,进入错误处理
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;  // 设置TIM5时钟源为内部时钟
  if (HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig) != HAL_OK)  // 配置时钟源并检查是否成功
  {
    Error_Handler();  // 如果配置失败,进入错误处理
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;  // 设置主输出触发模式为复位
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;  // 禁用主从模式
  if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK)  // 同步配置主模式
  {
    Error_Handler();  // 如果配置失败,进入错误处理
  }
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};  // GPIO初始化结构体

  __HAL_RCC_GPIOD_CLK_ENABLE();  // 使能GPIOD时钟
  __HAL_RCC_GPIOC_CLK_ENABLE();  // 使能GPIOC时钟

  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8, GPIO_PIN_RESET);  // 将GPIOC的6、7、8号引脚设置为低电平

  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8;  // 配置GPIOC的6、7、8号引脚
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  // 设置为推挽输出模式
  GPIO_InitStruct.Pull = GPIO_NOPULL;  // 不使用上拉或下拉电阻
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;  // 设置引脚速度为低速
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);  // 初始化GPIOC引脚
}

void Error_Handler(void)
{
  __disable_irq();  // 禁用中断
  while (1)  // 进入无限循环表示错误
  {
  }
}

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif /* USE_FULL_ASSERT */

二、PWM

  • PWM(脉宽调制,Pulse Width Modulation)是一种通过控制信号的占空比来调节能量传输的方法。它通常用于控制电机速度、LED亮度以及其他需要精确调节输出功率的场景。
  • 占空比:占空比是指信号在一个周期高电平持续的时间与整个周期时间的比率。占空比以百分比表示,100%意味着信号一直保持高电平,0%则表示信号一直保持低电平。
  • 频率:(Frequency):PWM 信号的频率是指信号周期的重复速率,通常以赫兹 (Hz) 为单位。频率高,信号变化快。选择适当的频率取决于具体应用,比如电机驱动通常使用较低频率,而 LED 调光则需要较高频率来避免人眼察觉到闪烁。
  • 在STM32等嵌入式系统中,通常可以利用定时器(Timers)来生成PWM信号。STM32的定时器可以配置成PWM模式,通过改变比较寄存器的值来调整占空比,从而生成不同的PWM信号。
  • 使用方法
    1. 因为PWM波需要时钟驱动,故此先配置RCC:在high SPeed Clock中选择Crystal/Ceramic模式。
    2. 这是时钟配置图
    在这里插入图片描述
    3. 对TIM3进行配置 :
    1. 采用内部时钟源
    2. 采用渠道一:PWM Genernation CH1
    3. 配置Prescaler为640-1和Counter Period 为100-1;64M/640*100 =1
    4. 将PC6管脚设置为TIM3_CH1,使PWM波从PC6输出,PC6连接LED灯,如此,以便可以看见呼吸灯效果。
    5. 在main函数中开启定时器的PWM输出,并设置为定时器3的通道1。
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
    6.	 动态的设置PWM的占空比,使用函数为  `__HAL_TIM_SET_COMPARE();`
while (1)
  {
	  for(int cmp=0;cmp<=100;cmp++)
	  {
		  __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,cmp);
		  //占空比:定义在间断、连续或短时工作制中,激励时间与整个周期时间之比。
		  //cmp/ARR(Counter Period的值)=占空比
		  HAL_Delay(100);//HAL_Delay() 的作用是为每次占空比调整后提供0.1秒的延时,这样你可以看到 PWM 信号的变化(占空比逐渐增加)。
	  }
  }
  • 代码示例
#include "main.h"

// 定义TIM3的句柄
TIM_HandleTypeDef htim3;

// 函数声明
void SystemClock_Config(void);         // 系统时钟配置函数
static void MX_GPIO_Init(void);        // GPIO初始化函数
static void MX_TIM3_Init(void);        // TIM3初始化函数

int main(void)
{
  // 初始化HAL库,设置系统时钟,初始化Flash接口和SysTick
  HAL_Init();

  // 配置系统时钟
  SystemClock_Config();

  // 初始化GPIO
  MX_GPIO_Init();

  // 初始化TIM3
  MX_TIM3_Init();
  
  // 开启TIM3的PWM输出,通道1(即启动PWM)
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);

  // 无限循环,控制PWM占空比
  while (1)
  {
    // 调整占空比从0%到100%
    for (int cmp = 0; cmp <= 100; cmp++)
    {
      // 设置PWM占空比,cmp表示占空比值
      __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, cmp);
      // 延迟100ms,给变化提供可见效果
      HAL_Delay(100);
    }
  }
}

// 配置系统时钟,设置HSE(外部高速时钟)为系统时钟源,并通过PLL倍频
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  // 配置HSE为外部时钟源,启用PLL倍频,倍频系数为8
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;        // 内部时钟保持开启,作为备选
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;   // 选择HSE作为PLL时钟源
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL8;           // PLL倍频系数为8
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)   // 配置振荡器并检查错误
  {
    Error_Handler();
  }

  // 配置系统时钟为PLL输出,AHB、APB1和APB2的分频因子
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                              | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;  // 系统时钟源为PLL输出
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;         // AHB时钟不分频
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;          // APB1时钟分频为2
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;          // APB2时钟不分频

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)   // 配置系统时钟并检查错误
  {
    Error_Handler();
  }
}

// 初始化TIM3定时器,配置PWM模式
static void MX_TIM3_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};     // 定时器时钟源配置结构体
  TIM_MasterConfigTypeDef sMasterConfig = {0};         // 定时器主从模式配置结构体
  TIM_OC_InitTypeDef sConfigOC = {0};                  // 输出比较配置结构体

  // 基本定时器配置
  htim3.Instance = TIM3;                               // TIM3定时器
  htim3.Init.Prescaler = 640 - 1;                      // 预分频器,时钟频率被分频为640
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;         // 计数模式为向上计数
  htim3.Init.Period = 100 - 1;                         // 自动重载寄存器(ARR)的值,决定PWM频率
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;   // 时钟分频为1
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;  // 关闭自动重载预装载
  
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)             // 初始化定时器并检查错误
  {
    Error_Handler();
  }

  // 配置TIM3的时钟源为内部时钟
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }

  // 初始化PWM功能
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }

  // 配置定时器主从模式
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

  // 配置PWM输出模式
  sConfigOC.OCMode = TIM_OCMODE_PWM1;                  // PWM模式1
  sConfigOC.Pulse = 0;                                 // 初始比较值为0
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;          // PWM输出高电平有效
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;           // 禁用快速模式
  
  // 配置TIM3通道1为PWM输出
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }

  // 配置后处理函数,初始化完通道后调用
  HAL_TIM_MspPostInit(&htim3);
}

// 初始化GPIO引脚,启用GPIO时钟
static void MX_GPIO_Init(void)
{
  __HAL_RCC_GPIOD_CLK_ENABLE();   // 启用GPIOD的时钟
  __HAL_RCC_GPIOC_CLK_ENABLE();   // 启用GPIOC的时钟
}

// 错误处理函数,进入无限循环
void Error_Handler(void)
{
  __disable_irq();    // 禁用所有中断
  while (1)
  {
    // 无限循环,用于指示错误发生
  }
}

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值