28. 定时器的互补输出模式

一、什么是互补输出

互补输出

  上图中,CH1 输出黄色的 PWM,它的互补通道 CH1N 输出绿色的 PWM。通过对比,可以知道这两个 PWM 刚好是反过来的,CH1 的 PWM 为高电平期间,CH1N 的 PWM 则是低电平,反之亦然,这就是 互补输出

带死区控制的互补输出

  上图中,CH1 输出的 PWM 和 CH1N 输出的 PWM 在高低电平转换间,插入了一段时间才实现互补输出。这段时间称为死区时间,可以通过 TIMx_BDTR 寄存器的 DTG[7:0] 位配置控制实现互补输出。这段时间称为 死区时间,可以通过 TIMx_BDTR 寄存器的 DTG[7:0] 位配置控制死区时间的长度。上图中,箭头指出的两段死区时间的长度是一样的,因为都是由同一个死区发生器产生。

二、互补输出的应用场合

  带死区控制的互补输出经常被用于控制电机的 H 桥中。

H桥简图

  上图是 H 桥的简图,实际控制电机正反转的 H 桥会根据复杂些,而且更多的是使用 MOS 管。上图的 H 桥搭建全部使用的是 NPN,并且导通逻辑都是基极为高电平时导通。如果 Q1 和 Q4 三极管导通,那么电机的电流方向是从左到右(假设电机正转);如果 Q2 和 Q3 三极管导通,那么电机的电流方向是从右到左(假设电机反转)。上述就是 H 桥控制电机正反转的逻辑原理。但是同一侧的三极管是不可以同时导通的,否则会短路,比如:Q1 和 Q2 同时导通或者 Q3 和 Q4 同时导通,这都是不可取的。

  当 OC1N 输出高电平的时候,OC1 输出就是低电平,刚好 Q2 和 Q3 导通,电机的电流方向是从右到左(假设电机反转);反之,OC1 输出高电平的时候,OC1N 输出就是低电平,刚好 Q1 和 Q4 导通,电机的电流方向是从左到右(假设电机正转),这似乎已经完美解决电机正反转问题了。实际上,元器件是有延迟特性的,比如:控制信号从 OC1 传导至电机,是要经过一定的时间的,复杂的 H 桥电路更是如此。由于元器件特性,就会导致直接使用互补输出信号驱动 H 桥时存在短路现象。为了避免这种情况,于是就有了带死区控制的互补输出来驱动 H 桥电路。用户必须根据与输出相连接的器件及其特性(电平转换器的固有延迟、开关器件产生的延迟)来调整死区时间。

三、死区时间的计算

捕获比较通道的输出部分

  死区时间是由 TIMx_CR1 寄存器的 CKD[1:0] 位和 TIMx_BDTR 寄存器的 DTG[7:0] 位来设置。

死区时间控制

  死区时间计算分三步走:

  第一步:通过 CKD[1:0] 位确定 \(T_{DTS}\)。根据 CKD[1:0] 位的描述,可以得到下面的式子:

\[T_{DTD} = \frac{1}{F_{DTS}} = \frac{2 ^ {CKD[1:0]}}{F_{clk}} \]

  其中:CKD[1:0]:CKD[1:0] 位设置的值。\(F_{CLK}\):定时器的时钟源频率。

  第二步:根据 DTG[7:5] 选择计算公式。

  第三步:代入选择的公式计算。

  假设定时器时钟源频率是 168MHz,我们设置 CKD[1:0] 位的值为 2,DTG[7:0] 位的值为 250。250 的二进制数为 11111010,即 DTG[7:5]为 111,所以选择第四条公式:\(DT (32 + DTG[4:0] × T_{DTG})\)。代入上面的式子可得:

\[T_{DTS} = \frac{2^{CKD[1:0]}}{F_{clk}} = \frac{2 * 2}{168000000} = 23.81ns \]

\[DT = (32 + DTG[4:0]) * 16 * T_{DTG} = (32 + 26) * 16 * 23.81ns = 22.01us \]

四、刹车功能

