PWM互补输出及死区时间代码和详解(寄存器版本)

原文: http://www.openedv.com/thread-62861-1-1.html

一直跟着原子哥的教程一直学到PWM这一章发现一头雾水,自己也没发过什么分享帖,感觉原子哥对PWM这章讲的比较少(mini板教程),后面自己上网找了些资料,网上关于PWM讲的也不多,所以学起来也挺困难的,一直对TIM1有7路PWM不理解,自己只能输出4路,另外三路也不知道怎么输出,通过网上少量资料和自己研究发现这三路用于互补输出,互补输出调试出来了发现又有死区时间,主要用于电机的H桥方面的控制,后面附上自己对高级定时器互补输出和死区时间设置的代码和详解。花了两小时整理了下资料,初学者难免有错希望指出,也希望对跟我一样的初学者学习PWM有点帮助

 

先简单了解下PWM和死区时间

简介:

脉冲宽度调制(PWM),是英文“ Pulse Width Modulation” 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制,高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样, STM32 最多可以同时产生 30 路 PWM 输出!

 

PWM的死区时间:

死区,简单解释:通常,大功率电机、变频器等,末端都是由大功率管、IGBT等元件组成的H桥或3相桥。每个桥的上半桥和下半桥是是绝对不能同时导通的,但高速的PWM驱动信号在达到功率元件的控制极时,往往会由于各种各样的原因产生延迟的效果,造成某个半桥元件在应该关断时没有关断,造成功率元件烧毁。死区就是在上半桥关断后,延迟一段时间再打开下半桥或在下半桥关断后,延迟一段时间再打开上半桥,从而避免功率元件烧毁。这段延迟时间就是死区。(就是上、下半桥的元件都是关断的)死区时间控制在通常的低端单片机所配备的PWM中是没有的。
PWM的上下桥臂的三极管是不能同时导通的。如果同时导通就会是电源两端短路。所以,两路触发信号要在一段时间内都是使三极管断开的。这个区域就叫做“死区”优点就不用说了。缺点是使谐波的含量有所增加。

7路PWM:
PWM互补输出及死区时间代码和详解(寄存器版本) 
PWM互补输出及死区时间代码和详解(寄存器版本) 
代码和详解:
利用高级定时器TIM1输出7路PWM进行观察
需要配置以下寄存器:
捕获/比较模式寄存器(TIMx_CCMR1/2)
捕获/比较使能寄存器(TIMx_CCER)
捕获/比较寄存器(TIMx_CCR1~4)
刹车和死区寄存器(TIMx_BDTR)

