Stm32中定时器的使用无疑是至关重要的,通过使用定时器可以使我们的流程控制更加简单可控,还可以使我们的代码模块化,方便管理与调试。Stm32的定时器的使用分为定时中断、输出比较、输入捕获、编码器接口这几个主要部分,下面一一来介绍。

定时器框图

image-20250201222055382

定时中断

定时中断基本结构

image-20250201222255621

影子寄存器

影子寄存器:在改变自动重装寄存器TIMx_ARR时,若开启影子寄存器,该寄存器中的值不会立即更新,而是会在产生又一个更新事件后,更新该寄存器中的值,可以防止某些意外情况的发生。比如,初始ARR的值为5,当计数器中的值为4时更改ARR为3,此时4 >3,但是不等于3,随着时间的增加,计数器中的值只会在超出计数器计数上限时溢出,当计数器中的值再次等于3时才会产生一次更新事件。

计数器溢出频率

计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

定时器中断代码演示

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启时钟

TIM_InternalClockConfig(TIM2); //设置时钟源

//时基单元设置
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//分频系数
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//psc
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//RCR,仅高级定时器才有
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除标志位
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//定时器中断使能

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC分组,全局仅需配置一次

//NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
NVIC_Init(&NVIC_InitStructure);

TIM_Cmd(TIM2, ENABLE);//打开定时器
}
void TIM2_IRQHandler(void)//中断函数
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Num ++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}

一般情况下的定时中断配置步骤

  1. 开启时钟;
  2. 选择时钟源;
  3. 配置时基单元;
  4. 中断配置;
  5. NVIC优先级配置;
  6. 中断函数编写。

关于时钟源的选择

  1. 内部时钟模式

    • RCC内部时钟
  2. 外部时钟模式2

    • ETR外部时钟:使用外部触发输入(ETR)作为时钟源
  3. 外部时钟模式1

    • ETR外部时钟
    • ITRx其他定时器
    • TIx捕获通道
  4. 编码器模式

    • TIx捕获通道

输出比较

简介

  • OC(Output Compare)输出比较输出比较

  • 可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形

  • 每个高级定时器和通用定时器都拥有4个输出比较通道

  • 高级定时器的前3个通道额外拥有死区生成和互补输出的功能

PWM

  • PWM(Pulse Width Modulation)脉冲宽度调制

  • 在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域

  • PWM参数:

    1. 频率 = $\frac{1}{T_{s}}$

    2. 占空比 = $\frac{T_{ON}}{T_{S}}$

    3. 分辨率 = 占空比变化步距

输出比较代码演示

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void PWM_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //AFIO时钟开启
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);//重映射
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//解除调试端口占用

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

//选择时钟源:内部时钟源:72MHz
TIM_InternalClockConfig(TIM2);

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//RCR
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

//输出比较配置
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//模式:PWM1,CNT<CCR时高电平,CNT>CCR时低电平
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//极性选择
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//使能
TIM_OCInitStructure.TIM_Pulse = 0;//CCR 捕获/比较寄存器
TIM_OC1Init(TIM2, &TIM_OCInitStructure);//通道1初始化

TIM_Cmd(TIM2, ENABLE);//开启定时器TIM2
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);//更改CCR寄存器值:通道1
}

注意:使用高级定时器输出PWM时需要调用TIM_CtrlPWMOutputs()函数,否则不能正常输出。

输入捕获

对于同一个定时器输入捕获输出比较只能使用其中一个。

简介

  • IC(Input Capture)输入捕获

  • 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数

  • 每个高级定时器和通用定时器都拥有4个输入捕获通道

  • 可配置为PWMI模式,同时测量频率和占空比

  • 可配合主从触发模式,实现硬件全自动测量

频率测量

  1. 测频法:在闸门时间T内,对上升沿计次,得到N,则频率$f_{x} = \frac{N}{T}$

  2. 测周法:两个上升沿内,以标准频率fc计次,得到N,则频率$f_x = \frac{f_{c}}{N}$

  3. 中界频率:测频法与测周法误差相等的频率点$f_{m}=\frac{\sqrt{f_{c}}}{T}$

输入捕获通道

image-20250203171218877

主从触发模式

image-20250203171253977

输入捕获基本结构

image-20250203171331674

PWMI结构

image-20250203171359863

代码演示

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void IC_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

//GPIO配置
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

//选择时钟源
TIM_InternalClockConfig(TIM3);

//时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

//输入捕获配置
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//通道
TIM_ICInitStructure.TIM_ICFilter = 0xF;//滤波器
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//极性选择:上升沿触发
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//预分频
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//直连通道
TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);//该函数能自动把对应的另一个通道配置成下降沿触发、交叉通道

//从模式
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);//触发源选择
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);//从模式选择:Reset模式

TIM_Cmd(TIM3, ENABLE);//开启定时器
}

编码器接口

简介

  • Encoder Interface 编码器接口

  • 编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度

  • 每个高级定时器和通用定时器都拥有1个编码器接口

  • 两个输入引脚借用了输入捕获的通道1和通道2

正交编码器

image-20250203171631395

编码器接口基本结构

image-20250203171702111

代码演示

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void Encoder_Init(void)
{
\\开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

\\GPIO配置
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

\\时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

\\输入捕获配置
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInit(TIM3, &TIM_ICInitStructure);

\\编码器接口
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);

TIM_Cmd(TIM3, ENABLE);
}

END