定时器是用于计时和产生精确时间延迟的关键模块。本文将重点讨论通用定时器的基本配置,特别是如何使用外部时钟信号,以及如何正确配置定时器的周期和分频器。为了帮助更好地理解这些概念,我们将结合一些通俗的例子进行讲解。
定时器的基本工作原理
1. 内部时钟模式:
定时器通常使用内部时钟源(例如72 MHz)作为计数器的时钟。这个时钟源的频率决定了定时器计数的速度。例如,72 MHz的频率意味着每秒钟有72,000,000个时钟脉冲。如果没有使用预分频器,定时器的计数器每秒钟会增加72,000,000次。
2. 预分频器:
预分频器用于降低时钟源的频率,从而减缓计数器的增速。假设我们将预分频器设置为10000 - 1(即9999),那么每10000个时钟脉冲合成一个新的计数脉冲。这样,定时器的频率会降低到72,000,000 ÷ 10,000 = 7200 Hz,即每秒钟7200个脉冲。
假设你有一个钟表,它的秒针每秒走一圈。你决定每走10000秒才算作一次完整的计数。于是,你将秒针的速度降低了10000倍,这样你每10000秒才计数一次。在这种情况下,预分频器就相当于把钟表的频率降低了。
3. 定时器周期
定时器的周期配置决定了计数器计数到达的值。周期设置为`周期值 - 1`。例如,如果周期设置为720 - 1(即719),定时器会在720个分频后的脉冲后触发更新事件。换句话说,如果定时器的频率是7200 Hz,那么周期设置为720会使定时器每10秒触发一次事件(720 ÷ 7200 = 0.1秒)。
如果你的钟表每秒走7200圈,你希望每720圈触发一次事件(比如响铃)。周期设置就像是设定了钟表走了720圈时触发一次响铃。
外部时钟模式2
定时器可以配置为使用外部时钟源(例如ETR引脚)进行计数。在外部时钟模式2中,ETR引脚上的每个外部信号变化(例如低电平到高电平的边沿)被视为一个时钟脉冲。每次外部信号的变化都会驱动定时器计数器进行计数,这样定时器可以根据外部事件来驱动计数器。
假设你用一个开关作为脉冲信号源,每次按下开关,定时器就增加一次计数。如果你把开关的每次按下事件作为定时器的时钟信号,那么定时器就会根据开关的操作来进行计数。
示例代码
下面的代码示例展示了如何配置一个定时器,设置为外部时钟模式2,并进行基本的初始化:
void Timer_Init(void)
{
// 使能定时器2和GPIOA的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIOA的Pin0为上拉输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置定时器2的外部触发模式2
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_Inverted, 0x00);
// 配置定时器2的基本参数
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; // 设置周期为10
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; // 设置分频器为1
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// 清除更新标志并使能更新中断
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
// 配置中断优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
// 启动定时器2
TIM_Cmd(TIM2, ENABLE);
}