在 STM32 定时器的配置中,TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock/1000000 - 1;
这里减去 1 是由定时器预分频器的工作原理决定的。下面为你详细解释:
定时器预分频器的工作原理
定时器的预分频器(Prescaler)用于对输入时钟信号进行分频,从而降低定时器计数器的计数频率。预分频器的值(TIM_Prescaler
)是一个无符号整数,它决定了输入时钟信号要经过多少次计数才会使定时器计数器加 1。
在 STM32 中,预分频器的工作方式是:当定时器的预分频器寄存器(TIMx_PSC
)中的值为 N
时,输入时钟信号要经过 N + 1
个时钟周期,定时器计数器才会加 1。也就是说,实际的分频系数是 TIM_Prescaler + 1
。
代码解释
假设 SystemCoreClock
是系统的核心时钟频率,例如在 STM32F103 系列中,系统时钟频率可以达到 72MHz。现在我们想要将定时器的计数频率设置为 1MHz,那么就需要对系统时钟进行分频。
分频系数 = 系统时钟频率 / 目标计数频率,即 SystemCoreClock / 1000000
。
但由于预分频器的实际分频系数是 TIM_Prescaler + 1
,所以为了得到我们期望的分频系数,需要将计算得到的理论分频系数减 1 后再赋值给 TIM_Prescaler
。
例如,如果系统时钟频率 SystemCoreClock
为 72MHz,我们希望定时器的计数频率为 1MHz,那么理论分频系数为 72000000 / 1000000 = 72
。但由于预分频器的实际分频系数是 TIM_Prescaler + 1
,所以需要将 TIM_Prescaler
设置为 72 - 1 = 71
,这样实际的分频系数就是 71 + 1 = 72
,从而将定时器的计数频率降低到 1MHz。
示例代码
#include "stm32f10x.h"
void TIM_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 使能定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 配置定时器时基结构体
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 定时器周期
TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock/1000000 - 1; // 预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数模式
// 初始化定时器时基
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 使能定时器
TIM_Cmd(TIM2, ENABLE);
}
在这个示例中,通过将 TIM_Prescaler
设置为 SystemCoreClock/1000000 - 1
,将定时器的计数频率设置为 1MHz,然后将 TIM_Period
设置为 1000 - 1
,可以实现 1ms 的定时周期。
综上所述,在设置定时器预分频器的值时减去 1 是为了补偿预分频器实际分频系数为 TIM_Prescaler + 1
的特性,从而得到我们期望的分频效果。
实际的分频系数为什么是 TIM_Prescaler + 1。
在 STM32 定时器中,实际的分频系数是 TIM_Prescaler + 1
,这主要是由硬件设计和计数逻辑决定的,下面从计数逻辑原理和硬件设计意图两方面来详细解释:
计数逻辑原理
定时器的预分频器本质上是一个计数器,它会对输入的时钟信号进行计数。当预分频器寄存器(TIMx_PSC
)被设置为一个值 TIM_Prescaler
时,预分频器会从 0 开始计数,一直计到 TIM_Prescaler
这个值。
- 假设
TIM_Prescaler
设置为 0,预分频器从 0 开始计数,计到 0 时,实际上已经经过了 1 个时钟周期,此时定时器计数器加 1,也就是实际的分频系数为 1(即0 + 1
)。 - 若
TIM_Prescaler
设置为 1,预分频器从 0 开始计数,依次经过 0、1 这两个值,总共经过了 2 个时钟周期,定时器计数器才加 1,实际的分频系数为 2(即1 + 1
)。 - 一般地,当预分频器寄存器的值为
TIM_Prescaler
时,预分频器会从 0 计数到TIM_Prescaler
,总共经历TIM_Prescaler + 1
个时钟周期,定时器计数器才会加 1,所以实际的分频系数就是TIM_Prescaler + 1
。
硬件设计意图
从硬件设计的角度来看,将实际分频系数设计为 TIM_Prescaler + 1
有以下好处:
实现最小分频系数为 1
如果直接将 TIM_Prescaler
作为分频系数,那么当 TIM_Prescaler
设置为 0 时,就无法实现分频(因为分频系数不能为 0)。而采用 TIM_Prescaler + 1
的方式,当 TIM_Prescaler
设置为 0 时,实际分频系数为 1,这样可以方便地实现最小分频系数为 1 的情况,使得定时器可以直接使用输入时钟信号进行计数。
简化计数逻辑
采用 TIM_Prescaler + 1
的计数方式,预分频器可以从 0 开始计数,这样在硬件实现上可以简化计数器的逻辑。计数器只需要简单地从 0 开始递增,当计数值等于 TIM_Prescaler
时,就触发定时器计数器加 1 的操作,无需额外的逻辑来处理分频系数为 1 的特殊情况。
示例说明
假设系统时钟频率为 8MHz,我们希望将定时器的计数频率设置为 4MHz,即需要对系统时钟进行 2 分频。根据实际分频系数为 TIM_Prescaler + 1
的规则,我们应该将 TIM_Prescaler
设置为 1,因为 1 + 1 = 2
,这样预分频器会在经过 2 个系统时钟周期后,让定时器计数器加 1,从而实现 2 分频的效果,将定时器的计数频率降低到 4MHz。
综上所述,实际的分频系数为 TIM_Prescaler + 1
是由定时器的计数逻辑和硬件设计决定的,这种设计方式既方便了实现最小分频系数为 1 的情况,又简化了硬件计数逻辑。
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 定时器周期 为什么要减1
在 STM32 定时器配置中,TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
这里减去 1 同样是由定时器计数器的工作机制决定的。下面为你详细解释其中的原理。
定时器计数器的工作机制
定时器的计数器会在每个时钟周期进行计数操作,计数方向可以是向上计数(从 0 开始递增)、向下计数(从预设值开始递减)或中央对齐计数等,常见的是向上计数模式。
定时器的自动重载寄存器(TIMx_ARR
)用于存储一个预设值,也就是 TIM_Period
所对应的值。当计数器的值达到自动重载寄存器中的值时,会产生更新事件(如计数器溢出),并且计数器会根据配置重新归零或者重新加载初始值继续计数。
在 STM32 定时器中,计数器是从 0 开始计数的。例如,当 TIM_Period
设置为 0 时,计数器从 0 开始计数,计到 0 时就达到了预设值,此时就会产生更新事件,实际上只经历了 1 个计数周期。
代码解释
当我们想要设置定时器的周期为 1000 个计数周期时,由于计数器是从 0 开始计数的,所以当计数器的值从 0 递增到 999 时,刚好经历了 1000 个计数周期(分别是 0、1、2、...、999 这 1000 个值),此时就达到了我们期望的 1000 个计数周期,因此需要将 TIM_Period
设置为 1000 - 1 = 999
。
示例理解
假设定时器的计数频率为 1kHz(即每 1ms 计数器加 1),我们希望定时器每 1s 产生一次更新事件。由于 1s 包含 1000 个 1ms,也就是需要 1000 个计数周期,此时就应该将 TIM_Period
设置为 1000 - 1 = 999
。当计数器从 0 开始计数,依次经过 0、1、2、...、999 这 1000 个值后,刚好过去了 1s 的时间,就会产生更新事件。
代码示例
#include "stm32f10x.h"
void TIM_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 使能定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 配置定时器时基结构体
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 定时器周期,1000个计数周期
TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock/1000000 - 1; // 预分频器,使计数频率为1MHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
// 初始化定时器时基
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 使能定时器
TIM_Cmd(TIM2, ENABLE);
}
在上述代码中,TIM_Period
设置为 1000 - 1
,结合预分频器的设置,定时器每 1000 个计数周期(也就是 1ms)就会产生一次更新事件。
综上所述,在设置定时器周期时减去 1 是为了补偿计数器从 0 开始计数的特性,从而得到我们期望的计数周期数和定时时间。