目录

一、通用定时器介绍

1.通用定时器功能框图

2.通用定时器的时钟来源

3.外部时钟源的功能

4.定时器相关的引脚统计

二、计数器的3种计数模式

1.向上计数

2.向下计数

3.中央对齐模式

三、PWM介绍

1.PWM的3个参数

(1)周期

(2)频率

(3)占空比

五、使用通用定时器输出PWM波形

1.定时器的基础配置

2.控制计数方向

3.配置通道方向和输出比较模式

3.1.捕获/比较模式寄存器1(TIMx_CCMR1)

3.2.输出比较模式:CCxS为00

3.3.输入捕获模式:CCxS不为00

4.通用定时器的输出比较功能

4.1.输出比较模式下的比较值

5.输出比较的8种模式

6.配置通道极性和使能

7.通用定时器寄存器映像表

六、代码编写

七、最终效果


一、通用定时器介绍

在整个STM32F10xxx系列中,通用定时器的数量和具体的系列相关,例如目前我手中的开发板的芯片有两种型号:STM32F103C8T6STM32F103ZET6。这两种芯片的通用定时器的数量就有所不同:

下图是两款芯片的数据手册,从截图中可以看出:

STM32F103ZET6的通用定时器的数量为4个:TIM2/3/4/5

STM32F103C8T6的通用定时器的数量为3个:TIM2/3/4

通用定时器拥有基本定时器所有的功能,并在此基础上增加了如下功能:

  1. 多种时钟源
  2. 向上计数、向下计数、向上/向下计数
  3. 输入捕获
  4. 输出比较
  5. PWM生成
  6. 支持针对定位的增量(正交)编码器和霍尔传感器电路
tips:
  • IM1、TIM8 是高级定时器
  • F103高容量系列和互联型系列还有 基本定时器 TIM6、TIM7

1.通用定时器功能框图

这个框图中也包含了基本定时器的相关结构:时基单元、触发控制器。在此基础上增加了时钟源、输入捕获、输出比较相关的硬件电路模块。

2.通用定时器的时钟来源

(1)内部时钟, 一般为72MHz
HSE外部晶振(8MHz)
→ PLL倍频(9倍频,8MHz×9=72MHz)
→ 系统时钟(SYSCLK=72MHz)
→ AHB总线预分频(默认1分频)
→ AHB时钟=72MHz
→ APB1总线预分频(默认2分频)
→ APB1时钟=36MHz
→ 定时器时钟倍频规则:若APB1预分频系数>1,则定时器时钟=APB1时钟×2;若=1,则等于APB1时钟
→ 最终通用定时器内部时钟=36MHz×2=72MHz
(2)外部时钟源模式1
使用定时器自身通道的输入信号作为时钟源,每个定时器有4个输入通道,在 STM32F1xx 系列通用定时器 中。
外部时钟源模式 1(External Clock Mode 1)仅支持使用通道 1(TI1)和通道 2(TI2)的信号作为触发输入 不支持通道 3、4
(3)外部时钟源模式2
使用定时器的特殊引脚ETR引脚的信号作为时钟源
大容量芯片(STM32F103ZET6等系列)中每个通用定时器都有一个ETR引脚,比如TIM3定时器的ETR引脚是PD2。
中小容量(C8T6)TIM3_ETR 不存在 PD2! C8T6 只有 TIM2_ETR=PA0、TIM4_ETR=PE0。
ETR引脚信号经过极性选择、边缘检测、预分频器、输入滤波得到ETR信号,传递给触发控制器。
(4)内部触发输入
使用一个定时器作为另一个定时器的预分频器

3.外部时钟源的功能

  • 一般用于定时器的级联
  • 大部分情况下,内部时钟源足够使用
  • 不配置时钟源的情况下,默认选择的就是内部时钟源

4.定时器相关的引脚统计

下面是根据芯片对应的数据手册统计的与通用定时器相关的通道引脚(仅统计了STM32F103C8T6和STM32F103ZET6两款常用芯片)

STM32F103C8T6(注意:下表仅统计了默认复用功能,未统计重映射)