刹车功能

  将 TIMx_BDTR 寄存器的位 12 BKE 位置 1 可以使能刹车信号。刹车输入信号极性由位 13 BKP 位设置。使能刹车功能后,由 TIMx_BDTR 的 MOE、OSSI、OSSR 位,TIMx_CR2 的 OISx、OISxN 位,TIMx_CCER 的 CCxE、CCxNE 位控制 OCx 和 OCxN 输出状态。无论何时,OCx 和 OCxN 输出都不能同时处在有效电平。

  发生刹车后,会产生如下步骤:

  1. MOE 位被清零,OCx 和 OCxN 为无效、空闲或复位状态(OSSI位选择)。
  2. OCx 和 OCxN 的状态:由相关控制位状态决定,当使用互补输出时,会根据情况自动控制输出电平。
  3. BIF 位置 1,如果使能了 BIE 位,还会产生刹车中断;如果使能了 TDE 位,会产生 DMA 请求。
  4. 如果 AOE 位置 1,在下一个更新事件 UEV 时,MOE 位被自动置 1。

五、常用的寄存器

5.1、TIM1和TIM8控制寄存器

TIM1和TIM8控制寄存器1

  TIMx_CR1 寄存器 CMS[9:8] 位指示定时器时钟(CK_INT)频率与死区发生器以及数字滤波器(ETR、TIx)所使用的死区及采样时钟(tDTS)之间的分频比。

  TIMx_CR1 寄存器位 7(ARPE)用于控制自动重载寄存器是否进行缓冲,如果 ARPE 位置 1,ARR 起缓冲作用,即只有在更新事件发生时才会把 ARR 的值写入其影子寄存器里;如果 ARPE 位置 0,那么修改自动重载寄存器的值时,该值会马上被写入其影子寄存器中,从而立即生效。

  TIMx_CR1 寄存器 CMS[6:5] 位,用于设置边沿对齐模式还是中心对齐模式。当 CMS[1:0] 位设置为 00 时,为边沿对齐模式,其它值为中心对齐模式。

  TIMx_CR1 寄存器位 4 DIR 位,用于控制定时器的计数方向。设置 DIR 位为 0 时,为递增计数。设置 DIR 位为 1 时,为递减计数。当定时器配置为中心对齐模式或编码器模式时,该位为只读状态。

  TIMx_CR1 寄存器位 0 CEN 位,用于使能计数器的工作,必须要设置该位为 1,计数器才会开始计数。

TIM1和TIM8控制寄存器2

5.2、TIM1和TIM8从模式控制寄存器

TIM1和TIM8从模式控制寄存器

  该寄存器的 SMS[2:0]位,用于从模式选择,其实就是选择计数器输入时钟的来源。比如通用定时器中断例程中我们设置 SMS[2:0]=000,禁止从模式,这样 PSC 预分频器的时钟就直接来自内部时钟(CK_INT),按照我们例程 System_Clock_Init() 函数的配置,频率为 84Mhz(APB1 总线时钟频率的 2 倍)。

5.3、TIM1和TIM8捕获/比较模式寄存器

TIM1和TIM8捕获/比较模式寄存器1

  该寄存器的有些位在不同模式下,功能不一样,我们前面已经说过。比如我们要让 TIM1 的 CH1 输出 PWM 波为例,该寄存器的模式设置位 OC1M[2:0]就是对应着通道 1 的模式设置,此部分由 3 位组成,总共可以配置成 8 种模式,我们使用的是 PWM 模式,所以这 3 位必须设置为 110 或者 111,分别对应 PWM 模式 1 和 PWM 模式 2。这两种 PWM 模式的区别就是输出有效电平的极性相反,这里我们设置为 PWM 模式 1。位 3 OC1PE 是输出比较通道 1 的预装使能,该位需要置 1,另外 CC1S[1:0]用于设置通道 1 的方向(输入/输出)默认设置为 0,就是设置通道作为输出使用。

5.4、TIM1和TIM8捕获/比较使能寄存器

TIM1和TIM8捕获/比较使能寄存器

  该寄存器控制着各个输入输出通道的开关和极性。如果我们想让要让 TIM1 的 CH1 输出 PWM 波,这里我们要使能 CC1E 位,该位是通道 1 输入/输出使能位,要想 PWM 从 IO 口输出,这个位必须设置为 1。如果我们想要通道 1 的互补通道输出相反的 PWM 波,我们需要把对应的捕获/比较 1 输出使能位CC1E 置 1。CC1P 和 CC1NP 分别是通道 1 输出和通道 1 互补输出的极性设置位。

