stm32 定时器输出比较(OC)与PWM的理解和应用

发布者:清新心情最新更新时间:2025-01-06 来源: elecfans关键字:stm32  定时器  输出比较  PWM 手机看文章 扫描二维码
随时随地手机看文章

1. 定时器TIM简介

  • TIM 是 stm32 微控制器中的定时器模块。stm32 包含多个定时器模块,每个定时器模块有不同的功能和配置,适用于各种应用场景。

  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断

  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从模式等多种功能

定时器分类:

image.png?imageView2/2/w/1000


基本定时器结构(时基单元):

image.png?imageView2/2/w/1000


2. TIM 定时器相关概念

  • 计数模式

    • 向上计数模式(Up-counter mode)

    • 向下计数模式(Down-counter mode)

    • 中心对齐模式(Center-aligned mode)

  • 预分频器(Prescaler)
    主要作用是控制(降低)计数器时钟频率。


计数器时钟频率 = 定时器输入时钟频率(CK_PSC) / (预分频值 + 1) = CK_PSC / (PSC + 1)


通常 CK_PSC 为内部时钟CK_INT,即为72MHz

  • 自动重装寄存器(ARR)
    设置计数器的周期值。当计数器达到该值时,会产生一个更新时间(中断或 DMA 请求),计数器重新从 0 开始计数。

  • 输出比较(Output Compare, OC)
    定时器的输出比较功能可以用来产生精准的输出信号。通过设置比较寄存器(CCR),可以控制输出引脚的状态。

  • 输入捕获(Input Capture)
    定时器的输入捕获功能可以测量外部信号的周期和脉宽。

  • PWM 模式(Pulse Width Modulation)
    定时器 PWM 模式可以产生占空比可调的 PWM 信号,常用于电机控制、LED调光等。

3. 输出比较(Output Compare, OC)

框图如下:


image.png?imageView2/2/w/1000

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

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

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

输出比较产生的信号模式:


image.png?imageView2/2/w/1000

4. PWM

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

  • 频率:PWM 信号的周期倒数。

f = 1 / Ts = CK_PSC / (PSC + 1) / (ARR + 1)
  • 占空比:高电平时间(Ton)与周期时间(Ts)的比值,通常用百分比表示。

占空比 = Ton / Ts = Ton / (ARR + 1) = CCR / (ARR + 1)

占空比决定了输出信号在一个周期内为高电平的时间比例

  • 分辨率:分辨率是占空比的最小变化步距。分辨率通常由定时器的计数位数(如8位、16位等)决定,影响 PWM 信号的精细度。

分辨率 = 1 / (计数器最大值 + 1) = 1 / (ARR + 1)

见图:


image.png?imageView2/2/w/1000

PWM 基本结构图,也是作为后续编程的参考图:


image.png?imageView2/2/w/1000

5. 应用举例

5.1 使用 PWM 驱动 LED 呼吸灯

这个例子主要通过 PWM 实现 LED 呼吸灯的效果。
基本思想:通过 TIM2 定时器,输出 PWM 脉宽给到 LED ,LED 呈现呼吸闪烁的过程。
实现思维逻辑:
根据 PWM 基本结构图,基本步骤为:

  1. 初始化时基单元

  2. 配置 OC

  3. 由于需要控制一个 LED 灯,所以需要配置一个 GPIO 用于输出 PWM 的信号

  4. 运行控制逻辑

硬件电路:
由于使用的是 TIM2 定时器的通道1即 TIM2_CH1_ETR 输出信号,而 TIM2_CH1_ETR 输出复用的是 PA0 口,如图:


image.png?imageView2/2/w/1000

PA0 口插上 LED 的正极,负极接在 GND 上。
代码:
PWM.c

void PWM_Init(void){

    // 初始化时基单元

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // TIM2 在 APB1 总线

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; // 时基单元结构体

    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频

    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; // 计数器模式

    TIM_TimeBaseStruct.TIM_Period = 100 - 1; // 计数周期 ARR

    TIM_TimeBaseStruct.TIM_Prescaler = 7200 - 1 ; // 预分频器 PSC

    TIM_TimeBaseStruct.TIM_RepetitionCounter = 0; // 重复计数器,高级定时器才会用到

    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);

    

    // OC 配置 定时器通道配置

    TIM_OCInitTypeDef TIM_OCInitStruct;

    TIM_OCStructInit(&TIM_OCInitStruct);

    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // OC 输出模式 见图7

    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // 输出极性 见图7 REF 置有效电平

    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // 输出使能状态

    TIM_OCInitStruct.TIM_Pulse = 0; // CCR 比较寄存器 0 ~ 100

    TIM_OC1Init(TIM2, &TIM_OCInitStruct);

    

    // GPIO 配置

    // 这里需要复用 PA0 口,所以 AFIO 一并启用

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

    GPIO_InitTypeDef GPIO_InitStruct;

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStruct);

    

    // 开启 TIM2 

    TIM_Cmd(TIM2, ENABLE);}void SetCompare1(uint16_t Compare1){

    TIM_SetCompare1(TIM2, Compare1);}