引脚

定时器通道

PA0

TIM2_CH1_ETR

PA1

TIM2_CH2

PA2

TIM2_CH3

PA3

TIM2_CH4

PA6

TIM3_CH1

PA7

TIM3_CH2

PB0

TIM3_CH3

PB1

TIM3_CH4

PB6

TIM4_CH1

PB7

TIM4_CH2

PB8

TIM4_CH3

PB9

TIM4_CH4

STM32F103ZET6(注意:下表仅统计了默认复用功能,未统计重映射)

引脚

定时器通道

PA0

TIM2_CH1_ETR/TIM5_CH1

PA1

TIM2_CH2/TIM5_CH2

PA2

TIM2_CH3/TIM5_CH3

PA3

TIM2_CH4/TIM5_CH4

PA6

TIM3_CH1

PA7

TIM3_CH2

PB0

TIM3_CH3

PB1

TIM3_CH4

PD2

TIM3_ETR

PB6

TIM4_CH1

PB7

TIM4_CH2

PB8

TIM4_CH3

PB9

TIM4_CH4

PE0

TIM4_ETR

二、计数器的3种计数模式

1.向上计数

从0开始增加,一直加到自动重装载寄存器的值。定时器默认使用这个模式。

2.向下计数

从自动重装载寄存器的值开始计数,直至减到0。

3.中央对齐模式

从0开始向上计数,一直计数到自动重装载寄存器的值-1。
再来一个时钟信号会产生更新事件,然后继续从自动重装载寄存器的值向下计数。
向上计数模式就可以覆盖大部分应用场景,芯片默认计数方向都是向上计数。

三、PWM介绍

PWM(pulse-width modulation,脉冲宽度调制)

简称脉宽调制,通过调整信号中的高电平的占比时间(占空比)来调整输出功率的大小,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。

STM32中通用定时器的脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器(自动重装载寄存器)确定频率、由TIMx_CCRx寄存器(捕获/比较寄存器)确定占空比的信号。

PWM通常用于控制电机转速、LED亮度调节等应用。要求被控制的电路必须要有一定的惯性。

所谓惯性指的是电路中的负载的状态不会发生突变。比如电机断电了也不会立即停止。LED灯是利用了人类眼睛的余晖(视觉暂留)效应(大概是0.1s)。

注意:PWM并不等同于数模转换。

1.PWM的3个参数

(1)周期

连续的两个上升沿或连续的两个下降沿之间的宽度,用T表示。

(2)频率

周期的倒数。PWM波形的频率决定了LED、显示屏等设备的刷新率,如果采用PWM调节亮度时就需要考虑人眼的余晖效应,频率不应设置过低,会导致闪烁。

(3)占空比

高电平宽度t除以周期T。占空比决定了输出功率的大小。
在使用PWM驱动惯性电器时,频率和周期确定好之后一般不再改变,通过改变占空比来调整输出功率。

五、使用通用定时器输出PWM波形

通用定时器的功能很强大,针对不同功能配置了众多的寄存器,我们在学习时可以按照功能来学习寄存器,这样更有利于对通用定时器的掌握。以下是使用通用定时器产生PWM波形的相关寄存器。

1.定时器的基础配置

在基本定时器的使用中,介绍了定时器的时基部分、触发部分的配置,主要用于设置定时器的输入频率(通过预分频器PSC控制)和计数器的溢出(通过自动重装载寄存器ARR控制)。具体的配置方法请参考上节内容。

2.控制计数方向

控制寄存器1(TIMx_CR1)
DIR:(direction)方向控制位
用于控制计数方向,0:向上计数;1:向下计数。当计数模式为中央对齐模式或编码器模式时,该位为只读。

3.配置通道方向和输出比较模式

每个定时器有4个输入输出通道,4个通道每个通道由一个CCR寄存器控制,可以同时实现4路比较,下图是4路输入输出的系统框图:

使用定时器的PWM输出时,GPIO端口配置成复用功能推挽输出模式(CNF-10;MODE-11)即可。

通用定时器中的通道可用于输入(捕获模式)或输出(比较模式),通道的方向由CCMRx寄存器的CCxS通道模式控制位定义,该控制位有2bit[1:0]。

在PWM模式时,需要配置为输出模式(比较模式)此时的CCxS位的值需配置为:00。

CCxS的不同,会导致CCMR1寄存器不同控制位的作用发生变化,在使用时需要特别注意,在配置通道时,需要先设定好通道的模式再做其他配置。

3.1.捕获/比较模式寄存器1(TIMx_CCMR1)

3.2.输出比较模式:CCxS为00

3.3.输入捕获模式:CCxS不为00

4.通用定时器的输出比较功能

通用定时器的输出比较功能主要是用来控制输出方波的,大多数时候都是用于输出PWM方波。当然也可以输出其他波形。但是只能是方波。主要通过控制CCR寄存器的值实现方波的占空比。
输出方波的基本原理:
以通道1为例:
  • 假设计数器向上计数,重装载寄存器的值为99(计数100后溢出)
  • 假设捕获/比较寄存器的值为60
  • 比较寄存器的值和计数器的值进行大小比较
  • 根据比较结果(> = < )不同,产生不同输出:高电平或低电平

4.1.输出比较模式下的比较值

在PWM模式1的工作模式下,CCR的值就代表了占空比的值。
捕获比较寄存器1(TIMx_CCR1)

5.输出比较的8种模式

由CCMR1寄存器的3个控制位OC1M[2,0]控制。假设计数器的值是CNT,比较寄存器1的值是CCR = 60。

模式1:OC1M[2,0]:000 输出冻结,啥都不输出,与比较结果无关。

模式2:OC1M[2,0]:001 强制输出高电平。一旦CNT==CCR,强制输出高电平,再也不改变了。

模式3:OC1M[2,0]:010 强制输出低电平,一旦CNT==CCR,强制输出低电平,再也不改变了。

模式4:OC1M[2,0]:011 输出翻转,一旦CNT==CCR,则翻转输出。高->低,低->高。

        这个时候,得到的方波是一个占空比为50%的方波,这是为什么呢?为什么不是60%?

        原因是:

                电平每次发生翻转都是在CNT == CCR的时候,这个周期是固定的。

                输出的波形是溢出频率的一半。

                这种模式可以用于调整相位。

模式5:OC1M[2,0]:100 强制输出低电平,与比较结果无关。

模式6:OC1M[2,0]:101 强制输出高电平,与比较结果无关。

模式7:OC1M[2,0]:110 PWM模式1(重点)

        CNT < CCR 输出高电平

        CNT >= CCR 输出低电平

输出波形如下:

        输出占空比为60%的PWM方波

        溢出频率等于PWM频率

        周期也相同

模式8:OC1M[2,0]:111 PWM模式2

        CNT < CCR 输出低电平

        CNT >= CCR 输出高电平

周期等于溢出周期

频率等于溢出率

占空比为40%

最常用的是模式7,OC1M[2,0]:110,这个时候占空比和CCR值是对应的。

6.配置通道极性和使能

捕获/比较使能寄存器(TIMx_CCER)

CC1P输入输出捕获1输出极性:

        为0:OC1高电平有效

        为1:OC1低电平有效

        影响的是PWM模式时的通道输出的有效电平,严格来讲,PWM模式输出低电平有效更为合理,因为是低电平点亮LED。

7.通用定时器寄存器映像表

六、代码编写

以下代码基于STM32F103C8T6芯片实现,使用芯片的GPIOA1引脚连接LED实现PWM驱动LED实现呼吸灯效果。

文件名:tim2.h

#ifndef __TIME2_H
#define __TIME2_H

#include "stm32f10x.h"

// 函数声明
// 初始化
void TIM2_Init(void);
// 开启定时器,开始输出PWM方波
void TIM2_Start(void);
// 停止定时器,停止输出PWM方波
void TIM2_Stop(void);
// 设置PWM占空比
void SET_DutyCycle(uint8_t dutycycle);