5.5、TIM1和TIM8计数器

TIM1和TIM8计数器

  TIM1/TIM8 的计数寄存器都是 16 位有效的,计数模式可以是递增计数模式、递减计数模式和中心对齐计数模式。和基本定时器一样,可以直接写该寄存器设置计数的初始值,也可以读取该寄存器获取计数器值。

5.6、TIM1和TIM8预分频器

TIM1和TIM8预分频器

  定时器的预分频寄存器都是 16 位的,即写入该寄存器的数值范围是 0 到 65535,表示 1 到 65536 分频。比如我们要 8400 分频,就往该寄存器写入 8399。

5.7、TIM1和TIM8自动重载寄存器

TIM1和TIM8自动重载寄存器

  在 F4 系列中,TIM2 和 TIM5 的自动重装载寄存器是 32 位的,其他通用定时器自动重载寄存器是低 16 位有效。该寄存器可以由 APRE 位设置是否进行缓冲。计数器的值会和自动重装寄存器影子寄存器进行比较,当两者相等,定时器就会溢出,从而发生更新事件,如果打开了更新中断,还会发生更新中断。

5.8、TIM1和TIM8捕获/比较寄存器

TIM1和TIM8捕获/比较寄存器1

  捕获/比较寄存器(TIMx_CCR1),该寄存器只有 1 个,对应通道 CH1。我们使用的是通道 1。在输出模式下,捕获/比较寄存器影子寄存器的值与 CNT 的值比较,根据比较结果产生相应动作,利用这点,我们通过修改这个寄存器的值,就可以控制 PWM 的占空比了。

5.9、TIM1和TIM8断路和死区寄存器

TIM1和TIM8断路和死区寄存器

  DTG[7:0] 位,用于设置死区时间。

  位 12 BKP 位是选择断路输入信号有效电平。

  位 13 BKE 位是断路输入功能,如果我们想要使用断路输入功能,需要将位 12 BKE 位置 1 即可。

  位 14 AOE 位是自动输出使能位,如果使能 AOE 位,那么在我们输入刹车信号后再断开了刹车信号,互补的 PWM 会自动恢复输出,如果失能 AOE 位,那么在输入刹车信号后再断开了刹车信号,互补的 PWM 就不会恢复输出,而是一直保持刹车信号输入时的状态。为了方便观察,我们使能该位,即置 1。

  位 15 MOE 位是使能主输出,想要高级定时器的通道正常输出,则必须设置 MOE 位为 1。

六、高级定时器对应通道引脚

【1】、TIM1 对应通道引脚及其重映射

通道名 通道引脚 重映射通道引脚 互补通道名 互补通道引脚 互补重映射通道引脚
Channel 1 PA8 PE9 Channel 1N PA7 PE8/PB13
Channel 2 PA9 PE11 Channel 2N PB0 PE10/PB14
Channel 3 PA10 PE13 Channel 3N PB1 PE12/PB15
Channel 4 PA11 PE14 Channel 4N

【2】、TIM8 对应通道引脚及其重映射

通道名 通道引脚 重映射通道引脚 互补通道名 互补通道引脚 互补重映射通道引脚
Channel 1 PC6 Channel 1N PA5 PA7
Channel 2 PC7 Channel 2N PB0 PB14
Channel 3 PC8 Channel 3N PB1 PB15
Channel 4 PC9 Channel 4N

七、互补输出配置步骤

7.1、使能定时器时钟和对应通道的GPIO时钟

  使能高级定时器的时钟。

#define __HAL_RCC_TIM1_CLK_ENABLE()     do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->APB2ENR, RCC_APB2ENR_TIM1EN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_TIM1EN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)
#define __HAL_RCC_TIM8_CLK_ENABLE()   do { \
                                      __IO uint32_t tmpreg = 0x00U; \
                                      SET_BIT(RCC->APB2ENR, RCC_APB2ENR_TIM8EN);\
                                      /* Delay after an RCC peripheral clock enabling */ \
                                      tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_TIM8EN);\
                                      UNUSED(tmpreg); \
                                      } while(0U)

  使能定时器通道引脚对应的 GPIO 的时钟。

