学习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