STM32单片机 定时器TIM输出比较 PWM波形

一.  OC(Output Compare)输出比较

  • 了解:IC(Input Capture)输入捕获、CC(Capture/Compare)输入捕获和输出比较单元
  • OC功能:用来输出PWM波形,PWM波形又是用来驱动电机的必要条件,用来做智能车、机器人等
  • 输出比较可以通过比较CNT计数器CCR捕获/比较寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
    • 当CNT>CCR、<CCR或者=CCR时,OC1就会对应的置1、置0。这样就可以输出PWM波了。
  • 每个高级定时器通用定时器都拥有4个输出比较通道。
  • 高级定时器的前3个通道额外拥有死区生成互补输出的功能

二.  PWM波形(Pulse Width Modulation)脉冲宽度调制

  • 是数字输出信号,由高低电平组成
  • 在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量常应用于电机控速等领域
  • 产生

例如:数字输出端口控制LED,按理说 LED 只能有完全亮和完全灭两种状态,怎么能实现控制亮度大小呢?那通过这个 PWM 波形就可以实现,我们让 LED 不断点亮熄灭,当这个点亮熄灭的频率足够大时, LED 就不会闪烁了,而是呈现出一个中等亮度。当我们调控这个点亮和熄灭的时间比例时,就能让 LED 呈现出不同的亮度级别。

例如:对于电机调速,我们以一个很快的频率给电机通电、断电,那么电机的速度就能维持在一个中等速度,这就是 PWM 的基本思想。

PWM 的秘诀就是天下武功唯快不破。要我闪的足够快,你就发现不了我到底是闪着亮的,还是一个正常的平稳的亮度。当然,PWM的应用场景必须要是一个惯性系统。就是说 LED 在熄灭的时候,由于余晖和人眼视觉暂留现象, LED 不会立马熄灭,而是有一定的惯性,过一小段时间才会熄灭。电机也是当电机断电时,电机的转动不会立马停止,而是有一定的惯性,过一会才停,这样具有惯性的系统才能使用PWM。

上图这种高低跳变的数字信号,可以等效为中间紫色虚线所表示的模拟量。

三.  PWM参数

  • 频率=1/Ts 
  • 占空比=Ton/Ts  。占空比:占空比越大,则模拟电压越趋近于高电平,反之则趋近于低电平
  • 分辨率=占空比变化步距

假设高电平5V,低电平0V,占空比为50%,则模拟电压为2.5V,若占空比为25%,则模拟电压为1.25V。

分辨率

  • 占空比是1%,2%,3%等等这样就是以1%的步距跳变
  • 占空比是1.1%,1.2%,1.3%这样是以0.1%的步距跳变
  • 分辨率就是占空比变化的细腻程度

