一、定时器
- 在 STM32 中,定时器(Timer) 是一种硬件外设,常用于生成精确的时间延迟、测量时间间隔、PWM(脉宽调制)输出、捕获输入信号的时间、生成定时中断等。
- STM32 的定时器类型通常包括基础定时器(如 TIM6 和 TIM7)、通用定时器(如 TIM1、TIM2)、高级定时器等。
- RCC(Reset and Clock Control,复位与时钟控制)是 STM32 微控制器中的一个重要模块,它负责管理和控制系统的时钟和复位功能。
- Prescaler(预分频器): 用于降低定时器的输入时钟频率。
- Counter Period(计数周期):这是定时器计数器的自动重装载值(ARR)。定时器每次从 0 计数到该值时会产生一次溢出事件,从而触发中断或其他定时功能。
- 据此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 秒会产生一次溢出中断。
- 使用方法(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](https://i-blog.csdnimg.cn/direct/6fb3685e01974d8aab90897fa1b9da9f.png)
6. 配置LED的管脚
7. 在main函数中启动定时器HAL_TIM_Base_Start_IT(&htim5);
8. 然后在HAL_TIM_PeriodElapsedCallback处理你想要做的事情。 - 代码示例
控制灯一闪一闪的
#include "main.h"
TIM_HandleTypeDef htim5;
void SystemClock_Config(void);
static void MX_TIM5_Init(void);
static void MX_GPIO_Init(void);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_6|GPIO_PIN_7);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_TIM5_Init();
MX_GPIO_Init();
HAL_TIM_Base_Start_IT(&htim5);
while (1)
{
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
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;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL8;
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;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
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;
htim5.Init.Prescaler = 64000-1;
htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
htim5.Init.Period = 1000-1;
htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim5) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
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};
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8, GPIO_PIN_RESET);
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_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);
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
二、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. 这是时钟配置图
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b64f58b8c1064f1fad8b90282e4be246.png)
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);
HAL_Delay(100);
}
}
#include "main.h"
TIM_HandleTypeDef htim3;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM3_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
while (1)
{
for (int cmp = 0; cmp <= 100; cmp++)
{
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, cmp);
HAL_Delay(100);
}
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
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;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL8;
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;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_TIM3_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
htim3.Instance = TIM3;
htim3.Init.Prescaler = 640 - 1;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 100 - 1;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
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();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_MspPostInit(&htim3);
}
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}