对教程的补充:PWM互补输出及死区时间代码和详解(原创)

2019-07-21 01:54发布

实战:PWM   一直跟着原子哥的教程一直学到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:


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

按代码行后面的序号解析:
[mw_shl_code=c,true]//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->SC=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 } } [/mw_shl_code] 自动重载,这里重载值为500,50ms一个周期 预分频:7200分频,频率为10KHZ



TIM1->CCER|=1<<0;//0为使能位,位1~3都默认为0,即高电平有效,关闭输入捕获,互补输出极性为“高电平有效,所谓的有效跟④有关,如下
CCMR14~6位为模式设置,,由于我们上面设置为高电平“有效”,这里又设置为PWM2模式,在⑦没有设置TIM1_CR1DIR位,所以默认为0,也就是向上计数,如下
可以推出TIMx_CNT从0开始往上计数,在小于CCR1(第⑨有设置)时会输出无效电平也就是低电平,当大于的时候输出有效电平也就是高电平,这样就达到了设置占空比的目的。一开始我们设ARR为500,CCR1为250,所以一开始输出低电平占50%,高电平也是50%   死区时间设置TIM1_BDTR(7:0),我们上面设置的是0x14 DT表示死区持续时间,Tdts为系统时钟周期,Tdtg表示乘以倍数后死区设置时间步进值。
TIM1_BDTR = 0x14是高3位为000,也就是调用以下这个公式(第一个公式),高3位可以根据自己需要设置

可以看到第⑧可以得知Tdts = 10Khz,Tdtg = Tdts = 10Khz,DT = 20(0x14) * 100us(10Khz) = 2000us = 2ms,也就是死区持续时间为2ms,最后配置输出使能


下面是输出的波形


通道1(低电平27ms,高电平23ms)
互补输出的通道(CH1N)(高电平23ms,低电平27ms)



通道2(7ms)


通道3(14.5ms)
通道4(2.5ms)  死区时间(2ms)
由于互补输出且有死区时间所以CH1加了2ms,而CH1N减了2ms,死区时间跟计算的一样是2ms,而最后一个CH4没有互补 输出所以自然是2.5ms不变,由于我们设置的10KHZ,而手册是以8M为例子的,大家也可以试一下,亲测没有问题,如果 8M死区时间精度也更高可以,可以设为usns级别。下面附上这个例程的代码



友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
49条回答
18377006655
1楼-- · 2019-07-26 12:31
 精彩回答 2  元偷偷看……
chenfei48
2楼-- · 2019-07-26 14:39
不错,一大早就看到好帖子,谢谢分享
botming
3楼-- · 2019-07-26 16:57
 精彩回答 2  元偷偷看……
Ldd69stm32
4楼-- · 2019-07-26 18:25
听君一席话胜读十年书~这个分享给力~
dsadsa
5楼-- · 2019-07-26 22:55
我的照葫芦画瓢,用的STM32F103ZET6,CH2n、CH3N互补PWM无输出

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<<6;     //使能PORTE的时钟
    RCC->APB2ENR|=1<<0;     //使能复用时钟

    GPIOA->CRH&=0XFFFFF00F;        //PA9、PA10推挽输出
        GPIOA->CRH|=0X00000BB0;       

    GPIOB->CRL&=0XFFFFFF00;        //PB0、PB1推挽输出
        GPIOB->CRL|=0X000000BB;       

        GPIOE->CRL&=0XF0FFFF0F;        //PE2、PE6推挽输出
        GPIOE->CRL|=0X03000300;       

    GPIOA->ODR|=1<<9;       //PA9上拉
    GPIOA->ODR|=1<<10;      //PA10上拉
    GPIOB->ODR|=1<<0;       //PB0上拉
    GPIOB->ODR|=1<<1;       //PB1上拉
    GPIOE->ODR|=1<<2;       //PE2上拉
    GPIOE->ODR|=1<<6;       //PE6上拉

       
        TIM1->ARR=arr;                        //设定计数器自动重装值
        TIM1->PSC=psc;                        //预分频器设置

    TIM1->CCMR1|=7<<12;          //CH2 PWM2模式                 
        TIM1->CCMR1|=1<<11;         //CH2预装载使能

    TIM1->CCMR2|=7<<4;          //CH3 PWM2模式                 
        TIM1->CCMR2|=1<<3;                 //CH3预装载使能

    TIM1->CCER|=1<<4;           //OC2 输出使能
    TIM1->CCER|=1<<8;           //OC3 输出使能

    TIM1->CCER|=1<<6;       //OC2N 输出使能      
    TIM1->CCER|=1<<10;      //OC3N 输出使能  

    TIM1->CR1|=0<<4;        //向下计数模式

    TIM1->BDTR|=0X14;       //设置死区时间为2ms
        TIM1->BDTR|=1<<15;           //MOE 主输出使能          

        TIM1->CR1|=0x80;           //ARPE使能
        TIM1->CR1|=0x01;            //使能定时器1   
}

int main(void)
{

        Stm32_Clock_Init(9); //系统时钟设置
        delay_init(72);             //延时初始化
        //uart_init(72,9600);  //串口初始化
        TIM1_PWM_Init(999,71);//不分频。PWM频率=72000/(71+1)=1000Khz
           while(1)
        {
                //TIM1->CCR1=500;
        TIM1->CCR2=250;
        TIM1->CCR3=125;
        //TIM1->CCR4=25;
        }
}





dsadsa
6楼-- · 2019-07-27 02:50
 精彩回答 2  元偷偷看……

一周热门 更多>