一、基本定时器
定时器的本质
定时器本质是一个计数器,所以牵扯到的问题就是多久产生一次计数,计数到到多少会发生什么世事件,因此引出相应的寄存器来设置。
相关寄存器
PSC预分频器:对输入时钟TIMxCLK进行分频--->决定计数的时间
自动重装载寄存器(ARR):当计数器=ARR时,触发相应事件 ,他有自己对应的影子寄存器,预装载寄存器用于设置定时器的参数,比如设置自动重装载值等。而这些设置的参数需要从预装载寄存器加载到对应的影子寄存器,影子寄存器中的值才会真正对定时器的运行产生作用
ARPE使能有缓冲功能,预装载寄存器的值不会立即更新到影子寄存器,要等到发生更新事件(如计数器溢出)时,预装载寄存器的值才会被传送到影子寄存器中。当ARPE位未使能时,预装载寄存器中的值会立即传送到影子寄存器,也就是对预装载寄存器的修改会马上生效,直接影响定时器的运行参数
定时器溢出时间
简而言之就是多少次计数乘以一次计数的时间。
HAL库实现基本定时器
TIM_HandleTypeDef g_timx_handle;
void btim_timx_int_init(uint16_t arr, uint16_t psc)
{
g_timx_handle.Instance = TIM6;
g_timx_handle.Init.Prescaler = psc;
g_timx_handle.Init.Period = arr;
HAL_TIM_Base_Init(&g_timx_handle);
HAL_TIM_Base_Start_IT(&g_timx_handle);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6)
{
__HAL_RCC_TIM6_CLK_ENABLE();
HAL_NVIC_SetPriority(TIM6_IRQn, 1, 3);
HAL_NVIC_EnableIRQ(TIM6_IRQn);
}
}
void TIM6_IRQHandler(void)
{
HAL_TIM_IRQHandler(&g_timx_handle);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6)
{
LED0_TOGGLE();
}
}
二、通用定时器+PWM模式
通用定时器的模式和功能更多,但计数的本质不变
实验:通过定时器PWM模式实现呼吸灯
核心思想:当配置为复用推挽输出+PWM模式后,GPIO的电平变化完全由定时器硬件自动控制,脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空
比的信号。
过程
1.配置定时器 --HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle)--与HAL_TIM_Base_Init对应;
2.配置pwm通道
3.启用PWM
4.初始化GPIO为推挽复用模式
核心理解:定时器的时钟源经过预分频之后的信号决定了计数器多久自加一次,而arr的值决定了加到多少置零重新加,而ccr决定了占空比得多少,小于ccr的值为有效电平,大于为无效电平,如果是固定的占空比则亮度是一样,所以需要周期变化ccr来变化占空比达到呼吸灯的效果,所以需要修改CCR
思路一:在主循环改变CCR
思路二:通过定时器中断触发改变CCR
void Pwm_Timer_Init(void)
{
// 初始化定时器2
g_pwm_timer1.Instance = TIM2;
g_pwm_timer1.Init.Prescaler = 72 - 1; // 72M分频系数
g_pwm_timer1.Init.Period = 500 - 1; // ARR
g_pwm_timer1.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_PWM_Init(&g_pwm_timer1);
// 配置pwm通道
TIM_OC_InitTypeDef sConfigOC;
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = (500 - 1) / 2; // 初始化占空比为50% ---CCR
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
HAL_TIM_PWM_ConfigChannel(&g_pwm_timer1, &sConfigOC, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&g_pwm_timer1, TIM_CHANNEL_2);
HAL_TIM_Base_Start_IT(&g_pwm_timer1);
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
// Enable the TIM6 clock
__HAL_RCC_TIM2_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
// 初始化GPIOA1
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// Configure the NVIC for TIM6
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
HAL_NVIC_SetPriority(TIM2_IRQn, 1, 3);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
}
void TIM2_IRQHandler(void)
{
// Handle the timer interrupt here
HAL_TIM_IRQHandler(&g_pwm_timer1);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// Handle the timer period elapsed callback here
if(htim->Instance == TIM2)
{
static uint16_t ledrpwmval = 0;
static uint8_t dir = 1;
if (dir)ledrpwmval++;
else ledrpwmval--;
if (ledrpwmval > 300)dir = 0;
if (ledrpwmval == 0)dir = 1;
__HAL_TIM_SET_COMPARE(Pwm_Timer_Get_Handle(), TIM_CHANNEL_2, ledrpwmval);
}
}
TIM_HandleTypeDef* Pwm_Timer_Get_Handle(void)
{
return &g_pwm_timer1;
}