Files
CHJ/user/MCU/lhl_timer.c
2026-03-20 21:19:53 +08:00

359 lines
14 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "../main/SystemInclude.h"
/*-=====================================================================================================
//TIMER1
-=====================================================================================================*/
/**------------------------------------------------------------------------
* @brief 启动TIM1更新中断模式周期定时器
* @note 时钟源为APB2_CLK函数自动计算预分频器使定时器周期接近1ms的整数倍。
* 实际周期通过内部公式 (APB2_CLK/16000) 分频后ARR = 16 * uploadCounter 实现。
* uploadCounter 范围为 1~4000ms超过范围将被自动限幅。
* @param uploadCounter: 期望的定时周期,单位:毫秒 (1~4000)
* @example: StartTimer1_UIE(10); // 启动10ms周期定时器
**/
void StartTimer1_UIE(u16 uploadCounter)
{
if(uploadCounter>4000)uploadCounter = 4000;
if(uploadCounter<= 0)uploadCounter = 1;
u32 APB2_CLK = LHL_RCC_GetPCLK2Freq();
u32 prescaler = (APB2_CLK / 16000UL) - 1; //固定分频系数 (APB1_CLK / 32000UL) - 1;
u32 period =(u32)(16UL * uploadCounter);//动态ARR计数 (uint32_t)(32000UL * uploadCounter) / 1000UL;
TIM_InitTypeDef TIM_InitStructure;
TIM_InitStructure.Period = period;
TIM_InitStructure.Prescaler = prescaler;
TIM_InitStructure.ClockDivision = TIM_CKD_DIV1;
TIM_InitStructure.CounterMode = TIM_COUNTERMODE_UP;
TIM_InitStructure.AutoReloadPreload = ENABLE;
LHL_TIM_Init(TIM1, &TIM_InitStructure);
/*使能TIM1更新中断UIE */
LHL_TIM_ITConfig(TIM1, TIM_IT_UPDATE, ENABLE);
NVIC_EnableIRQ(TIM1_IRQn);
NVIC_SetPriority(TIM1_IRQn,1);
LHL_TIM_Start(TIM1);
}
/**------------------------------------------------------------------------
* @brief 修改TIM1更新中断模式的定时周期
* @note 此函数仅修改自动重装载寄存器ARR不改变预分频器。因此只能在定时器运行中
* 动态调整周期但需确保新的周期值在合理范围内。函数会自动重置计数器CNT
* 并清除更新标志,避免立即产生更新事件。
* @param uploadCounter: 新的定时周期,单位:毫秒 (1~4000)
* @example: ResetTimer1_UIE(20); // 将定时器周期改为20ms
**/
void ResetTimer1_UIE( u16 uploadCounter )
{
// 参数检查
if(uploadCounter>4000)uploadCounter = 4000;
if(uploadCounter<= 0)uploadCounter = 1;
// 计算新ARR的值
u32 period =(u32)(16UL * uploadCounter); //PSC不变ARR计算与StartTimer1相同
TIM1->CNT = 0; //先重置计数器 再设置ARR避免立即溢出
LHL_TIM_ClearPending(TIM1, TIM_IT_UPDATE);//当前CNT已经接近或超过ARR这里可能产生更新事件 所以需要先清除标志
TIM1->ARR = (u16)period ; //修改ARR 如果PSC需要改变必须停止定时器
LHL_TIM_Start(TIM1); //确保定时器启用
}
/**------------------------------------------------------------------------
* @brief 启动TIM1比较模式定时器输出比较无电平输出
* @note 时钟源为APB2_CLK预分频器固定为251自动重装载值固定为64000。
* 因此计数器时钟频率为 APB2_CLK / (251+1) ≈ 16kHz计数周期为 64000/16kHz = 4s。
* 比较值 uploadCounter 应在 1~64000 之间,对应比较时刻为 uploadCounter / 16kHz 毫秒。
* 超出范围将被限幅。
* @param uploadCounter: 比较值 (1~64000),需换算为目标时间 t(ms) = uploadCounter / 16
* @example: StartTimer1_CC1(320); // 启动比较定时器比较值320对应20ms
**/
void StartTimer1_CC1(u16 uploadCounter)
{
if(uploadCounter>64000)uploadCounter = 64000;
if(uploadCounter<= 0)uploadCounter = 1;
TIM_InitTypeDef TIM_InitStructure;
// TIM_InitStructure.Period = 65535;
// TIM_InitStructure.Prescaler = 244;
TIM_InitStructure.Period = 64000;
TIM_InitStructure.Prescaler = 251;
TIM_InitStructure.ClockDivision = TIM_CKD_DIV1;
TIM_InitStructure.CounterMode = TIM_COUNTERMODE_UP;
TIM_InitStructure.AutoReloadPreload = DISABLE;
LHL_TIM_Init(TIM1, &TIM_InitStructure);
TIM1->CCR1 = uploadCounter;
TIM1->CNT = 0;
LHL_TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);/* 使能TIM1比较中断CC1 */
NVIC_EnableIRQ(TIM1_IRQn);
NVIC_SetPriority(TIM1_IRQn, 1);
LHL_TIM_Start(TIM1); /* 运行TIM1 */
}
/**------------------------------------------------------------------------
* @brief 修改TIM1比较模式的比较值不重置计数器
* @note 此函数仅更新比较寄存器CCR1不清除计数器CNT用于动态调整下一次比较时刻。
* 适合实现可变周期或相位控制。函数同时清除比较中断标志,确保新比较值生效。
* @param uploadCounter: 新的比较值 (1~64000)
* @example: ResetTimer1_CC1(327 + TIM1->CNT); // 在当前计数器值基础上再延时约20ms触发中断
**/
void ResetTimer1_CC1( u16 uploadCounter )
{
TIM1->CCR1 = uploadCounter;
TIM1->SR = (uint16_t)~TIM_IT_CC1;
LHL_TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);
}
/**------------------------------------------------------------------------
* @brief 停止TIM1定时器并禁用所有中断
* @note 函数停止定时器计数禁用TIM1的所有中断更新、比较1/2、触发
* 清除相应中断标志并关闭NVIC中TIM1的中断使能。
* @param 无
* @example: StopTimer1(); // 停止TIM1
**/
void StopTimer1(void)
{
LHL_TIM_Stop(TIM1);
NVIC_DisableIRQ( TIM1_IRQn );
LHL_TIM_ClearFlag(TIM1, TIM_IT_UPDATE|TIM_IT_CC1|TIM_IT_CC2|TIM_IT_TRIGGER);
LHL_TIM_ITConfig( TIM1, TIM_IT_UPDATE|TIM_IT_CC1|TIM_IT_CC2|TIM_IT_TRIGGER, DISABLE );
}
/*-=====================================================================================================
//TIMER2
-=====================================================================================================*/
void StartTimer2_UIE(u16 uploadCounter)
{
if(uploadCounter>4000)uploadCounter = 4000;
if(uploadCounter<= 0)uploadCounter = 1;
u32 APB1_CLK = LHL_RCC_GetPCLK1Freq();
u32 prescaler = (APB1_CLK / 16000UL) - 1; //固定分频系数 (APB1_CLK / 32000UL) - 1;
u32 period =(u32)(16UL * uploadCounter);//动态ARR计数 (uint32_t)(32000UL * uploadCounter) / 1000UL;
TIM_InitTypeDef TIM_InitStructure;
TIM_InitStructure.Period = period;
TIM_InitStructure.Prescaler = prescaler;
TIM_InitStructure.ClockDivision = TIM_CKD_DIV1;
TIM_InitStructure.CounterMode = TIM_COUNTERMODE_UP;
TIM_InitStructure.AutoReloadPreload = ENABLE;
LHL_TIM_Init(TIM2, &TIM_InitStructure);
/*使能TIM1更新中断UIE */
LHL_TIM_ITConfig(TIM2, TIM_IT_UPDATE, ENABLE);
NVIC_EnableIRQ(TIM2_IRQn);
NVIC_SetPriority(TIM2_IRQn, 1);
LHL_TIM_Start(TIM2);
}
void ResetTimer2_UIE(u16 uploadCounter)
{
// 参数检查
if(uploadCounter>4000)uploadCounter = 4000;
if(uploadCounter<= 0)uploadCounter = 1;
// 计算新ARR的值
u32 period =(u32)(16UL * uploadCounter); //PSC不变ARR计算与StartTimer1相同
TIM2->CNT = 0; //先重置计数器 再设置ARR避免立即溢出
LHL_TIM_ClearPending(TIM2, TIM_IT_UPDATE);//当前CNT已经接近或超过ARR这里可能产生更新事件 所以需要先清除标志
TIM2->ARR = (u16)period ; //修改ARR 如果PSC需要改变必须停止定时器
LHL_TIM_Start(TIM2); //确保定时器启用
}
void StopTimer2(void)
{
LHL_TIM_Stop(TIM2);
NVIC_DisableIRQ( TIM2_IRQn );
LHL_TIM_ClearFlag(TIM2, TIM_IT_UPDATE|TIM_IT_CC1|TIM_IT_CC2|TIM_IT_TRIGGER);
LHL_TIM_ITConfig( TIM2, TIM_IT_UPDATE|TIM_IT_CC1|TIM_IT_CC2|TIM_IT_TRIGGER, DISABLE );
}
/*-=====================================================================================================
//PWM输出定时器
-=====================================================================================================*/
/**------------------------------------------------------------------------
* @brief PWM输出定时器
* @note 时钟源APB2_CLK
* @param uploadCounter: PWM期望的周期单位Hz 范围:(1~400)
duty :PWM占空比 范围:(0-100)
* @example: StartTimer1_PWM(10,50); // 启动10Hz(100ms)周期定时器,PWM输出占空比50%
StartTimer1_PWM(400,50); //启动400Hz(2.5ms)周期定时器,PWM输出占空比50%
**/
void StartTimer1_PWM(u16 freq_hz , u16 duty)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_InitTypeDef TIM_InitStructure;
TIM_PWMInitTypeDef TIM_PWMInitStructure;
u32 APB2_CLK = LHL_RCC_GetPCLK2Freq();
u16 period =10000; //固定ARR计数
u32 prescaler = (APB2_CLK /(period*freq_hz) )-1;
u16 dutyValue = period*duty/100;
/* 1. TIM1 通道1 GPIO复用配置 */
GPIO_InitStructure.Pin = GPIO_PIN_5; // P0.5->TIM1 CHANNEL1
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Current = GPIO_CURRENT_16mA;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.SchmittTrigger = ENABLE;
GPIO_InitStructure.Alternate = GPIO0_5_AF_TIM1_CH1;
LHL_GPIO_Init(pGPIO0, &GPIO_InitStructure);
/* 2. TIM1 时基配置 */
TIM_InitStructure.Prescaler = prescaler; // 设置定时器时钟
TIM_InitStructure.Period = period-1; // 设置定时器周期
TIM_InitStructure.ClockDivision = TIM_CKD_DIV1;
TIM_InitStructure.CounterMode = TIM_COUNTERMODE_UP;
TIM_InitStructure.AutoReloadPreload = ENABLE; // 开启ARR预加载
LHL_TIM_Init(TIM1, &TIM_InitStructure);
/* 3. TIM1 通道1输出PWM配置 */
TIM_PWMInitStructure.Pulse = dutyValue; // 设置占空比
TIM_PWMInitStructure.PWMMode = TIM_PWMMODE_PWM1; // PWM模式1
TIM_PWMInitStructure.PWMPolarity = TIM_PWMPOLARITY_ACTIVE_HIGH; // PULSE期间输出有效电平
TIM_PWMInitStructure.PWMPreload = ENABLE; // 开启CCR预加载
TIM_PWMInitStructure.PWMFastMode = DISABLE;
LHL_TIM_PWM_Init(TIM1, &TIM_PWMInitStructure, TIM_CHANNEL_1);
/* 4. 开启TIM1 的PWM输出 */
LHL_TIM_Start(TIM1);
}
void ResetTimer1_PWM(u16 duty)
{
//限幅
if(duty > 100)duty = 100;
if(duty < 0)duty = 0;
u16 dutyValue = 100*duty ; //10000*duty/100;
//更新占空比
LHL_TIM_PWM_SetDutyCycle(TIM1, dutyValue, TIM_CHANNEL_1);
}
/*-=====================================================================================================
//定时器中断回调 - 多源支持
-=====================================================================================================*/
// 为TIM1各中断源定义独立的回调函数指针
static timer_irq_callback_t tim1_update_irq_callback = NULL; // 更新中断回调
static timer_irq_callback_t tim1_cc1_irq_callback = NULL; // 捕获/比较1中断回调
static timer_irq_callback_t tim1_cc2_irq_callback = NULL; // 捕获/比较2中断回调
static timer_irq_callback_t tim1_trigger_irq_callback = NULL; // 触发中断回调
// 为TIM2各中断源定义独立的回调函数指针
static timer_irq_callback_t tim2_update_irq_callback = NULL;
static timer_irq_callback_t tim2_cc1_irq_callback = NULL;
static timer_irq_callback_t tim2_cc2_irq_callback = NULL;
static timer_irq_callback_t tim2_trigger_irq_callback = NULL;
/**------------------------------------------------------------------------
* @brief 为指定定时器的特定中断源注册回调函数
* @param timer: 定时器指针 (pTIM1 / pTIM2)
* @param it_flag: 中断标志,可取值为 TIM_IT_UPDATE, TIM_IT_CC1, TIM_IT_CC2, TIM_IT_TRIGGER
* @param tim_irq_callback: 用户定义的回调函数指针若为NULL则取消该中断源的注册
* @note 该函数会自动使能对应定时器的NVIC中断如果尚未使能。建议在启动定时器后
* 为所需的中断源分别调用本函数注册处理函数。多个中断源可独立注册,互不影响。
* 传入的 it_flag 必须是上述宏之一函数内部通过switch语句选择对应的回调指针进行赋值。
* @example:
* // 为TIM1更新中断注册回调
* Timer_register_irq_callback(pTIM1, TIM_IT_UPDATE, my_update_callback);
* // 为TIM1比较1中断注册回调
* Timer_register_irq_callback(pTIM1, TIM_IT_CC1, my_cc1_callback);
**/
void Timer_register_irq_callback(TIM_TypeDef *timer, uint16_t TIM_IT, timer_irq_callback_t tim_irq_callback)
{
if (timer == pTIM1) {
switch (TIM_IT) {
case TIM_IT_UPDATE: tim1_update_irq_callback = tim_irq_callback; break;
case TIM_IT_CC1: tim1_cc1_irq_callback = tim_irq_callback; break;
case TIM_IT_CC2: tim1_cc2_irq_callback = tim_irq_callback; break;
case TIM_IT_TRIGGER: tim1_trigger_irq_callback= tim_irq_callback; break;
default: return;
}
NVIC_EnableIRQ(TIM1_IRQn);
}
else if (timer == pTIM2) {
switch (TIM_IT) {
case TIM_IT_UPDATE: tim2_update_irq_callback = tim_irq_callback; break;
case TIM_IT_CC1: tim2_cc1_irq_callback = tim_irq_callback; break;
case TIM_IT_CC2: tim2_cc2_irq_callback = tim_irq_callback; break;
case TIM_IT_TRIGGER: tim2_trigger_irq_callback= tim_irq_callback; break;
default: return;
}
NVIC_EnableIRQ(TIM2_IRQn);
}
}
/**------------------------------------------------------------------------
* @brief TIM1全局中断处理函数支持多源回调
* @note 读取TIM1的状态寄存器SR通过位掩码判断产生的中断类型更新、比较1、比较2、触发
* 若对应中断源已注册回调函数则调用之。多个中断可能同时发生因此使用多个if语句
* 分别检查每个标志位。处理完毕后通过向SR写入~irq_type清除已响应的中断标志
* 避免重复进入中断。
* @see 中断标志位定义 TIM_IT_UPDATE (0x0001), TIM_IT_CC1 (0x0002), TIM_IT_CC2 (0x0004),
* TIM_IT_TRIGGER (0x0040)
**/
void TIM1_IRQHandler(void)
{
uint16_t irq_type = TIM1->SR; // 读取中断标志
// 更新中断---------------------------------------
if ((irq_type & TIM_IT_UPDATE) && tim1_update_irq_callback != NULL) {
tim1_update_irq_callback();
}
// 捕获/比较1中断---------------------------------
if ((irq_type & TIM_IT_CC1) && tim1_cc1_irq_callback != NULL) {
tim1_cc1_irq_callback();
}
// 捕获/比较2中断---------------------------------
if ((irq_type & TIM_IT_CC2) && tim1_cc2_irq_callback != NULL) {
tim1_cc2_irq_callback();
}
// 触发中断---------------------------------------
if ((irq_type & TIM_IT_TRIGGER) && tim1_trigger_irq_callback != NULL) {
tim1_trigger_irq_callback();
}
// 清除所有已处理的中断标志
TIM1->SR = (uint16_t)~irq_type;
}
/**------------------------------------------------------------------------
* @brief TIM2全局中断处理函数支持多源回调
* @note 同TIM1_IRQHandler处理TIM2的各中断源。
**/
void TIM2_IRQHandler(void)
{
uint16_t irq_type = TIM2->SR;
if ((irq_type & TIM_IT_UPDATE) && tim2_update_irq_callback != NULL) {
tim2_update_irq_callback();
}
if ((irq_type & TIM_IT_CC1) && tim2_cc1_irq_callback != NULL) {
tim2_cc1_irq_callback();
}
if ((irq_type & TIM_IT_CC2) && tim2_cc2_irq_callback != NULL) {
tim2_cc2_irq_callback();
}
if ((irq_type & TIM_IT_TRIGGER) && tim2_trigger_irq_callback != NULL) {
tim2_trigger_irq_callback();
}
TIM2->SR = (uint16_t)~irq_type;
}