#define __HAL_RCC_GPIOA_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)
#define __HAL_RCC_GPIOB_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)
#define __HAL_RCC_GPIOC_CLK_ENABLE()  do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)
#define __HAL_RCC_GPIOD_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)

7.2、配置定时器基本工作参数

  HAL 库提供定时器输出 PWM 的初始化函数,它的说明如下:

HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim);

  其中,htimTIM_HandleTypeDef 结构体类型指针变量(亦称定时器句柄),结构体定义如下:

typedef struct
{
    TIM_TypeDef *Instance;                              // 寄存器基地址
    TIM_Base_InitTypeDef Init;                          // 定时器初始化结构体
    HAL_TIM_ActiveChannel Channel;                      // 定时器通道
    DMA_HandleTypeDef *hdma[7];                         // DMA管理结构体

    HAL_LockTypeDef Lock;                               // 锁对象
    __IO HAL_TIM_StateTypeDef State;                    // 定时器状态
    __IO HAL_TIM_ChannelStateTypeDef ChannelState[4];   // 定时器通道状态
    __IO HAL_TIM_ChannelStateTypeDef ChannelNState[4];  // 定时器互补通道状态
    __IO HAL_TIM_DMABurstStateTypeDef DMABurstState;    // DMA溢出状态
} TIM_HandleTypeDef;

  Instance指向定时器寄存器基地址,可选值如下:

#define TIM1                ((TIM_TypeDef *) TIM1_BASE)
#define TIM8                ((TIM_TypeDef *) TIM8_BASE)

  Init定时器初始化结构体,用于配置定时器的相关参数,它的定义如下:

typedef struct
{
    uint32_t Prescaler;             // 预分频系数
    uint32_t CounterMode;           // 计数模式
    uint32_t Period;                // 自动重装载值
    uint32_t ClockDivision;         // 时钟分频因子
    uint32_t RepetitionCounter;     // 重复计数器值
    uint32_t AutoReloadPreload;     // 自动重装载值预载入功能
} TIM_Base_InitTypeDef;

  Prescaler预分频系数,即写入预分频寄存器的值,范围 0 到 65535。

  CounterMode计数器计数模式,。

#define TIM_COUNTERMODE_UP                 0x00000000U                          /*!< Counter used as up-counter   */
#define TIM_COUNTERMODE_DOWN               TIM_CR1_DIR                          /*!< Counter used as down-counter */
#define TIM_COUNTERMODE_CENTERALIGNED1     TIM_CR1_CMS_0                        /*!< Center-aligned mode 1        */
#define TIM_COUNTERMODE_CENTERALIGNED2     TIM_CR1_CMS_1                        /*!< Center-aligned mode 2        */
#define TIM_COUNTERMODE_CENTERALIGNED3     TIM_CR1_CMS                          /*!< Center-aligned mode 3        */

  Period自动重载值,即写入自动重载寄存器的值,范围 0 到 65535。

  AutoReloadPreload自动重载预装载使能,即控制寄存器 1(TIMx_CR1)的 ARPE 位。

#define TIM_AUTORELOAD_PRELOAD_DISABLE                0x00000000U               /*!< TIMx_ARR register is not buffered */
#define TIM_AUTORELOAD_PRELOAD_ENABLE                 TIM_CR1_ARPE              /*!< TIMx_ARR register is buffered */

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

typedef enum 
{
    HAL_OK = 0x00U,             // 成功
    HAL_ERROR = 0x01U,          // 错误
    HAL_BUSY = 0x02U,           // 忙碌
    HAL_TIMEOUT = 0x03U         // 超时
} HAL_StatusTypeDef;

7.3、设置对应通道引脚的工作模式

  HAL 库中,提供 HAL_GPIO_Init() 函数用于配置 GPIO 功能模式,初始化 GPIO。该函数的声明如下:

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init);

  该函数的第一个形参 GPIOx 用来 指定端口号,可选值如下:

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)

  第二个参数是 GPIO_InitTypeDef 类型的结构体变量,用来 设置 GPIO 的工作模式,其定义如下:

