为什么需要定时器级联?

STM32的通用定时器(如TIM2、TIM3)多为16位,最大计数值为65535。当需要定时超过1秒(假设72MHz主频,预分频为7200,则单次定时最大约0.91秒)或对外部高频脉冲进行长时间计数时,单个定时器会溢出。虽然可以使用中断计数扩展,但会占用CPU且精度受限。定时器级联通过硬件将两个定时器串联,形成32位甚至更宽的计数器,无需CPU干预,实现高精度长时间定时或大范围脉冲计数。

级联原理:主从模式

级联的核心是主从模式(Master-Slave)。主定时器(Master)在发生特定事件(如更新事件、触发输出)时,通过内部触发信号(ITRx)驱动从定时器(Slave)的时钟或使能。常见两种用法:

  • 级联定时:主定时器输出一个周期性的触发信号,作为从定时器的时钟源,从定时器对主定时器的溢出次数进行计数,从而扩展定时范围。
  • 脉冲计数:外部脉冲输入到主定时器的ETR引脚,主定时器对脉冲计数,溢出时触发从定时器加1,实现大范围脉冲计数。

STM32的触发连接关系固定:TIM1触发TIM2/TIM5,TIM2触发TIM3/TIM4,TIM3触发TIM4,TIM4触发TIM2等。本文以TIM2作为主,TIM3作为从,实现32位定时器。

硬件连接与配置步骤

硬件准备

  • STM32F103C8T6最小系统板
  • 外部脉冲源(如信号发生器或按键电路)
  • 杜邦线若干

配置步骤(以STM32CubeMX + HAL库为例)

  1. 时钟配置:将系统时钟配置为72MHz(APB1定时器时钟为72MHz)。
  2. TIM2配置(主)
    • 选择内部时钟源(Internal Clock)。
    • 设置预分频器(PSC)和自动重载值(ARR),例如PSC=7199,ARR=999,则溢出周期为 (7199+1)*(999+1)/72MHz = 0.1秒。
    • 在Trigger Output (TRGO)中选择Update Event,即每次溢出产生触发信号。
  3. TIM3配置(从)
    • 选择时钟源为External Clock Mode 1,并设置触发源为ITR2(因为TIM2触发TIM3的ITR2)。
    • 设置预分频器为0,自动重载值设为65535(或更大,但16位最大65535)。
    • 使能TIM3的更新中断,用于在总定时达到后处理。
  4. 中断配置:使能TIM3全局中断,并设置优先级。
  5. 生成代码:在CubeMX中生成工程,然后添加用户代码。

完整代码示例

以下代码基于STM32CubeMX生成的工程,实现TIM2+TIM3级联,定时总时长为 0.1秒 * 65536 ≈ 6553.6秒(约1.82小时)。

/* 在main.c中 */

// 全局变量,用于记录级联溢出次数
volatile uint32_t total_ticks = 0;

// 初始化函数(CubeMX已生成,此处补充用户代码)
void MX_TIM2_TIM3_Cascade_Init(void)
{
    // TIM2配置为主,TIM3配置为从,已在CubeMX中完成
    // 启动级联定时器
    HAL_TIM_Base_Start(&htim2);  // 启动主定时器
    HAL_TIM_Base_Start(&htim3);  // 启动从定时器,并开始计数
    HAL_TIM_Base_Start_IT(&htim3); // 使能从定时器更新中断
}

// 中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM3) {
        total_ticks++;
        // 此处可添加用户处理,如翻转LED
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    }
}

// 主函数中调用
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_TIM2_Init();
    MX_TIM3_Init();
    MX_TIM2_TIM3_Cascade_Init();

    while (1)
    {
        // 主循环可读取total_ticks,计算总时间
        // 例如:总时间 = total_ticks * 0.1秒 + 当前TIM3计数值*0.1秒/65536
    }
}

脉冲计数模式代码

若需对外部脉冲计数,将TIM2配置为外部时钟模式,ETR引脚接脉冲源,TIM3仍作为从。配置要点:

  • TIM2的时钟源选择ETR,预分频和ARR可设为最大值(如ARR=65535)。
  • TIM2的TRGO选择Update Event。
  • TIM3配置与上述相同。
// TIM2外部脉冲计数初始化(CubeMX配置)
void MX_TIM2_ETR_Init(void)
{
    // 在CubeMX中设置TIM2 Clock Source为ETR2,Prescaler=0,ARR=65535
    // 使能TIM2的TRGO为Update Event
}

// 启动计数
HAL_TIM_Base_Start(&htim2);
HAL_TIM_Base_Start(&htim3);
HAL_TIM_Base_Start_IT(&htim3);

// 在中断回调中,total_ticks即为脉冲数的高16位
// 总脉冲数 = (total_ticks << 16) | __HAL_TIM_GET_COUNTER(&htim2);

注意事项

  • 触发源选择:务必确认主定时器对应的从定时器触发源(如TIM2->TIM3是ITR2,TIM3->TIM4是ITR1等),错误配置将导致无法级联。
  • 预分频与ARR设置:主定时器的溢出频率决定了从定时器的计数频率,需根据所需精度计算。从定时器的ARR应设为65535以最大化范围,但也可根据需求调整。
  • 中断处理:从定时器溢出中断频率较高(本例为10Hz),中断服务函数应尽量简短,避免影响实时性。
  • 32位扩展:级联后实际计数为32位(主16位+从16位),但需注意从定时器的计数方向(向上/向下)需与主定时器匹配。
  • 调试技巧:使用逻辑分析仪观察主定时器的TRGO引脚(可映射到GPIO)验证触发信号。
  • 功耗考虑:级联定时器在低功耗模式下可能无法工作,需根据应用场景选择。

总结

定时器级联是STM32高级定时功能之一,通过硬件自动连接,无需CPU干预即可实现32位计数。本文以TIM2+TIM3为例,详细介绍了级联定时和脉冲计数两种模式,并提供了完整代码。掌握此技术,可轻松应对长时间定时、高精度脉冲计数等场景,提升嵌入式系统设计的灵活性和效率。