📄 smcpos.c
字号:
/* SOFTWARE LICENSE AGREEMENT:
* Microchip Technology Incorporated ("Microchip") retains all ownership and
* intellectual property rights in the code accompanying this message and in all
* derivatives hereto. You may use this code, and any derivatives created by
* any person or entity by or on your behalf, exclusively with Microchip,s
* proprietary products. Your acceptance and/or use of this code constitutes
* agreement to the terms and conditions of this notice.
*
* CODE ACCOMPANYING THIS MESSAGE IS SUPPLIED BY MICROCHIP "AS IS". NO
* WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
* TO, IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE APPLY TO THIS CODE, ITS INTERACTION WITH MICROCHIP,S
* PRODUCTS, COMBINATION WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.
*
* YOU ACKNOWLEDGE AND AGREE THAT, IN NO EVENT, SHALL MICROCHIP BE LIABLE, WHETHER
* IN CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE OR BREACH OF STATUTORY DUTY),
* STRICT LIABILITY, INDEMNITY, CONTRIBUTION, OR OTHERWISE, FOR ANY INDIRECT, SPECIAL,
* PUNITIVE, EXEMPLARY, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, FOR COST OR EXPENSE OF
* ANY KIND WHATSOEVER RELATED TO THE CODE, HOWSOEVER CAUSED, EVEN IF MICROCHIP HAS BEEN
* ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
* ALLOWABLE BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO
* THIS CODE, SHALL NOT EXCEED THE PRICE YOU PAID DIRECTLY TO MICROCHIP SPECIFICALLY TO
* HAVE THIS CODE DEVELOPED.
*
* You agree that you are solely responsible for testing the code and
* determining its suitability. Microchip has no obligation to modify, test,
* certify, or support the code.
*/
#include "general.h"
#include "smcpos.h"
SFRAC16 __OffsetTheta;
SFRAC16 Op1, Op2, Op3, Res1, FilteredOmega;
void SMC_Position_Estimation (SMC *s)
{
// long result;
// int acc_copy;
register int resultA asm("A");
asm("PUSH CORCON");
CORCONbits.SATA = 1;
CORCONbits.SATB = 1;
CORCONbits.ACCSAT = 1;
// Sliding mode current observer
//
//
// s->EstIalpha = s->Fsmopos * s->EstIalpha +
// s->Gsmopos * (s->Valpha - s->Ealpha - s->Zalpha)
//
//
Op1 = s->Fsmopos;
Op2 = s->EstIalpha;
asm("MOV _Op1, W4");
asm("MOV _Op2, W5");
asm("MPY W4*W5, A");
Op1 = s->Valpha;
Op2 = s->Ealpha;
Op3 = s->Zalpha;
asm("MOV _Op1, W0");
asm("MOV _Op2, W1");
asm("MOV _Op3, W2");
asm("LAC W1, B");
asm("ADD W2, #0, B");
asm("NEG B");
asm("ADD W0, #0, B");
asm("SAC.R B, #0, W4");
Op1 = s->Gsmopos;
asm("MOV _Op1, W5");
asm("MPY W4*W5, B");
asm("ADD A");
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
s->EstIalpha = Res1;
//
//
// s->EstIbeta = s->Fsmopos * s->EstIbeta +
// s->Gsmopos * (s->Vbeta - s->Ebeta - s->Zbeta)
//
//
Op1 = s->Fsmopos;
Op2 = s->EstIbeta;
asm("MOV _Op1, W4");
asm("MOV _Op2, W5");
asm("MPY W4*W5, A");
Op1 = s->Vbeta;
Op2 = s->Ebeta;
Op3 = s->Zbeta;
asm("MOV _Op1, W0");
asm("MOV _Op2, W1");
asm("MOV _Op3, W2");
asm("LAC W1, B");
asm("ADD W2, #0, B");
asm("NEG B");
asm("ADD W0, #0, B");
asm("SAC.R B, #0, W4");
Op1 = s->Gsmopos;
asm("MOV _Op1, W5");
asm("MPY W4*W5, B");
asm("ADD A");
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
s->EstIbeta = Res1;
// Current errors
// s->IalphaError = s->EstIalpha - s->Ialpha;
Op1 = s->EstIalpha;
Op2 = s->Ialpha;
asm("MOV _Op1, W0");
asm("MOV _Op2, W1");
asm("LAC W0, A");
asm("LAC W1, B");
asm("SUB A");
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
s->IalphaError = Res1;
// s->IbetaError = s->EstIbeta - s->Ibeta;
Op1 = s->EstIbeta;
Op2 = s->Ibeta;
asm("MOV _Op1, W0");
asm("MOV _Op2, W1");
asm("LAC W0, A");
asm("LAC W1, B");
asm("SUB A");
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
s->IbetaError = Res1;
// Sliding control calculator
if (s->IalphaError > 0)
s->Zalpha = s->Kslide;
else if (s->IalphaError == 0)
s->Zalpha = 0;
else
s->Zalpha = -s->Kslide;
if (s->IbetaError > 0)
s->Zbeta = s->Kslide;
else if (s->IbetaError == 0)
s->Zbeta = 0;
else
s->Zbeta = -s->Kslide;
// Sliding control filter -> back EMF calculator
// s->Ealpha = s->Ealpha + s->Kslf * (s->Zalpha-s->Ealpha)
Op1 = s->Zalpha;
Op2 = s->Ealpha;
Op3 = s->Kslf;
asm("MOV _Op1, W0");
asm("MOV _Op2, W1");
asm("MOV _Op3, W5");
asm("LAC W0, A");
asm("LAC W1, B");
asm("SUB A");
asm("SAC.R A, #0, W4");
asm("MPY W4*W5, A");
asm("ADD A");
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
s->Ealpha = Res1;
// s->Ebeta = s->Ebeta + s->Kslf * (s->Zbeta-s->Ebeta)
Op1 = s->Zbeta;
Op2 = s->Ebeta;
Op3 = s->Kslf;
asm("MOV _Op1, W0");
asm("MOV _Op2, W1");
asm("MOV _Op3, W5");
asm("LAC W0, A");
asm("LAC W1, B");
asm("SUB A");
asm("SAC.R A, #0, W4");
asm("MPY W4*W5, A");
asm("ADD A");
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
s->Ebeta = Res1;
// New filter used to calculate Position
// s->EalphaFinal = s->EalphaFinal + s->KslfFinal * (s->Ealpha-s->EalphaFinal)
Op1 = s->Ealpha;
Op2 = s->EalphaFinal;
Op3 = s->KslfFinal;
asm("MOV _Op1, W0");
asm("MOV _Op2, W1");
asm("MOV _Op3, W5");
asm("LAC W0, A");
asm("LAC W1, B");
asm("SUB A");
asm("SAC.R A, #0, W4");
asm("MPY W4*W5, A");
asm("ADD A");
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
s->EalphaFinal = Res1;
// s->EbetaFinal = s->EbetaFinal + s->KslfFinal * (s->Ebeta-s->EbetaFinal)
Op1 = s->Ebeta;
Op2 = s->EbetaFinal;
Op3 = s->KslfFinal;
asm("MOV _Op1, W0");
asm("MOV _Op2, W1");
asm("MOV _Op3, W5");
asm("LAC W0, A");
asm("LAC W1, B");
asm("SUB A");
asm("SAC.R A, #0, W4");
asm("MPY W4*W5, A");
asm("ADD A");
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
s->EbetaFinal = Res1;
asm("POP CORCON");
// Rotor angle calculator -> Theta = atan(Ebeta,-Ealpha)
s->Theta = cordic(s->EbetaFinal,-s->EalphaFinal);
AccumTheta += s->Theta - PrevTheta;
PrevTheta = s->Theta;
AccumThetaCnt++;
if (AccumThetaCnt == diIrpPerCalc)
{
resultA = __builtin_mpy(AccumTheta,Q15((float)(SPEED_CONSTANT/2.0)),0,0,0,0,0,0);
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
s->Omega = Res1;
// s->Omega *= 2;
AccumThetaCnt = 0;
AccumTheta = 0;
}
// Q15(Omega) * PWM Frequency * 60
// Speed RPMs = -----------------------------------------------------------
// Q15(Speed Constant) * Cycler per Speed Calc * Motor Poles
// For example:
// Omega = .5
// PWM Frequency = 8,000
// Speed Constant = 1.5625
// Cycles per Speed Calc = 8
// Motor Poles (pole pairs * 2) = 4
// Then:
// Speed in RPMs is 4,800 RPMs
// FilteredOmega = FilteredOmega + FilterCoeff * (s->Omega-FilteredOmega)
asm("PUSH CORCON");
CORCONbits.SATA = 1;
CORCONbits.SATB = 1;
CORCONbits.ACCSAT = 1;
Op1 = s->Omega;
Op2 = FilteredOmega;
Op3 = Q15(0.01);
asm("MOV _Op1, W0");
asm("MOV _Op2, W1");
asm("MOV _Op3, W5");
asm("LAC W0, A");
asm("LAC W1, B");
asm("SUB A");
asm("SAC.R A, #0, W4");
asm("MPY W4*W5, A");
asm("ADD A");
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
FilteredOmega = Res1;
asm("POP CORCON");
if (FilteredOmega < Q15(OMEGA0))
{
resultA = __builtin_mpy(FilteredOmega,SLOPEFRAC0,0,0,0,0,0,0);
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
__OffsetTheta = Res1;
__OffsetTheta += FilteredOmega * SLOPEINT0;
__OffsetTheta += CONSTANT0;
}
else if (FilteredOmega < Q15(OMEGA1))
{
resultA = __builtin_mpy(FilteredOmega,SLOPEFRAC1,0,0,0,0,0,0);
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
__OffsetTheta = Res1;
__OffsetTheta += FilteredOmega * SLOPEINT1;
__OffsetTheta += CONSTANT1;
}
else if (FilteredOmega < Q15(OMEGA2))
{
resultA = __builtin_mpy(FilteredOmega,SLOPEFRAC2,0,0,0,0,0,0);
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
__OffsetTheta = Res1;
__OffsetTheta += FilteredOmega * SLOPEINT2;
__OffsetTheta += CONSTANT2;
}
else if (FilteredOmega < Q15(OMEGA3))
{
resultA = __builtin_mpy(FilteredOmega,SLOPEFRAC3,0,0,0,0,0,0);
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
__OffsetTheta = Res1;
__OffsetTheta += FilteredOmega * SLOPEINT3;
__OffsetTheta += CONSTANT3;
}
else if (FilteredOmega < Q15(OMEGA4))
{
resultA = __builtin_mpy(FilteredOmega,SLOPEFRAC4,0,0,0,0,0,0);
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
__OffsetTheta = Res1;
__OffsetTheta += FilteredOmega * SLOPEINT4;
__OffsetTheta += CONSTANT4;
}
else if (FilteredOmega < Q15(OMEGA5))
{
resultA = __builtin_mpy(FilteredOmega,SLOPEFRAC5,0,0,0,0,0,0);
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
__OffsetTheta = Res1;
__OffsetTheta += FilteredOmega * SLOPEINT5;
__OffsetTheta += CONSTANT5;
}
else if (FilteredOmega < Q15(OMEGA6))
{
resultA = __builtin_mpy(FilteredOmega,SLOPEFRAC6,0,0,0,0,0,0);
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
__OffsetTheta = Res1;
__OffsetTheta += FilteredOmega * SLOPEINT6;
__OffsetTheta += CONSTANT6;
}
else if (FilteredOmega < Q15(OMEGA7))
{
resultA = __builtin_mpy(FilteredOmega,SLOPEFRAC7,0,0,0,0,0,0);
asm("SAC.R A, #0, W0");
asm("MOV W0, _Res1");
__OffsetTheta = Res1;
__OffsetTheta += FilteredOmega * SLOPEINT7;
__OffsetTheta += CONSTANT7;
}
else
__OffsetTheta = DEFAULTCONSTANT;
s->Theta = s->Theta + __OffsetTheta;
return;
}
SFRAC16 FracDiv(SFRAC16 num, SFRAC16 den)
{
SFRAC16 FracDivResult;
__asm__ volatile("repeat #17\n\t"
"divf %1,%2\n\t"
"mov w0,%0" : /* output */ "=g"(FracDivResult)
: /* input */ "r"(num),
"e"(den)
: /* clobber */ "w0");
return FracDivResult;
}
void SMCInit(SMC *s)
{
float TemporalFloat;
// R * Ts
// Fsmopos = 1 - --------
// L
// Ts
// Gsmopos = ----
// L
// Ts = Sampling Period. If sampling at PWM, Ts = 50 us
// R = Phase Resistance. If not provided by motor datasheet,
// measure phase to phase resistance with multimeter, and
// divide over two to get phase resistance. If 4 Ohms are
// measured from phase to phase, then R = 2 Ohms
// L = Phase inductance. If not provided by motor datasheet,
// measure phase to phase inductance with multimeter, and
// divide over two to get phase inductance. If 2 mH are
// measured from phase to phase, then L = 1 mH
TemporalFloat = (float)(1 - PhaseRes * dLoopTimeInSec / PhaseInd);
if (TemporalFloat < 0.0)
s->Fsmopos = Q15(0.0);
else
s->Fsmopos = Q15(TemporalFloat);
TemporalFloat = (float)(dLoopTimeInSec / PhaseInd);
if (TemporalFloat > 1.0)
s->Gsmopos = Q15(0.99999);
else
s->Gsmopos = Q15(TemporalFloat);
s->Kslide = Q15(0.5);
s->Kslf = Q15(0.037);
s->KslfFinal = Q15(0.067);
return;
}
unsigned int const Angles[15]={
8192, 4836, 2555,
1297, 651, 326,
163, 81, 41,
20, 10, 5,
3, 1, 1};
int cordic(int x, int y)
{
unsigned char quadrant, step;
int currangle, newx;
if((x >= 0) && (y >= 0)) // Quadrant 0, 0 < angle < 90
quadrant = 0;
else if((x < 0) && (y >= 0)) // Quadrant 1, 90 < angle < 180
{
quadrant = 1;
x = -x;
}
else if((x < 0) && (y < 0)) // Quadrant 2, 180 < angle < 270
{
quadrant = 2;
x = -x;
y = -y;
}
else if((x >= 0) && (y < 0)) // Quadrant 3, 270 < angle < 360
{
quadrant = 3;
y = -y;
}
currangle = 0;
for(step = 0; step < 10; step++)
{
if(y < 0)
{
newx = x - (y >> step);
y = (x >> step) + y;
x = newx;
currangle += Angles[step];
}
else
{
newx = x + (y >> step);
y = -(x >> step) + y;
x = newx;
currangle -= Angles[step];
}
}
switch(quadrant)
{
case 0: currangle = -currangle;
break;
case 1: currangle = 32768 + currangle;
break;
case 2: currangle = 32768 - currangle;
break;
case 3: currangle = currangle;
break;
}
return currangle;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -