1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-318813-1-1.html 4)正点原子官方B站:https://space.bilibili.com/394620890 5)正点原子STM32MP157技术交流群:691905614
第十八章 高级定时器实验本章我们主要来学习高级定时器,STM32MP157有2个高级定时器(TIM1和TIM8)。我们将通过四个实验来学习高级定时器的各个功能,分别是高级定时器输出指定个数PWM实验、高级定时器输出比较模式实验、高级定时器互补输出带死区控制实验和高级定时器PWM输入模式实验。 本章分为如下几个小节: 18.1、高级定时器简介; 18.2、高级定时器输出指定个数PWM实验; 18.3、高级定时器输出比较模式实验; 18.4、高级定时器互补输出带死区控制实验; 18.5、高级定时器PWM输入模式实验; 18.1 高级定时器简介 高级定时器的框图在通用定时器框图的基础上加了一些功能,前面如果学习了通用定时器的框图和实验,再看高级定时器的框图时就很好理解了。高级定时器的框图和通用定时器的框图十分相似,但仍有微弱差异,如下是高级定时器的框图:
图18.1. 1高级定时器框图 框图中大部分的模块我们在基本定时器以及通用定时器的框图中都有详细的讲解,这里部分模块我们不再重复详细讲解。 ①是时钟源部分;②是控制器部分(从模式控制器、编码器接口和触发控制器TRGO);③是时基单元;④是输入捕获部分;⑤是输入捕获和输出比较公用部分;⑥是输出比较部分;⑦是断路功能部分。下面我们介绍高级定时器相关的部分。 ③时基单元 高级定时器时基单元功能包括四个寄存器,分别是计数器寄存器(CNT),预分频器寄存器(PSC),自动重载寄存器(ARR)和重复计数器寄存器(RCR)。其中重复计数器RCR是高级定时器独有的,这也是时基单元与通用定时器以及基本定时器的差别。前面三个寄存器都是16位有效,TIMx_RCR寄存器有16位,且16位可用。 重复计数器RCR 在基本/通用定时器发生上/下溢事件时,直接会产生中断然后生成更新事件,但对于高级定时器来说不是这样子,高级定时器在硬件结构上多出了重复计数器,在定时器发生上溢/下溢事件时递减重复计数器的值,只有当重复计数器的值递减为0的时候才会生成更新时间,所以当发生了RCR寄存器的值递减到0+1次的上/下溢时才会产生更新事件。 ⑦断路功能 ⑦中有两个断路输入(TIMx_BKIN和TIMx_BKIN2),用于将定时器的输出信号置于用户可选的安全配置中。断路功能就是电机控制的刹车功能,断路功能的目的是保护由 TIM1 和 TIM8 定时器产生的 PWM 信号所驱动的功率开关,高级定时器的PWM输出的功能特性就是用来控制电机的。使能相应的控制位(TIMx_BDTR寄存器中的MOE、OSSI和OSSR位,TIMx_CR2寄存器中的OISx和OISxN位)来控制是否输出信号以及输出信号的状态,但是无论何时,OCx和OCxN输出不能在同一时间同时处于有效电平上。 因为有两个断路输入,所以断路源可以分为断路 (BRK) 通道的源和断路 2 (BRK2) 通道的源。断路 (BRK) 通道的源可以是连接到 BKIN 引脚的外部源或者内部源(如系统中断、比较器的输出和时钟故障等),断路 2 (BRK2) 的源可以是连接到 BKIN 引脚的外部源或者来自比较器输出的内部源。断路源是或运算关系,如下图是断路BKIN概况。
图18.1. 2短路源是或运算关系 系统复位后默认禁止刹车电路,即默认关闭了断路功能(TIMx_BDTR寄存器中的MOE位为0)。设置TIMx_BDTR寄存器中的BKE位和BKE2 位可以使能或者禁止断路功能。断路输入信号的极性可以通过配置BKP位和BKP2位来选择。 了解完这两个差异之后,我们直接通过实际的实验来学习高级定时器。 18.2 高级定时器输出指定个数PWM实验 本实验配置好的实验工程已经放到了开发板光盘中,路径为:开发板光盘A-基础资料\1、程序源码\11、M4 CubeIDE裸机驱动例程\CubeIDE_project\ 11-1 ATIM_PWM_OUT。 本小节我们来学习使用高级定时器输出指定个数PWM,本实验以高级定时器8为例TIM1操作也类似。关于定时器如何输出PWM波的知识,请大家回顾通用定时器PWM输出实验的介绍。下面下面我们先来了解本实验会涉及的寄存器。 18.2.1 TIM1/TIM8寄存器
- 控制寄存器 1(TIMx_CR1)
图18.2.1. 1 TIMx_CR1寄存器 上图中我们这里仅介绍本章节会用到的一些位,其中: 位0 [CEN] 是计数器使能位,我们前面就遇见过,将该位写入: 0:禁止计数器; 1:使能计数器。 位4 [DIR]是计数器方向设置位,当定时器配置为中心对齐模式或编码器模式时,该位为只读状态,将该位: 0:计数器递增计数; 1:计数器递减计数。 位7 [ARPE]是自动重载预装载使能位(Auto-reload preload enable),该位用于控制自动重载寄存器的影子寄存器是否有效,在基本定时器的章节我们已经讲过,可以回顾前面的内容。将该位: 0:TIMx_ARR 寄存器不进行缓冲; 1:TIMx_ARR 寄存器进行缓冲。 2. 捕获/比较使能寄存器(TIMx_ CCER)
图18.2.1. 2 TIMx_ CCER寄存器 该寄存器控制着各个输入/输出通道的开关,每4个位控制一个通道,我们以通道1(CC1)为例介绍第0~3位。 位0[CC1E]是捕获/比较 1 输出使能位,如果CC1 通道配置为输出,将该位: 0:关闭输出,即OC1 未激活; 1:开启输出,即OC1 信号输出到相应的输出引脚上。 位1[CC1P]是捕获/比较 1 输出极性配置位,对于CC1 通道配置为输出的情况,将该位: 0:OC1 高电平有效 1:OC1 低电平有效 位2[CC1NE]是捕获/比较 1 互补输出使能位,将该位: 0:关闭捕获/比较 1 互补输出; 1:开启捕获/比较 1 互补输出。 位3[CC1NP]是捕获/比较 1 互补输出极性配置位,当CC1 通道配置为输出时,将该位: 0:OC1N 高电平有效; 1:OC1N 低电平有效。 以上的4位就介绍这么多,对于其它的通道也是类似的,直接参考通道1(CC1)的各位配置即可。 3. 事件产生寄存器(TIMx_ EGR)
图18.2.1. 3 TIMx_ EGR寄存器 该寄存器主要是用户用软件更新各类事件和某些寄存器位,这里我们只介绍第0位: 位0[UG]是更新定时器事件的控制位,作用类似定时器溢出更新中断,区别是这里是通过软件去更新,而定时器溢出更新中断是硬件自己完成。本实验就是用到该位去更新定时器事件,对该位写1就可以重新初始化计数器并生成寄存器更新事件,由硬件自动清零。 4. 重复计数器寄存器(TIMx_ RCR)
图18.2.1. 4 TIMx_ RCR寄存器 用户可通过该寄存器设置从预装载寄存器向活动寄存器周期性传输数据。该该寄存器共16位REP[15:0],在PWM模式下,(REP+1) 对应于边沿对齐模式下的 PWM 周期数或者中心对齐模式下的 PWM 半周期数,所以我们可以利用该寄存器来控制要输出PWM波的个数。生成下一重复更新事件之前,无论向TIMx_RCR寄存器写入何值都无影响,所以我们写完该寄存器后,如果要想马上生效就得手动更新事件,即对TIMx_EGR寄存器的UG位写1。 5. 捕获/比较寄存器2(TIMx_ CCR2) 捕获/比较寄存器(TIMx_ CCRx)总共有4个,分别对应4个通道。本小节的实验我们使用的是通道2(CC2),所以直接看TIMx_CCR2:
图18.2.1. 5 TIMx_ CCR2寄存器 该寄存器有16位,对于输出模式,CCR2[15:0] 为预装载值,该值与CNT的值比较,根据比较结果产生相应动作,利用这点,我们通过修改这个寄存器的值,就可以控制PWM的输出脉宽。 6. TIM1/TIM8断路和死区寄存器(TIMx_ BDTR)
图18.2.1. 6 TIMx_ BDTR寄存器 对于通用定时器,只需要配置以上提到的寄存器就够了,如果是高级定时器,我们还需要配置TIMx_ BDTR,该寄存器我们只关注位15[MOE],对此位清0,则禁止禁止 OC 和 OCN 输出,如果要想高级定时器的PWM正常输出,则必须设置MOE位为1,即使能 OC 和 OCN 输出。 18.2.2 定时器的HAL库驱动 本节实验涉及到的HAL库驱动在前面通用定时器章节已经讲解,如HAL_TIM_PWM_Init和HAL_TIM_PWM_ConfigChannel函数都已经在前面章节介绍过,这里就不再重复介绍了。 18.2.3 硬件设计
- 例程功能 用TIM8_CH2输出指定个数PWM,按键KEY0每按下一次,就输出5个PWM,输出的PWM控制BEEP的开和关,开关一次表示一个周期的PWM波形。LED0用于指示程序在运行。
- 硬件资源 1)LED0、KEY0按键和蜂鸣器 BEEP KEY0 LED0 PC7 PG3 PI0 图18.2.2. 1硬件资源 2)定时器8输出通道2(TIM8_CH2) 定时器属于STM32MP157的内部资源,只需要软件设置好即可正常工作。
- 原理图 按键KEY0是低电平有效,蜂鸣器接在PC7上,关于蜂鸣器的实验我们在前面的章节也有介绍过,当PC7输出低电平的时候,蜂鸣器发声,当PC7输出高电平的时候,蜂鸣器停止发声。从原理图看出PC7还可以复用为TIM8_CH2,程序上通过配置PC7复用为TIM8_CH2,然后按下KEY0按键后,TIM8_CH2就输出5个PWM波,从而蜂鸣器发声5次。
图18.2.2. 2引脚部分原理图 从《STM32MP157A&D数据手册》中也可以查阅PC7的复用关系:
图18.2.2. 3 《STM32MP157A&D数据手册》部分截图 18.2.4 软件设计
- STM32CubeMX配置 (1)配置PI0复用为TIM2_CH1 新建一个工程ATIM_PWM_OUT,进入STM32CubeMX插件配置界面后,在Pinout & Configuration处配置PC7复用为TIM8_CH2,如下图所示:
图18.2.4. 1配置PC7复用为TIM8_CH2 (2)配置TIM8时基等参数 在TimersTIM8中配置TIM8_CH2参数,其中,模式配置如下:
图18.2.4. 2配置模式为PWM输出 和第17.3小节通用定时器的配置类似,这里选择定时器8的通道2生成PWM波形。配置完TIM8_CH2的模式后,我们配置TIM8_CH2的时基等参数,如下红框部分需要我们手动配置,其它部分就不需要配置了:
图18.2.4. 3配置TIM8通道2参数 上图的配置参数,除了红框中的要配置以外,其它选项保持默认,本实验用不到刹车功能,所以就不会使能BRK和BRK2。配置部分的介绍如下: Counter Settings(计数器配置)配置如下: Prescaler用于配置定时器预分频值,这里配置为20900-1; Counter Mode用于配置计数模式,我们选择向上计数Up; Counter Period用于配置定时器自动重装载值,我们设置为5000-1; Internal Clock Division (CKD)配置为No Division即时钟不分频; auto-reload preload用于配置自动重载是否使能,我们选择 Enable使能自动重载; 上述参数可以计算定时器的时钟频率为:
计数器的溢出时间:
Repetition Counter(RCR-16 bits value)默认选择为0,这里注意,高级定时器和基本定时器以及通用定时器不同,高级定时器的TIMx_RCR递减为0时才会发生更新事件。 PWM Generation Channel 2用于配置通道2的参数,其中: Mode:用于配置PWM的模式,这里选择PWM mode1,即PWM模式1。另外还有PWM模式2,可以理解PWM mode l是与PWM mode 2模式互补的波,PWM模式1为高电平时PWM模式2为低电平,反之亦然。 Pulse (16 bits value):是占空比值,也可以说是脉冲宽度,即TIMx_CCR2的值,也就是有效电平的值,可以配置在0-5000之间,例如配置0。这里配置2500,即占空比为50%。在后面的实验中,我们可以对TIMx_CCR2寄存器写入新的值来改变占空比,从而控制蜂鸣器声音的大小。 占空比= Output compare preload:输出比较预加载项选择Enable,即在定时器工作时是否能修改Pulse的值,如果禁用此项,表示定时器工作时不能进行修改,只能等到更新事件到来的时候才能进行修改,所以这里选择使能。 Fast Mode用于配置PWM脉冲快速模式,这里我们也不需要,可以不配置。 CH Polarity:输出极性,这里我们选择High,即高电平有效(我们的蜂鸣器是低电平有效,即低电平时蜂鸣器发声)。 CH Idle State用于配置通道的空闲状态,也就是配置PWM不输出时的状态,这里默认选择Reset,即PWM关闭状态下默认为低电平。如果配置为Set就是PWM关闭状态下默认为高电平。 以上配置中要注意的是输出极性和PWM模式,其中TIMx_CNT为TIM8的计数寄存器,用于计数器计数,TIMx_CCR2为TIM8的比较寄存器,其值由用户设置,如上面设置为2500。 板子上的的蜂鸣器是低电平有效,如果配置输出极性为High: 在PWM模式1下当向上计数时,如果TIMx_CNTTIMx_CCR2时通道2为有效电平,否则为无效电平。 如果在PWM模式2下,在向上计数时,如果TIMx_CNTTIMx_CCR2时通道2为无效电平,否则为有效电平。 板子上的蜂鸣器是低电平有效,如果配置输出极性为Low: 在PWM模式1下当向上计数时,如果TIMx_CNTTIMx_CCR2时通道2为无效电平,否则为有效电平。 如果在PWM模式2下,在向上计数时,如果TIMx_CNTTIMx_CCR2时通道2为有效电平,否则为无效电平。 例如如果本实验输出极性为Low,配置为PWM模式2、向上计数、比较值始终为600、自动重装载值为500,如果这么配置的话,计数器最大值只能为500,永远小于600,即TIMx_CNTEGR |= 1 RCR = npwm - 1; 48 TIM8->EGR |= 1 Instance, Channel, TIM_CCx_ENABLE); 8 if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET) 9 { 10 /* 使能主输出 */ 11 __HAL_TIM_MOE_ENABLE(htim); 12 } 13 /* 启用外围设备,但在触发模式下除外,在触发模式下使用触发自动启用 */ 14 tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS; 15 if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr)) 16 { 17 __HAL_TIM_ENABLE(htim);/* 使能TIM1 */ 18 } 19 return HAL_OK; 20 }
18.3.5 硬件设计
- 例程功能 使用定时器1的输出比较模式的翻转功能,让TIM1_CH1、TIM1_CH2和这2路通道输出50%占空比的PWM波,并且每个通道输出不同相位的PWM波,分别是相位25%、相位50%。
- 硬件资源 定时器1输出通道3和4(TIM1_CH3和TIM1_CH4)以及LED0,LED0用于指示程序在运行。 TIM1_CH3 TIM1_CH4 LED0 PE13 PE14 PI0 表18.3.5. 1硬件资源
- 原理图 如下图,底板的JP1排针上引出PE13和PE14,这两个引脚分别对应TIM1_CH3和TIM1_CH4。程序中通过配置这两个IO口输出不同相位的正弦波,通过示波器测试波形进行验证。
图18.3.5. 1原理图部分截图 18.3.6 软件设计
- 程序流程图
图18.3.6. 1程序流程图 2. STM32CubeMX配置 (1)配置PE13和PE14复用为TIM1_CH3和TIM1_CH4 新建一个工程ATIM_PWM_CP,进入STM32CubeMX插件配置界面后,在Pinout & Configuration处配置PE13和PE14复用为TIM1_CH3和TIM1_CH4,如下图所示:
图18.3.6. 2配置TIM1的两个通道 本节实验我们会用到LED0来指示LED0亮,所以同时配置PI0:
图18.3.6. 3配置LED0 (2)配置TIM1时基等参数 在TimersTIM1中先配置TIM1的模式,模式配置如下图所示,我们选择内部时钟,通道3和通道4选择为输出比较功能:
图18.3.6. 4配置模式为输出比较 接下来我们配置TIM1的时基参数和通道3以及通道4的参数,如下红框部分需要我们手动配置,其它部分就保持默认配置:
图18.3.6. 5配置TIM1通道3和通道4的参数 上图的配置参数,除了红框中的要配置以外,其它选项保持默认,本实验用不到刹车功能,所以就不会使能BRK和BRK2。配置部分的介绍如下: Counter Settings(计数器参数配置)配置如下: Prescaler用于配置定时器预分频值,这里配置为209-1; Counter Mode用于配置计数模式,我们选择向上计数Up; Counter Period用于配置定时器自动重装载值,我们设置为1000-1; Internal Clock Division (CKD)配置为No Division即时钟不分频; Repetition Counter(RCR-16 bits value)用于配置高级定时器的TIMx_RCR值,这里注意,高级定时器和基本定时器以及通用定时器不同,高级定时器的TIMx_RCR递减为0时才会发生更新事件。我们这里配置为0,即计数器溢出即发生更新事件。 auto-reload preload用于配置自动重载是否使能,我们选择 Disable,即不使能自动重载; 定时器1的时钟为2倍APB2,即频率为209MHZ,写入自动重载寄存器的值为999。由前面基本定时器讲解的定时器溢出公式得定时器溢出的周期:
结合前面讲的PWM波的周期是2倍的ARR计数器所用的计数器时间,所以得到PWM波的周期是2ms,频率为500HZ。
Output Compare Channel 3用于配置通道3的PWM输出参数,其中:
Mode:用于配置PWM的模式,这里选择Toggle on match,即翻转。 Pulse (16 bits value):是TIMx_CCRx的值,也就是有效电平的值,可以配置在0-1000之间,例如配置0。这里配置500,即初相位为50%。 相位= Output compare preload:输出比较预加载项选择Enable,即在定时器工作时是否能修改Pulse的值,如果禁用此项,表示定时器工作时不能进行修改,只能等到更新事件到来的时候才能进行修改,所以这里选择使能。 CH Polarity:输出极性,这里默认选择High,即高电平有效,修改为Low也是可以的,在本实验中无关紧要,只是想输出PWM波而已。 CH Idle State用于配置通道的空闲状态,也就是配置PWM不输出时的状态,这里默认选择Reset,即PWM关闭状态下默认为低电平。如果配置为Set就是PWM关闭状态下默认为高电平。 (3)配置GPIO 本实验还会用到LED0,LED0配置和前面实验的一样:
图18.3.6. 6配置LED0 (4)配置时钟 本实验我们采用外部24MHz的时钟HSE(也可以采用内部时钟),配置时钟树,经过PLL3锁相环以后,APB1的时钟频率为最大209MHz(也可以配置其它频率):
图18.3.6. 7配置HSE 我们选择HSE,作为锁相环PLL3的时钟源,在MCU子系统时钟里输入209并回车,STM32CubeMX会自动为我们计算参数,然后再手动配置APB1DIV、APB2DIV和APB3DIV的分频值为2。当APB1DIV的分频数大于1的时候,基本定时器的倍频器倍频值始终为2,所以通用定时器TIM8的时钟频率为209MHz。
图18.3.6. 8配置系统时钟 (5)配置生成独立的文件 配置生成独立的.c和.h头文件,如下图:
图18.3.6. 9配置生成独立的.c和.h文件 3. 生成工程并添加BSP文件夹 配置好后,按下“Ctrl+S”保存修改配置,生成工程,将上一章节的BSP文件夹拷贝到本机工程实验中,因为我们会用到LED0的驱动,其它的LED和按键的驱动,如果不用的话就自行删除掉,只留下LED0部分。如下:
图18.3.6. 10生成工程 4. 分析tim.c文件 tim.c文件代码如下,代码中已经给出详细注释,可以很容易看懂。本节实验我们没有用到刹车功能。
1 #include "tim.h"
2
3 TIM_HandleTypeDef htim1; /* TIM1句柄 */
4
5 /**
6 * TIM1时基以及TIM1通道3和通道4初始化函数
7 * @brief 高级定时器TIM1输出比较模式 初始化函数(使用输出比较模式)
8 * @note
9 * 配置高级定时器TIM1 2路输出比较模式PWM输出,实现50%占空比,不同相位控制
10 * 注意,本例程输出比较模式,每2个计数周期才能完成一个PWM输出,因此输出频率减半
11 * 另外,我们还可以开启中断在中断里面修改CCRx,从而实现不同频率/不同相位的控制
12 * 但是我们不推荐这么使用,因为这可能导致非常频繁的中断,从而占用大量CPU资源
13 *
14 * 高级定时器的时钟来自APB2,当APB2DIV≥2分频的时候
15 * 高级定时器的时钟为APB2时钟的2倍, 而APB2为104.5M, 所以定时器时钟 = 209Mhz
16 * 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
17 * Ft=定时器工作频率,单位:Mhz
18 * 本例配置TIM1两个通道的PWM波周期为2ms,频率为500Hz
19 * 通道3相位为50%,通道4相位为25%
20 * @param 无
21 * @retval 无
22 */
23 void MX_TIM1_Init(void)
24 {
25 TIM_ClockConfigTypeDef sClockSourceConfig = {0};
26 TIM_MasterConfigTypeDef sMasterConfig = {0};
27 TIM_OC_InitTypeDef sConfigOC = {0};
28 TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
29
30 htim1.Instance = TIM1; /* 定时器1 */
31 htim1.Init.Prescaler = 209-1; /* 定时器分频 */
32 htim1.Init.CounterMode = TIM_COUNTERMODE_UP; /* 向上计数模式*/
33 htim1.Init.Period = 1000-1; /* 自动重装载值 */
34 htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;/* 不分频 */
35 htim1.Init.RepetitionCounter = 0; /* 重复计数器寄存器为0 */
36 /* 不使能影子寄存器TIMx_ARR */
37 htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
38 if (HAL_TIM_Base_Init(&htim1) != HAL_OK) /* 初始化定时器时基 */
39 {
40 Error_Handler();
41 }
42 /* 使用内部时钟 */
43 sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
44 /* 时钟源配置 */
45 if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
46 {
47 Error_Handler();
48 }
49 if (HAL_TIM_OC_Init(&htim1) != HAL_OK) /* 输出比较模式初始化 */
50 {
51 Error_Handler();
52 }
53 /* 定时器主从模式配置,这里我们没有用到主从模式 */
54 sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
55 sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
56 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
57 if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
58 {
59 Error_Handler();
60 }
61 /* 定时器通道3和通道4的配置 */
62 sConfigOC.OCMode = TIM_OCMODE_TOGGLE; /* 输出比较模式翻转功能 */
63 sConfigOC.Pulse = 500-1; /* 设置通道3输出比较寄存器的值为500-1 */
64 sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;/* 输出比较极性为高 */
65 sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;/* 互补输出比较极性位高 */
66 sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;/* 失能输出比较快速模式 */
67 /* 选择空闲状态下非工作状态 */
68 sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
69 /* 设置空闲状态下非工作状态 */
70 sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
71 /* 初始化定时器的输出比较通道3 */
72 if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
73 {
74 Error_Handler();
75 }
76 /* 通道3预装载使能 */
77 __HAL_TIM_ENABLE_OCxPRELOAD(&htim1, TIM_CHANNEL_3);
78 sConfigOC.Pulse = 250-1;/* 设置通道4输出比较寄存器的值为250-1 */
79 /* 初始化定时器的输出比较通道4 */
80 if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
81 {
82 Error_Handler();
83 }
84 /* 通道4预装载使能 */
85 __HAL_TIM_ENABLE_OCxPRELOAD(&htim1, TIM_CHANNEL_4);
86 /* 设置运行模式下非工作状态选项 */
87 sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
88 /* 设置在空载下非工作状态选项 */
89 sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
90 sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;/* 锁电平参数 */
91 sBreakDeadTimeConfig.DeadTime = 0; /* 死区时间设置为0 */
92 /* 失能TIMx刹车输入 */
93 sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
94 /* TIM1刹车输入管脚极性为高电平有效 */
95 sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
96 sBreakDeadTimeConfig.BreakFilter = 0; /* TIM1刹车输入滤波为0 */
97 /* TIM1刹车输入BRK为输入模式 */
98 sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;
99 /* TIM1刹车输入BRK2失能 */
100 sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
101 /* 刹车输入BRK2高电平有效 */
102 sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
103 /* TIM1刹车输入2滤波为0 */
104 sBreakDeadTimeConfig.Break2Filter = 0;
105 sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT;
106 /* 失能自动输出功能,只能后期手动通过软件设置MOE */
107 sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
108 /* 配置间隔功能,停滞时间,锁定级别 */
109 if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
110 {
111 Error_Handler();
112 }
113 HAL_TIM_MspPostInit(&htim1); /* 定时器输出比较底层驱动 */
114 }
115 /**
116 * @brief 定时器输出比较模式时钟初始化函数
117 * @param timHandle :定时器句柄
118 * @note 此函数会在MX_TIM1_Init中调用,如HAL_TIM_Base_Init函数中
119 * @retval 无
120 */
121 void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
122 {
123 if(tim_baseHandle->Instance==TIM1)
124 {
125 __HAL_RCC_TIM1_CLK_ENABLE(); /* 开启TIM1时钟 */
126 }
127 }
128 /**
129 * @brief 定时器输出比较模式通道引脚初始化函数
130 * @param timHandle :定时器句柄
131 * @note 此函数会被MX_TIM1_Init函数调用
132 * @retval 无
133 */
134 void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
135 {
136
137 GPIO_InitTypeDef GPIO_InitStruct = {0};
138 if(timHandle->Instance==TIM1)
139 {
140 __HAL_RCC_GPIOE_CLK_ENABLE(); /* 开启GPIOE的时钟 */
141 /**TIM1 GPIO Configuration
142 PE13 ------> TIM1_CH3
143 PE14 ------> TIM1_CH4
144 */
145 /* 指定引脚为13和14 */
146 GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14;
147 /* 指定模式为复用推挽输出 */
148 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */
149 GPIO_InitStruct.Pull = GPIO_NOPULL; /* 无上/下拉 */
150 /* 速度等级为低,也可以配置为其它等级 */
151 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
152 GPIO_InitStruct.Alternate = GPIO_AF1_TIM1; /* 复用选择为AF1 */
153 HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); /* 初始化GPIOE */
154 }
155 }
156 /**
157 * @brief 定时器输出比较模式时基反初始化函数
158 * @param timHandle :定时器句柄
159 * @note 如果需要,用户可以调用此函数来关闭TIM1
160 * @retval 无
161 */
162 void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
163 {
164 if(tim_baseHandle->Instance==TIM1)
165 {
166 __HAL_RCC_TIM1_CLK_DISABLE(); /* 关闭TIM1时钟 */
167 }
168 }
169 /* USER CODE BEGIN 1 */
- 修改main.c文件 main.c文件中的部分代码如下所示,其中标红的字体之间的代码是我们手动添加的。
1 #include "main.h"
2 #include "tim.h"
3 #include "gpio.h"
4
5 /* USER CODE BEGIN Includes */
6 #include "./BSP/Include/led.h"
7 /* USER CODE END Includes */
8
9 void SystemClock_Config(void);
10
11 int main(void)
12 {
13 HAL_Init(); /* HAL库初始化 */
14
15 if(IS_ENGINEERING_BOOT_MODE())
16 {
17 SystemClock_Config(); /* 初始化系统时钟 */
18 }
19
20 MX_GPIO_Init(); /* 初始化GPIO */
21 MX_TIM1_Init(); /* 初始化TIM1 */
22 /* USER CODE BEGIN 2 */
23 led_init(); /* 初始化led */
24 HAL_TIM_OC_Start(&htim1,TIM_CHANNEL_3); /* 启动定时器1通道3 */
25 HAL_TIM_OC_Start(&htim1,TIM_CHANNEL_4); /* 启动定时器1通道4 */
26 /* USER CODE END 2 */
27 while (1)
28 {
29 /* USER CODE BEGIN 3 */
30 LED0_TOGGLE(); /* LED0翻转 */
31 HAL_Delay(500); /* 延时500ms */
32 }
33 /* USER CODE END 3 */
34 }
第24和第25行,使能TIM1通道3和通道4的输出比较信号的生成。
- 编译运行 本次实验需要使用示波器来验证。找4根一边是公头一边是母头的杜邦线,两根杜邦线分别接在JP1排针引出的PE13和PE14上,两根杜邦线接在板子引出的地线上,例如底板的JP7和JP8排针处有引出地线,如下图:
图18.3.6. 11连接好PWM输出排针 将引出的线接入到示波器的两个通道,用于测量TIM1的通道3和通道4的波形。本实验笔者使用正点原子自主研发的DS100 Mini数字示波器,此示波器支持两个通道,屏幕分辨率为480*320,采样率为250MSa/S,可以保存波形:
图18.3.6. 12正点原子DS100 Mini数字示波器 开发板接好线,拨码开关拨成MCU启动方式,然后开发板上电。STM32CubeIDE进入Debug模式,点击运行按钮来运行调试,可以看到开发板底板的LED0灯在闪烁,说明程序已经在运行了。如下图,测试出波形周期为2ms,频率为500Hz,和我们前面计算的一致:
图18.3.6. 13波形周期 ARR值为固定为1000,所以占空比则固定为50%。通道3和通道4我们分别配置为500和250,即两者的相位分别为50%和25%,两者下降沿或者上升沿相位差为25%(即250us),如下图所示,绿色波形是PE13引脚输出的,黄色波形是PE14引脚输出的:
图18.3.6. 14两波形相位差 18.4 高级定时器互补输出带死区控制实验 本实验配置好的实验工程已经放到了开发板光盘中,路径为:开发板光盘A-基础资料\1、程序源码\11、M4 CubeIDE裸机驱动例程\CubeIDE_project\ 11-3 ATIM_PWM_DT。 18.4.1 互补输出和死区时间 本小节我们来学习如何使用高级定时器的互补输出和死区插入功能。
- 互补输出 定时器的PWM互补输出很好理解,也就是两个PWM波形是互补的状态,例如,如果CHx输出高电平,则CHxN输出低电平,两者的PWM波形是互补输出的特性。如下图,OCXREF是参考基准信号,OCx和OCxN是两个通道CHx和CHxN的输出,即主输出OCx或互补输出OCxN,OCxN与OCXREF时序相位同步,OCXN信号与OCXREF时序反相同步,两个输出波形互补:
图18.4.1. 1定时器的PWM互补输出波形 2. 死区时间 说到互补输出,还会涉及到一个死区时间的概念,死区时间可以理解为某个处于相对无效状态的时间或空间。死区时间在步进电机、伺服电机、逆变器和开关电源中使用的比较多,例如下图是一个逆变器电机驱动原理示意图红框的部分是两组桥,每组桥由功率器件组成,每组桥的上半部分是上桥(S1和S3),下半部分是下桥(S2和S4),每组桥的上半桥和下半桥是不能同时导通的,否则电源会通过上下两个桥形成短路,烧毁功率器件。
图18.4.1. 2逆变器电机驱动原理示意图 在高速的PWM驱动信号在达到功率元件的控制极时,会由于各种各样的原因产生延迟,从而造成某个半桥不能立马关闭,会延迟一段时间才真正关闭,这个时候就可能会与另外一个半桥处于同时开启的状态,从而造成短路。死区时间就是在上半桥关断后,延迟一段时间再打开下半桥,或者在下半桥关断后,延迟一段时间再打开上半桥,从而避免两个桥同时开启的状态。所延迟的这段延迟时间就是死区时间,即上、下半桥的元件都是关断的状态。关于桥大家可不必深究,我们引出桥只是为了说明死区时间。 如下图,OCXREF是参考基准信号,OCx和OCxN是两个通道CHx和CHxN的输出,OCxN与OCXREF时序相位同步,只是其上升沿相对OCXREF上升沿存在延迟,OCXN信号与OCXREF时序反相同步,并且其上升沿相对OCXREF下降沿存在延迟,这个延迟时间就是插入的死区时间:
图18.4.1. 3死区时间 高级控制定时器 (TIM1/TIM8) 可以输出两路互补信号,并管理死区时间,用户必须根据与输出相连接的器件及其特性(电平转换器的固有延迟、开关器件产生的延迟)来调整死区时间,每路输出可以独立选择输出极性(主输出OCx或互补输出OCxN),可通过对TIMx_CCER寄存器中的 CCxP 和 CCxNP 位执行写操作来完成极性选择。值得注意的是,如果延迟时间大于有效输出(OCx 或 OCxN)的宽度,否则相应通道的输出呈无效状态,即不会产生相应的脉冲。 3. 死区时间计算方法 1)先确死区及采样时钟频率: 通过配置TIMx_CRx寄存器的CKD[1:0]位指示定时器时钟(CK_INT)频率与死区发生器和数字滤波器(ETR、TIx)所使用的死区及采样时钟(tDTS)之间的分频比,当CKD[1:0]位配置为: 00:t DTS =tCK_INT 01:t DTS =2tCK_INT 10:t DTS =4tCK_INT 后面的实验中我们会默认配置CKD[1:0]位为10,即tDTS =4*tCK_INT。再结合定时器1的内部时钟为2倍APB2,即209MHZ,得到fDTS = fCK_INT /4 = 209MHZ/4 = 52.25MHZ。由fDTS = 52.25MHZ得到采样时钟tDTS = 19.14 ns。 2)确定DTG[7:0]位: 可以通过配置断路和死区寄存器(TIMx_ BDTR)来控制定时器的断路和死区功能,该寄存器的低8位DTG[7:0]位用于用于定义插入到互补输出之间的死区持续时间,死区时间DT 与该持续时间相对应,死区时间计算方法:
表18.4.1. 1死区时间计算方法 本实验中我们会配置配置死区发生器为十进制数的100,即二进制数0110 0100,这是符合TIMx_BDTR寄存器所讲的DTG[7:0]位的第一种情况: DT=DTG[7:0] * tdtg,其中 tdtg = tDTS,DT是死区时间,可以得到死区时间DT = 100*19.14 ns =1.914us。本实验我们也是设置DTG[7:0]的值为100,到后面下载验证小节,我们通过示波器验证一下这个死区时间和我们这里的计算值是否正确。 关于此寄存器的各位我们会在下面的寄存器小节介绍。 18.4.2 TIM1/TIM8寄存器 高级定时器互补输出带死区控制除了用到定时器的时基单元:计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、自动重载寄存器(TIMx_ARR) 之外。主要还用到以下这些寄存器:
- 控制寄存器 1(TIMx_CR1) TIM1/TIM8的控制寄存器1描述如下图所示:
图18.4.2. 1 TIMx_CR1寄存器 上图中我们只列出了本实验需要用的一些位,其中: 位7(APRE)用于控制自动重载寄存器的影子寄存器是否有效,在基本定时器的时候已经讲过,请回顾。本实验中,该位要置1。 CKD[1:0]位指示定时器时钟(CK_INT)频率与死区发生器以及数字滤波器(ETR、TIx)所使用的死区及采样时钟(t DTS)之间的分频比: 00:t DTS =tCK_INT 01:t DTS =2tCK_INT 10:t DTS =4tCK_INT 11:保留,不要设置成此值 我们设置CKD[1:0]位为10,t DTS =4*tCK_INT,即f DTS = fCK_INT /4,再结合定时器1的内部时钟为2倍APB2,即209MHZ,得到fDTS = fCK_INT /4 = 209MHZ/4 = 52.25MHZ。 CEN位,用于使能计数器的工作,必须要设置该位为1,才可以开始计数。 2. 捕获/比较模式寄存器1/2(TIMx_CCMR1/2) TIM1/TIM8的捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器一般有2个:TIMx_CCMR1和TIMx _CCMR2。TIMx_CCMR1控制CH1和CH2,而TIMx_CCMR2控制CH3和CH4。TIMx_CCMR2寄存器描述如下图所示:
图18.4.2. 2 TIMx_CCMR2寄存器 该寄存器的有些位在不同模式下,功能不一样,我们现在用到输出比较模式。 本实验我们用到了定时器1输出比较的通道3,所以我们需要配置TIMx_CCMR2模式设置位OC3M[3:0],我们使用的是PWM模式1,所以这4位必须设置为0110。除此之外,我们还要设置输出比较的预装载使能位,通道3对应输出比较的预装载使能位OC3PE置1。 3. 捕获/比较使能寄存器(TIMx_ CCER) TIM1/TIM8的捕获/比较使能寄存器,该寄存器控制着各个输入输出通道的开关和极性。TIMx_CCER寄存器描述如下图所示:
图18.4.2. 3 TIMx_CCER寄存器 该寄存器比较简单,要让TIM1的通道3输出,我们需要把对应的捕获/比较3输出使能位CC3E置1。因为我们还需要通道3的互补输出通道也输出,因此还需要把CC3NE位置1。CC3P和CC3NP分别是通道3输出和通道3互补输出的极性配置位,因为我们需要的是互补的PWM波,所以通道3输出和通道3互补输出的极性要相同,这里都设置为低电平有效,即CC3P和CC3NP位都置1。 4. 捕获/比较寄存器1/2/3/4(TIMx_CCR1/2/3/4) 捕获/比较寄存器(TIMx_ CCR1/2/3/4),该寄存器总共有4个,对应4个通道CH1~CH4。我们使用的是通道3,所以来看看TIMx_ CCR3寄存器描述如下图所示:
图18.4.2. 4 TIMx_ CCR3寄存器 对于高级定时器该寄存器16位有效位,和通用定时器PWM输出实验一样,在PWM 模式1的情况下,我们通过改变该寄存器的值来改变PWM波的占空比。 5. TIM1/TIM8断路和死区寄存器(TIMx_ BDTR) TIM1/TIM8断路和死区寄存器,该寄存器各位描述如下图所示:
图18.4.2. 5 TIMx_ BDTR寄存器 该寄存器控制定时器的断路和死区控制的功能,我们先看断路控制: 高级定时器TIM1/TIM8有两路断路输入通道(BRK和BRK),本实验我们用到断路1通道(PA6引脚)。断路也就是我们所说的“刹车输入”。要使用断路就要使能位12(BKE),将该位置1即可。