typedef struct
{
  uint32_t Pin;         // 引脚号
  uint32_t Mode;        // 模式设置
  uint32_t Pull;        // 上下拉设置
  uint32_t Speed;       // 速度设置
  uint32_t Alternate;   // 复用功能设置
}GPIO_InitTypeDef;

  成员 Pin 表示 引脚号,范围:GPIO_PIN_0 到 GPIO_PIN_15。

#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected    */
#define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected    */
#define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected    */
#define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected    */
#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected    */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected    */
#define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected    */
#define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected    */
#define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected    */
#define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected    */
#define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected   */
#define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected   */
#define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected   */
#define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected   */
#define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected   */
#define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected   */

  成员 Mode 是 GPIO 的 模式选择,有以下选择项:

#define  GPIO_MODE_AF_PP                        0x00000002U     // 推挽式复用

  成员 Pull 用于 配置上下拉电阻,有以下选择项:

#define  GPIO_NOPULL        0x00000000U     // 无上下拉
#define  GPIO_PULLUP        0x00000001U     // 上拉
#define  GPIO_PULLDOWN      0x00000002U     // 下拉

  成员 Speed 用于 配置 GPIO 的速度,有以下选择项:

#define  GPIO_SPEED_FREQ_LOW         0x00000000U    // 低速
#define  GPIO_SPEED_FREQ_MEDIUM      0x00000001U    // 中速
#define  GPIO_SPEED_FREQ_HIGH        0x00000002U    // 高速
#define  GPIO_SPEED_FREQ_VERY_HIGH   0x00000003U    // 极速

  成员 Alternate 用于 配置具体的复用功能,不同的 GPIO 口可以复用的功能不同,具体可参考数据手册。

#define GPIO_AF1_TIM1          ((uint8_t)0x01)  /* TIM1 Alternate Function mapping */

#define GPIO_AF3_TIM8          ((uint8_t)0x03)  /* TIM8 Alternate Function mapping  */

7.4、配置PWM模式和比较值

  定时器的 PWM 通道设置初始化函数。其声明如下:

HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim, TIM_OC_InitTypeDef *sConfig, uint32_t Channel);

  其中,htimTIM_HandleTypeDef 结构体类型指针变量(亦称定时器句柄)。

  sConfigTIM_OC_InitTypeDef 结构体类型指针变量,用于配置定时器的输出比较参数。

typedef struct
{
    uint32_t OCMode;            // 输出比较模式选择
    uint32_t Pulse;             // 设置比较值
    uint32_t OCPolarity;        // 设置输出比较极性
    uint32_t OCNPolarity;       // 设置互补输出比较极性
    uint32_t OCFastMode;        // 使能或失能输出比较快速模式
    uint32_t OCIdleState;       // 空闲状态OC1输出
    uint32_t OCNIdleState;      // 空闲状态OC1N输出
} TIM_OC_InitTypeDef;

  成员变量 OCMode 用来 设置模式,可选值如下:

#define TIM_OCMODE_PWM1                     (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1)                    /*!< PWM mode 1                             */
#define TIM_OCMODE_PWM2                     (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0) /*!< PWM mode 2                             */

  成员变量 Pulse 用来 设置捕获比较值

  成员变量 TIM_OCPolarity 用来 设置输出极性

#define TIM_OCPOLARITY_HIGH                0x00000000U                          /*!< Capture/Compare output polarity  */
#define TIM_OCPOLARITY_LOW                 TIM_CCER_CC1P                        /*!< Capture/Compare output polarity  */

  成员变量 OCNPolarity 用来 设置互补输出比较极性

#define TIM_OCNPOLARITY_HIGH               0x00000000U                          /*!< Capture/Compare complementary output polarity */
#define TIM_OCNPOLARITY_LOW                TIM_CCER_CC1NP                       /*!< Capture/Compare complementary output polarity */

  成员变量 OCIdleState 用来 空闲状态OC1输出

#define TIM_OCIDLESTATE_SET                TIM_CR2_OIS1                         /*!< Output Idle state: OCx=1 when MOE=0 */
#define TIM_OCIDLESTATE_RESET              0x00000000U                          /*!< Output Idle state: OCx=0 when MOE=0 */

  成员变量 OCNIdleState 用来 空闲状态OC1N输出