#endif

文件名:tim2.c

#include "tim2.h"

// 初始化
void TIM2_Init(void)
{
    // 1.开启时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

    // 2.设置GPIOA1引脚的工作模式,PWM方波输出,引脚需要工作在复用功能推挽输出模式:CNF-10;MODE-11;
    GPIOA->CRL &= GPIO_CRL_CNF1_0;
    GPIOA->CRL |= GPIO_CRL_CNF1_1;
    GPIOA->CRL |= GPIO_CRL_MODE1;

    // 3.设置TIM2定时器,为了使得LED刷新频率可以达到合适的效果,需要考虑人眼的余晖效应,这里将频率定为100Hz
    // 3.1.设置定时器的输入频率,假设ARR的值为99(100次计数),输出100Hz的频率,在输入的时钟频率为72MHz的前提下,PSC的值的计算过程为:
    // 输出频率 = 1 / [(1 / (定时器输入时钟频率 / (PSC+1))) * (ARR + 1)]
    // 由频率计算公式推到出周期计算公式:
    // 输出时钟周期 = (1 / (定时器输入时钟频率 / (PSC+1))) * (ARR + 1)
    // 已知:输如频率 = 72MHz    ARR+1 = 100;    计算PSC
    // 1/100Hz = (1 / (72MHz / (PSC+1))) * 100
    // 72MHz / (PSC+1) = 100Hz * 100
    // 72MHz / (100Hz * 100) = (PSC+1)
    // PSC+1 = 7200
    // PSC = 7199
    // 不开启预装载功能(默认)
    TIM2->CR1 &= ~TIM_CR1_ARPE;
    TIM2->PSC = 7199;
    // 3.2.设置预装载寄存器的值,为了方便调整PWM的占空比,这个值设定为99,也就是计数100次后溢出。
    TIM2->ARR = 99;
    // 3.3.设置定时器的计数方向,0代表向上计数
    TIM2->CR1 &= ~TIM_CR1_DIR;
    // 3.4.设置定时器的通道方向,输出,CCxS-00
    TIM2->CCMR1 &= ~TIM_CCMR1_CC2S;
    // 3.5.配置定时2器通道2的输出模式:PWM模式1
    TIM2->CCMR1 &= ~TIM_CCMR1_OC2M_0;
    TIM2->CCMR1 |= TIM_CCMR1_OC2M_1;
    TIM2->CCMR1 |= TIM_CCMR1_OC2M_2;
    // 3.6.配置一个默认的占空比:99%,因为LED是共阳,因此占空比高代表LED暗。
    TIM2->CCR2 = 99;
    // 3.7.开启定时器通道输出使能
    TIM2->CCER |= TIM_CCER_CC2E;
}
// 开启定时器,开始输出PWM方波
void TIM2_Start(void)
{
    TIM2->CR1 |= TIM_CR1_CEN;
}
// 停止定时器,停止输出PWM方波
void TIM2_Stop(void)
{
    TIM2->CR1 &= ~TIM_CR1_CEN;
}
// 设置PWM占空比
void SET_DutyCycle(uint8_t dutycycle)
{
    TIM2->CCR2 = dutycycle;
}

文件名:main.c

#include "tim2.h"
#include "delay.h"


int main(void)
{
    // 初始化定时器2
    TIM2_Init();
    // 开启定时器
    TIM2_Start();
    // 定义PWM占空比变化的方向,0代表向上计数,1代表向下计数
    uint8_t dir = 0;
    // 定义占空比的初始值
    uint8_t dutycycle = 99;
    while(1)
    {
        // 确定方向
        if(dutycycle == 99)
        {
            dir = 1;
        }
        else if (dutycycle == 0)
        {
            dir = 0;
        }

        // 占空比变化
        if(dir == 0)
        {
            dutycycle++;
        }
        else
        {
            dutycycle--;
        }

        // 占空比调整
        SET_DutyCycle(dutycycle);
        Delay_nms(20);

    }
}

七、最终效果

https://mp.csdn.net/mp_others/manage/video

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