四.  通用定时器的输出比较通道框图

  1. 左边就是 CNT 计数器和 CCR1 第一路的捕获/比较寄存器,它俩进行比较。
  2. 当 CNT 大于 CCR1 或者 CNT 等于 CCR1 时,就会给这个输出模式控制器传一个信号,然后输出模式控制器就会改变它输出 OC1 ref 的高低电瓶。ref  信号实际上就是指这里信号的高低电平,这个 ref 是 reference 的缩写,意思是参考信号。
  3. ETRF 输入,这是定时器的一个小功能,一般不用,不需要了解。
  4. 输出模式控制器工作原理
    1. 什么时候给 ref 高电瓶,什么时候给 ref 低电瓶?我们看一下下面的这个表,这就是输出比较的 8 种模式,也就是这个输出模式控制器里面的执行逻辑,这个模式控制器的输入是 CNT 和 CCR 的大小关系。输出是REF的高低电平,里面可以选择多种模式来更加灵活的控制 ref 输出,模式可以通过寄存器(TIMx_CCMR1)来进行配置,你需要哪个模式就可以选那个模式。那具体都是怎么操作的呢?我们来看一下这个表。冻结,描述是 CNT 等于 CCR 时, REF保持为原状态,CNT等于 CCR 时维持原状态。那其实这个 CNT 和 CCR 就根本没有用,是吧?所以你也可以把它理解成 CNT 和 CCR 无效 REF 保持为原状态,这都是一样的效果。那这个模式也比较简单,它根本就不管 CNT 谁大谁小,直接 REF 保持不变,维持上一个状态就行了。这有什么用呢?比如你正在输出 PWM 波,突然想暂停一会输出,就可以设置成这个模式,一旦切换为冻结模式后,输出就暂停了,并且高低电平也维持为暂停时刻的状态保持不变,这就是冻结模式的作用。 
    2. 匹配时置有效电平,匹配时置无效电平和匹配时电平翻转,这个有效电平和无效电平一般是高级定时器里面的一个说法,是和关断、刹车这些功能配合表述的。他说的比较严谨,所以叫有效电平和无效电平。在这里为了理解方面,你可以直接认为置有效电平就是置高电平,置无效电平就是置低电平。那这三个模式都是当 CNT 与 CCR 值相等时执行操作:

      1. 第一个是 CNT 等于 CCR 时 REF 置有效电平,也就是高电平。
      2. 第二个是相等时置无效电平,也就是低电平。
      3. 第三个是相等时电平翻转。
    3. 这些模式就可以用作波形输出了,比如相等时电瓶翻转这个模式,这个可以方便的输出一个频率可调占空比始终为 50% 的 PWM 波形。比如你设置CCR为0,那 CNT 每次更新清零时,就会产生一次 CNT = CCR 的事件,这就会导致输出电平翻转一次,每更新两次,输出为一个周期,并且高电平和低电平的时间是始终相等的,也就是占空比始终为50%。当你改变定时器更新频率时,输出波形的频率也会随之改变,它俩的关系是,输出波形的频率=更新频率/2,因为更新两次输出才为一个周期,这就是这个匹配时电平翻转模式的用途。

      上面这两个相等时置高电瓶和低电瓶感觉用途并不是很大,因为他们都只是一次性的,置完高或低电平后就不管事了,所以这两模式不适合输出连续变化的波形,如果你想定时输出一个一次性的信号,那可以考虑一下这两个模式。

    4. 强制为无效电平和强制为有效电平,这两个模式是 CNT 与CCR无效,REF强制为无效电平或者强制为有效电平。这里这两个模式和冻结模式也差不多,如果你想暂停波形输出,并且在暂停期间保持低电平或者高电平,那你就可以设置这两个强制输出模式。

    5. PWM 模式1和 PWM 模式2,非常重要,他们可以用于输出频率和占空比都可调的 PWM 波形,也是我们主要使用的模式。

      1. PWM 模式1,计数器为向上计数的情况下,它是 CNT 小于 CCR 时, REF 置有效点平, CNT 大于等于 CCR 时 REF 至无效点平。在向下技术的情况下,是 CNT大于 CCR时,REF置无效电瓶CNT小于等于 CCR 时, REF 制有效电平。这个情况比较多,一般我们都只使用向上计数,所以这里向下计数的描述我们就暂时不看了。他们之间也只有大小关系,极性这些东西不同,基本思想都是一样的,我们着重分析一个向上计数的就可以了,然后再对比看一下

        PWM 模式2,在向上计数的情况下, CNT小于CCS, REF 置无效电瓶。CNT大于等于 CCS 时, REF 至有效电瓶。经过观察可以发现它的大小比较关系,和上面这是一样的,区别就是输出的高低电平反过来了,所以 PWM 模式 2 实际上就是 PWM 模式1输出的取反,改变PWM 模式1和 PWM 模式 2 就只是改变了 REF 电瓶的极性而已。

  5. ref 信号可以前往主模式控制器。你可以把这个 ref 映射到主模式的 TRGO 输出上去,不过 ref 的主要去向还是下面这一路,
  6. 通过下面这一路到达一个极性选择,给这个寄存器写 0 信号就会往上走,就是信号电平不翻转,进来啥样出去还是啥样,写一的话信号就会往下走,就是信号通过一个非门取反,那输出的信号就是输入信号高低电平反转的信号,这就是极性选择,就是选择是不是要把高低电平反转一下。
  7. 接着就是输出使能电路了,选择要不要输出。
  8. 最后是 OC1 引脚,这个引脚就是 CH1 通道的引脚,在引脚定义表里就可以知道具体是哪个 GPIO 了。

五.  PWM基本结构

  •  蓝线:CNT
  • 黄线:ARR
  • 红线:CCR,CCR的值可以控制占空比
  • 绿线:输出

REF是一个频率可调,占空比可调的PWM波形。

参数计算

 CK_PSC:预分频器时钟,CK表示CLOCK时钟的意思

分辨率ARR越大越好,CCR越大越好

六.  高级定时器输出比较通道

