×
单片机 > 单片机程序设计 > 详情

STM32F103ZET6 — PWM(TIM1)

发布时间:2020-06-09 发布时间:
|

简介

首先聊聊 PWM 输出。脉宽调制(PWM,Pulse Width Modulation)是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。尤其是电机控制。


PWM 的主要参数包括频率和占空比。


死区的概念是在 PWM 控制电机引入的。由于 H 桥的存在,每个桥的上半桥和下半桥是绝对不能同时导通的,但高速的PWM驱动信号在达到功率元件的控制端时,往往会由于各种各样的原因产生延迟的效果,造成某个半桥元件在应该关断时没有关断,造成功率元件烧毁。


死区就是在互补的输出中,人为的插入 Delay,以致 H 桥不至于出现同时导通的情况。类似于 DTPHx 和 DTPHx_ 信号 :

STM32 的定时器功能十分强大,不仅仅实现了定时器的基本功能,对于高级定时器 TIM1/TIM8,更是能够输出 PWM,波形,同时还带插入死区的互补输出。天生就是拿来做电机控制的一把好手。


TIM1 对应的 Pin

TIM1 能够输出 4 路 PWM 信号,所有逻辑全部做在了硬件里面,也就是说, TIM1/TIM8 的硬件逻辑中,绑定了几个引脚,不单纯的只是 Timer:

如图所示,TIM1 的四个 Channel 1 ~ Channel 4 分别对应着 PA8 ~ PA11,互补输出的信号 Channel 1 N ~ Channel 3 N 分别对应到了 PB13 ~ PB15 管脚。


对应到单板上:

     

好了,然后连接逻辑分析仪上去:


环境准备 Ready!!!



TIM1 功能

TIM1 的功能能强大,不仅仅支持计数器,同时可以支持 4 个独立通道的:


输入捕获

输出比较

PWM 生成

单脉冲输出

死区时间可编程的互补输出

刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态

支持针对定位的增量(正交)编码器和霍尔传感器电路

触发输入作为外部时钟或者按周期的电流管理

这里关注 PWM 相关的逻辑。


TIM1 时钟

TIM1 的时钟来源于 APB2 的时钟输入:


需要开启 TIM 的时钟输入。这里配置的 APB2 时钟为 72MHz。


TIMxCLK 输入到 TIM1 模块后,会有一个预分频器(TIMx_PSC),对其进行分频,分出来的频率就供给 TIM1 的频率 (上限 65536)。同时存在一个自动装载寄存器 (TIMx_ARR) ,这个寄存器的作用是当计数器以预分频后的时钟进行计数后,达到这个装载寄存器的值,就完成一次 Timer 事件。


TIM1 还包含一个重复计数器,灵活的配置这个寄存器,能够使得多次达到 TIMx_ARR 的值后,才产生对应的事件。


TIM1 有一个预装载的功能,如果打开这个功能,代表更新 TIMx_ARR 寄存器的值,将在下一次完成事件后,才对 TIMx_ARR 进行更新,否则,立即更新 TIMx_ARR。


计数器的方式有三种:向上计数,向下计数,以及中央对齐模式(向上/向下计数) ,非常灵活。具体的详见 DataSheet。



TIM1 的 PWM 功能

TIM1 的支持的配置相当繁多,这里仅仅以向上计数的方式介绍。


在 PWM 模式中,主要是控制波形的周期和占空比:


周期的控制方式由,TIMx_ARR寄存器确定


由TIMx_CCRx寄存器确定占空比的信号。


这里引入了一个 TIMx_CCRx 寄存器,他是一个比较输出的寄存器:


下面是一个PWM模式1的例子。当 TIMx_CNT < TIMx_CCRx 时,PWM 输出的参考信号 OCxREF 为高,否则为低。以此来达到控制占空比的要求。


针对这个, STM32 有一个有效电平的概念,即先定义有效电平是 1 还是 0,然后根据和比较寄存器的比较情况,来输出有效电平。(够繁琐)。


TIM1 的配置过程

1. 当然,还是需要首先开启管脚的时钟和配置管脚的 mode


2. 配置 NVIC (如果有中断需要)


3. 开启 TIM1/TIM8 的时钟


4. 配置 TIMx_ARR 寄存器,来确定周期


5. 配置分频寄存器,确定分频参数


6. 配置计数方式(向上,向下,中央)


7. 配置 PWM1 模式


8. 开启输出和互补输出


9. 配置 TIMx_CCRx ,比较寄存器的值(后面可以更改)


10. 配置输出信号的极性