#define TIM_OCNIDLESTATE_SET               TIM_CR2_OIS1N                        /*!< Complementary output Idle state: OCxN=1 when MOE=0 */
#define TIM_OCNIDLESTATE_RESET             0x00000000U                          /*!< Complementary output Idle state: OCxN=0 when MOE=0 */

  Channel 是定时器通道,范围:TIM_CHANNEL_1 到 TIM_CHANNEL_4。

#define TIM_CHANNEL_1                      0x00000000U                          /*!< Capture/compare channel 1 identifier      */
#define TIM_CHANNEL_2                      0x00000004U                          /*!< Capture/compare channel 2 identifier      */
#define TIM_CHANNEL_3                      0x00000008U                          /*!< Capture/compare channel 3 identifier      */
#define TIM_CHANNEL_4                      0x0000000CU                          /*!< Capture/compare channel 4 identifier      */
#define TIM_CHANNEL_ALL                    0x0000003CU                          /*!< Global Capture/compare channel identifier  */

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

7.5、配置刹车功能和死区时间

  设置死区参数,比如:设置死区时间、运行模式的关闭输出状态、空闲模式的关闭输出状态、刹车输入有效信号极性和是否允许刹车后自动恢复输出等。

HAL_StatusTypeDef HAL_TIMEx_ConfigBreakDeadTime(TIM_HandleTypeDef *htim, const TIM_BreakDeadTimeConfigTypeDef *sBreakDeadTimeConfig);

  其中,htimTIM_HandleTypeDef 结构体类型指针变量(亦称定时器句柄)。

  sBreakDeadTimeConfigTIM_BreakDeadTimeConfigTypeDef 结构体类型指针变量,用于配置定时器的死区参数。

typedef struct
{
    uint32_t OffStateRunMode;       // 运行模式下的关闭状态选择
    uint32_t OffStateIDLEMode;      // 空闲模式下的关闭状态选择
    uint32_t LockLevel;             // 寄存器锁定设置
    uint32_t DeadTime;              // 死区时间设置
    uint32_t BreakState;            // 是否使能刹车功能
    uint32_t BreakPolarity;         // 刹车极性选择
    uint32_t BreakFilter;           // 刹车输入滤波器设置
    uint32_t AutomaticOutput;       // 自动恢复输出使能,即使能AOE位
} TIM_BreakDeadTimeConfigTypeDef;

  成员变量 OffStateRunMode 用来配置 运行模式下的关闭状

#define TIM_OSSR_ENABLE                          TIM_BDTR_OSSR                  /*!< When inactive, OC/OCN outputs are enabled (still controlled by the timer)           */
#define TIM_OSSR_DISABLE                         0x00000000U                    /*!< When inactive, OC/OCN outputs are disabled (not controlled any longer by the timer) */

  成员变量 OffStateIDLEMode 用来配置 空闲模式下的关闭状态

#define TIM_OSSI_ENABLE                          TIM_BDTR_OSSI                  /*!< When inactive, OC/OCN outputs are enabled (still controlled by the timer)           */
#define TIM_OSSI_DISABLE                         0x00000000U                    /*!< When inactive, OC/OCN outputs are disabled (not controlled any longer by the timer) */

  成员变量 LockLevel 用来配置 寄存器锁定

#define TIM_OSSI_ENABLE                          TIM_BDTR_OSSI                  /*!< When inactive, OC/OCN outputs are enabled (still controlled by the timer)           */
#define TIM_OSSI_DISABLE                         0x00000000U                    /*!< When inactive, OC/OCN outputs are disabled (not controlled any longer by the timer) */

  成员变量 BreakState 用来配置 是否使能刹车功能

#define TIM_BREAK_ENABLE                   TIM_BDTR_BKE                         /*!< Break input BRK is enabled  */
#define TIM_BREAK_DISABLE                  0x00000000U                          /*!< Break input BRK is disabled */

  成员变量 BreakPolarity 用来配置 刹车极性

