-
头文件
UserParms.h
C
文件
ACIM.c
Encoder.c
InitCurModel.c
UserParms.h
//#define TORQUE_MODE
#define DIAGNOSTICS
//****************
振荡器
************************************
#define dFoscExt 7372800 //
外部晶振或时钟频率
(Hz)
#define dPLL 8 //
PLL
比率
#define dLoopTimeInSec 0.00005 //
PWM
周期
- 100 uS,
10Khz PWM
#define
dDeadTimeSec 0.000002 //
以秒为单位的死区时间
// Derived
#define dFosc (dFoscExt*dPLL) //
时钟频率
( Hz)
#define dFcy (dFosc/4) //
指令频率
( Hz)
#define dTcy (1.0/dFcy) //
指令周期
(s)
#define dDeadTime
(int)(dDeadTimeSec*dFcy) //
以
dTcys
为单位的死区时间
#define dLoopInTcy
(dLoopTimeInSec/dTcy) //
以
Tcy
为单位的基本循环周期
#define dDispLoopTime 0.100
//
显示和按钮状态查询循环
//****************
电机参数
********************************
#define diPoles 1 //
极对数
#define diCntsPerRev 2000 //
每转的编码器线数
#define diNomRPM 3600 //
电机铭牌上的转速(
RPM
)
#define dfRotorTmConst 0.078 //
以秒为单位的转子时间常数,来自电机制造商
//****************
测量
*************************************
#define diIrpPerCalc 30 //
每次速度计算的
PWM
循环次数
//************** PI
系数
************************************
#define dDqKp 0x2000 // 4.0
(NKo = 4)
#define dDqKi
0x0100; // 0.125
#define
dDqKc 0x0100; // 0.125
#define dDqOutMax 0x5A82; //
0.707
设定该值以避免饱和
#define dQqKp
0x2000; // 4.0 (NKo = 4)
#define dQqKi 0x0100; // 0.125
#define dQqKc 0x0100; //
0.125
#define dQqOutMax
0x5A82; // 0.707
设定该值以避免饱和
#define dQrefqKp 0x4000 // 8.0 (NKo =
4)
#define dQrefqKi 0x0800
// 1.0
#define dQrefqKc
0x0800 // 1.0
#define
dQrefqOutMax 0x3FFF //
0.4999
设定该值以避免饱和
//**************
ADC
换算
************************************
//
标定常数
:
由校准或硬件设计确定。
#define dqK 0x3FFF; //
等于
0.4999
#define dqKa 0x3FFF; //
等于
0.4999
#define dqKb 0x3FFF; //
等于
0.4999
//**************
弱磁
**************
**************************
//
在恒转矩范围内的磁通给定值。
//
根据经验确定、给出额定压
/
频比
#define dqK1 3750; //
ACIM.c
< br>/********************************************** ************************
**
*
作者
: John
Theys/Dave Ross *
**
*
文件名
: ACIM.c *
*
日期
: 10/31/03 *
*
文件版本
: 3.00 *
**
*
使用工具
: MPLAB ->
6.43 *
*
编译器
-> 1.20.00 *
**
*
链接文件
: *
**
**
*************
**************************************************
********
*10/31/03 2.00
发布电机运行正常,但仍有些遗留的小问题
*
*12/19/03 2.01
完成结构,为所有用户定义变量创建
UserParms.h
。
*
*0
2/12/043.00-
从项目中去除了不需要的文件。
* -
将
iRPM
改为
int
以纠正浮点计算问题。
* -CalcVel()
和转速控制环仅在
iIrpPerC
alc
指定的数个循环周期后执行
*
*-
增加了
iDispLoopCount
变量以安排显示和按钮子程序的执行时间
*
-trig.s
文件改为使用程序空间来存储正弦数据。
*-
增加了
DiagnosticsOutput()
函数,该函数使用输
出比较通道来输出控
*
制变量信息。
*-
增加了
TORQUE_MODE
定义以忽略转速控制环。
*-
关闭
curmodel.s
文件中的
p>
SATDW
位。自动饱和功能阻止转差角计算正确
< br>
*
翻转返回。
*************************************************
***********************
*
代码说明
*
*
该文件给出了使用
dsPIC30F
实现的三相交流感应电机矢量控制实例。
*
采用空间矢量调制作为控制策略。
***************************
********************************************/
/***************************
全局定义
***********************/
#define INITIALIZE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/***********************
全局定义结束
********************/
unsigned short uWork;
short iCntsPerRev;
short iDeltaPos;
union {
struct
{
unsigned
DoLoop:1;
unsigned
OpenLoop:1;
unsigned
RunMotor:1;
unsigned
Btn1Pressed:1;
unsigned
Btn2Pressed:1;
unsigned
Btn3Pressed:1;
unsigned
Btn4Pressed:1;
unsigned
ChangeMode:1;
unsigned
ChangeSpeed:1;
unsigned :7;
}bit;
WORD Word;
} uGF; //
通用标志
tPIParm PIParmQ;
tPIParm PIParmQref;
tPIParm PIParmD;
tReadADCParm ReadADCParm;
int iRPM;
WORD iMaxLoopCnt;
WORD iLoopCnt;
WORD iDispLoopCnt;
/**************
**************************************************
******/
void
__attribute__((__interrupt__))
_ADCInterrupt(void);
void
SetupBoard( void );
bool
SetupParm(void);
void
DoControl( void );
void
Dis_RPM( BYTE bChrPosC, BYTE bChrPosR );
void
DiagnosticsOutput(void);
/********************
主函数开头
*************************/
int main ( void )
{
SetupPorts();
InitLCD();
while(1)
{
= 0; //
清除标志
//
初始化模式
op = 1; //
以开环模式起动
//
初始化
LED
pinLED1 = 0;
pinLED2 = !op;
pinLED3 = 0;
pinLED4 = 0;
//
初始化控制板
SetupBoard();
//
对用户指定参数进行初始化并在出错时停止
if( SetupParm()
)
{
//
错误
or=0;
return;
}
//
清零
i
和
= 0;
= 0;
= 0;
iMaxLoopCnt = 0;
Wrt_S_LCD(
Wrt_S_LCD(
//
使能
ADC
中断并开始主循环定时
= 0;
= 1;
if(!or)
{
//
初始化电流偏移量补偿
while(!pinButton1)
//
在此处等待直至按钮
1
按下
{
ClrWdt();
//
开始偏移量累加
//
并在等待时对电流偏移量进行累加
MeasCompCurr();
}
while(pinButton1);
//
当按钮
1
释放时
or = 1; //
随后起动电机
}
//
电机运行
Mode = 1;
//
使能电机控制
PCB
上的驱动器
IC
pinPWMOutputEnable_ = 0;
Wrt_S_LCD(
Wrt_S_LCD(
//
电机运行循环
while(1)
{
ClrWdt();
//
如果使用
OC7
和
OC8
显示矢量控制变量,
//
调用更新代码。
#ifdefDIAGNOSTICS
DiagnosticsOutput();
#endif
//
每隔
50
毫秒执行更新
LCD
显示和查询按钮状态的代码。
//
if(iDispLoopCnt >= dDispLoopCnt)
{
//Display RPM
Dis_RPM(5,0);
//
按钮
1
控制电机的起停
if(pinButton1)
{
if( !1Pressed )
1Pressed = 1;
}
else
{
if( 1Pressed )
{
//
按钮刚被释放
1Pressed = 0;
//
开始停止过程
or = 0;
pinPWMOutputEnable_ = 1;
break;
}
}
//
在运行时按钮
2
将控制开
/
闭环模式之间的切换
if(pinButton2)
{
if( !2Pressed )
2Pressed = 1;
}
else
{
if( 2Pressed )
{
//
按钮刚释放
2Pressed = 0;
Mode = 1;
op = !
op;
pinLED2 = !op;
}
}
//
在运行时按钮
3
将加倍
/
减半速度或转矩给定
if(pinButton3)
{
if( !3Pressed )
3Pressed = 1;
0 = 0;
}
else
{
if( 3Pressed )
{
//
按钮刚释放
3Pressed = 0;
Speed = !Speed;
pinLED3 = Speed;
0 = 1;
}
}
//
按钮
4
不具备任何功能
if(pinButton4)
{
if( !4Pressed )
4Pressed = 1;
}
else
{
if( 4Pressed )
{
//
按钮刚被释放
4Pressed = 0;
//***
此处加入按钮
4
功能的代码
}
}
} //
显示和按钮查询代码结束
} //
电机运行循环结束
}
//
主循环结束
//
不应执行到此处
while(1){}
}
//-------------
--------------------------------------------------
-----//
对
Id
控制环、
Iq
控制环和速度控制环中的每个控制环执行一次
PI
迭代
void DoControl( void )
{
short i;
//
假定
ADC
通道
0
具有来自速度电位器(
AN7
)的有符号小数格式原始
A/D
值
ReadSignedADC0(
&ReadADCParm );
//
设定给定速度
if(Speed)
f =
ue/8;
else
f = ue/16;
if( op )
{
//
开环:强制旋转角、
Vd
和
Vq
if(
Mode )
{
//
改变为开环模式
Mode = 0;
//
同步角度
ux = ux;
//
未使用
VqRef
和
VdRef
= 0;
= 0;
}
ch = f;
//
为
CorrectPhase
所需的给定值和符号
//
计算
1.15
格式的转子磁通旋转角。
ch = ch;
CurModel();
= 0;
if( ch >= 0 )
i = ch;
else
i = -ch;
uWork = i <<2;
if( uWork > 0x5a82 )
uWork = 0x5a82;
if( uWork <
0x1000 )
uWork = 0x1000;
= uWork;
OpenLoop();
= ux;
}
else
//
闭环矢量控制
{
if( Mode )
{
//
改变为闭环模式
Mode = 0;
//
同步角度以及准备
qdImag
ux = ux;
=
}
//
根据电流模型计算角度
ch = ch;
CurModel();
= ux;
//
计算弱磁控制模式的
qVdRef
FdWeakening();
//
设定给定速度
//
如果应用运行在转矩模式,转速控制环将被忽略。
//
从电位器读取的转速给定值将直接用作转矩给定
VqRef
。
#ifdefTORQUE_MODE
= f;
#else
//
通过对比每一次转速计算中的
中断数和速度计数采样数来确定是否可以获得新的转速信
息。
//
//
如果可以获得新的转速信息,则计算新的转速值并执行
//
转速控制环。
if(tDwn == rCalc)
{
//
根据编码器累计计数值来计算转速
CalcVel();
//
执行转速控制环
s = ch;
= f;
CalcPI(&PIParmQref);
=
}
#endif
//
Q
的
PI
控制
s =
=
CalcPI(&PIParmQ);
=
//
D
的
PI
控制
s =
=
CalcPI(&PIParmD);
=
}
}
//-
--------------------------------------------------
-----------------//
ADC
中断服务程序执行速度计算以及电压矢量更新循环。
//
ADC
采样和转换由
PWM
周期触发。
//
速度计算假定计算之间的间隔时间是固定的。
//-------------------------
-------------------------------------------
void
__attribute__((__interrupt__)) _ADCInterrupt(void)
{
= 0;
//
递增控制显示和按钮功能执行的计数变量。
//
iDispLoopCnt++;
//
累计自上一次中断后的编码器计数
CalcVelIrp();
if( or )
{
//
置位用于诊断的
LED1
pinLED1 =
1;
//
使用
TMR1
来测量用于诊断的中断时间
TMR1 = 0;
iLoopCnt = TMR1;
MeasCompCurr();
//
根据
qSin
、
qCos
、
qIa
、
qIb
计算
qId
、
qIq
ClarkePark();
//
计算控制值
DoControl();
//
根据
qAngle
计算
qSin
、
qCos
SinCos();
//
根据
qSin
、
qCos
、
qVd
、
qVq
计算
qV
alpha
、
InvPark()
;
//
根据
qValpha
、
qVbeta
计算
Vr1
、
Vr2
、
Vr3
。
CalcRefVec();
qVbeta
//
根据
Vr1
、
Vr2
、
Vr3
计算和设定
PWM
占空比
CalcSVGen();
//
测量循环时间
iLoopCnt = TMR1 - iLoopCnt;
if( iLoopCnt > iMaxLoopCnt )
iMaxLoopCnt = iLoopCnt;
//
清零用于诊断的
LED1
pinLED1 = 0;
}
}
//-------------
--------------------------------------------------
-----//
SetupBoard
//
//
初始化控制板
//-------------------------
-------------------------------------------
void
SetupBoard( void )
{
BYTE b;
//
禁止
ADC
中断
= 0;
//
复位电机控制功率模块上的所有故障。
pinFaultReset = 1;
for(b=0;b<10;b++)
Nop();
pinFaultReset = 0;
//
确保
PFC
开关是关闭的。
pinPFCFire = 0;
//
确保制动开关是关闭的。
pinBrakeFire = 0;
}
//-
--------------------------------------------------
-----------------//
Dis_RPM
//
//
显示
RPM
//----------
--------------------------------------------------
--------
void
Dis_RPM( BYTE bChrPosC, BYTE bChrPosR )
{
if (Cnt < 0)
Wrt_S_LCD(
else
Wrt_S_LCD(
iRPM = Cnt*60/
(eriod*rCalc*erRev);
Wrt_Signed_Int_LCD( iRPM, bChrPosC+1,
bChrPosR);
}
p>
//---------------------------------------
-----------------------------bool
SetupParm(void)
{
//
开启抗饱和功能以确保能够平滑处理溢出。
= 0;
//
设置所需参数
//
选取定标值为
8
倍速度和电流标称值
//
在定标时使用
8
倍电机标称机械转速(单位为
RPM
)
MechRPM = diNomRPM*8;
//
极对数
= diPoles
//
由
ds
PIC
正交编码配置检测的每转编码器计数值。
//
erRev = diCntsPerRev;
//
以秒为单位的转子时间常数
TmConst = dfRotorTmConst;
//
基
本循环周期(单位为秒)
。
(
PWM
中断周期)
eriod = dLoopInTcy * dTcy;
//
循环周期(以周期为单位)
*
秒
/
周期
//
编码器转速中断周期(单位为秒)
。
pPeriod = eriod;
//
每次转速计算的
vel
中断数。
rCalc = diIrpPerCalc; //
以循环为单位
//
电机的定标机械速度(单位为转
/
秒)
MechRPS = MechRPM/60.0;
//
定标后的电机磁通转速(单位为转
/
秒)
<
/p>
//
通过该值定标后的所有无量纲磁通转速。
FluxRPS = *MechRPS;
//
磁通矢量每转一周的最小周期时间(单位为秒)
FluxPeriod = 1.0/FluxRPS;
//
在最大磁通转速时的每个
LoopTime
的转数系数
FracRevPerLoop = eriod *
FluxRPS;
//
定标后的电机磁通转速(单位为弧度
/
秒)
//
通过该值定标后的所有无量纲转速(单位为弧度
/
秒)
。
FluxSpeed = 6.283 *
FluxRPS;
//
iScaleMechRPM
时的编码器计数频率
CntRate = erRev *
(MechRPM/60.0);
// =============
开环
======================
a = 32768.0 * 2 * * eriod
*
MechRPS;
ch =
dqOL_VelMech;
f = ch;
InitOpenLoop();
// =============
编码器
===============
if( InitEncoderScaling() )
//
出错
return True;
// ============= ADC -
测量电流和电位器
======================
//
定标常数:由校准或硬件设计决定。
= dqK;
= dqKa;
= dqKb;
//
初始偏移量
InitMeasCompCurr( 450, 730 );
//
=============
电流模型
===============
if(InitCurModelScaling())
//
出错
return True;
// =============
弱磁
===============
//
恒转矩范围的弱磁常数
1 = dqK1; //
磁通给定值
// ============= PI
D
项
===============
= dDqKp;
= dDqKi;
= dDqKc;
x = dDqOutMax;
n
= -x;
InitPI(&PIParmD);
//
============= PI Q
项
===============
= dQqKp;
= dQqKi;
= dQqKc;
x = dQqOutMax;
n
= -x;
InitPI(&PIParmQ);
//
============= PI Qref
项
===============
= dQrefqKp;
= dQrefqKi;
= dQrefqKc;
x = dQrefqOutMax;
n = -x;
InitPI(&PIParmQref);
//
============= SVGen ===============
//
将
PWM
周期设定为循环时间
riod = dLoopInTcy;
//
============= TIMER #1 ======================
PR1 = 0xFFFF;
= 1;
= 1; //
预分频比为
8
时
=>
一个单位为
1.08504 uS
//
=============
电机
PWM ======================
PDC1 = 0;
PDC2 = 0;
PDC3 =
0;
PDC4 = 0;
//
中心对齐
PWM
。
//
注:
PWM
周期设定为
dLoopInTcy/2
< br>,但由于先采用递增计数随后为递减计数,
// =>
在计数到零时中断标志置
1 =>
因此实际中断周期为
dLoopInTcy
//
PTPER = dLoopInTcy/2; //
将
PWM
周期设定为循环时间,该参数在
parms.h
中定义
PWMCON1 =
0x0077; //
使能
PWM
1,2,3
对工作在互补模式
DTCON1 = dDeadTime; //
死区时间
DTCON2 = 0;
FLTACON = 0; //
未使用
PWM
故障引脚
FLTBCON = 0;
PTCON = 0x8002; //
使能
PWM
中心对齐
//
SEVTCMP:
特殊事件比较计数寄存器
//
相对于
PWM
周期的
ADC
捕获设定相位:
0
偏移量和递增计数
SEVTCMP = 2; //
不能为
0
,否则关断触发器(文档中未列明)
R = 0;
// =============
编码器
===============
MAXCNT = erRev;
POSCNT = 0;
QEICON = 0;
=
7; //
通过
MAXCNT
脉冲复位
x4
= 0; //
不要让索引脉冲复位计数器
= 0; //
方向
DFLTCON = 0; //
数字滤波器设定为关闭
//
============= ADC -
测量电流和电位器给定值
======================
//
ADC
设定为对以下通道同时进行采样:
// CH0=AN7, CH1=AN0,
CH2=AN1, CH3=AN2
//
采样由
PWM
触发,且采样结果以有符号小数形式存放。
ADCON1 = 0;
//
有符号小数格式(
DOUT =
sddd dddd dd00 0000
)
= 3;
//
电机控制
PWM
间隔终止采样并启动转换
= 3;
//
同时采样选择位(仅当
CHPS =01
或
1x
时应用)
//
同时采样
CH0
、
CH1
、
CH2
和
CH3
(当
CHPS = 1x
时)
//
同时采样
CH0
和
CH1
(当
CHPS=01
时)
= 1;
//
在上一次转换结束后立即开始采样。
//
SAMP
位自动置位。
= 1;
ADCON2 = 0;
//
同时采样
CH0
、
CH1
、
CH2
、
CH3
(当
CHPS = 1x
时)
= 2;
ADCON3 = 0;
//
A/D
转换时钟选择位
= 8 *
Tcy
= 15;
/* ADCHS
:
ADC
输入通道选择寄存器
*/
ADCHS = 0;
// CH0
为
AN7
0SA = 7;
//
CH1
正极性输入为
AN0
,
CH2
正极性输入为
AN1
,
CH3
正极性输入为
AN2
123SA = 0;
/*
ADPCFG
:
ADC
端口配置寄存器
*/
//
将所有端口设置为数字端口
ADPCFG = 0xFFFF;
0 = 0; // AN0
模拟
1
= 0; // AN1
模拟
2 = 0; // AN2
模拟
7 = 0; // AN7
模拟
/* ADCSSL
:
ADC
输入扫描选择寄存器
*/
ADCSSL = 0;
//
开启
A/D
模块
= 1;
#ifdefDIAGNOSTICS
//
对用于诊断模式的输出比较通道
7
和
8
进行初始化。
// PWM
模式中使用比较
//
Timer2
用作时基
PR2 = 0x1FFF;
OC7CON = 0x0006;
OC8CON = 0x0006;
= 1;
#endif
return False;
}
#ifdefDIAGNOSTICS
void DiagnosticsOutput(void)
{
int Data;
if(IFS0bits.T2IF)
{
IFS0bits.T2IF = 0;
Data = ( >> 4) + 0xfff;
if(Data > 0x1ff0) Data = 0x1ff0;
if(Data < 0x000f) Data =
0x000f;
OC7RS = Data;
Data = (ch) + 0x0fff;
if(Data > 0x1ff0) Data =
0x1ff0;
if(Data < 0x000f)
Data = 0x000f;
OC8RS =
Data;
}
}
#endif
Encoder.c
//
编码器子程序的定标
#include
#include
#include
/**************************
********************************
InitEncoderScaling
对编码器子程序的定标常量进行初始化。
函数参数:
CntsPerRev
:来自正交编
码器的每转编码器计数值
Scal
ingSpeedInRPS
:用于基本转速定标的每秒转数
IrpPerCalc
:每次转速计
算的
CalcVelIrp
中断数
VelIrpPeriod
:
VelCalcIrp
中断间的周期(单位为秒)
对于
CalcAng
:
运行时方程:
qMechAng = qKang *
(POSCNT*4) / 2^Nang
定标方程:
qKang =
(2^15)*(2^Nang)/CntsPerRev.
对于
CalcV
elIrp
、
CalcVel
:
运行时方程:
qMechVel = qKvel * (2^15 *
Delta / 2^Nvel)
定标方程:
fVelCalcPeriod =
fVelIrpPeriod * iIrpPerCalc
MaxCntRate = CntsPerRev * ScaleMechRPS
MaxDeltaCnt =
fVelCalcPeriod * MaxCntRate
qKvel = (2^15)*(2^Nvel)/MaxDeltaCnt
***
**************************************************
*****/
bool InitEncoderScaling( void )
{
float fVelCalcPeriod,
fMaxCntRate;
long
MaxDeltaCnt;
long K;
erRev = erRev;
K = 32768;
K *= 1 << Nang;
= K/erRev;
rCalc = rCalc;
fVelCalcPeriod = pPeriod * rCalc;
fMaxCntRate = erRev *
MechRPS;
MaxDeltaCnt =
fVelCalcPeriod * fMaxCntRate;
// qKvel =
(2^15)*(2^Nvel)/MaxDeltaCnt
K = 32768;
K *=
1 << Nvel;
K /=
MaxDeltaCnt;
if( K >= 32768
)
//
出错
return True;
=
K;
//
对
C
alcVelIrp
使用的局部变量进行初始化。
InitCalcVel();
return False;
}
InitCurModel.c
//
电流模型子程序定标
#include
#include
#include
/**************************
********************************
InitCurModelScaling
对电流模型子程序中的定标常数进行初始化。
物理常数:
fRotorTmConst
转子时间常数,单位为秒。
方程的物理形式:
励磁电流(安培)
:
Imag = Imag +
(fLoopPeriod/fRotorTmConst)*(Id - Imag)
转差速度,单位为
RPS
:
VelSlipRPS = (1/fRotorTmConst) *
Iq/Imag / (2*pi)
转子磁通速度,单位为
RPS
:
VelFluxRPS = iPoles * VelMechRPS +
VelSlipRPS
转子磁通角(弧度)
:
AngFlux =
AngFlux + fLoopPeriod * 2 * pi * VelFluxRPS
定标后的变量:
qImag
采用最大电流定标后的励磁电流。
qVelSlip
采用
fScaleMechRPS
定标后的机械转差速度,单位为
RPS
。
qAngFlux
采用
pi
定标后的磁通角。
定标后的方程:
qImag = qImag +
qKcur * (qId - qImag)
qVelSlip = Kslip * qIq/qImag
qAngFlux = qAngFlux +
Kdelta * (qVelMech + qVelSlip)
定标因子:
qKcur = (2^15)
* (fLoopPeriod/fRotorTmConst)
qKdelta = (2^15) * 2 * iPoles *
fLoopPeriod * fScaleMechRPS
qKslip = (2^15)/(2 * pi * fRotorTmConst
* iPoles * fScaleMechRPS)
***************************
*******************************/
bool
InitCurModelScaling( void )
{
= 32768.0 *
eriod / TmConst;
a = 32768.0 * 2 * * eriod
*
MechRPS;
=
32768.0/(6.2832 * *
MechRPS*TmConst);
//
允许的最大转差速度
ipVel = 32768.0/8;
//
对
Cu
rrModel
使用的局部变量进行初始化。
InitCurModel();
return False;
}
MeasCurr.s
;******************************************
*************************
;
MeasCompCurr
;
;
说明:
;
读
ADC
通道
1
和通道
2
,使用
qKa
和
qKb
将其换算为有符号小数值,
;
并将结果存放到
ParkParm
的
qIa
和
qIb
中。
; ADC-
Ave
的滚动平均值被保持并在换算前从
ADC
值中减去。
;
;
;
具体来说,偏移量将作为
32
位有符号整数进行累计。
; iOffset += (ADC-Offset)
;
并采用以下公式通过偏移量来校正
原始的
ADC
读数
; CorrADC = ADCBUFn - iOffset/2^16
;
将给出一个偏移时间常数
~ MeasurementPeriod*2^16
;
;
在转换结束之前不要调用该子程序。
;
;
定标常数
qKa
和
qKb
必须在其他代码中设定,使
; qIa = 2 * qKa * CorrADC1
; qIb = 2 * qKb * CorrADC2
;
采用
2
作为因子以允许
qKa
和
qKb
保持
1.15
格式。
;
;
函数原型:
; void MeasCompCurr( void );
; void InitMeasCompCurr(
short iOffset_a, short iOffset_b );
;
;
开始时:必须调用
InitMeasCompCurr
。
;
进入时:
MeasCurrParm
结构必须包含
qKa
和
qKb
。
; ADC
通道
1
和
2
必须包括有符号小数值。
;
退出时:
ParkParm
将包含
qIa
和
qIb
。
;
;
参数:
;
输入参数:
;
无
;
返回值:
; V
oid
;
所需的
SFR
设定:
; = 0
;
如果累加器可能溢出,必须设定:
; = 1
;
;
所需的支持子程序:
;
无
;
局部堆栈使用:
;
无
;
修改的寄存器:
; w0,w1,w4,w5
;
执行时间:
;
29
个周期
;************************************************
*******************
global _MeasCompCurr
global MeasCompCurr
_MeasCompCurr:
MeasCompCurr:
;; CorrADC1 = ADCBUF1 -
iOffsetHa/2^16
;; qIa = 2 * qKa * CorrADC1
mov.w _MeasCurrParm+ADC_iOffsetHa,w0
sub.w _ADCBUF1,WREG w0 =
ADC - Offset
clr.w w1
btsc w0,#15
setm w1
mov.w w0,w5
mov.w _MeasCurrParm+ADC_qKa,w4
mpy w4*w5,A
sac A,#-1,w4
mov.w w4,_ParkParm+Park_qIa
;; iOffset +=
(ADC-Offset)
add
_MeasCurrParm+ADC_iOffsetLa
mov.w w1,w0
addc
_MeasCurrParm+ADC_iOffsetHa
;; CorrADC2 =
ADCBUF2 - iOffsetHb/2^16
;;
qIb = 2 * qKb * CorrADC2
mov.w _MeasCurrParm+ADC_iOffsetHb,w0
sub.w _ADCBUF2,WREG w0 = ADC -Offset
clr.w w1
btsc w0,#15
setm
w1
mov.w w0,w5
mov.w _MeasCurrParm+ADC_qKb,w4
mpy w4*w5,A
sac A,#-1,w4
mov.w w4,_ParkParm+Park_qIb
;; iOffset += (ADC-Offset)
add
_MeasCurrParm+ADC_iOffsetLb
mov.w w1,w0
addc
_MeasCurrParm+ADC_iOffsetHb
return
ClarkePark.s
p>
;****************************************
***************************
; ClarkePark
;
;
说明:
;
计算
Clarke
和
Park
变换。
;
假定
Cos
和
Sin
值在
qSin
和
qCos
中。
;
;
Ialpha = Ia
; Ibeta =
Ia*dOneBySq3 + 2*Ib*dOneBySq3;
;
其中
Ia+Ib+Ic = 0
;
; Id = Ialpha*cos(Angle) +
Ibeta*sin(Angle)
; Iq =
-Ialpha*sin(Angle) + Ibeta*cos(Angle)
;
;
该子程序同样适用于整数定标和
1.15
定标格式。
;
;
函数原型:
;
; void
ClarkePark( void )
;
;
进入时:
ParkParm
结构必须包含
qSin
、
qCos
、
;
退出时:
ParkParm
将包含
qId
和
qIq
。
;
;
参数:
;
输入参数:
;
返回值:
; V
oid
;
所需的
SFR
设定:
; = 0
;
如果
(I
a+2*Ib)/sqrt(3)
可能出现溢出,必须设定
; = 1
;
;
所需的支持子程序:
;
无
;
局部堆栈使用:
;
无
;
修改的寄存器:
; w3 -> w7
;
执行时间:
qIa
和
qIb
。
;
20
个周期
;************************************************
*******************
;
include
;
外部引用
include
;
寄存器使用
.equ ParmW, w3
指向
ParkParm
结构的指针
.equ Sq3W, w4 OneBySq3
.equ SinW, w4
;
替代
Work0W
.equ CosW, w5
.equ IaW, w6
qIa
的拷贝
.equ IalphaW, w6
替代
Ia
.equ IbW, w7
qIb
的拷贝
.equ IbetaW, w7
用
Ibeta
替代
Ib
;
常量
equ OneBySq3, 0x49E7
1/sqrt(3)
,采用
1.15
格式
;===================
代码
=====================
section .text
global _ClarkePark
global ClarkePark
_ClarkePark:
ClarkePark:
;; Ibeta = Ia*OneBySq3 +
2*Ib*OneBySq3;
mov.w #OneBySq3,Sq3W
1/sqrt(3)
,采用
1.15
格式
mov.w
_ParkParm+Park_qIa,IaW
mpy
Sq3W*IaW,A
mov.w
_ParkParm+Park_qIb,IbW
mac
Sq3W*IbW,A
mac Sq3W*IbW,A
mov.w
_ParkParm+Park_qIa,IalphaW
mov.w IalphaW,_ParkParm+Park_qIalpha
sac A,IbetaW
mov.w IbetaW,_ParkParm+Park_qIbeta
;;
已经计算
Ialpha
和
Ibeta
。现在进行旋转。
;;
得到
ParkParm
结构的
qSin
、
qCos
。
mov.w
_ParkParm+Park_qSin,SinW
mov.w _ParkParm+Park_qCos,CosW
;; Id =
Ialpha*cos(Angle) + Ibeta*sin(Angle)
mpy
SinW*IbetaW,A Ibeta*qSin -> A
mac CosW*IalphaW,A
将
Ialpha*qCos
加到
A
mov.w
#_ParkParm+Park_qId,ParmW
sac A,[ParmW++]
存放到
qId
,将指针递增
1
指向
qIq
;; Iq = -Ialpha*sin(Angle) +
Ibeta*cos(Angle)
mpy
CosW*IbetaW,A Ibeta*qCos -> A
msc SinW*IalphaW,A
从
A
减去
Ialpha*qSin
sac
A,[ParmW]
存入
qIq
return
.end
CurModel.s
;******************************************
*************************
;
子程序:
CurModel
;********************************************
***********************
;
为文件中所有子程序共有
.include
.include
.include
;********************************************
***********************
; CurModel
;
;
说明:
;
;
物理常数:
;
fRotorTmConst
转子时间常数,单位为秒
;
;
方程的物理形式:
;
励磁电
流(安培)
:
; Imag = Imag +
(fLoopPeriod/fRotorTmConst)*(Id - Imag)
;
;
转差速度,单位为
RPS
:
; VelSlipRPS =
(1/fRotorTmConst) * Iq/Imag / (2*pi)
;
;
转子磁通速度,单位为
RPS
:
; VelFluxRPS = iPoles *
VelMechRPS + VelSlipRPS
;
;
转子磁通角(弧度)
:
; AngFlux = AngFlux + fLoopPeriod * 2 *
pi * VelFluxRPS
;
;
定标后的变量:
;
qdImag
采用最大电流定标的励磁电流(
1.31
)
;
qVelSlip
采用
fScale
MechRPS
定标后的机械转差速度(单位为
RPS
)
;
qAngFlux
采用
pi
定标后的磁通角
;
;
定标后的方程:
; qdImag = qdImag + qKcur *
(qId - qdImag)
; qVelSlip = qKslip * qIq/qdImag
;
qAngFlux = qAngFlux + qKdelta * (qVelMech +
qVelSlip)
;
;
定标因子:
; qKcur =
(2^15) * (fLoopPeriod/fRotorTmConst)
; qKdelta =
(2^15) * 2 * iPoles * fLoopPeriod * fScaleMechRPS
;
qKslip = (2^15)/(2 * pi * fRotorTmConst * iPoles *
fScaleMechRPS)
;
;
函数原型:
;
; void
CurModel( void )
;
;
进入时:
CurModelParm
结构必须包含
qKcur
、
qKslip
、
iKpoles
、
;
qKdelta
、
qVelMech
和
qMaxSlipVel
;
退出时:
CurModelParm
将包含
qAngFlux
、
qdImag
和
qVelSlip
;
;
参数:
;
输入参数:
;
无
;
返回值:
;
V
oid
;
所需的
SFR
设定:
; = 0
;
= 0
;
;
所需的支持子程序:
;
无
;
局部堆栈使用:
;0
;
修改的寄存器:
: w0-w7,AccA
;
执行时间:
;
72
个指令周期
< br>;********************************************** *********************
;
;===================
代码
=====================
.section .text
;
用于
CurModel
的寄存器
.equ SignW, w2
跟踪符号变化
.equ ShiftW, w3
执行除法之前的移位位数
.equ IqW, w4
Q
电流(
1.15
)
.equ KslipW, w5
Kslip
常数(
1.15
)
.equ ImagW, w7
励磁电流(
1.15
)
.global _CurModel
.global CurModel
_CurModel:
CurModel:
;; qdImag =
qdImag + qKcur * (qId - qdImag) ;
励磁电流
mov.w _CurModelParm+CurMod_qdImag,w6
mov.w _CurModelParm+CurMod_qdImag+2,w7
lac
w7,A
mov.w w6,ACCALL
mov.w _ParkParm+Park_qId,w4
sub.w w4,w7,w4 qId-
qdImagH
mov.w
_CurModelParm+CurMod_qKcur,w5
mac w4*w5,A
将
Kcur*(Id-
Imag)
加到
Imag
sac A,w7
mov.w ACCALL,w6
mov.w w6,_CurModelParm+CurMod_qdImag
mov.w
w7,_CurModelParm+CurMod_qdImag+2
;; qVelSlip = qKslip *
qIq/qdImag
;;
首先将
qIqW
和
qdImagW
置为正数,并将符号位存放在
SignW
中
clr SignW
将标志符号设定为正
;; if( IqW < 0 ) =>
翻转
SignW
并设定
IqW = -IqW
mov.w _ParkParm+Park_qIq,IqW
cp0
IqW
bra Z,jCurModSkip
bra NN,jCurMod1
neg
IqW,IqW
com SignW,SignW
;
翻转符号位
jCurMod1:
;; if(
ImagW < 0 ) =>
翻转
SignW
并设定
ImagW = -ImagW
cp0 ImagW
bra NN,jCurMod2
neg ImagW,ImagW
com
SignW,SignW
翻转符号位
jCurMod2:
;;
在
Acc A
中计算
Kslip*|IqW|
以保持
1.31
格式
mov.w
_CurModelParm+CurMod_qKslip,KslipW
mpy
IqW*KslipW,A
;;
确保
denominator >
numerator
,否则跳过项
sac A,w0
暂时的
cp ImagW,w0 |qdImag| -
|Kslip*qIq|
bra
LEU,jCurModSkip
跳过项:
|qdImag| <= |Kslip*qIq|
;;
在
6010
(芯片本身的错误)以
后版本中将不再需要。
clr.w
ShiftW
;;
计算不将最高有效位直接置
1
(保留符号位)的情况下,可将
ImagW
移位多少位。
;;
ff1l ImagW,ShiftW
sub.w ShiftW,#2,ShiftW
为将
1
放入
bit 14
,需要移位的位数
;;
移位:
ImagW = ImagW << ShiftW
sl ImagW,ShiftW,ImagW
;;
对
AccA
进行移位,需要将
(-ShiftW)
左移。
neg ShiftW,ShiftW
;; |Kslip*qIq| =
|Kslip*qIq| << ShiftW
sftac
A,ShiftW
;;
执行除法操作
|qKslip*qIq|/|ImagW|
。此时结果将为正且
<
1.0
,同时具有最高的精度。
;;
;;
sac
A,w6
repeat #17
divf w6,ImagW w0 = KslipW*IqW/ImagW,
w1 = remainder
;;
限制最大转差速度
mov.w
_CurModelParm+CurMod_qMaxSlipVel,w1
cp w1,w0 qMaxSlipSpeed - |
Kslip*qIq/qdImag |
bra
NN,jCurMod4
;;
结果太大
:
用
qMaxSlipSpeed
代替
mov.w w1,w0
bra jCurMod4
jCurModSkip:
;;
整个项被跳过
-
将其置为
= 0
clr.w w0
jCurMod4:
;;
设定正确的符号
btsc SignW,#0
neg w0,w0
;;
用于测试
mov.w
w0,_CurModelParm+CurMod_qV
elSlip
;;
加入机械转速
mov.w _CurModelParm+CurMod_qVelMech,w4
add.w w0,w4,w4
mov.w
w4,_CurModelParm+CurMod_qV
elFlux
;;
将
AngFlux
载入
Acc A
mov.w
_CurModelParm+CurMod_qAngFlux,w1
lac w1,A
mov.w
_CurModelParm+CurMod_qKdelta,w5
mac w4*w5,A
sac A,w4
mov.w w4,_CurModelParm+CurMod_qAngFlux
return
InvPark.s
;******************************************* ************************
;
InvPark
;
;
说明
:
;
计算
Park
反变换。假定
Cos
和
Sin
值位于
ParkParm
结构中。
;
;
Valpha = Vd*cos(Angle) - Vq*sin(Angle)
; Vbeta = Vd*sin(Angle) + Vq*cos(Angle)
;
该子程序同样适用于整数定标和
1.15
定标格式。
;
;
函数原型:
; void InvPark( void )
;
进入时:
ParkParm
结构必须包含
qCos
、
qSin
、
qVd
和
qVq
。
;
退出时:
ParkParm
将包含
qV
alpha
和
qVbeta
。
;
;
参数:
;
输入参数:无
;
返回值
: Void
;
所需的
SFR
设定:
= 0
;
所需的支持子程序:无
;
局部堆栈使用
:
无
;
修改的寄存器:
w3 -> w7, A
;
执行时间:大约
14
个指令周期
;**************
**************************************************
***
;
include
;
外部引用
include
;
寄存器使用
.equ ParmW, w3
指向
ParkParm
结构的指针
.equ SinW, w4
.equ CosW, w5
.equ VdW, w6
qVd
的拷贝
.equ VqW, w7
qVq
的拷贝
;===================
代码
=====================
.section .text
.global _InvPark
.global InvPark
_InvPark:
InvPark:
;;
从
ParkParm
结构获得
qVd
和
qVq
mov.w
_ParkParm+Park_qVd,VdW
mov.w _ParkParm+Park_qVq,VqW
;;
从
ParkParm
结构获得
qSin
和
qCos
mov.w
_ParkParm+Park_qSin,SinW
mov.w _ParkParm+Park_qCos,CosW
;; Valpha =
Vd*cos(Angle) - Vq*sin(Angle)
mpy CosW*VdW,A Vd*qCos -> A
msc SinW*VqW,A
;
从
A
减去
Vq*qSin
mov.w #_ParkParm+Park_qValpha,ParmW
sac A,[ParmW++]
存储到
qValpha
,指针递增
1
指向
qVbeta
;; Vbeta = Vd*sin(Angle) +
Vq*cos(Angle)
mpy
SinW*VdW,A Vd*qSin -> A
mac CosW*VqW,A
将
Vq*qCos
加到
A
sac A,[ParmW]
;
存储到
Vbeta
return
CalcRef.s
;**************
**************************************************
***
; CalcRefVec
;
;
说明:
;
根据
qValpha
和
qVbeta
计算定标后的参考矢量(
Vr1, Vr2,
Vr3
)
。
;
该方法是一种改进后的
Clarke
反变换。与常规
Clarke
反变换相比,将
Valpha
和
Vbeta
;
进行了交换。
;
; Vr1 = Vbeta
;
Vr2 = (-Vbeta/2 + sqrt(3)/2 * Valpha)
; Vr3 =
(-Vbeta/2 - sqrt(3/2) * Valpha)
;
;
函数原型:
;
; void CalcRefVec( void )
;
;
进入时:
ParkParm
结构必须包含
qCos
、
qSin
、
qValpha
和
qVbeta
。
;
退出时:
SVGenParm
将包含
qVr1
、
;
;
参数:
;
输入参数:
;
无
;
返回值:
; V
oid
;
所需的
SFR
设定:
; = 0
;
所需的支持子程序:
;
无
;
局部堆栈使用:
;
无
;
修改的寄存器:
; w0, w4, w5, w6
;
执行时间:
qVr2
和
qVr3
;
大约
20
个指令周期
;**************
**************************************************
***
;
.include
;
外部引用
.include
.include
;
寄存器使用
.equ WorkW, w0
;
工作寄存器
.equ ValphaW, w4
qValpha
(定标后)
.equ VbetaW, w5
qVbeta
(定标后)
.equ ScaleW, w6
定标
;
常量
.equ Sq3OV2,0x6ED9
sqrt(3)/2
,采用
1.15
格式
;===================
代码
=====================
.section .text
.global _CalcRefVec
.global CalcRefVec
_CalcRefVec:
CalcRefVec:
;;
从
ParkParm
结构获得
qValpha
和
qVbeta
。
mov.w
ParkParm+Park_qValpha,ValphaW
mov.w
_ParkParm+Park_qVbeta,VbetaW
;;
使
Vr1 = Vbeta
mov.w VbetaW,_SVGenParm+SVGen_qVr1
;;
装载
Sq(3)/2
mov.w
#Sq3OV2,ScaleW
;; AccA =
-Vbeta/2
neg.w
VbetaW,VbetaW
lac
VbetaW,#1,A
;; Vr2 = -Vbeta/2 + sqrt(3)2 * Valpha)
mac ValphaW*ScaleW,A
将
Valpha*sqrt(3)/
2
加到
A
sac A,WorkW
mov.w WorkW,_SVGenParm+SVGen_qVr2
;; AccA =
-Vbeta/2
lac VbetaW,#1,A
;;
Vr3 = (-Vbeta/2 - sqrt(3)2 * Valpha)
msc ValphaW*ScaleW,A
从
A
减去
Valpha*sqrt(3)/2
。
sac A,WorkW
mov.w WorkW,_SVGenParm+SVGen_qVr3
return
.end
CalcVel.s
;**************
**************************************************
***
;
子程序:
InitCalcVel,
CalcVel
;
;******************************************* ************************
;
为文件中所有子程序共有
.include
.include
;**
**************************************************
***************
; void
InitCalcVel(void)
;
对局部转速变量进行初始化。
;
在进入时
iIrpPerCalc
必须被设定。
;**************************
*****************************************
;
InitCalcVel
的寄存器使用
.equ Work0W, w4
工作寄存器
.equ PosW, w5
当前位置:
POSCNT
;**************
**************************************************
***
.global _InitCalcVel
.global
InitCalcVel
_InitCalcVel:
InitCalcVel:
;;
此后的
5
条指令禁止中断。
DISI #5
;;
装载
iPrevCnt
和
zero Delta
;;
编码器值。注意:要获得精确的转速,
qVelMech
必须计算两次。
;;
mov.w POSCNT,PosW
当前编码器值
mov.w PosW,_EncoderParm+Encod_iPrevCnt
clr.w
_EncoderParm+Encod_iAccumCnt
;;
装载
iVelCntDwn
mov.w
_EncoderParm+Encod_iIrpPerCalc,WREG
mov.w
WREG,_EncoderParm+Encod_iVelCntDwn
return
;**
**************************************************
***************
;
CalcVelIrp
;
;
以指定的间隔,被定时器中断调用。
;
;
中断间隔
VelPeriod
,必须小于最大转速时转过
1/2
转所需的最小时间。
;
;
该子程序将对
iIrpPerCal
c
中断的编码器计数变化进行累加,
;
时间周期
= iIrpPerCalc *
VelPeriod
,随后将累加值复制到
;
iDeltaCnt
以供
CalcV
el
子程序进行转速计算使用。
;
随后该累加值被置回零并开始一个新的累加操作。
;
;
函数原型:
void CalcVelIrp( void );
;
;
进入时:
EncoderParm
必须包含
iPrevCnt
、
iAccumCnt
和
iVelCntDwn
;
;
退出时:
EncoderParm
将包含
iPrevCnt
、
iAccumCnt
和
iDeltaCnt
;(
如果递减计数到达零)
。
;
;
参数:
;
输入参数无
;
;
返回:
; V
oid
;
;
所需的
SFR
设定:
:
无
;
;
所需的支持子程序:无
;
;
局部堆栈使用:
3
;
;
修改的寄存器:无
;
;
执行时间:大约
29
个指令周期(如果产生了新的
<
/p>
iDeltaCnt
)
。
;
-
-
-
-
-
-
-
-
-
上一篇:VC_GPIB_Demo
下一篇:超声波测距报告(带报警)