11. 开启每个输出通道的预装入功能


12. 配置死区时间


13. 开启 TIM1 的整个模块的预装入功能


14. 打开 PWM 输出


15. 开启 TIM1 的使能位


#include "stm32f10x.h"

#include "stm32f10x_rcc.h"

#include "stm32f10x_gpio.h"

#include "stm32f10x_dma.h"

#include "stm32f10x_tim.h"

#include "stm32f10x_pwr.h"

 

#include "sk_pwm.h"

 

#define TIMx_DEFAULT_PERIOD     (1000)

#define CCRx_DEFAULT_VALUE      (TIMx_DEFAULT_PERIOD / 2)

#define TIMx_DEFAULT_PRE        (72)

 

static void SK_PWM_PortInit(void)

{

    GPIO_InitTypeDef  stGpioInit;

 

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);

 

    stGpioInit.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;

    stGpioInit.GPIO_Mode = GPIO_Mode_AF_PP;

    stGpioInit.GPIO_Speed = GPIO_Speed_50MHz;

 

    GPIO_Init(GPIOA, &stGpioInit);

 

    stGpioInit.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;

    GPIO_Init(GPIOB, &stGpioInit);

}

 

static void SK_PWM_NvicInit(void)

{

    NVIC_InitTypeDef NVIC_InitStructure;

 

    NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);

}

 

static void SK_PWM_ClockInit(void)

{

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_TIM8, ENABLE);

}

 

static void SK_PWM_ModeInit(void)

{

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    TIM_OCInitTypeDef  TIM_OCInitStructure;

    TIM_BDTRInitTypeDef      TIM1_BDTRInitStruct;

 

    // Configure the period and prescaler

    TIM_TimeBaseStructure.TIM_Period = (TIMx_DEFAULT_PERIOD - 1);

    TIM_TimeBaseStructure.TIM_Prescaler = (TIMx_DEFAULT_PRE - 1);

    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

 

    // Configure the Output mode

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;

    TIM_OCInitStructure.TIM_Pulse = CCRx_DEFAULT_VALUE;

    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

    TIM_OCInitStructure.TIM_OCNPolarity= TIM_OCPolarity_High;

    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;

    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

    TIM_OC1Init(TIM1, &TIM_OCInitStructure);

    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);

 

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

    TIM_OCInitStructure.TIM_Pulse = CCRx_DEFAULT_VALUE / 2;

    TIM_OC2Init(TIM1, &TIM_OCInitStructure);

    TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);

 

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

    TIM_OCInitStructure.TIM_Pulse = CCRx_DEFAULT_VALUE / 4;

    TIM_OC3Init(TIM1, &TIM_OCInitStructure);

    TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);

 

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

    TIM_OCInitStructure.TIM_Pulse = CCRx_DEFAULT_VALUE / 10;

    TIM_OC4Init(TIM1, &TIM_OCInitStructure);

    TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable);

 

    TIM1_BDTRInitStruct.TIM_OSSRState = TIM_OSSRState_Disable;

    TIM1_BDTRInitStruct.TIM_OSSIState = TIM_OSSIState_Disable;

    TIM1_BDTRInitStruct.TIM_LOCKLevel = TIM_LOCKLevel_OFF;

    TIM1_BDTRInitStruct.TIM_DeadTime = 205;

    TIM_BDTRConfig(TIM1, &TIM1_BDTRInitStruct);

 

    TIM_ARRPreloadConfig(TIM1, ENABLE);

    TIM_CtrlPWMOutputs(TIM1, ENABLE);

 

    TIM_ClearFlag(TIM1, TIM_FLAG_Update);

    TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);

}

 

static void SK_PWM_Enable(void)

{

    TIM_Cmd(TIM1, ENABLE);

}

 

void SK_PWM_Init(void)

{

    SK_PWM_PortInit();

    SK_PWM_ClockInit();

    SK_PWM_NvicInit();

    SK_PWM_ModeInit();

    SK_PWM_Enable();

}

 

void TIM1_UP_IRQHandler(void)

{

    static uint32_t cnt = 0;

    if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)

    {

        TIM_ClearITPendingBit(TIM1, TIM_IT_Update);

    }

}

 


测试结果

使用逻辑分析仪抓到的结果如下:


1. 不带死区控制的互补输出

互补输出时间 delta 测量为 0,即几乎是同时输出翻转:

2. 带死区控制的互补输出

互补输出时间 delta 测量为 5us,实现死区控制:


关键字:STM32F103ZET6  PWM  TIM1 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
STM32单片机的复用端口初始化的步骤及方法