#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; }