需要结合外部电路来理解,在其外面通常接一个正极接着一个大功率开关管(一般是MOS管),再来一个MOS管,最后GND

  • 上面一个MOS管左边是控制极,比如说给高电平,右边两根上下的线就导通,低电平就断开,下面的MOS管也是一样,这就是一个基本的推挽电路,中间向右的是输出。
  • 如果上管导通、下管断开,那就是输出高电平,反之低电平;都导通就是电源短路,是不允许的,都断开就是高阻态。
  • 如果又两个这样的推挽电路就形成了H桥电路,可以控制直流电机正反转。
  • 如果有三个这样的推挽电路,就可以用于驱动三相无刷电机。
  • 如果要用单片机来控制,那就需要用两个控制极(如上图推挽电路),并且这两个电极电平相反互补。
  • OC1 和 OC1N 就是两个互补的输出端口,分别控制上下管的导通和关闭
  • 为避免上管还没完全关闭,下管就打开的短暂同时导通现象,就有了死区生成电路

死区生成电路

会在上管关闭的时候延迟一会再导通下管,会在下管关闭的时候延迟一会再导通上管,避免了同时导通的现象。

七,使PA0引脚输出一个PWM波,驱动LED灯呈现不同亮度 代码部分

根据PWM基本结构书写代码

  1. RCC开启时钟,打开要用的TIM外设和GPIO外设的时钟
  2. 配置时基单元,包括时钟源选择
  3. 配置输出比较单元,CCR的值,输出比较模式、极性选择、输出使能
  4. 配置GPIO,将PWM对应的GPIO口初始化为复用推挽输出的配置(参考引脚定义表)
  5. 运行控制,启动计数器从而输出PWM波
/*重要*/
//根据选择的GPIO,初始化相应的输出比较通道
//配置输出比较模块,参数1:选择定时器,参数二:结构体,输出比较的参数
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);


//TIM_OCInitStruct结构体成员有
TIM_OCMode;            //设置输出比较模式
TIM_OutputState;       //设置输出使能
TIM_OutputNState; 
TIM_Pulse;             //设置CCR
TIM_OCPolarity;        //设置输出比较的极性
TIM_OCNPolarity;       //高级定时器
TIM_OCIdleState;    
TIM_OCNIdleState;      //高级定时器



//仅高级定时器使用,在使用高级定时器输出PWM时,需要调用这个函数,使能主输出,否则PWM将不能正常输出
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);




//给输出比较结构体赋一个默认值的
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);


//用来配置强制输出模式,如果在运行中想要暂停输出波形并且强制输出高电平或低电平
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);


//用来配置CCR寄存器预装功能的,预装功能就是影子寄存器,就是写入的值不会立即生效,而是在更新事件才会生效
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);

//用来配置快速使能,手册单脉冲一节有讲
void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);


//外部事件时清除REF信号
void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);


//用来单独设置输出比较的极性的,带N的就是高级定时器里互补通道的配置,OC4没有互补通道,所以没有OC4N函数
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);


//用来单独修改输出使能参数的
void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);


//选择输出比较模式
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);


/*重要*/
//用来单独更改CCR寄存器值的函数,更改占空比
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

GPIO口 和 输入比较通道的对应关系

根据要使用的GPIO口,初始化相应的输出比较通道。

可见引脚定义图。

TIM_OCInitTypeDef 结构体成员

  •   TIM_OCMode;            //设置输出比较模式
    • TIM_OCMode_Inactive   //相等时置无效电平
    • TIM_OCMode_Active   //相等时置有效电平
    • TIM_OCMode_Timing  //冻结模式
    • TIM_OCMode_Toggle    //相等时电平反转
    • TIM_OCMode_PWM1    //PWM模式1
    • TIM_OCMode_PWM2       //PWM模式2
  •   TIM_OutputState;       //设置输出使能
    • TIM_OutputState_Disable   //失能
    • TIM_OutputState_Enable     //使能
  •   TIM_Pulse;                 //设置CCR寄存器值
  •   TIM_OCPolarity;         //设置输出比较的极性
    • TIM_OCPolarity_High     //高极性,极性不反转,ref波形直接输出,有效电平为高电平
    • TIM_OCPolarity_Low      //低极性,ref 电平取反,有效电平为低电平

由于有许多变量是高级定时器才用的,不赋值,成员变量值是不确定的,会导致一些问题

解决办法

使用 TIM_OCStructInit 函数给所有结构体成员都赋初始值。

//给输出比较结构体赋一个默认值的
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值