按代码行后面的序号解析:
[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//TIM1_CH1 PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM1_PWM_Init(u16 arr,u16 psc)
{                           
    RCC->APB2ENR|=1<<11;   //TIM1时钟使能   
    RCC->APB2ENR|=1<<2;     //使能PORTA时钟        
    RCC->APB2ENR|=1<<3;     //使能PORTB时钟    
    RCC->APB2ENR|=1<<0;     //开启复用时钟  
     
    GPIOA->CRH&=0XFFFF0000;   //PA8,9,10,11清除之前的设置
    GPIOA->CRH|=0X0000BBBB;   //PA8,9,10,11复用功能输出
     
    GPIOB->CRH&=0X000FFFFF;  //PB13,14,15清除之前的设置
    GPIOB->CRH|=0XBBB00000;  //PB13,14,15复用功能输出
     
    GPIOB->ODR|=1<<13;       //PB13 输出上拉,低电平有效
    GPIOB->ODR|=1<<14;       //PB14
    GPIOB->ODR|=1<<15;       //PB15
    GPIOA->ODR|=1<<8;        //PA8
    GPIOA->ODR|=1<<9;        //PA9
    GPIOA->ODR|=1<<10;       //PA10
     
    TIM1->ARR=arr;           //设定计数器自动重装值     ①1
    TIM1->PSC=psc;           //预分频器设置             ②2
     
    TIM1->CCER|=1<<0;      //TIM1CH1 输出使能,高电平有效      ③3
    TIM1->CCER|=1<<4;      //TIM1CH2 输出使能
    TIM1->CCER|=1<<8;      //TIM1CH3 输出使能
    TIM1->CCER|=1<<12;      //TIM1CH4 输出使能
    TIM1->CCER|=1<<2;      //TIM1CH1N 互补输出使能
    TIM1->CCER|=1<<6;      //TIM1CH2N 互补输出使能
    TIM1->CCER|=1<<10;      //TIM1CH3N 互补输出使能
     
    TIM1->CCMR1|=7<<4;     //CH1 PWM2模式           ④4
    TIM1->CCMR1|=1<<3;     //CH1预装载使能  
 
    TIM1->CCMR1|=7<<12;    //CH2 PWM2模式        
    TIM1->CCMR1|=1<<11;    //CH2预装载使能  
 
    TIM1->CCMR2|=7<<4;     //CH3 PWM2模式        
    TIM1->CCMR2|=1<<3;     //CH3预装载使能 
  
    TIM1->CCMR2|=7<<12;    //CH4 PWM2模式        
    TIM1->CCMR2|=1<<11;    //CH4预装载使能  
     
        TIM1->BDTR|=0x14;       //死区时间设置          ⑤5
    TIM1->BDTR|=1<<15;     //MOE 主输出使能        ⑥6
    TIM1->CR1 |= 0x80;       //ARPE使能,开始所有输出通道,默认向上计数   ⑦7
    TIM1->CR1 |= 0x01;       //使能计数器                                          
}
 
int main(void)
{          
    Stm32_Clock_Init(9);       //系统时钟设置
    delay_init(72);             //延时初始化
    TIM1_PWM_Init(499,7199);    // 72M/7200=10khz, 1/10khz * 500=50ms   ⑧8
    while(1)
    {                   
        TIM1->CCR1=250;  //占空比:50%  低电平时长25ms   ⑨9
        TIM1->CCR2=125;  //占空比:75%  低电平时长12.5ms
        TIM1->CCR3=50;     //占空比:90% 低电平时长5ms
        TIM1->CCR4=25;     //占空比:95% 低电平时长2.5ms 
    }
}

自动重载,这里重载值为500,50ms一个周期

预分频:7200分频,频率为10KHZ


PWM互补输出及死区时间代码和详解(寄存器版本) 
PWM互补输出及死区时间代码和详解(寄存器版本) 

TIM1->CCER|=1<<0;//0为使能位,位1~3都默认为0,即高电平有效,关闭输入捕获,互补输出极性为“高电平有效,所谓的有效跟④有关,如下

PWM互补输出及死区时间代码和详解(寄存器版本) 
CCMR14~6位为模式设置,,由于我们上面设置为高电平“有效”,这里又设置为PWM2模式,在⑦没有设置TIM1_CR1DIR位,所以默认为0,也就是向上计数,如下

PWM互补输出及死区时间代码和详解(寄存器版本) 
可以推出TIMx_CNT从0开始往上计数,在小于CCR1(第⑨有设置)时会输出无效电平也就是低电平,当大于的时候输出有效电平也就是高电平,这样就达到了设置占空比的目的。一开始我们设ARR为500,CCR1为250,所以一开始输出低电平占50%,高电平也是50%

 

死区时间设置TIM1_BDTR(7:0),我们上面设置的是0x14

DT表示死区持续时间,Tdts为系统时钟周期,Tdtg表示乘以倍数后死区设置时间步进值。

PWM互补输出及死区时间代码和详解(寄存器版本) 
TIM1_BDTR = 0x14是高3位为000,也就是调用以下这个公式(第一个公式),高3位可以根据自己需要设置
PWM互补输出及死区时间代码和详解(寄存器版本) 
可以看到第⑧可以得知Tdts = 10Khz,Tdtg = Tdts = 10Khz,DT = 20(0x14) * 100us(10Khz) = 2000us = 2ms,也就是死区持续时间为2ms,最后配置输出使能⑥
PWM互补输出及死区时间代码和详解(寄存器版本) 

下面是输出的波形
PWM互补输出及死区时间代码和详解(寄存器版本)



通道1(低电平27ms,高电平23ms)

互补输出的通道(CH1N)(高电平23ms,低电平27ms)
PWM互补输出及死区时间代码和详解(寄存器版本) 

通道2(7ms)
PWM互补输出及死区时间代码和详解(寄存器版本) 

通道3(14.5ms)

PWM互补输出及死区时间代码和详解(寄存器版本) 

通道4(2.5ms)

PWM互补输出及死区时间代码和详解(寄存器版本)

死区时间(2ms)

PWM互补输出及死区时间代码和详解(寄存器版本) 

由于互补输出且有死区时间所以CH1加了2ms,而CH1N减了2ms,死区时间跟计算的一样是2ms,而最后一个CH4没有互补

输出所以自然是2.5ms不变,由于我们设置的10KHZ,而手册是以8M为例子的,大家也可以试一下,亲测没有问题,如果

8M死区时间精度也更高可以,可以设为usns级别。下面附上这个例程的代码

PWM互补输出及死区时间代码和详解(寄存器版本)