This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS320F28335: Q format in the official routine of eQEP module

Part Number: TMS320F28335


Dear team:

1, Is there a more detailed comment on the following code?

long tmp;
unsigned int pos16bval,temp1;
_iq Tmp1,newp,oldp;

p->DirectionQep = EQep1Regs.QEPSTS.bit.QDF; // Motor direction: 0=CCW/reverse, 1=CW/forward

pos16bval=(unsigned int)EQep1Regs.QPOSCNT; // capture position once per QA/QB period
p->theta_raw = pos16bval+ p->cal_angle; // raw theta = current pos. + ang. offset from QA

// The following lines calculate p->theta_mech ~= QPOSCNT/mech_scaler [current cnt/(total cnt in 1 rev.)]
// where mech_scaler = 4000 cnts/revolution
tmp = (long)((long)p->theta_raw*(long)p->mech_scaler); // Q0*Q26 = Q26
tmp &= 0x03FFF000;
p->theta_mech = (int)(tmp>>11); // Q26 -> Q15
p->theta_mech &= 0x7FFF;

// The following lines calculate p->elec_mech
p->theta_elec = p->pole_pairs*p->theta_mech; // Q0*Q15 = Q15
p->theta_elec &= 0x7FFF;

2, I use the f28335 chip. Do I need to calculate the angle feedback with the incremental encoder, as in the Q format of the official documents?

if(EQep1Regs.QFLG.bit.UTO==1) // If unit timeout (one 100Hz period)
{
/** Differentiator **/
// The following lines calculate position = (x2-x1)/4000 (position in 1 revolution)
pos16bval=(unsigned int)EQep1Regs.QPOSLAT; // Latched POSCNT value
tmp = (long)((long)pos16bval*(long)p->mech_scaler); // Q0*Q26 = Q26
tmp &= 0x03FFF000;
tmp = (int)(tmp>>11); // Q26 -> Q15
tmp &= 0x7FFF;
newp=_IQ15toIQ(tmp);
oldp=p->oldpos;

if (p->DirectionQep==0) // POSCNT is counting down
{
if (newp>oldp)
Tmp1 = - (_IQ(1) - newp + oldp); // x2-x1 should be negative
else
Tmp1 = newp -oldp;
}
else if (p->DirectionQep==1) // POSCNT is counting up
{
if (newp<oldp)
Tmp1 = _IQ(1) + newp - oldp;
else
Tmp1 = newp - oldp; // x2-x1 should be positive
}

if (Tmp1>_IQ(1))
p->Speed_fr = _IQ(1);
else if (Tmp1<_IQ(-1))
p->Speed_fr = _IQ(-1);
else
p->Speed_fr = Tmp1;

// Update the electrical angle
p->oldpos = newp;

// Change motor speed from pu value to rpm value (Q15 -> Q0)
// Q0 = Q0*GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
p->SpeedRpm_fr = _IQmpy(p->BaseRpm,p->Speed_fr);
//=======================================

EQep1Regs.QCLR.bit.UTO=1; // Clear interrupt flag
}

p->theta_mech / p->theta_elec is angle feedback, Q15 format, I only need to interrupt every call, and then > > 15 / or ÷ 32768 (the 15th power of 2) is the real data?

p->SpeedRpm_fr = _IQmpy(p->BaseRpm,p->Speed_fr); The result here is in Q0 format. Can I use it directly?

Best Regards

  • mech_scaler is in Q26 format, where 26 bits are used to represent fractions. This can be helpful if the encoder has more pulses per rotation. Basically, mech_scaler = 1/(Number of pulses per rotation). The pulse count is converted to normalised value and then extracted into Q15 and then to the Q default used by the project. 

    This is a pretty old code and there double Q migration from 26 to 15 and then to Q. May be the code uses the angle in both number formats, Q15 for LUT access of sine table and general Q for generic math used in the system. Suggest to spend some time playing around with IQ math and figuring the nuances out.

    If speed_fr is a normalised variable with a range of 0 to 1.0, then yes, you can use the speedRpm calc as is.