359 lines
14 KiB
C
Executable File
359 lines
14 KiB
C
Executable File
#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;
|
||
}
|