51c嵌入式※~电路~合集32~PWM

发布于:2025-06-11 ⋅ 阅读:(23) ⋅ 点赞:(0)

我自己的原文哦~      https://blog.51cto.com/whaosoft/13982773

一、PWM 

PWM有着非常广泛的应用,比如直流电机的无极调速,开关电源、逆变器等等,个人认为,要充分理解或掌握模拟电路、且有所突破,很有必要吃透这三个知识点:

  • PWM
  • 电感
  • 纹波

PWM是一种技术手段,PWM波是在这种技术手段控制下的脉冲波,如果你不理解是把握不住PWM波的!

如下图所示,这种比喻很形象也很恰当,希望对学习的朋友有所帮助与启发。

PWM全称Pulse Width Modulation:脉冲宽度调制(简称脉宽调制,通俗的讲就是调节脉冲的宽度),是电子电力应用中非常重要的一种控制技术,在理解TA之前我们先来了解几个概念 。 

  • 脉冲周期T:单位是时间,比如纳秒ns、微妙μs、毫秒ms等;
  • 脉冲频率f:单位是赫兹Hz、千赫兹kHz等,与脉冲周期成倒数关系,即f=1/T;
  • 脉冲宽度W:简称脉宽,是脉冲高电平持续的时间,单位是时间,比如纳秒ns、微妙μs、毫秒ms等;
  • 占空比D:脉宽除以脉冲周期得到的值,百分数表示,比如50%,也常有小数或分数表示的,比如0.5或1/2。

以上之间的关系如下图所列的公式:

工程应用中的PWM波是幅值、周期(或频率)不变,脉宽(或占空比)可调的脉冲波,接下来我们来认清该PWM波到底是什么,TA隐藏着什么思想?

当我们想要控制一个直流电机的转速,我们可以通过改变其两端电压即可,但是该种方法有很大的局限性,可调直流电源构造复杂、成本高昂,应用起来很不现实。

所以我们采用另外的控制方式:电压源→驱动器→直流电机,电压源提供直流电压,不同的驱动器控制不同的直流电机,应用非常灵活,其中驱动器对电机的调速控制就是利用PWM。

可调直流电源控制与PWM控制都是能调速的,那么它们有什么相同之处呢?

如下图,电机为某相同转速时,红色代表驱动器输出幅值不变的PWM波,蓝色代表可调直流电源输出的电压,两者都是直接作用到负载。

由以上得知:

当PWM波的占空比越大时,所对应的直流电压与PWM波的幅值越接近;反之与0V越接近。

周期的红色PWM波脉宽下的矩形面积之和与蓝色直流电压的面积相等,即伏秒积相等:

U红(幅值) × ton = U蓝 × T

两端同时除以T,得到如下关系式:

U红(幅值) × 占空比 = U蓝

例如当PWM波的幅值为24V,占空比为50%时,与直流电压12V作用到电机上所产生的效果是一模一样的,即速度相同,即24V×50%=12V。

另外,既然满足这个关系,那PWM波的频率是不是可以随意了,答案当然不是,频率太低会导致电机运转不畅,振动大,噪音大;频率太高会导致驱动器开关损耗较大,甚至有电机会啸叫而不转的情况。

一般1k~30k的PWM频率较为普遍,几百Hz的也有,实际上还是根据电机功率在测试时确定合适的PWM频率范围为宜。

如下图为实物测试,脉宽在变化,周期不变的PWM波。

所加负载如下图,为PWM控制电机调速实物测试,有刷直流电机的PWM无极调速,其中LED是并联在电机输入端的,其亮弱反映电机速度的变化。

要点:

  • PWM波其实就是一种脉宽可连续调节的矩形脉冲波;
  • 占空比其实就是描述脉宽与脉冲周期的比值,是量化值,便于分析研究,当我们用占空比来表达时,对脉宽就不那么关心了;
  • 占空比调节就是脉宽调节,表达不一样,但本质是一样的;
  • PWM波满足伏秒积计算:U红(幅值) × 占空比 = U蓝,作用效果与直流电压一样。

二、SPWM 与 SVPWM 原理及算法

SPWM与SVPWM

所谓SPWM,就是在PWM的基础上改变了调制脉冲方式,脉冲宽度时间占空比按正弦规律排列,这样输出波形经过适当的滤波可以做到正弦波输出。它广泛地用于直流交流逆变器等,比如高级一些的UPS就是一个例子。三相SPWM是使用SPWM模拟市电的三相输出,在变频器领域被广泛的采用。

SPWM(Sinusoidal PWM)法是一种比较成熟的,目前使用较广泛的PWM法。前面提到的采样控制理论中的一个重要结论:冲量相等而形状不同的窄脉冲加在具有惯性的环节上时,其效果基本相同。SPWM法就是以该结论为理论基础,用脉冲宽度按正弦规律变化而和正弦波等效的PWM波形即SPWM波形控制逆变电路中开关器件的通断,使其输出的脉冲电压的面积与所希望输出的正弦波在相应区间内的面积相等,通过改变调制波的频率和幅值则可调节逆变电路输出电压的频率和幅值。

SVPWM的主要思想是以三相对称正弦波电压供电时三相对称电动机定子理想磁链圆为参考标准,以三相逆变器不同开关模式作适当的切换,从而形成PWM波,以所形成的实际磁链矢量来追踪其准确磁链圆。传统的SPWM方法从电源的角度出发,以生成一个可调频调压的正弦波电源,而SVPWM方法将逆变系统和异步电机看作一个整体来考虑,模型比较简单,也便于微处理器的实时控制。

SPWM与SVPWM的原理

SPWM原理

正弦PWM的信号波为正弦波,就是正弦波等效成一系列等幅不等宽的矩形脉冲波形,其脉冲宽度是由正弦波和三角波自然相交生成的。正弦波波形产生的方法有很多种,但较典型的主要有:对称规则采样法、不对称规则采样法和平均对称规则采样法三种。第一种方法由于生成的PWM脉宽偏小,所以变频器的输出电压达不到直流侧电压的倍;第二种方法在一个载波周期里要采样两次正弦波,显然输出电压高于前者,但对于微处理器来说,增加了数据处理量当载波频率较高时,对微机的要求较高;第三种方法应用最为广泛的,它兼顾了前两种方法的优点。SPWM虽然可以得到三相正弦电压,但直流侧的电压利用率较低, 最大是直流侧电压的倍,这是此方法的最大的缺点。

SVPWM原理

电压空间矢量PWM(SVPWM)的出发点与SPWM不同,SPWM调制是从三相交流电源出发,其着眼点是如何生成一个可以调压调频的三相对称正弦电源。而SVPWM是将逆变器和电动机看成一个整体,用八个基本电压矢量合成期望的电压矢量,建立逆变器功率器件的开关状态,并依据电机磁链和电压的关系,从而实现对电动机恒磁通变压变频调速。若忽略定子电阻压降,当定子绕组施加理想的正弦电压时,由于电压空间矢量为等幅的旋转矢量,故气隙磁通以恒定的角速度旋转,轨迹为圆形。

SVPWM比SPWM的电压利用率高15%,这是两者最大的区别,但两者并不是孤立的调制方式,典型的SVPWM是一种在SPWM的相调制波中加入了零序分量后进行规则采样得到的结果,因此SVPWM有对应SPWM的形式。反之,一些性能优越的SPWM方式也可以找到对应的SVPWM算法,所以两者在谐波的大致方向上是一致的,只不过SPWM易于硬件电路实现,而SVPWM更适合于数字化控制系统。

SPWM与SVPWM的算法

SPWM算法

SPWM 脉冲生成原理如图所示。

将一个正弦信号作为基准调制波 ,与一个高频等腰三角载波进行比较 ,得到一个等距、等幅但宽度不同的脉冲序列。脉冲系列的占空比按正弦规律来安排。当正弦值为最大值时,脉冲的宽度也最大 ,而脉冲间的间隔则最小;反之 ,当正弦值较小时,脉冲的宽度也小,而脉冲间的间隔则较大 ,这就是 SPWM 脉冲。用 6个 SPWM 脉冲序列分别控制6个IGBT导通或者截至 ,便能在三相定子绕组上得到交流信号,从而驱动PMSM 运转。

SVPWM 算法

图 1中,开关矢量[ a b c ]T共有8种取值,即6个IGBT的开关状态的组合一共有8个,这8种开关组合决定了8个基本空间矢量,如图3所示。将两个相邻的基本空间矢量 U0和 U60所包围的电压Uout映射到和轴6-8上,得到式1,其中 T表示一个 PWM 周期时间长度,T1和T2分别是在一个周期时间T中基本空间矢量U0和U60各自的作用时间,T是零矢量在一个周期中的作用时间,T0+T1+T2=T。

 如果定义式2,则可以得到每个扇区中包围这个扇区的两个基本矢量在一个PWM周期中的作用时间T1和 T29-10},如表 1所示。

对于式3,定义3个变量a,b,c,如果Vref1》0,则a=1,否则a=0;如果 Vref2》0,则 b=1,否则b=0;如果Vref3》0,则c=1,否则c=0。设N=4c+2b+a,则很容易得到N与扇区数sector的对应关系,如表1。

为了保证三相桥臂在一个PWM周期中导通的占空比,所应设置的比较值分别定义为Tcm1,Tcm2和Tcm3,并定义式4,则N与扇区数sector及Tcm 1,Tcm2和Tcm3的关系如表1所示。将Tcm1,Tcm2和Tcm3与设置为连续增 /减模式的DSP芯片定时器进行比较后得到PWM脉冲 ,控制图1中的3个桥臂的通断,从而在PM SM的3相定子绕组产生相位差为120°的正弦波形电流 ,形成圆形磁场,驱动电机运转。 

SPWM和SVPWM的对比

按照波形面积相等的原则,每一个矩形波的面积与相应位置的正弦波面积相等,因而这个序列的矩形波与期望的正弦波等效。这种调制方法称作正弦波脉宽调制(SPWM),这种序列的矩形波称作SPWM波。

图为三相PWM波形,其中

urU、urV、urW为U,V,W三相的正弦调制波uc为双极性三角载波;

uUN’、uVN’、uWN’为U,V,W三相输出与电源中性点N之间的相电压矩形波形;

uUV为输出线电压矩形波形,其脉冲幅值为+Ud和-Ud;

uUN为三相输出与电机中点N之间的相电压。

经典的SPWM控制主要着眼于使变压变频器的输出电压尽量接近正弦波,并未顾及输出电流的波形。而电流滞环跟踪控制则直接控制输出电流,使之在正弦波附近变化,这就比只要求正弦电压前进了一步。然而交流电动机需要输入三相正弦电流的最终目的是在电动机空间形成圆形旋转磁场,从而产生恒定的电磁转矩。

如果对准这一目标,把逆变器和交流电动机视为一体,按照跟踪圆形旋转磁场来控制逆变器的工作,其效果应该更好。这种控制方法称作“磁链跟踪控制”,而磁链的轨迹是交替是由使用不同的电压空间矢量得到的,所以又称“电压空间矢量PWM(SVPWM,Space Vector PWM)控制”。

随着逆变器工作状态的切换,电压空间矢量的幅值不变,而相位每次旋转π/3,直到一个周期结束。这样,在一个周期中6个电压空间矢量共转过2π弧度,形成一个封闭的正六边形。

在一个周期内,6个磁链空间矢量呈放射状,矢量的尾部都在O点,其顶端的运动轨迹也就是6个电压空间矢量所围成的正六边形。

在任何时刻,所产生的磁链增量的方向决定于所施加的电压,其幅值则正比于施加电压的时间。如果交流电动机仅由常规的六拍阶梯波逆变器供电,磁链轨迹便是六边形的旋转磁场,这显然不象在正弦波供电时所产生的圆形旋转磁场那样能使电动机获得匀速运行。如果要逼近圆形,可以增加切换次数,设想磁链增量由图中的11,12,13,14这4段组成。这时,每段施加的电压空间矢量的相位都不一样,可以用基本电压矢量线性组合的方法获得。 

可把逆变器的一个工作周期用6个电压空间矢量划分成6个区域,称为扇区(Sector),如图所示的Ⅰ、Ⅱ、Ⅲ、Ⅵ,每个扇区对应的时间均为π/3。由于逆变器在各扇区的工作状态都是对称的,分析一个扇区的方法可以推广到其他扇区。 

调制比即为逆变器输出电压与直流母线电压的比值,直流母线电压利用率是指逆变电路所能输出的交流电压基波最大幅值Um和直流电压Ud之比。SPWM中在调制度最大为1时,输出相电压的基波幅值为Ud/2,输出线电压的基波幅值为3/2Ud,直流电压利用率仅为0.866。 

SVPWM中,输出相电压的基波幅值与输出线电压的基波幅相等值为3/3Ud,直流电压利用率为1。SVPWM比SPWM的直流利用率提高了15.47%。SPWM和SVPWM谐波都主要集中在采样频率及其整数倍附近,且谐波幅值的极大值随采样频率倍数的增大而迅速衰减。从谐波分布趋势上讲,SPWM相对集中,幅值较大:SVPWM则相对分散,幅值较小。由下表2计算所得的总谐波畸变率可知,SVPWM方式输出波形的谐波含量低于SPWM方式。

传统的SPWM方法从电源的角度出发,以生成一个可调频调压的正弦波电源为目的。SVPWM方法将逆变系统和异步电机看作一个整体来考虑,模型比较简单,也便于微处理器的实时控制。SVPWM本身的产生原理与PWM没有任何关系,只是像罢了,SVPWM合成的驱动波形和PWM很类似,因此我们还叫它PWM,又因这种PWM是基于电压空间矢量去合成的,所以就叫它SVPWM了。

综上所述,SVPWM与SPWM的原理和来源有很大不同,但是他们确实殊途同归的。SPWM由三角波与正弦波调制而成,而SVPWM却可以看作由三角波与有一定三次谐波含量的正弦基波调制而成。相比之下SVPWM的主要有以下特点:

(1)在每个小区间虽有多次开关切换,但每次开关切换只涉及一个器件, 所以开关损耗小。

(2)利用电压空间矢量直接生成三相PWM波,计算简单。

(3)逆变器输出线电压基波最大值为直流侧电压,比一般的SPWM逆变器 输出电压高15%

三、呼吸灯的PWM原理

定时器生成PWM波

    PWM全称是Pulse Width Modulation,通过控制高频信号的占空比,眼睛当成低通滤波器,可以控制亮暗。再循环更改PWM的阈值,就弄出了呼吸的效果

    这里采用一个比较简单的方法生成PWM波:设置定时器中断然后根据阈值判断置高和置低。

void TIM3_IRQHandler(void)  
{
        TIM_ClearITPendingBit(TIM3,TIM_IT_Update);  
        if(counter==255)            
            counter = 0;
        else 
            counter +=1;
        if(mode == 0){
            if(counter < pwm)              
                GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1); 
            else 
                GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);    
        }
        if(mode == 1)
        {
            if(counter < pwm)              
                GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2); 
            else 
                GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);     
        }  
        if(mode ==2){
            if(counter < pwm)              
                GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); 
            else 
                GPIO_ResetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); 
        }
}

程序流程

  • 开启外设时钟(GPIO和TIM)
void RCC_Configuration(void)                
{
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);                                                       
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE); 
}
  • 配置GPIO
  • 配置时钟, 使能中断(计数阈值,预分频,时钟分频,计数模式)
void tim3()                           //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz
{
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;   //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure  
TIM_TimeBaseStructure. TIM_Period =9;         //配置计数阈值为9,超过时,自动清零,并触发中断
TIM_TimeBaseStructure.TIM_Prescaler =71;     //    时钟预分频值,除以多少
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 时钟分频倍数
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 计数方式为向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);      //  初始化tim3
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //  使能TIM3的溢出更新中断
TIM_Cmd(TIM3,ENABLE);                     //           使能TIM3
}
  • 配置中断优先级
void nvic()                                 //配置中断优先级
{    
 NVIC_InitTypeDef NVIC_InitStructure;  //    //   命名一优先级变量
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);    //     将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级
 NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //  设置使能
 NVIC_Init(&NVIC_InitStructure);                        //  初始化
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group
 NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//    打断优先级为1,与上一个相同,不希望中断相互打断对方
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;     //  响应优先级1,低于上一个,当两个中断同时来时,上一个先执行
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 NVIC_Init(&NVIC_InitStructure);
}
  • 写中断服务函数

代码实现

    为了方便按键检测,除了TIM3配置PWM波之外,TIM4用来检测是否有输入。由于使用开漏输出,这里使用5V电源。

#include "stm32f10x.h"
#include "math.h"
#include "stdio.h"
u8  counter=0; 
int  pwm=100;
int flag=0;
int mode =0;
int velocity =0;
int turning=1;
void RCC_Configuration(void);    //时钟初始化,开启外设时钟
void GPIO_Configuration(void);   //IO口初始化,配置其功能
void tim3(void);                 //定时器tim4初始化配置
void tim4(void);                 //定时器tim4初始化配置
void nvic(void);                 //中断优先级等配置
void exti(void);                 //外部中断配置
void delay_nus(u32);           //72M时钟下,约延时us
void delay_nms(u32);            //72M时钟下,约延时ms
void breathing(int velocity){
        switch(velocity){
                case 0:
                    if(flag)
                            pwm +=1;
                            if(pwm>240) flag=0;
                    if(flag == 0){
                            pwm -=1;
                            if(pwm<10) flag=1;
                    }
                    break;
                case 1:
                    if(flag)
                            pwm +=2;
                            if(pwm>240) flag=0;
                    if(flag == 0){
                            pwm -=2;
                            if(pwm<10) flag=1;
                    }
                    break;
                case 2:
                    if(flag)
                            pwm +=3;
                            if(pwm>240) flag=0;
                    if(flag == 0){
                            pwm -=3;
                            if(pwm<10) flag=1;
                    }
                    break;
        }
}
void assert_failed(uint8_t* file, uint32_t line)
{
    printf("Wrong parameters value: file %s on line %d\r\n", file, line);
    while(1);
}
void TIM4_IRQHandler(void)   //TIM4的溢出更新中断响应函数 ,读取按键输入值,根据输入控制pwm波占空比
{
        u8 key_in1=0x01,key_in2=0x01;
        TIM_ClearITPendingBit(TIM4,TIM_IT_Update);//     清空TIM4溢出中断响应函数标志位
        key_in1= GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_12);  // 读PC12的状态
        key_in2= GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13);// 读PC13的状态
        if(key_in1 && key_in2) turning =1;
        breathing(velocity);
        if(key_in1==0 && turning){
                turning =0;
        velocity = (velocity + 1) % 3;
    }//调速度
    if(key_in2==0 && turning){
                turning =0;
        mode = (mode + 1) % 3;
    }//调颜色
}   
void TIM3_IRQHandler(void)      //    //TIM3的溢出更新中断响应函数,产生pwm波
{
        TIM_ClearITPendingBit(TIM3,TIM_IT_Update);  //   //  清空TIM3溢出中断响应函数标志位
        if(counter==255)            //counter 从0到255累加循环计数,每进一次中断,counter加一
            counter = 0;
        else 
            counter +=1;
        if(mode == 0){
            if(counter < pwm)              //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
                GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1); //将PC14 PC15置为高电平
            else 
                        GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);     // 将PC14 PC15置为低电平
        }
        if(mode == 1)
        {
            if(counter < pwm)              //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
                GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2); //将PC14 PC15置为高电平
            else 
                        GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);     // 将PC14 PC15置为低电平
        }  
        if(mode ==2){
            if(counter < pwm)              //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
                GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); //将PC14 PC15置为高电平
            else 
                GPIO_ResetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); // 将PC14 PC15置为低电平
        }
}   
int main(void)
{
    RCC_Configuration();                                                                    
  GPIO_Configuration();                         
    tim4();
    tim3();
  nvic(); 
    while(1)
    { 
    }   
}   
void delay_nus(u32 n)       //72M时钟下,约延时us
{
  u8 i;
  while(n--)
  {
    i=7;
    while(i--);
  }
}
void delay_nms(u32 n)     //72M时钟下,约延时ms
{
    while(n--)
      delay_nus(1000);
}
void RCC_Configuration(void)                 //使用任何一个外设时,务必开启其相应的时钟
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);    //使能APB2控制外设的时钟,包括GPIOC, 功能复用时钟AFIO等,                                                                              
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE); //使能APB1控制外设的时钟,定时器tim3、4,其他外设详见手册             
}
void GPIO_Configuration(void)            //使用某io口输入输出时,请务必对其初始化配置
{
    GPIO_InitTypeDef GPIO_InitStructure;   //定义格式为GPIO_InitTypeDef的结构体的名字为GPIO_InitStructure  
                                          //typedef struct { u16 GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; } GPIO_InitTypeDef;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;    //配置IO口的工作模式为上拉输入(该io口内部外接电阻到电源)
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //配置IO口最高的输出速率为50M
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13;  //配置被选中的管脚,|表示同时被选中
    GPIO_Init(GPIOC, &GPIO_InitStructure);                  //初始化GPIOC的相应IO口为上述配置,用于按键检测
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;       //配置IO口工作模式为 推挽输出(有较强的输出能力)
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //配置IO口最高的输出速率为50M
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2;  //配置被选的管脚,|表示同时被选中
    GPIO_Init(GPIOA, &GPIO_InitStructure);        //初始化GPIOA的相应IO口为上述配置
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //失能STM32 JTAG烧写功能,只能用SWD模式烧写,解放出PA15和PB中部分IO口
}
void tim4()                           //配置TIM4为基本定时器模式,约10ms触发一次,触发频率约100Hz
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;   //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure  
    TIM_TimeBaseStructure. TIM_Period =9999;          // 配置计数阈值为9999,超过时,自动清零,并触发中断
    TIM_TimeBaseStructure.TIM_Prescaler =71;         //  时钟预分频值,除以多少
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数方式为向上计数
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);      //  初始化tim4
    TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //清除TIM4溢出中断标志
    TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);   //  使能TIM4的溢出更新中断
    TIM_Cmd(TIM4,ENABLE);                //        使能TIM4
}
void tim3()                           //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;   //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure  
    TIM_TimeBaseStructure. TIM_Period =9;         //配置计数阈值为9,超过时,自动清零,并触发中断
    TIM_TimeBaseStructure.TIM_Prescaler =71;     //    时钟预分频值,除以多少
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 时钟分频倍数
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 计数方式为向上计数
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);      //  初始化tim3
    TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志
    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //  使能TIM3的溢出更新中断
    TIM_Cmd(TIM3,ENABLE);                     //           使能TIM3
}
void nvic()                                 //配置中断优先级
{    
     NVIC_InitTypeDef NVIC_InitStructure;  //    //   命名一优先级变量
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);    //     将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级
     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断
     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高
     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //  设置使能
     NVIC_Init(&NVIC_InitStructure);                        //  初始化
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group
     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断
     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//    打断优先级为1,与上一个相同,不希望中断相互打断对方
     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;     //  响应优先级1,低于上一个,当两个中断同时来时,上一个先执行
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
     NVIC_Init(&NVIC_InitStructure);
}

二、早期555定时器芯片

由模拟 IC 奇才 Hans Camenzind 设计的 555 被称为有史以来最伟大的芯片之一。据说是世界上最畅销的集成电路,已售出数十亿。

    一个带有 Signetics 标志的 8 针 555 定时器。它没有 555 标签,而是标有“52B 01003”和 7304 日期代码,表示 1973 年的第 4 周。

    乏味地打磨环氧树脂封装以露出芯片(下图),并确定芯片是 555 定时器。Signetics 在 1972 年年中发布了 555 定时器,下面的芯片有一个 1973 年 1 月的日期代码(7304),所以它一定是最早的 555 定时器之一。奇怪的是,它没有标为 555,所以它可能是原型或内部版本。

    我拍摄了详细的模具照片,在这篇博文中进行了讨论。

    555 定时器的封装被打磨,露出硅芯片,中间的小方块。

    555 定时器有数百种应用,从定时器或锁存器到压控振荡器或调制器的任何操作。下图说明了 555 定时器如何作为一个简单的振荡器工作。在 555 芯片内部,三个电阻形成一个分压器,产生 1/3 和 2/3 的电源电压的参考电压。外部电容器将在这些限制之间充电和放电,从而产生振荡。更详细地说,电容器将通过外部电阻器缓慢充电 (A),直到其电压达到 2/3 参考电压。在该点 (B),上(阈值)比较器关闭触发器并关闭输出。这会打开放电晶体管,使电容器 (C) 缓慢放电。当电容器上的电压达到 1/3 参考电压 (D) 时,较低(触发)比较器打开,设置触发器和输出,循环重复。电阻器和电容器的值控制时间,从微秒到几小时。

    显示 555 定时器如何作为振荡器工作的图表。在 555 定时器的控制下,外部电容器通过外部电阻器进行充电和放电。

    总而言之,555 定时器的关键组件是检测电压上限和下限的比较器、设置这些限制的三电阻分压器以及跟踪电路是充电还是放电的触发器。555 定时器还有两个我上面没有提到的引脚(复位和控制电压),它们可用于更复杂的电路。

    从显微镜图像的合成中创建了下面的照片。在硅的顶部,一层薄薄的金属连接芯片的不同部分。这种金属在照片中以浅色痕迹清晰可见。在金属下方,一层薄薄的玻璃状二氧化硅层在金属和硅之间提供绝缘,除了二氧化硅中的接触孔允许金属连接到硅的地方。在芯片的边缘,细线将金属焊盘连接到芯片的外部引脚。

    如上,555 计时器的模具照片。

    芯片上不同类型的硅更难看到。芯片的区域用杂质处理(掺杂)以改变硅的电特性。N 型硅具有过量的电子(负),而 P 型硅缺乏电子(正)。在照片中,这些区域显示为略有不同的颜色,周围有细黑色边框。这些区域是芯片的组成部分,形成晶体管和电阻器。在windows中,保存的时候先另存在桌面,再拖进去覆盖即可!

    晶体管是芯片中的关键元件。555 定时器使用 NPN 和 PNP 双极晶体管。如果您研究过电子学,您可能已经看过如下图所示的 NPN 晶体管图,显示了晶体管的集电极 (C)、基极 (B) 和发射极 (E),晶体管被图示为P硅夹在两个对称的N硅层之间,NPN 层构成 NPN 晶体管。事实证明,芯片上的晶体管看起来不像这样,而且基极通常甚至不在中间!

    如上,NPN 晶体管的原理图符号,以及其内部结构的简化图。

    下面的照片显示了 555 中的一个晶体管的特写,因为它出现在芯片上。硅中稍有不同的色调表明已掺杂形成 N 和 P 区域的区域。白色区域是硅顶部芯片的金属层 - 这些形成连接到集电极、发射极和基极的导线。

    如上图,裸片上 NPN 晶体管的结构。

    照片下方是一个横截面图,说明了晶体管的构造方式。除了你在书中看到的 NPN 之外,还有很多其他东西,但如果你仔细观察“E”下方的垂直横截面,你会发现形成晶体管的 NPN。发射极 (E) 线连接到 N+ 硅。其下方是连接到基极触点 (B) 的 P 层。在其下方是(间接)连接到收集器(C)的 N+ 层。6 晶体管被 P+ 环包围,将其与相邻组件隔离。

    在IC内部的PNP晶体管:

    如上图,555定时器芯片中的PNP晶体管。标注了集电极(C)、发射极(E)和基极(B)的连接,以及N和P掺杂硅。基极围绕发射极形成一个环,集电极围绕基极形成一个环。

    555中的输出晶体管比其他晶体管大得多,并且具有不同的结构,以产生高电流输出。下面的照片显示了输出晶体管之一。注意被大集电极包围的发射极和基极的多个互锁“手指”。

    如上图,555定时器芯片中的大电流NPN输出晶体管。集电极(C)、基极(B)和发射极(E)被标记。

    电阻器是如何在硅中实现的?

    电阻器是模拟芯片的关键部件。不幸的是,IC 中的电阻器很大且不准确。不同芯片的电阻可能相差 50%。因此,模拟 IC 的设计只有电阻的比率很重要,而不是绝对值,因为比率几乎保持不变。

    如上,555定时器内部的电阻。电阻器是两个金属触点之间的一条 P 硅。

    上面的照片显示了 555 中的一个 10KΩ 电阻器,它由一条 P 硅(粉灰色)形成,在两端与金属线接触。其他金属线穿过电阻器。电阻器具有螺旋形状,以使其长度适合可用空间。下面的电阻是一个 100KΩ 的夹点电阻。夹层电阻器顶部的 N 硅层使导电区域更薄(即夹住它),形成更高但不太准确的电阻。

    555定时器内部的收缩电阻器。电阻器是两个金属触点之间的一条P硅。顶部的N层夹住电阻,增加电阻。垂直金属线穿过该电阻器。

    有一些子电路在模拟 IC 中很常见,但起初可能看起来很神秘。电流镜就是其中之一。如果您看过模拟 IC 框图,您可能已经看到下面的符号,指示电流源,并想知道电流源是什么以及为什么要使用它。这个想法是你从一个已知的电流开始,然后你可以用一个简单的晶体管电路,电流镜“克隆”电流的多个副本。

    如上图,电流源的原理图符号。

    以下电路显示了如何用两个相同的晶体管实现电流镜。参考电流流经右侧的晶体管。(在这种情况下,电流由电阻设定。)由于两个晶体管具有相同的发射极电压和基极电压,它们产生相同的电流,因此右边的电流与左边的参考电流相匹配。

    如上,电流镜电路,右边的电流复制左边的电流。

    电流镜的一个常见用途是替换电阻器。如前所述,IC 内部的电阻器既大又不准确,不便之处。尽可能使用电流镜而不是电阻器来节省空间。此外,与两个电阻器产生的电流不同,电流镜产生的电流几乎相同。

    如上图,三个晶体管在555定时器芯片中形成一个电流镜。它们共用同一个基极,两个晶体管共用发射极。

    上述三个晶体管构成一个具有两个输出的电流镜。注意,三个晶体管共享基极连接,连接到右边的集电极,右边的发射极连接在一起。在原理图中,右侧的两个晶体管被绘制为单个双集电极晶体管Q19。

    要了解的第二个重要电路是差分对,它是模拟 IC 中最常见的双晶体管子电路。 您可能想知道比较器如何比较两个电压,或者运算放大器如何减去两个电压。这是差分对的工作。

    如上,简单差分对电路原理图。电流源通过差分对发送固定电流I。如果两个输入相等,则电流均分。

    上面的示意图显示了一个简单的差分对。底部的电流源提供固定电流 I,该电流在两个输入晶体管之间分配。如果输入电压相等,则电流将平均分成两个分支(I1 和 I2)。如果其中一个输入电压比另一个高一点,则相应的晶体管会以指数方式传导更多的电流,因此一个分支获得更多电流,而另一个分支获得更少。一个小的输入差异足以将大部分电流引导到“获胜”分支,从而打开或关闭比较器。555 芯片使用一个差分对作为阈值比较器,另一对作为触发比较器。

四、 有刷直流电机使用H桥电路进行PWM驱动

本文介绍有刷直流电机使用H桥电路进行PWM驱动时的两个典型示例。

①将PWM信号输入至两个输入引脚之一并直接驱动
    在“通过H桥电路驱动有刷直流电机:输出状态的切换”中介绍了通过H桥电路驱动电机时,可以利用两个逻辑输入在4种状态(停止,正转,反转,制动)之间进行切换。第一种PWM驱动方法是将PWM信号输入至这两个输入引脚之一。

    这是某款有刷直流电机驱动器IC的框图。使用IN1和IN2逻辑切换H桥。

    两个真值表的左侧是标准的切换逻辑。其中,将正转(H/L)和反转(L/H)的H输入用作PWM输入。参见右侧的真值表。这就是停止(OPEN)、正转PWM驱动、反转PWM驱动和制动。在这种控制中,需要将逻辑信号和PWM信号从微控制器等发送到IN1/IN2。当然,驱动器IC需要支持这种驱动方法。

    在这种方法中,PWM输入信号的占空比与电源电压VM的乘积为等效平均施加电压。

②具备Vref PWM控制功能的电机驱动器
    有些电机驱动器IC是配备了PWM功能的IC。在该示例中,驱动器IC是在内部配有三角波发生器和比较器,并可以进行输出PWM驱动和PWM占空比调节的类型。

    通过框图和波形图可以了解其工作机制。比较器会输出三角波发生器的三角波与外部的基准电压Vref比较后的结果。比较器的输出占空比由Vref决定,当Vref为三角波的最小幅值0V时,占空比为0%;当Vref为三角波的最大幅值Vph时,占空比为100%。该比较器的输出(PWM信号)与IN1/IN2逻辑相组合,经由H桥输出至OUT1/OUT2。

    从原理上看,就像所介绍的,①和②都可以通过H桥进行PWM驱动,但是由于内置了防止同时导通的电路,以及②中使用了模拟电路来生成PWM信号,因此可能需要进行一些理论之外的调整(例如线性度和误差)。

二、详解STM32启动文件

本文对STM32启动文件startup_stm32f10x_hd.s的代码进行讲解,此文件的代码在任何一个STM32F10x工程中都可以找到。

启动文件使用的ARM汇编指令汇总

Stack——栈

Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=
Stack_Mem SPACE Stack_Size
__initial_sp

    开辟栈的大小为 0X00000400(1KB),名字为 STACK, NOINIT 即不初始化,可读可写, 8(2^3)字节对齐。

    栈的作用是用于局部变量,函数调用,函数形参等的开销,栈的大小不能超过内部SRAM 的大小。如果编写的程序比较大,定义的局部变量很多,那么就需要修改栈的大小。如果某一天,你写的程序出现了莫名奇怪的错误,并进入了硬 fault 的时候,这时你就要考虑下是不是栈不够大,溢出了。

    EQU:宏定义的伪指令,相当于等于,类似于C 中的 define。

    AREA:告诉汇编器汇编一个新的代码段或者数据段。STACK 表示段名,这个可以任意命名;NOINIT 表示不初始化;READWRITE 表示可读可写, ALIGN=3,表示按照 2^3对齐,即 8 字节对齐。

    SPACE:用于分配一定大小的内存空间,单位为字节。这里指定大小等于 Stack_Size。

    标号__initial_sp 紧挨着 SPACE 语句放置,表示栈的结束地址,即栈顶地址,栈是由高向低生长的。

Heap——堆

    开辟堆的大小为 0X00000200(512 字节),名字为 HEAP, NOINIT 即不初始化,可读可写, 8(2^3)字节对齐。__heap_base 表示对的起始地址, __heap_limit 表示堆的结束地址。堆是由低向高生长的,跟栈的生长方向相反。

    堆主要用来动态内存的分配,像 malloc()函数申请的内存就在堆上面。这个在 STM32里面用的比较少。

PRESERVE8
 THUMB

   PRESERVE8:指定当前文件的堆栈按照 8 字节对齐。

    THUMB:表示后面指令兼容 THUMB 指令。THUBM 是 ARM 以前的指令集, 16bit,现在 Cortex-M 系列的都使用 THUMB-2 指令集, THUMB-2 是 32 位的,兼容 16 位和 32 位的指令,是 THUMB 的超集。关于堆栈的文章:​​关于C语言堆栈的经典讲解​​。

向量表

AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size

    定义一个数据段,名字为 RESET,可读。并声明 __Vectors、 __Vectors_End 和__Vectors_Size 这三个标号具有全局属性,可供外部的文件调用。

    EXPORT:声明一个标号可被外部的文件使用,使标号具有全局属性。如果是 IAR 编译器,则使用的是 GLOBAL 这个指令。

    当内核响应了一个发生的异常后,对应的异常服务例程(ESR)就会执行。为了决定 ESR的入口地址, 内核使用了―向量表查表机制‖。这里使用一张向量表。向量表其实是一个WORD(32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地址。向量表在地址空间中的位置是可以设置的,通过 NVIC 中的一个重定位寄存器来指出向量表的地址。在复位后,该寄存器的值为 0。因此,在地址 0 (即 FLASH 地址 0) 处必须包含一张向量表,用于初始时的异常分配。要注意的是这里有个另类:0 号类型并不是什么入口地址,而是给出了复位后 MSP 的初值。下图是F103的向量表。

__Vectors DCD __initial_sp ;栈顶地址
DCD Reset_Handler ;复位程序地址
DCD NMI_Handler
DCD HardFault_Handler
DCD MemManage_Handler
DCD BusFault_Handler
DCD UsageFault_Handler
DCD 0 ; 0 表示保留
DCD 0
DCD 0
DCD 0
DCD SVC_Handler
DCD DebugMon_Handler
DCD 0
DCD PendSV_Handler
DCD SysTick_Handler
;外部中断开始
DCD WWDG_IRQHandler
DCD PVD_IRQHandler
DCD TAMPER_IRQHandler
;限于篇幅,中间代码省略
DCD DMA2_Channel2_IRQHandler
DCD DMA2_Channel3_IRQHandler
DCD DMA2_Channel4_5_IRQHandler
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors

    __Vectors 为向量表起始地址, __Vectors_End 为向量表结束地址,两个相减即可算出向量表大小。

    向量表从 FLASH 的 0 地址开始放置,以 4 个字节为一个单位,地址 0 存放的是栈顶地址, 0X04 存放的是复位程序的地址,以此类推。从代码上看,向量表中存放的都是中断服务函数的函数名,可我们知道 C 语言中的函数名就是一个地址。

    DCD:分配一个或者多个以字为单位的内存,以四字节对齐,并要求初始化这些内存。在向量表中, DCD 分配了一堆内存,并且以 ESR 的入口地址初始化它们。

复位程序

AREA |.text|, CODE, READONLY

    定义一个名称为.text 的代码段,可读。

    复位子程序是系统上电后第一个执行的程序,调用 SystemInit 函数初始化系统时钟,然后调用 C 库函数_mian,最终调用 main 函数去到 C 的世界。

    WEAK:表示弱定义,如果外部文件优先定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。这里表示复位子程序可以由用户在其他文件重新实现,这里并不是唯一的。

    IMPORT:表示该标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似。这里表示 SystemInit 和__main 这两个函数均来自外部的文件。

    SystemInit()是一个标准的库函数,在 system_stm32f10x.c 这个库文件中定义。主要作用是配置系统时钟,这里调用这个函数之后,单片机的系统时钟配被配置为 72M。__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈,并在函数的最后调用main 函数去到 C 的世界。这就是为什么我们写的程序都有一个 main 函数的原因。

     LDR、 BLX、 BX 是 CM4 内核的指令,可在《CM3 权威指南 CnR2》第四章-指令集里面查询到,具体作用见下表:

 中断服务程序

    在启动文件里面已经帮我们写好所有中断的中断服务函数,跟我们平时写的中断服务函数不一样的就是这些函数都是空的,真正的中断服务程序需要我们在外部的 C 文件里面重新实现,这里只是提前占了一个位置而已。

    如果我们在使用某个外设的时候,开启了某个中断,但是又忘记编写配套的中断服务程序或者函数名写错,那当中断来临的时,程序就会跳转到启动文件预先写好的空的中断服务程序中,并且在这个空函数中无线循环,即程序就死在这里。

NMI_Handler PROC ;系统异常
EXPORT NMI_Handler [WEAK]
B .
ENDP
;限于篇幅,中间代码省略
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC ;外部中断
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMP_STAMP_IRQHandler [WEAK]
;限于篇幅,中间代码省略
LTDC_IRQHandler
LTDC_ER_IRQHandler
DMA2D_IRQHandler
B .
ENDP

   B:跳转到一个标号。这里跳转到一个‘.’,即表示无线循环

用户堆栈初始化

ALIGN

    ALIGN:对指令或者数据存放的地址进行对齐,后面会跟一个立即数。缺省表示 4 字节对齐。

;用户栈和堆初始化,由 C 库函数_main 来完成
IF :DEF:__MICROLIB ;这个宏在 KEIL 里面开启
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory ; 这个函数由用户自己实现
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ALIGN
ENDIF
END

    首先判断是否定义了__MICROLIB ,如果定义了这个宏则赋予标号__initial_sp(栈顶地址)、 __heap_base(堆起始地址)、 __heap_limit(堆结束地址)全局属性,可供外部文件调用。有关这个宏我们在 KEIL 里面配置,具体见下图。然后堆栈的初始化就由 C 库函数_main 来完成。

 延庆川北小区45孙老师 东屯 收卖废品垃圾破烂炒股 废品孙

    如果没有定义__MICROLIB,则才用双段存储器模式,且声明标号__user_initial_stackheap 具有全局属性,让用户自己来初始化堆栈。

    前文的汇编代码,需要注意:

  • IF,ELSE,ENDIF:汇编的条件分支语句,跟 C 语言的 if ,else 类似
  • END:文件结束

五、 为什么PWM驱动芯片用图腾柱

推挽电路的应用非常广泛,比如单片机的推挽模式输出,PWM控制器输出,桥式驱动电路等。推挽的英文单词:Push-Pull,顾名思义就是推-拉的意思。所以推挽电路又叫推拉式电路

图1:锯木头

❤推挽电路有很多种,根据用法的不同有所差异,但其本质都是功率放大,增大输入信号的驱动能力,且具有两个特点:

①很强的灌电流,即向负载注入大电流;

②很强的拉电流,即从负载抽取大电流。

相x关x文章:​​动画演示什么是拉电流、灌电流​​。

图2

❤如图3NPN+PNP三极管组成的推挽电路,这就是我们常用的互补推挽电路。特点是输出阻抗很小,驱动能力很强。相关文章:STM32的GPIO电路原理。

图3:互补推挽电路

❤如图4,输入信号由低电平跳变到高电平,上管导通。

图4:上管导通

❤如图5,输入信号由高电平跳变到低电平,下管导通。

图5:下管导通

❤如图6,NPN+PNP构成的互补推挽电路是共射极输出,在任意时刻,有且只有其中一个管子导通有输出。

图6:共射极输出

❤有朋友觉得三极管不都是集电极(C)作为输出吗?怎么画风变了。按常规思路应该是如图7所示的电路图;如果单独输入是0V或12V,那么该电路看似没有毛病,但是输入信号是变化的,电压信号高低电平的跳变有过渡的过程,所以在某个中间电压时会出现两个管子同时导通的情况,这是要炸管的,切记!

图7:错误的推挽电路

❤如图8为推挽驱动MOS管的电机调速电路,MOS管的G极灌电流及拉电流都很大,于是MOS管的开通和关断时间都非常短,平台电压也非常窄,可有效降低开关损耗。

图8:电机调速电路

关于三极管和MOS管的特性,前面的文章有详细讲解,有不明白的朋友可以翻一翻。

❤当然,如图9把三极管替换成MOS管也是完全可以的,驱动能力会更强劲。

图9:MOS管结构的互补推挽

以上互补推挽电路的输入信号幅值必须和推挽供电电压一致,比如推挽供电电压为12V,那么输入的PWM信号的幅值也必须是12V。如果输入低于12V,输出也也会低于12V,参考图6所示,那么在管子上形成的压降会导致管子发热严重

❤那么有没有小电压驱动大电压的推挽结构呢,当然有,在很多驱动芯片里非常常见,我们管TA叫图腾柱;图10所示。

图10

图11的红框内,图腾柱由NPN+NPN构成,上管前级有个非门。(实际上,芯片框图对有些功能只以模块化展示,涉及的细节属于绝密是不可能呈现出来的)

为什么芯片采用图腾柱而不是互补推挽呢?原因是芯片内部的工作电压为5V(VCC经过芯片内部的线性电源得到5V),由前面对互补推挽的分析得知该结构并不适用于小电压驱动大电压;于是图腾柱结构的推挽孕育而生。

图11:图腾柱

❤如图12为图腾柱仿真电路,信号源为5V/1k的方波,二极管D1的作用是防止Q3、Q4同时导通而导致炸管

图12:图腾柱仿真电路

❤如图13为图腾柱仿真波形,输出与输入相位相反,黄色表示Ui输入波形,蓝色表示Uo输出波形,实现了小电压驱动大电压的推挽输出。

图13:图腾柱仿真波形

❤如图14为互补推挽仿真电路,信号源为12V/1k的方波。

图14:互补推挽仿真电路

❤如图15为互补推挽仿真波形,输出与输入相位一致,黄色表示Ui输入波形,蓝色表示Uo输出波形。

图15:互补推挽仿真波形

❤然而,我们常用的运放也是推挽输出,运放的一个特性就是输入阻抗很大,输出阻抗很小,输出如图16红框所示,输出阻抗不到200Ω。

图16:运放的推挽输出

❤如图17,运放输出端与反相输入端直接相连就构成了常用的跟随器,输出电压等于输入电压,驱动能力大大增强。

图片

图17:跟随器

要点:

①图腾柱是NPN+NPN结构,互补推挽是NPN+PNP结构;

②图腾柱有非线性特征,只能用于PWM输出,而互补推挽有线性特征,除了用于PWM输出外,还可用于模拟信号输出;

③图腾柱多见于PWM芯片驱动,用于直接驱动功率MOS管;互补推挽多见于搭建的电路以及MCU(单片机)、运放等芯片;

④PWM控制时,图腾柱输入电压可小于驱动电压,而互补推挽必须是输入电压与驱动电压相等。

关于图腾柱和互补推挽,很多时候都被认定是同一个电路(且存在争议),其实不然,正确认识以及了解它们的区别后,相信读者对它们有个全新的认识。

六、 xx