定时器类型

类型 编号 总线 功能
高级定时器 TIM1、TIM8 APB2 拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能
通用定时器 TIM2、TIM3、TIM4、TIM5 APB1 拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
基本定时器 TIM6、TIM7 APB1 拥有定时中断、主模式触发DAC的功能
graph LR
    %% 定义样式类
    classDef gpio fill:#e1f5fe,stroke:#4fc3f7,stroke-width:2px,color:#0277bd,rx:8,ry:8;
    classDef rcc fill:#ffebee,stroke:#ef9a9a,stroke-width:2px,color:#c62828,rx:8,ry:8;
    classDef source fill:#e8f5e9,stroke:#a5d6a7,stroke-width:2px,color:#2e7d32,rx:8,ry:8;
    classDef mux fill:#fff9c4,stroke:#fff59d,stroke-width:2px,color:#f57f17,rx:8,ry:8;
    classDef core fill:#ffe0b2,stroke:#ffb74d,stroke-width:2px,color:#e65100,rx:8,ry:8;
    classDef nvic fill:#e0f2f1,stroke:#80cbc4,stroke-width:2px,color:#00695c,rx:8,ry:8;
    classDef ext fill:#fff3e0,stroke:#ffcc80,stroke-width:2px,color:#ef6c00,rx:8,ry:8;

    subgraph ClockSources [时钟源]
        direction TB
        RCC[RCC
内部时钟]:::rcc ETR[ETR
外部时钟]:::source ITRx[ITRx
其他定时器]:::ext TIx[TIx
捕获通道]:::ext end GPIO_ETR[GPIO]:::gpio --> ETR GPIO_TIx[GPIO]:::gpio --> TIx MUX_Sub[多路复用]:::mux ITRx --> MUX_Sub TIx --> MUX_Sub MUX[内部时钟模式
外部时钟模式2
外部时钟模式1
编码器模式]:::mux RCC --> MUX ETR --> MUX MUX_Sub --> MUX subgraph TimeBase [时基单元] direction LR PSC[PSC
预分频器]:::core CNT[CNT
计数器]:::core ARR[ARR
自动重装器]:::core PSC --> CNT ARR <--> CNT end MUX --> PSC RunCtrl[运行控制]:::core --> TimeBase IRQCtrl[中断
输出控制]:::nvic NVIC[NVIC]:::nvic CNT --> IRQCtrl IRQCtrl --> NVIC %% 整体样式设置 style ClockSources fill:#f8fbff,stroke:#d6e4f0,stroke-width:2px,stroke-dasharray: 5 5 style TimeBase fill:#fce4ec,stroke:#f48fb1,stroke-width:2px

掌握函数

函数 说明
TIM_InternalClockConfig 配置时钟源
TIM_TimeBaseInitTypeDef 定义结构体变量
TIM_TimeBaseInit 配置TIM2的时基单元
TIM_ClearFlag 中断输出配置
TIM_ITConfig 开启的更新中断
NVIC_PriorityGroupConfig NVIC中断分组
TIM_Cmd TIM使能
TIM_GetITStatus 判断是否是触发中断

配置步骤

  • 开启时钟
  • 配置时钟源
  • 时基单元初始化
  • 中断输出配置
  • NVIC中断分组
  • NVIC配置
  • TIM使能

代码演示

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 Timer_Init(void)
{
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟

/*配置时钟源*/
TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟

/*时基单元初始化*/
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; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元

/*中断输出配置*/
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器更新标志位
//TIM_TimeBaseInit函数末尾,手动产生了更新事件
//若不清除此标志位,则开启中断后,会立刻进入一次中断
//如果不介意此问题,则不清除此标志位也可

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断

/*NVIC中断分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2
//即抢占优先级范围:0~3,响应优先级范围:0~3
//此分组配置在整个工程中仅需调用一次
//若有多个中断,可以把此代码放在main函数内,while循环之前
//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置

/*NVIC配置*/
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择配置NVIC的TIM2线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC线路的抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设

/*TIM使能*/
TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
}
1
2
3
4
5
6
7
8
9
10
11
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否是TIM2的更新事件触发的中断
{
Num ++; //Num变量自增,用于测试定时中断
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}

TIM 输出比较

模式 描述
冻结 CNT=CCR时,REF保持为原状态
匹配时置有效电平 CNT=CCR时,REF置有效电平
匹配时置无效电平 CNT=CCR时,REF置无效电平
匹配时电平翻转 CNT=CCR时,REF电平翻转
强制为无效电平 CNT与CCR无效,REF强制为无效电平
强制为有效电平 CNT与CCR无效,REF强制为有效电平
PWM模式1 向上计数:CNT<CCR时,REF置有效电平,CNT≥CCR时,REF置无效电平
向下计数:CNT>CCR时,REF置无效电平,CNT≤CCR时,REF置有效电平
PWM模式2 向上计数:CNT<CCR时,REF置无效电平,CNT≥CCR时,REF置有效电平
向下计数:CNT>CCR时,REF置有效电平,CNT≤CCR时,REF置无效电平
graph LR
    %% 定义样式类 (去除了不受支持的 rx 和 ry 属性)
    classDef timeCore fill:#ffe0b2,stroke:#ffb74d,stroke-width:2px,color:#e65100;
    classDef runCtrl fill:#fff3e0,stroke:#ffcc80,stroke-width:2px,color:#ef6c00;
    classDef compUnit fill:#f3e5f5,stroke:#ce93d8,stroke-width:2px,color:#6a1b9a;
    classDef gpio fill:#e1f5fe,stroke:#4fc3f7,stroke-width:2px,color:#0277bd;

    %% 左侧输入信号 (给个占位字符避免空括号报错)
    Input((" ")):::timeCore
    Input --> PSC

    %% 运行控制
    RunCtrl[运行控制]:::runCtrl
    
    %% 时基单元
    subgraph TimeBase [时基单元]
        direction LR
        PSC[PSC
预分频器]:::timeCore CNT[CNT
计数器]:::timeCore ARR[ARR
自动重装器]:::timeCore PSC --> CNT ARR <--> CNT end RunCtrl --> PSC %% 输出比较单元 subgraph OutputCompare ["输出比较单元 x4"] direction LR CCR[CCR
捕获/比较器]:::compUnit %% 关键修复:使用 HTML 实体编码代替 < 和 ≥ Logic["CNT < CCR时,REF置有效电平
CNT ≥ CCR时,REF置无效电平"]:::compUnit Polarity[极性选择
输出使能]:::compUnit CCR --> Logic Logic -->|REF| Polarity end CNT --> CCR GPIO[GPIO]:::gpio Polarity --> GPIO %% 整体样式 style TimeBase fill:#f8fbff,stroke:#d6e4f0,stroke-width:2px,stroke-dasharray: 5 5 style OutputCompare fill:#f8fbff,stroke:#d6e4f0,stroke-width:2px,stroke-dasharray: 5 5 style Input display:none
  • PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
  • PWM占空比: Duty = CCR / (ARR + 1)
  • PWM分辨率: Reso = 1 / (ARR + 1)

PWM配置步骤

  • 开启时钟
  • GPIO重映射
  • GPIO初始化
  • 配置时钟源
  • 输出比较初始化
  • TIM使能

掌握函数

函数 说明
GPIO_PinRemapConfig GPIO重映射 注:必须先开启AFIO的时钟
TIM_OCInitTypeDef 定义结构体变量
TIM_OCStructInit 结构体初始化
TIM_OC1Init 配置输出比较通道
TIM_SetCompare1/2/3/4 设置CCR1/2/3/4的值

代码演示

1
2
3
4
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare); //设置CCR1的值
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
/*时基单元初始化*/
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; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元

/*输出比较初始化*/
TIM_OCInitTypeDef TIM_OCInitStructure; //定义结构体变量
TIM_OCStructInit(&TIM_OCInitStructure); //结构体初始化,若结构体没有完整赋值
//则最好执行此函数,给结构体所有成员都赋一个默认值
//避免结构体初值不确定的问题
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式,选择PWM模式1
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); //将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
...
graph LR
    %% 定义样式类 (淡蓝、浅紫、浅黄、浅橙,去除了绿色)
    classDef gpio fill:#e1f5fe,stroke:#4fc3f7,stroke-width:2px,color:#0277bd,rx:8,ry:8;
    classDef purple fill:#f3e5f5,stroke:#ce93d8,stroke-width:2px,color:#6a1b9a,rx:8,ry:8;
    classDef yellow fill:#fff9c4,stroke:#fff59d,stroke-width:2px,color:#f57f17,rx:8,ry:8;
    classDef orange fill:#ffe0b2,stroke:#ffb74d,stroke-width:2px,color:#e65100,rx:8,ry:8;

    %% 左侧 GPIO
    GPIO[GPIO]:::gpio

    %% 输入捕获单元 (淡蓝色背景)
    subgraph InputCapture [输入捕获单元1]
        direction LR
        F[滤波器]:::purple
        Edge[边沿检测
极性选择]:::purple Mux[/多路复用器/]:::yellow Div[分频器]:::purple CCR1[CCR1
捕获/比较器]:::purple end %% 中间上方控制 (淡黄、浅橙) TrigSrc[/触发源选择/]:::yellow Reset[从模式
Reset]:::orange %% 时基单元 (淡蓝色背景) subgraph TimeBase [时基单元] direction LR PSC[PSC
预分频器]:::orange CNT[CNT
计数器]:::orange ARR[ARR
自动重装器]:::orange end %% 连接关系 GPIO --> F F --> Edge %% 信号分流 Edge -- "TI1FP1" --> Mux Edge --> TrigSrc TrigSrc --> Reset Reset ==> PSC PSC --> CNT ARR <--> CNT Mux --> Div Div --> CCR1 %% 计数器向下连接到捕获/比较器 (加粗箭头) CNT ==> CCR1 %% 整体样式设置 (淡蓝色虚线背景框) style InputCapture fill:#f8fbff,stroke:#d6e4f0,stroke-width:2px,stroke-dasharray: 5 5 style TimeBase fill:#f8fbff,stroke:#d6e4f0,stroke-width:2px,stroke-dasharray: 5 5

掌握函数

函数 说明
TIM_PrescalerConfig 设置PSC的值
TIM_ICInitTypeDef 定义结构体变量
TIM_PWMIConfig 配置的输入捕获通道
TIM_SelectInputTrigger 触发源选择
TIM_SelectSlaveMode 从模式选择复位
TIM_GetCapture1/2/3/4 读取CCR1/2/3/4中的值
TIM_EncoderInterfaceConfig 编码器接口配置

代码演示

1
2
3
4
5
6
7
8
9
10
...
/*时基单元初始化*/
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; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
...
1
2
3
4
5
6
7
8
9
10
11
...
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare); //设置CCR1的值
}

void PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate); //设置PSC的值
}
...
1
2
3
4
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1); //测周法得到频率fx = fc / N,这里不执行+1的操作也可
}