svpwm之先把电机转起来

学习FOC一段时间,怎是没有长进,一直看书,FOC框架比较复杂。我在想可不可以输出一个固定频率的SVPWM先把电机转动起来

 FOC框架如上图,我先实现SVPWM部分,如下图框选的部分,生成7段式SVPWM

1.硬件平台选择

硬件平台 MCU我选MSP430F5501,公司使用这个芯片做了几款USB 键盘,有很多测试板,之前都直接丢掉,现在拿来做个电机测试,发挥一下余热(最主要是烧坏了,可以再换一块),三相桥我选择L298N,这个模块淘宝上很便宜,7-8元就可以买一个模块,烧坏了也不用维修。电机我有一个安川的小电机和一个教学用的异步电机

搭建如下图

连接示意图如下:

 其中MSP430这个板子只用了4个脚,用于控制三相桥的三个信号,这个三个信号需要使用定时器输出,所以要选带定时器的引脚,我的板子上MSP430的定时器脚只有P1和P2端口上有,因为我的MSP430封装比较小,虽然有多个定时器,输出脚没有引出来。L298N上的ENA和ENB俩个信号,我把它并到一块,随便接入MSP430的一个引脚。MSP430的供电由L298N模块输出

MSP430部分图纸

 2.定时器

MSP430选用定时器Timer0,选用增减计数。MSP430 MCLK=24M,SMCLK=12M,ACLK=32K

定时器Timer0选用SMCLK=12M,125us产生一次中断,也就是8K频率

则增减计数CCR0值就是 (12M/8K)/2 = (12000/8)/2 =  1500/2 = 750。

计数器如下图

 其中CCR0,就是750

调节CCR1,CCR2,CCR3就可调节6个扇区的PWM

3.看代码

我的代码比较简单,只有两个文件。main.c和bldc.c.我使用的环境是MSP430 GCC,你可以使用其它环境开发,完全没有问题,最主要的是中断函数声明不一样,这个要注意。

main.c的内容如下:

#include <msp430.h>

void main(void)
{

    volatile int i;
    WDTCTL = WDTPW + WDTHOLD; 
    set_core_volt();        //提高核心电压
    port_init();
    bldc_init();
    bldc_en();
    __bis_SR_register(GIE);
    while(1){

        __delay_cycles(10000000);

        led_triggle();
        //__bis_SR_register(LPM0_bits + GIE); //进入低功耗
    }
}

main.c 就实现一个简单功能

关闭看门狗

设置MSP430 的核心电压,因为要提高msp430的频率,我外接晶体是XT2,震荡频率是4M

初始化端口

初始化bldc端口

bldc_en,就是把L298N的ENA和ENB给高电平

接下来看看 bldc.c源码:

#include <msp430.h>

//设置核心电压等级,为25M做准备
void set_core_volt()
{
    PMMCTL0_H = PMMPW_H;                //写入电源管理的解锁key
    SVSMLCTL |= SVSMLRRL_1 + SVMLE; //配置SVML电压
    PMMCTL0 = PMMPW + PMMCOREV_3;   //配置内核电压,25M需要配置为 V_3
    while((PMMIFG & SVSMLDLYIFG) == 0); //等待配置完成

    PMMIFG &= ~(SVMLVLRIFG + SVMLIFG + SVSMLDLYIFG);
    if((PMMIFG & SVMLIFG) == 1)     //判断核心电压是否上升
        while((PMMIFG & SVMLVLRIFG) == 0);

    SVSMLCTL &= ~SVMLE;     //关掉SVML模块
    PMMCTL0_H = 0x00;         //上锁    
}

//MSP430F5501
//输出端口可用 P1.0-P1.6
//板上端口:
// P4.7 LED  高电平点亮
// P4.0 蜂鸣器 高电平发声
//P5.2 P5.3 板载4M晶体

void port_init()
{
    P4DIR = BIT0 + BIT7;    //设置LED和蜂鸣器
    P5SEL = BIT2 + BIT3;    //设置晶振管脚
    //切换时钟源
    //首先把所有时钟切为内部时钟REFFO,REFO=32768Hz
    UCSCTL6 &=  ~XT2OFF;    //开启XT2晶振
    UCSCTL4 = SELA_2 + SELS_2 + SELM_2;      //ACLK = SMCLK = MCLK = REFO = 32K
    UCSCTL0 = 0x0000;
    //等到XT2震荡稳定
    do{
        //清除XT2,XT1,DCO失效标志
        UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);
        SFRIFG1 &= ~OFIFG;      //清除故障失效中断标志
    }while(SFRIFG1 & OFIFG);    //测试晶振失效标志

    UCSCTL6 &= ~XT1DRIVE0;      //减少XT1的驱动能力,降低功耗
    __bis_SR_register(SCG0);    //禁止FLL
    UCSCTL3 = SELREF__XT2CLK;   //FLL参考时钟选择XT2, FLLREFDIV = 0不分频,则FLL输入4MHz
    UCSCTL1 |= DCORSEL_5;       //选择DCO频率范围
    UCSCTL2 = FLLD_1 + FLLN2_L; //DCOCLK = 24M, DCOCLKDIV = 12M
    __bic_SR_register(SCG0);    //启用PLL
    __delay_cycles(2500);     //等待DCO稳定

    UCSCTL4 = SELA_2 + SELS_4 + SELM_3; //ACLK = REFO = 32K; SMCLK = DCOCLKDIV =12M; MCLK = DCO = 24M

}

void led_triggle()
{
    P4OUT ^= BIT7;
}

//BLDC端口初始化
//P1.2 --> in0 -->TA0.1
//P1.3 --> in1 -->TA0.2
//P1.4 --> in2 -->TA0.3
//P1.0 --> ENA
//P1.1 --> ENB
// 计数上限 125us 一次中断 12M/(750*2)
#define CNT_MAX 750
void bldc_init()
{
    P1DIR |= 0x3f;  //P1.0,P1.1-->ENA,ENB
    P1OUT &= 0xfc;  //端口输出 0
    P1SEL |= BIT2 + BIT3 + BIT4;  //设置为第二功能模式 p1.2-p1.4

    //启动定时器A0, 设置125us中断
    //SMCLK = 12M,增减计数模式
    TA0CTL = TASSEL_2 + MC_3;
    TA0CCTL0 = CCIE;
    TA0CCR0 = CNT_MAX;   //12M/8K
    //配置CCR1 取反置位模式
    TA0CCTL1 = OUTMOD_6 + CLLD_1;
    TA0CCR1 = 0;
    //配置CCR2
    TA0CCTL2 = OUTMOD_6 + CLLD_1;
    TA0CCR2 = 0;
    //配置CCR3
    TA0CCTL3 = OUTMOD_6 + CLLD_1;
    TA0CCR3 = 0;

}


//打开使能端
void bldc_en()
{
    P1OUT |= 0x03;
}

//关闭使能
void bldc_dis()
{
    P1OUT &= 0xfc;
}


/**
 * 以8K频率
 * 125us 计算一次
 * 
*/

//总时间,总计数,使用增减计数
#define T_SUM   CNT_MAX*2
//有效调节时间
#define T_VALID 700 
//每个0矢量的时间 在总时间中有4个0矢量
//调节0矢量,可调节输出电压
#define T_ZERO 25
//T_VALID + T_ZERO*2 = T_SUM/2

//每转动一度增减的计数值 
#define T_DX (T_VALID/60)
//每次中断转动度数,调节速度
#define D_DEG 1

//定时器 B CCR0的比较中断
static int rect = 0;
static volatile int tb_err = 0;
void __attribute__((interrupt(TIMER0_A0_VECTOR))) TIMERA0_ISR(void)
{
    /*
    * 计数器采用取反置位模式
    */
    int dx = T_DX * D_DEG;  //每次中断需要计数器增加的值
    rect += D_DEG;
    if(rect >= 360)
        rect -= 360;
    //扇区判断
    if(rect == 0){
        TA0CCR1 = T_ZERO;  //0矢量
        TA0CCR2 = T_VALID + T_ZERO ;
        TA0CCR3 = T_VALID + T_ZERO ;
    }else if(rect < 60){//第一扇区,100 110 
        TA0CCR2 -= dx ;
    }else if(rect == 60){
        TA0CCR1 = T_ZERO;   
        TA0CCR2 = T_ZERO;//常开
        TA0CCR3 = T_VALID + T_ZERO;
    }else if(rect < 120){//第二扇区,110 010
        TA0CCR1 += dx;
    }else if(rect == 120){
        TA0CCR1 = T_VALID + T_ZERO;   
        TA0CCR2 = T_ZERO;//常开
        TA0CCR3 = T_VALID + T_ZERO;
    }else if(rect < 180){//第三扇区,010 011
        TA0CCR3 -= dx;
    }else if(rect == 180){
        TA0CCR1 = T_VALID + T_ZERO;   
        TA0CCR2 = T_ZERO;
        TA0CCR3 = T_ZERO;
    }else if(rect < 240){//第四扇区,011 001
        TA0CCR2 += dx;
    }else if(rect == 240){
        TA0CCR1 = T_VALID + T_ZERO;   
        TA0CCR2 = T_VALID + T_ZERO;//常开
        TA0CCR3 = T_ZERO;
    }else if(rect < 300){//第五扇区,001 101
        TA0CCR1 -= dx;
    }else if(rect == 300){
        TA0CCR1 = T_ZERO;   
        TA0CCR2 = T_VALID + T_ZERO;
        TA0CCR3 = T_ZERO;
    }else if(rect < 360){//第六扇区,101,100
        TA0CCR3 += dx;
    }else{ //程序出错,关闭使能
        tb_err ++;
    }
}

int get_tb_err()
{
    return tb_err;
}


void __attribute__((interrupt(TIMER0_A1_VECTOR))) TIMERA1_ISR(void)
{

  switch( TA0IV )
 {
   case  2: // CCR1
       
   break;                           
   case  4: // CCR2
   break;                          

   case 14: ;                  // overflow
    break;
 }
}

这个代码不到200行。

set_core_volt,这个函数不用说了,目的就是设置msp430的核心电压,不同型号的430,设置方法应该不一样,msp430f5xx,6xx应该按照这个函数设置。或者你直接调用库函数。

port_init,这个函数主要初始化蜂鸣器IO端口和LED端口。最主要是开启XT2 外部晶体震荡器,并把msp430的时钟配置好。初始化后,频率如下:

ACLK = REFO = 32K;

SMCLK = DCOCLKDIV =12M;

MCLK = DCO = 24M

蜂鸣器在我的工程中没有使用,初始化它,最主要怕他瞎响,给他一个低电平,让他关闭。

LED主要用于闪烁,看看MSP430有没有烧坏,供电是否正常。

bldc_init:主要是设置定时器输出管脚,打开P1的第二功能输出。定时器的管脚也要设置为输出。

初始化定时器CCR。选择Timer0的时钟源和计数值。

bldc_en/bldc_dis :就是控制L298N的ENA和ENB这两个管脚,后续我把这俩个管脚合到一块,只用一个IO控制。就普通管脚就可以。只要给一个高电平或者低电平就可以。或者你不想控制,直接使用L298N的短路帽子把它直接使能。

TIMERA0_ISR:最主要一个中断函数,每125us执行一次中断,每次中断修改相邻的两个开关的占空比。

rect 是代表一个角度,每个中断角度增加一个固定值D_DEG, 这个D_DEG可以取 1,2,3,4,5,6。也可以取其它值,取值越大表明每一次中断转动的角度越大,速度也就越快。但是要保证 60/D_DEG 一定要是一个整数,不然计数值达不到 0,60,120,180,240,300,那几个赋占空比的条件没有执行到,后续就错乱了。

T_ZERO :表示0矢量占用时间,一个周期中计数值是 T_SUM = CNT_MAX*2 = 750*2 = 1500.

                0矢量要占用一定的时间T_ZERO,有效输出占用一定的时间 T_VALID

                关系是: T_SUM = 2*(T_VALID + 2*T_ZERO)

                其中 T_VALID越大代表有效输出暂用输出越大,最终旋转矢量的模长越长

                我们的T_SUM是一个固定值,所以只能调节T_VALID和T_ZERO来调节输出电压

 dx:表示每次转动,计数器需要增加或者减少多少计数,一个扇区总共60度 ,

          转动一度需要增/减 T_DX = T_VALID/60这麽多个计数值,

           则转动D_DEG度则需要 D_DEG*T_DX计数值。

    7段式svpwm是 :

T_ZERO, T_VALID1, T_VALID2, T_ZERO, T_ZERO, T_VALID2,T_VALID1,T_ZERO

T_VALID1+TVALID2 = T_VALID

下面最主要是调节T_VALID1和T_VALID2.

旋转矢量一定是个圆形,我们按照圆形旋转的计算相邻计数值,但是旋转矢量它的模长不是固定的,我仿真一下他的模长是 在 85%-99%变动,是因为我们直接使用增加/减少dx,没有计算合适的dx导致。

TIMERA1_ISR:没有使用

4.看看试验效果

转PMSM电机

转异步电机,图片太大,就不传了。

转动PMSM时候,一定要限流,转动异步机还好,电压给14V的时候,电流稳定只有500mA

但是PMSM时候,电流直接就2A,不限流的话,L298N直接烧坏了,L298N最大电流就2A。

我的PMSM是24V,我试100V的PMSM,电流只有200mA。

可以通过修改T_VALID和T_ZERO的值来降低电流,降低有效输出的时间。

24V电机参数,30W

 

 

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jjinl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值