#define TIM_BREAKPOLARITY_LOW              0x00000000U                          /*!< Break input BRK is active low  */
#define TIM_BREAKPOLARITY_HIGH             TIM_BDTR_BKP                         /*!< Break input BRK is active high */

  成员变量 AutomaticOutput 用来配置 自动恢复输出使能,即使能 AOE 位

#define TIM_AUTOMATICOUTPUT_DISABLE        0x00000000U                          /*!< MOE can be set only by software */
#define TIM_AUTOMATICOUTPUT_ENABLE         TIM_BDTR_AOE                         /*!< MOE can be set by software or automatically at the next update event (if none of the break inputs BRK and BRK2 is active) */

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

7.6、使能输出并启动计数器

HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);

  其中,htimTIM_HandleTypeDef 结构体类型指针变量(亦称定时器句柄)。

  Channel定时器通道,范围:TIM_CHANNEL_1 到 TIM_CHANNEL_4。

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

7.7、使能互补输出

HAL_StatusTypeDef HAL_TIMEx_PWMN_Start(TIM_HandleTypeDef *htim, uint32_t Channel)

  其中,htimTIM_HandleTypeDef 结构体类型指针变量(亦称定时器句柄)。

  Channel定时器通道,范围:TIM_CHANNEL_1 到 TIM_CHANNEL_4。

  该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

7.8、修改比较值控制占空比

#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \
  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) :\
   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) :\
   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) :\
   ((__HANDLE__)->Instance->CCR4 = (__COMPARE__)))

  __HANDLE__TIM_HandleTypeDef 结构体类型指针变量,__CHANNEL__ 对应 PWM 的输出通道,__COMPARE__ 则是要写到捕获/比较寄存器(TIMx_CCR1/2/3/4)的值。实际上该宏定义最终还是往对应的捕获/比较寄存器写入比较值来控制 PWM 波的占空比。

八、程序源码

  这时,我们将定时器 1 设置为自增计数模式。定时器 1 的时钟源频率为 2 倍 APB2 总线时钟频率,即频率为 168MHz,当预分频寄存器的值为 167,自动重载寄存器的值为 999。根据定时器溢出公式由公式得:

\[T_{out} = \frac{(arr + 1) * (psc + 1)}{F_{clk}} = \frac{(999 + 1) * (167 + 1)}{168000000} = 0.001s \]

  由频率是周期的倒数关系得到 PWM 的频率为 1MHz。

  我们设置 CKD[1:0] 位的值为 2,代入上面的式子可得:

\[T_{DTS} = \frac{1}{F_{DTS}} = \frac{2^{CKD[1:0]}}{F_{clk}} = \frac{2 * 2}{168000000} = 23.81ns \]

  设置 DTG 为 100,它的二进制数为:0110 0100,即 DTG[7:5] 为 011,所以选择第一条公式:\(DT = DTG[7:0] × T_{DTG}\)

\[DT = DTG[7:0] × t_{DTG} = 100 * 23.81ns = 2.38us \]

  定时器 1 初始化函数,内容如下:

TIM_HandleTypeDef g_tim1_handle;

/**
 * @brief 定时器PWM功能初始化函数
 * 
 * @param htim 定时器句柄
 * @param TIMx 定时器寄存器基地址,可选值: TIMx, x可选范围: [1, 8]
 * @param prescaler 预分频系数,可选值: 0 ~ 65535
 * @param period 自动重装载值,可选值: 0 ~ 65535
 * @param channel 输出PWM的通道,可选值: TIM_CHANNEL_x, x可选范围: 1 ~ 4
 * @param polarity 输出比较极性,可选值: [TIM_OCPOLARITY_LOW, TIM_OCPOLARITY_HIGH]
 * @param NPolarity 输出比较极性,可选值: [TIM_OCNPOLARITY_LOW, TIM_OCNPOLARITY_HIGH]
 * @param pluse 输出比较值,可选值: 0 ~ 65535
 * @param deadTime 死区时间,可选值: 0 ~ 65535
 * @param breakPolarity 刹车极性,可选值: [TIM_BREAKPOLARITY_LOW, TIM_BREAKPOLARITY_HIGH]
 */