main.c:

int main() {
    PWM_Init();

    int8_t i = 0;
    while(1) {
        for(i = 0; i <= 100; i++) {
            SetCompare1(i);
            Delay_ms(10);
        }
        for(i = 100; i >= 0; i--) {
            SetCompare1(i);
            Delay_ms(10);
        }
    }}

5.1 使用 PWM 驱动 舵机

PWM 驱动舵机与 PWM 驱动 LED 呼吸灯整个流程基本一致,不同的是要根据舵机的特性来控制 PWM 输出的脉宽信号。
舵机的特性:


image.png?imageView2/2/w/1000

舵机预分频器计算:

  1. 预分频器:72,TIM_CLK = 72MHz / 72 = 1MHz

  2. 定时周期: TIM_Period = PWM周期 * TIM_CLCK频率 - 1
    其中 PWM 周期为 20ms, 定时器时钟频率为 1MHz,因此 TIM_Period = 20ms * 1MHZ - 1 = 20000 - 1

角度到脉宽的线性变换公式:
[图片上传失败...(image-254-1717677170131)]
代码:
大部分的代码都与 LED 呼吸灯类似,就是几个参数有所改变:

void PWM_Init(void)

{

    //

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;

    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;

    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseStruct.TIM_Period = 20000 - 1;

    TIM_TimeBaseStruct.TIM_Prescaler = 72 - 1;

    TIM_TimeBaseStruct.TIM_RepetitionCounter = 0;

    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);

    

    // OC

    TIM_OCInitTypeDef TIM_OCInitStruct;

    TIM_OCStructInit(&TIM_OCInitStruct);

    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;

    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;

    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;

    TIM_OCInitStruct.TIM_Pulse = 0;

    TIM_OC1Init(TIM2, &TIM_OCInitStruct);

    

    //

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

    GPIO_InitTypeDef GPIO_InitStruct;

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStruct);

    

    TIM_Cmd(TIM2, ENABLE);

}


void Servo_Angle(float angle)

{

    TIM_SetCompare1(TIM2, angle / 180.0 * 2000 + 500);

}


main.c

int main() {
    OLED_Init();
    PWM_Init();
    Key_Init();

    float angle = 0;
    Servo_Angle(0);
    char str[100] = {0};
    int8_t is_rt = 0;
    while(1) {
        if(Key_Read() ==1) {
            if(is_rt == 1) angle -= 20;
            else angle += 20;
        }
        Servo_Angle(angle);
        sprintf(str, 'angle: %d    ', (int)angle);
        OLED_ShowString(1, 1, str);
        if(angle == 180) is_rt = 1;
        if(angle == 0) is_rt = 0;
        
    }}


关键字:stm32  定时器  输出比较  PWM 引用地址:stm32 定时器输出比较(OC)与PWM的理解和应用

上一篇:STM32识别肌电信号
下一篇:stm32 USART串口应用不知道你会了多少

推荐阅读最新更新时间:2026-03-24 18:37

