【DSP学习】【从零开始拖动永磁直流无刷电机PMSM】【28377S】

本文介绍了利用DSP TMS320F28377S开启定时中断的详细过程,包括中断的三级系统和中断响应机制。接着,文章深入讲解了三相电机的工作原理,特别是如何通过控制EPWM产生不同电压矢量以驱动电机。通过电压矢量分解,阐述了如何在不同扇区调整电压大小和方向。最后,讨论了读取电机电角度的方法,如绝对式和增量式编码器的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

步骤1:开启定时中断

什么是中断:我的理解是:

中断分为3个等级:1级,外设级,包括【PIEIFR与PIEIER】;2级PIE级【PIEACK】(同组响应), 3级,CPU级【IER和IFR】,以及【INTM(全局中断)】。

某个寄存器(源于自身或外设)的值符合预设条件(< = >)时,一级中断系统的“PIE中断标志寄存器”(PIEIFR),对应的位被“自动”置一(重复操作,自动置1,需要手动清零)(例如:PIEIFR1.7=1=第一组第七个中断信号被触发),发出一级中断信号。

如果一级中断系统的“PIE中断使能寄存器”(PIEIER)中,对应【PIEIFR1.7】的【PIEIER1.7】预先被置一(单次操作,手动置1),那么一级中断信号就可以传送到二级中断系统【PIE中断应答寄存器(PIEACK)】

就像中层干部管理多个基层,一个二级中断系统,也管理着多个一级中断系统,【PIEIFR1.1】~【PIEIFR1.16】均被【PIEACK_GROUP1】管理着。但【PIEACK_GROUP1】在同一个时间下,只能处理一个一级中断系统,假如【PIEACK_GROUP1】已经被【PIEIFR1.7】触发,那么【PIEACK_GROUP1】=1(重复操作,自动置1,需要手动清零),且暂时不接受【PIEIFR1.1】~【PIEIFR1.16】的再次发送过来的第一组的中断信号(包括【PIEIFR1.7】再次发送也不接收)。直到手动置零,才能响应下一个同组中断信号

如果二级中断系统【PIEACK_GROUP1】接收到了中断信号【PIEIFR1.7=1】,而且没有在处理同组其他中断信号(即【PIEACK_GROUP1】=0),那么中断信号将被发送到第三级中断系统【CPU中断】。

和第一级类似,CPU中断也有标志寄存器【IFR】和使能寄存器【IER】,当中断信号通过了前两级系统时,【CPU_IFR】自动置一,CPU处理完自动信号时,自动置零,当【CPU_IER】被预先手动置一时(单次操作,手动置1),中断信号发送至【全局使能(INTM)】。

正如一个二级中断系统管理着多个(16个)一级中断系统,但同一时间只能响应一个中断信号;三级中断系统也管理着多个(12个)二级中断系统,同一时间也是只能响应(group1~group12)的一个信号。【INTM】=0,则代表可以接收中断信号,接收后,会自动置一;当CPU把预先编写好的响应该中断信号的函数执行完毕后,必须手动把【INTM】置零(使用汇编指令EINT,Enable Global Interrupts,置0,则为预备接收状态)。

c1d6c134cd524c9dbe6db64df60260f0.png

分享文章:【http://t.csdn.cn/tMwsB

分享文章:【DSP_TMS320F28377中断理解_f280377 外部中断-优快云博客

//文件类型:源文件
//文件名:init_interrupt.c

#include "F28x_Project.h"

void init_interrupt_my()
{
	//关闭全局中断
	DINT;

	    EALLOW; // 关闭写保护
	    PieVectTable.TIMER0_INT = &TIMER0_ISR;
	    //中断信号指向函数,按住ctrl键+鼠标左键点击【TIMER0_ISR】=
	    //进入TIMER0中断对应的函数
	    EDIS;    //开启写保护

	    InitCpuTimers();//初始化CPU时钟
	    
	    ConfigCpuTimer(&CpuTimer0,2,3125 );
	   /*ConfigCpuTimer(CPUTIMER_VARS , Freq,  Period);
	    * (CPU计数器)触发时间间隔=Freq(单位M)* Period(单位us)/DSP时钟频率(200M)
	    * =2M*3125us/200M=31.25us
	    * 当Freq==DSP时钟频率时,Period=触发时间间隔,单位us。
	    * 
	    */ 
	    CpuTimer0Regs.TCR.all = 0x4000;//使能开始计数

	    PieCtrlRegs.PIEIER1.bit.INTx7 = 1;
	    //1.7 - Timer 0 Interrupt==使能一级中断系统的“PIE中断使能寄存器”(PIEIER)
	    
	    IER |= M_INT1;
	    //使能三级中断系统,CPU中断使能寄存器【IER】

	    EINT;  // Enable Global Interrupts使能全局中断
}

 

 

//
// 1.7 - Timer 0 Interrupt
//
interrupt void TIMER0_ISR(void)
{
	/*需要声明
#include "F2837xS_device.h"
#include "F2837xS_Examples.h"
#include "MY_headers.h"
	 */

//	Theta_from_pos();//读取当前电角度

	UQ_output(20000,1);//输出UQ/

	//	last==执行任何中断函数,都需要在最后清除中断标志,不然无法再次响应(同个、同组)中断

		CpuTimer0Regs.TCR.bit.TIF=1;//清除本中断的标志位,置1=清除,
		PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;//清除同组中断的标志位,置1=清除
		EINT;//开全局中断
}

//文件位置:  【工程】 / F2837xS_common / F2837xS_DefaultISR.c

步骤2:理解三相电机,各相电压会产生,什么大小、什么方向的力矩矢量

在文章【【DSP学习】【从零开始输出EPWM】【28377S】_CCS_base的博客-优快云博客】中我写了怎么产生任意的EPWM波形,一般DSP产生的EPWM波形只有不到5V,需要经过各种放大器,把电压升高,才能够拖动电机(3.3→15→60........)。归根到底,还是控制三相电机三个端子的电压大小,来产生不同的电流大小、方向,产生不同的磁场力矩、达到不同的速度。以下讲解从三相电压到力矩电流Iq的过程。

本节主要内容源于文章【FOC?看这篇文章就够了 - 知乎】,我只截取、简化、再述部分内容,强烈建议阅读原文。

 

2.1:简化的电路原理图。

804bb6bc0144495dbcf1ba7d00da62f7.png

VT1和VT6代表一组EPWM,称为EPWM_u;还有对应的EPWM_v、EPWM_w。

VT1和VT6,只能有一个高、一个低;如果两个都高,那么正负直接短接,如果PCB没有设计保护电流就会损坏PCB;这是EPWM配置时需要死区的原因。

如果两个都低,那么相当于U相悬空,V相电流=W相电流=整回路电流=I1=Udc/(RV+RW)=Udc/2R(各相电阻相等);如果没有悬空,一高两低/两高一低,那么整回路电流=I2=Udc/((3/2)R),显然I2>I1,则意味着没有悬空状况下,产生的磁场、磁力会大于有悬空状况,所以一般情况下,VT1和VT6,只能有一个高、一个低。

一个确定的电压方向,会产生确定的电流方向。电机外层由永磁体产生磁场,磁场方向固定。转子由电感的电流产生磁场,电流方向会改变磁场方向、转子运动也会改变磁场方向。当转子磁场方向与定子垂直(超前或落后90°)时,力矩最大,因此,需要通过编码器时刻检测当前转子位置、再输出超前或落后90°的磁场方向(等同于电压方向、或电流方向)【存疑】

电压方向,一般只有8种状态:U1~U6有电流(U0&U7不产生电流):

5c3679c484d844ae9e879f2db2fed334.png

2.2:U1~U6对应的初始电压矢量方向:

...14d11235b4a14814a232bf38c8659a66.png

a68320ac6a4a45e0aad947f7909094cf.png

 

在同一时刻,输出的初始电压矢量只有一个,初始电压矢量大小定义为:|Umax|=|U1|=|U2|=|...|=|U6|。方向定义为:0/60/120/180/240/300度。

在一个控制周期内(比如T=31.25us内),通过调整两个相邻初始电压矢量的时间比例(比如让T4=T6=T/2)【T4=在一个T内,输出U4的时间】,可以调整最终电压矢量Uθ的方向(比如θ=30°)。

通过增加U0(或U7)电压矢量的时间比例T0(或T7),可以调整最终电压矢量Uθ的大小【0~Umax*sqrt(3)/2)】(为了最终输出的 各方向的 电压矢量 最大值均相等,即在六边形内作内切圆,目标电压矢量Uθ的起点位于原点,终点位于内切圆内任意一点)。

综上所述,我们的目标是输出任意方向(0° ≤ θ<360°)任意大小【0 ≤ |Uθ| ≤ Umax*sqrt(3)/2)】的电压矢量 Uθ,Uθ 由相邻两个初始电压矢量的时间占比Ta&Tb、以及无大小矢量 T0(或T7) 三者(或四者)共同共同决定。

 

2.3:各扇区:α、β轴坐标转换

当Uθ处于不同的扇区时,Uθ会被分解成不同的两个初始电压矢量。

以“第一扇区”为例,将Uθ分解为与T4+T6相关的函数:Uθ*T=U4*T4+U6*T6+U0*T0

把Uθ投影在α轴上,得到:Uθ*T*cosθ=U4*T4*cos(0°)+U6*T6*cos(60°)

即:(Uθ/Umax)*T*cosθ=T4+T6*1/2           ——公式1;

把Uθ投影在β轴上,得到:Uθ*T*sinθ=U4*T4*sin(0°)+U6*T6*sin(60°)

即:(Uθ/Umax)*T*sinθ=T6*(sqrt(3)/2)           ——公式2;

由于:0 ≤ Uθ ≤ Umax*(sqrt(3)/2);

所以:0 ≤ (Uθ/Umax) *2/sqrt(3)≤ 1;

设:(Uθ/Umax) *2/sqrt(3)= Uq ;(力矩电压,最小为0,最大为1)

则:(Uθ/Umax) = Uq *(sqrt(3)/2)           ——公式3;

将公式3 带入公式1、2,并整理得到:

T4=Uq*T*0.5*(sqrt(3)*cosθ - sinθ)           ——解1;

T6=Uq*T*sinθ           ——解2;

同理可得剩余5个扇区的公式与解。以下为公式表:

...

5b0c84ae0dfe4c4696d83703691571ce.png

扇区矢量表

最好自己推导一遍。

 

2.4:输出ta、tb、t0

如何在一个EPWM周期内,输出Ta、Tb、T0?

根据【扇区矢量表】,在扇区1,需要输出T4&T6。也就是UVW在“T4”时间内,为“100”【U高,V低,W低】,在T6时间内,为“110”【U高,V高,W低】,因此:U高的时间=T4+T6;V高的时间=T6;W高的时间=0。

733dcdab85714874b09ad4b780ca02dc.png

在epwm的UPDOWM模式下,TBPRD寄存器控制周期长度,CMPA寄存器控制发送动作的时机(此处为 :向上计数时,在CMPA点,置高;向下计数时,在CMPA点,置低)。TBPRD=c点=T/2点

 

U高的时间=ae时间长度=T4+T6;因此=【T-(T4+T6)】/2

V高的时间=bd时间长度=T6;=【T-(T6)】/2

W高的时间=0;

 

同理可得其他5个扇区的寄存器调整公式,详见【扇区矢量表】。

 

步骤3:读取电机当前电角度

通过编码器读取当前电角度

绝对式编码器:无需每次都调零;每个位置量对应一个确定的电角度。一个电角度可以对应多个位置量。

 

 

增量式编码器:需要调零;电机每转一圈输出X个脉冲。根据脉冲宽度测量当前速度,根据脉冲个数测量位置。

 

步骤4:输出UQ的函数:

#include "F28x_Project.h"
#include "MY_headers.h"
#include "math.h"

	extern int i;

	Uint32 pos_current;//当前编码器位置

	double theta_current;//当前电角度

	int direction_run=1;//转动方向

	double Theta_run;//需要输出的电角度

	double Theta_radian;//需要输出的电角度转为弧度制

	const double Pi=3.1415;//定值π

	Uint16 Theta_err;//报错计数

	double double_temp;//用于储存Iq
	double double_temp1;//用于储存Ta
	double double_temp2;//用于储存Tb

	Uint16 T1_6[7];//T1_6[0]用于储存当前扇区;其他储存Ta、Tb

	Uint16 T_PWM=3125;//epwm总周期时间



	void init_T1_6()
	{
		for (i=0;i<7;i++)
			T1_6[i]=0;
	}

double Theta_from_pos()
{
	//8583+8865+8766=26214
	//如果U4电压矢量对应的位置为u0
	//那么u0+8583=v0;u0+8583+8865=w0;u0+8583+8865+8766=u0;
	//编码器参数;上电无需重测

	Uint16 pos_u0;
	pos_u0=25698;

	Uint16 pos_v0;
	pos_v0=pos_u0+8583;

	Uint16 pos_w0;
	pos_w0=pos_v0+8865;

//	Uint16 pos_t=26215;

	Uint16 pos_h=Read_FPGA(0x00300003);//使用EMIF读取绝对式编码器位置
	Uint16 pos_l=Read_FPGA(0x00300002);

//	pos_current=(pos_h-40960)*0x100+pos_l;//错误
	pos_current=(pos_h-40960);
	pos_current=pos_current*0x10000+pos_l;

	Uint32 pos_temp= pos_current;

	pos_temp=pos_temp%26214;//
	if(pos_temp<pos_u0){
		pos_temp=pos_temp+26214;
	}

	if(pos_temp>=pos_u0&&pos_temp<pos_v0){
		theta_current=(pos_temp-pos_u0);
		theta_current=theta_current/8583*120;
	}
	else if(pos_temp>=pos_v0&&pos_temp<pos_w0){
		theta_current=(pos_temp-pos_v0);
		theta_current=theta_current/8865*120;
		theta_current=theta_current+120;
	}
	else if(pos_temp>=pos_w0){
		theta_current=(pos_temp-pos_w0);
		theta_current=theta_current/8766*120;
		theta_current=theta_current+240;
	}
	else{
		Theta_err=Theta_err+1;
	}

	if(theta_current>360||theta_current<0){
		Theta_err=Theta_err+1;
	}


	return(theta_current);
}


void UQ_output(Uint16 UQ,int UQ_direction){

	double_temp=UQ;
	double_temp=double_temp/65535;//double_temp=UQ

	Theta_from_pos();//读取当前电角度

	if(UQ_direction==0){
		Theta_run=theta_current+90;//电角度+90
	}
	else{
		Theta_run=theta_current-90;//电角度-90
	}

	if(Theta_run>=360){
		Theta_run=Theta_run-360;
	}
	else if(Theta_run<0){
		Theta_run=Theta_run+360;
	}

	Theta_radian=Theta_run/180*Pi;//转为弧度制

	if (Theta_run>=0&&Theta_run<60){//第一扇区
		double_temp1=double_temp*0.5*T_PWM*(sqrt(3)*cos(Theta_radian)-sin(Theta_radian));
		double_temp2=double_temp*T_PWM*sin(Theta_radian);
		init_T1_6();
		T1_6[0]=1;
		T1_6[4]=double_temp1;
		T1_6[6]=double_temp2;
		//(3125-T1_6[]-T1_6[])/2

		EPwm6Regs.CMPA.bit.CMPA=(3125-T1_6[4]-T1_6[6])/2;
		EPwm7Regs.CMPA.bit.CMPA=(3125-T1_6[6])/2;
		EPwm8Regs.CMPA.bit.CMPA=1562;

	}
	else if (Theta_run>=60&&Theta_run<120){//第2扇区
		double_temp1=double_temp*0.5*T_PWM*(sin(Theta_radian)-sqrt(3)*cos(Theta_radian));
		double_temp2=double_temp*0.5*T_PWM*(sin(Theta_radian)+sqrt(3)*cos(Theta_radian));
		init_T1_6();
		T1_6[0]=2;
		T1_6[2]=double_temp1;
		T1_6[6]=double_temp2;

		EPwm6Regs.CMPA.bit.CMPA=(3125-T1_6[6])/2;
		EPwm7Regs.CMPA.bit.CMPA=(3125-T1_6[2]-T1_6[6])/2;
		EPwm8Regs.CMPA.bit.CMPA=1562;


	}
	else if (Theta_run>=120&&Theta_run<180){//第3扇区
		double_temp1=double_temp*T_PWM*sin(Theta_radian);;
		double_temp2=double_temp*(-0.5)*T_PWM*(sin(Theta_radian)+sqrt(3)*cos(Theta_radian));
		init_T1_6();
		T1_6[0]=3;
		T1_6[2]=double_temp1;
		T1_6[3]=double_temp2;

		EPwm6Regs.CMPA.bit.CMPA=1562;
		EPwm7Regs.CMPA.bit.CMPA=(3125-T1_6[2]-T1_6[3])/2;
		EPwm8Regs.CMPA.bit.CMPA=(3125-T1_6[3])/2;

	}
	else if (Theta_run>=180&&Theta_run<240){//第4扇区
		double_temp2=double_temp*0.5*T_PWM*(sin(Theta_radian)-sqrt(3)*cos(Theta_radian));
		double_temp1=double_temp*T_PWM*(-1)*sin(Theta_radian);
		init_T1_6();
		T1_6[0]=4;
		T1_6[1]=double_temp1;
		T1_6[3]=double_temp2;

		EPwm6Regs.CMPA.bit.CMPA=1562;
		EPwm7Regs.CMPA.bit.CMPA=(3125-T1_6[3])/2;
		EPwm8Regs.CMPA.bit.CMPA=(3125-T1_6[1]-T1_6[3])/2;

	}
	else if (Theta_run>=240&&Theta_run<300){//第5扇区
		double_temp1=double_temp*(-0.5)*T_PWM*(sin(Theta_radian)+sqrt(3)*cos(Theta_radian));
		double_temp2=double_temp*0.5*T_PWM*(sqrt(3)*cos(Theta_radian)-sin(Theta_radian));
		init_T1_6();
		T1_6[0]=5;
		T1_6[1]=double_temp1;
		T1_6[5]=double_temp2;

		EPwm6Regs.CMPA.bit.CMPA=(3125-T1_6[5])/2;
		EPwm7Regs.CMPA.bit.CMPA=1562;
		EPwm8Regs.CMPA.bit.CMPA=(3125-T1_6[1]-T1_6[5])/2;

	}
	else if (Theta_run>=300&&Theta_run<360){//第6扇区
		double_temp1=double_temp*0.5*T_PWM*(sin(Theta_radian)+sqrt(3)*cos(Theta_radian));
		double_temp2=double_temp*T_PWM*(-1)*sin(Theta_radian);
		init_T1_6();
		T1_6[0]=6;
		T1_6[4]=double_temp1;
		T1_6[5]=double_temp2;

		EPwm6Regs.CMPA.bit.CMPA=(3125-T1_6[4]-T1_6[5])/2;
		EPwm7Regs.CMPA.bit.CMPA=1562;
		EPwm8Regs.CMPA.bit.CMPA=(3125-T1_6[5])/2;

	}
	else{//报警
		Theta_err=Theta_err+1;
	}

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值