void TIM_OCN_Init(TIM_HandleTypeDef *htim, TIM_TypeDef *TIMx, uint16_t prescaler, uint16_t period, uint32_t channel, uint32_t polarity, uint32_t NPolarity, uint32_t pluse, uint32_t deadTime, uint32_t breakPolarity)
{
    TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
    TIM_BreakDeadTimeConfigTypeDef TIM_BreakDeadTimeConfigStruct = {0};

    htim->Instance = TIMx;                                                      // 定时器寄存器基地址
    htim->Init.CounterMode = TIM_COUNTERMODE_UP;                                // 计数模式
    htim->Init.Prescaler = prescaler;                                           // 预分频系数
    htim->Init.Period = period;                                                 // 自动重装载值
    HAL_TIM_PWM_Init(htim);

    TIM_OC_InitStruct.OCMode = TIM_OCMODE_PWM1;                                 // PWM模式1
    TIM_OC_InitStruct.Pulse = pluse;                                            // 比较值
    TIM_OC_InitStruct.OCPolarity = polarity;                                    // 输出比较极性
    TIM_OC_InitStruct.OCIdleState = TIM_OCIDLESTATE_RESET;                      // 输出比较空闲状态
    TIM_OC_InitStruct.OCNPolarity = NPolarity;                                  // 互补输出比较极性
    TIM_OC_InitStruct.OCNIdleState = TIM_OCNIDLESTATE_RESET;                    // 互补输出比较空闲状态
    HAL_TIM_PWM_ConfigChannel(htim, &TIM_OC_InitStruct, channel);


    TIM_BreakDeadTimeConfigStruct.OffStateRunMode = TIM_OSSR_DISABLE;           // 运行模式下的关闭状态选择
    TIM_BreakDeadTimeConfigStruct.OffStateIDLEMode = TIM_OSSI_DISABLE;          // 空闲模式下的关闭状态选择
    TIM_BreakDeadTimeConfigStruct.LockLevel = TIM_LOCKLEVEL_OFF;                // 寄存器锁定设置
    TIM_BreakDeadTimeConfigStruct.DeadTime = deadTime;                          // 死区时间
    TIM_BreakDeadTimeConfigStruct.BreakState = TIM_BREAK_ENABLE;                // 刹车使能
    TIM_BreakDeadTimeConfigStruct.BreakPolarity = breakPolarity;                // 刹车极性
    TIM_BreakDeadTimeConfigStruct.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE; // 自动恢复输出使能
    HAL_TIMEx_ConfigBreakDeadTime(htim, &TIM_BreakDeadTimeConfigStruct);
}

  定时器 PWM 模式底层初始化函数,内容如下:

/**
 * @brief 定时器PWM模式底层初始化函数
 * 
 * @param htim 定时器句柄
 */
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    if (htim->Instance == TIM1)
    {
        __HAL_RCC_TIM1_CLK_ENABLE();                                            // 使能TIM1的时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();                                           // 使能TIM1的Channel 1和Channel N1对应的GPIO时钟

        GPIO_InitStruct.Pin = GPIO_PIN_7 | GPIO_PIN_8;                          // TIM1的Channel 1和Channel N1对应的GPIO引脚
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;                                 // 复用功能
        GPIO_InitStruct.Pull = GPIO_NOPULL;                                     // 不使用上下拉
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;                           // GPIO输出速度
        GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;                              // 复用功能选择
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
}

  main() 函数,内容如下:

int main(void)
{
    HAL_Init();
    System_Clock_Init(8, 336, 2, 7);
    Delay_Init(168);

    TIM_OCN_Init(&g_tim1_handle, TIM1, 167, 999, TIM_CHANNEL_1, TIM_OCPOLARITY_HIGH, TIM_OCNPOLARITY_HIGH, 5, 100, TIM_BREAKPOLARITY_HIGH);
    HAL_TIM_PWM_Start(&g_tim1_handle, TIM_CHANNEL_1);                           // 使能输出并启动计数器
    HAL_TIMEx_PWMN_Start(&g_tim1_handle, TIM_CHANNEL_1);                        // 使能互补输出

    while (1)
    {
  
    }
  
    return 0;
}
posted @ 2023-12-14 18:45  星光映梦  阅读(174)  评论(0编辑  收藏  举报