STM32通用定时器输出比较(OC)与PWM模式详解
STM32通用定时器的输出比较(OC)和PWM模式 通用定时器OC & PWM模式详解 1️⃣什么是“输出比较”模式(OC模式)????️ 想象你有个计时器小朋友,它不停地数数,1、2、3、4、5…… 现在你告诉它:“当数到10的时候,给我敲一下桌子!”???? 计时器就会监视自己数的数,一旦到10,就立马敲桌子!这就是“输出比较”模式。 计数器 CNT:一直计数的数字 比较寄存器 CCR:目标数字,敲桌子的时间点 只要 CNT == CCR,就触发“敲桌子”事件,比如切换IO电平,或者发中断。 2️⃣ OC模式有什么作用????? 你可以用它做定时开关,比如定时开灯、关灯 还能产生特定波形,比如方波(高
[单片机]
stm32定时器PWM模式和输出比较模式
pwm模式是输出比较模式的一种特例,包含于输出比较模式中 /** @defgroup TIM_Output_Compare_and_PWM_modes * @{ */ #define TIM_OCMode_Timing ((uint16_t)0x0000) #define TIM_OCMode_Active ((uint16_t)0x0010) #define TIM_OCMode_Inactive ((uint16_t)0x0020) #define TIM_OCMode_Toggle ((uint16_t)0x0030) #define TIM_OCMode_
[单片机]
<font color='red'>stm32</font><font color='red'>定时器</font><font color='red'>PWM</font>模式和<font color='red'>输出</font><font color='red'>比较</font>模式
STM32用一个定时器输出多路不同频率及占空比的PWM输出比较
我们使用STM32输出PWM时会使用定时器的PWM输出模式来进行生成,但是这样子生成PWM是有局限的,它只能生成四路频率相同的PWM,当你设定了TIMx_PSC(预分频寄存器)和TIMx_ARR(自动重装载寄存器),这时PWM的频率就被定下来了,为系统的时钟/TIMx_PSC+1/TIMx_ARR+1,你可以通过改变各个通道的CCR寄存器来改变占空比。但是如果我们想生成多路不同频率的PWM的话,使用这个方法只能使用多个定时器了,这样对于定时器资源较少的板子无疑是不可取的,在前几周准备蓝桥杯比赛的时候我发现了32定时器有一个输出比较的模式,可以生成多路不同频率及占空比的PWM。 配置代码如下: __IO u16 CCR1
[单片机]
定时器输出比较模式产生的PWM波的频率计算
定时器的输出比较模式产生的PWM波的频率计算的公式:72M/((2*(arr+1))*(psc+1) ) 比如设置: PWM_Init(1000-1,72-1); (PWM_Init(arr,psc);) 则每路PWM频率为500Hz 。 PWM_Init(arr,psc) { TIM_TimeBaseStructure.TIM_Period = arr; TIM_TimeBaseStructure.TIM_Prescaler =psc; }
[单片机]
STM32之关于通用定时器输出比较方式
1.简单介绍 对于STM32中通用定时器的应用,定时器可以测量输入信号的脉冲长度(输入采集)或者产生输出波形(输出比较和PWM)。 如果小伙伴对于STM32的PWM不满意,因为相位无法控制,只能改变占空比。所以如果想改变PWM的相位的话,我们就可以用到输出比较方式了。 2.知识的架构 1)输出比较:打开一个TIMx计数器,再打开TIMx的一路或几路输出比较器(共4路),都配置好以后,计数器开始计数,当计数器里的值和比较寄存器里的值相等时,产生输出比较中断,在中断中将计数器中的值读出,与翻转周期相加再写道比较寄存器中,使得和下一个事件有相同的翻转周期。 大致意思为打开计数器后,计数值不断增加,到增加到比较寄存器的值时,电平翻转,也会
[单片机]
STM32 PWM比较输出
关键配置 对应GPIO NVIC中断管理 定时器初始化 定时器输出模式TIM_OC初始化(模式为TIM_OCMode_Toggle) 频率与占空比的计算 计数频率的计算 若使用比较输出,则ARR表示计数的上限,基本无用。计数频率由TIM_Prescaler成员变量配置。若配置TIM_Prescaler=71,输入时钟为72 M H z 72MHz72MHz,则计数频率为: 计数规则 当计数器数到CCR所储存的值时,输出电平就会翻转。 频率和占空比的配置   由计数规律我们可以通过修改CCR值的方法来得到我们想要的频率和占空比。   比如:计数频率为 1 M 1M1M,想要得到10 K 10K10K,占空比为80 % 80%80
[单片机]
<font color='red'>STM32</font> <font color='red'>PWM</font>波<font color='red'>比较</font><font color='red'>输出</font>
STM32定时器输出比较模式中的疑惑
OCx与OCxREF和CCxP之间的关系 初学STM32,我这个地方卡了很久,现在终于有些明白了,现在把我的理解写下与大家共享,如果有不对的地方,还请指出。 OCxREF就是一个参考信号,并且约定: OCxREF=1,称OCxREF有效。反之,OCxREF=0,称OCxREF无效; ‘1’电平(高电平)称为OCxREF的有效电平,‘0’ 电平(低电平)称为OCxREF的无效电平。 ——依据参考手册:The output stage generates an intermediate waveform which is then used for reference:OCxRef (active high). The polari
[单片机]
<font color='red'>STM32</font><font color='red'>定时器</font><font color='red'>输出</font><font color='red'>比较</font>模式中的疑惑
STM32 PWM占空比的计算及输出比较时CCR不同
下面的这个是stm32的定时器逻辑图,上来有助于理解: TIM3的ARR寄存器和PSC寄存器, 确定PWM频率。 这里配置的这两个定时器确定了PWM的频率,我的理解是:PWM的周期(频率)就是ARR寄存器值与PSC寄存器值相乘得来,但不是简单意义上的相乘,例如要设置PWM的频率参考上次通用定时器中设置溢出时间的算法,例如输出100HZ频率的PWM,首先,确定TIMx的时钟,除非APB1的时钟分频数设置为1,否则通用定时器TIMx的时钟是APB1时钟的2倍,这时的TIMx时钟为72MHz,用这个TIMx时钟72MHz除以(PSC+1),得到定时器每隔多少秒涨一次,这里给PSC赋7199,计算得定时器每隔0.0001秒涨一次,即
[单片机]
<font color='red'>STM32</font> <font color='red'>PWM</font>占空比的计算及<font color='red'>输出</font><font color='red'>比较</font>时CCR不同
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved