分享

STM32驱动直流电机的程序与电路设计(IR2110S自举电路+H桥+高级定时器和死区PWM)

 goodwangLib 2020-07-14

本文介绍如何使用STM32F103单片机,通过官方固件库,设置高级定时器TIM1输出嵌入死区的互补PWM,来驱动直流电机的程序设计与电路设计。硬件电路采用IR2110S芯片作为mos管的驱动,驱动IRF840组成的H桥。IR2110S芯片使用中,有一个比较难理解的点——自举电容,本文对其原理也有涉及。
原理图文件
电机驱动电路简化原理图
上图是系统的简化原理图,左侧是单片机。中间是IR2110S芯片,为了方便讲解,把芯片内部结构列出一些。右侧是MOS管组成的H桥。其中M是直流电机,有正反转。其中VCC是15V,MOTOR_VCC是24V,电压可以改变,最大不超过500V。单片机一般是3.3V或5V,无法直接驱动电机。可以借助H桥来实现对直流电机的控制。
H桥由于形似H得名。
VT1,VT4导通,电机正转
VT2,VT3导通,电机翻转
VT1,VT3导通,短路,板子烧坏
VT2,VT4,导通短路。
所以,驱动电机的问题就变成了MOS管导通的问题。
实际电路中我选用了IRF840,这是N沟道的MOS管。N-MOS导通的条件:VGS大于一定值
对于IRF840,VGS>10V
IRF840数据手册说明
所以MOS管导通的问题就变成了VGS>10V的问题

如果,VT1与VT3都独立配置一个电源,独立配置一套驱动,导通问题就变得简单了。但是,电路设计会变得复杂。
我们使用1个驱动芯片IR2110S,一路驱动电源。
简单介绍下IR2110S芯片
IR2110是独立一桥臂双通道,栅极驱动,高压,高速单片机专用功率器件集成驱动电路。2片IR2110就能构成H桥驱动电路。
IR2110S是3.3V版本
感兴趣的可以自己来做个阅读理解。
IR2110芯片简介
简单来说,IR2110是个3.3V控制10-20V的一个驱动。开关速度也很快,120ns
内部结构
IR2110内部结构
右上角的两个MOS管,中间是非门连接,不会同时导通。
中间一系列怎么变化,我也不是很清楚。
HIN是1,VM1导通,VM2截止。VB与HO连在一起
HIN是0,VM1截止,VM2导通。VS与HO连在一起
LIN是1,VM3导通,VM4截止。VCC与LO连在一起
LIN是0,VM3截止,VM4导通。LO与COM连在一起
先看下桥臂。左侧下桥臂导通,很简单:
LIN为高,VM3导通,VCC接在LO上(暂时忽略二极管压降),VGS= VCC,导通
LIN输入高电平

上桥臂导通的情况,先假设没有电容。
VM1导通时,VCC接在HO上,为G极提供了接近15V的电压。但是,VS的电压是多少?不知道。
如果,VT3导通,VS就是0,VT1也导通了,烧坏。
如果VT4导通,VS通过电机接地(电机内部可以先等效为电阻)。但是VT1导通以后,VS接近24V,HO只有15V,VT1又截止了。电机还是不能工作。
我们面临的问题是,上桥臂没有地。怎么办?
VCC接在G上
这个时候,就需要自举电容。
VT4导通,VS接地。电容一端是地,一端是15V,所以VCC通过D给C充电。
又因为VM1导通,所以C横跨在GS上。所以,C可以作为电压源,为GS供电。这是一个悬浮的电压源。
VT1导通后,VS接近24V,不再是地。所以VCC15无法为G提供足够的电压。
自举电容可以放电维持VT1工作,电容存有15V的电,可以保持MOS管的导通。由于电容两极的压差不能突变,而电容下边变成了对地24V(暂不考虑MOS管压降),所以这一瞬间,电容上边的电压是对地39V。这时,VCC无法为电容充电。由于二极管的存在,电容的电不会倒灌给VCC。
电容电量又是有限的,放电会导致电容的电压降低。等到两级压差不到10V的时候,VT1又不工作了。并且,此时IR2110芯片内部的欠压检测逻辑就会工作,把HO拉到VS,让VGS=0。
所以,自举电容电压小于10V之前,要充电。如果HIN一直是高电平,电容就没有充电的机会,等到自举电容的的电压跌落到某个阈值以下,HO就变为低电平。
此时可以关掉VT1,也就是断开VM1,VB与HO断开,不论是VCC还是电容都不再为G极提供电源。如果此时打开VT3,让VS接地,则电容一边是高电平,一边是低电平,开始充电。然后再断开VT3,打开VM1,VT4保持不变,让电容放电维持VT1导通,就可以循环往复,保持电机运行。
即,VT1的导通要依靠电容放电来维持。HIN不能为持续的高电平,占空比也不能达到100,或低频的PWM(频率低,一个周期内放电时间长)。必须是高频的PWM,保证自举电容有周期性的,足够的充电时间,才能维持较高的悬浮电源电压。
除此之外,还要注意死区问题,由于绝对不可以把同侧桥臂的上下半桥同时打开,而IR2110S,MOS管与电机切换状态都存在延时,导致从程序命令某半桥关断,到实际关断,有一段时间的延迟。例如,在延迟期间,上半桥正在关闭,下半桥暂时还不能打开,直到上半桥完全关闭,下半桥才能打开。中间等待的这段时间,就是死区。死区时间与硬件密切相关。笔者手上就有两个不同型号的电机,一个在3us的死区时可以工作,另一个则不可以。
所以,写程序要注意到上下桥不能同时导通,高频,不能是100的占空比,以及死区这几个问题。接下来尝试用STM32的高级定时器,输出嵌入死区的互补PWM。
以下是定时器1的初始化代码,使用两个通道输出PWM,一个周期是100us,频率是10KHz,3us的死区时间。默认通道一的占空比是50%,通道2的占空比是0%,让电机以47%(占空比减去死区)的速度正转。
通道2输出占空比是0,可以让右侧上半桥总是截止,下半桥总是导通。下半桥没有自举电容。如此一来,只需要左侧上半桥导通,就可以让电机正转。控制左侧桥臂的占空比,就能控制电机的占空比。
定时器1的通道1引脚是PA8PB13,通道2的输出引脚是PA9PB14。

void PWM_Configuration(void)
{
    GPIO_InitTypeDef    GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    //开启TIM和相应端口时钟
    //启动GPIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  | RCC_APB2Periph_GPIOB,
                     ENABLE);
    //启动AFIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    //启动TIM1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

    //GPIO做相应设置,为AF输出             //PA8,PB13一组互补输出  A9,PB14一组互补输出
    //PA.8/9口设置为TIM1的OC1输出口
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    //PB.13/14口设置为TIM1_CH1N和TIM1_CH2N输出口
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA, GPIO_Pin_8 | GPIO_Pin_9);
    GPIO_SetBits(GPIOB, GPIO_Pin_13 | GPIO_Pin_14);

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);    
    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);

    //TIM1基本计数器设置(设置PWM频率)10KHz 
    TIM_BaseInitStructure.TIM_Period = 100-1;      //10khz  好计算。按照1%的精确度,理论最大72000/100 = 720KHz
    TIM_BaseInitStructure.TIM_Prescaler = 72-1;
    TIM_BaseInitStructure.TIM_ClockDivision = 0;
    TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
    TIM_BaseInitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM1, &TIM_BaseInitStructure);
    //启用ARR的影子寄存器(直到产生更新事件才更改设置)
    TIM_ARRPreloadConfig(TIM1, ENABLE);

    //TIM1_OC1模块设置(设置1通道占空比)
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//TIM脉冲宽度调制模式1
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出通道使能
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;//互补输出
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;//TIM输出比较极性高
    //TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
    TIM_OCInitStructure.TIM_Pulse = 50;//待装入捕获比较寄存器的脉冲值
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);

    //启用CCR1寄存器的影子寄存器(直到产生更新事件才更改设置)
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);

    
    //TIM1_OC2模块设置(设置2通道占空比)
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 0;
    TIM_OC2Init(TIM1, &TIM_OCInitStructure);
    //启用CCR2寄存器的影子寄存器(直到产生更新事件才更改设置)
    TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);

    //OCx输出信号与参考信号相同,只是它的上升沿相对参考信号的上升沿有一个延迟
    //OCxN输出信号与参考信号相同,只是它的上升沿相对参考信号的下降沿有一个延迟

    //死区设置
    TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
    TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
    TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_2;
    //bit7~5 = 111,则deadtime = (32 + (bit4~bit0)* 16*1/fosc)ns = (32+31)*16*1/72000000 = 14us
    TIM_BDTRInitStructure.TIM_DeadTime = 0xab; //这里调整死区大小0-0xff3us
    TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
    TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
    TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
    TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);

    //TIM1_OC通道输出PWM
    TIM_CtrlPWMOutputs(TIM1, ENABLE);

    //TIM1开启
    TIM_Cmd(TIM1, ENABLE);
}

带死区的上下半桥驱动信号波形
从上图中,可以清楚地看到,单片机输出的上、下桥臂控制信号,不存在同时为高电平的时候,也就是同侧上下桥臂不会同时导通。切换状态时,某半桥臂的控制信号拉低3us以后,另半桥臂的控制信号才能拉高,这就是所谓的带死区的互补PWM。
输出的电机控制电压
上图是输出的电机控制电压。可以看出周期是100us,高电平持续时间大约一半。
初始化之后,在程序运行时,可以调用

TIM_SetAutoreload(TIM1,xx);

来设置自动重装值。在初始化的时候把此值设置为了100,如果改为80,效果如下:
72分频80自动重装值波形
可以看出,一个周期变成了80us。
也可以使用函数

                TIM_SetCompare1(TIM1,xx);
                TIM_SetCompare2(TIM1,xx);

来分别为通道1与通道2设置比较值。例如,把比较值设置为80,而自动重装值还是100,那么占空比就是80%了。
80%占空比
下边是用按键控制电机的一个小demo。实现了按键1启停,按键2切换正反转,按键34增减转速的功能。

int main(void)
{    
    static u8 motorValue = 50,oldvalue = 50;
    static u8 startStop = 1,dir = 1;//startStop = 1启动  =0停止 dir =1 正转
    volatile u8 key = 0;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    LED_Init();
    KEY_Init();
    delay_init();
    PWM_Configuration();
    Motor_IO_Init();
    LED1 = LED_ON;
    while(1)    
    {
        key = KEY_Scan(0);
        if(key)
        {
            switch(key)
            {
                case KEY1_PRES:
                        startStop = !startStop;
                break;
                case KEY2_PRES:
                        dir = !dir;
                break;
                case KEY3_PRES:
                        motorValue += 5;
                break;
                case KEY4_PRES:
                        motorValue -= 5;
                break;
                default:
                        break;
            }
            if(motorValue>249)//<0
                motorValue = 0;
            else if(motorValue >94)
                motorValue = 94;
            if (startStop)
            {
                if(dir)
                {
                    TIM_SetCompare2(TIM1,0);
                    delay_ms(500);
                    TIM_SetCompare1(TIM1,motorValue);
                }
                else
                {
                    TIM_SetCompare1(TIM1,0);
                    delay_ms(500);
                    TIM_SetCompare2(TIM1,motorValue);            
                }
            }
            else
            {
                TIM_SetCompare1(TIM1,0);
                TIM_SetCompare2(TIM1,0);
            }            
        }

    }
}
//S1 启动&停止  S2翻转   S3+5   S4-5

完整的原理图文件在这里

新增了一个用集成芯片的驱动方案链接

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多