目录
一、QEP概述
一、采用增量型光电编码器来判别电机转速方向的基本原理
增量式编码器是将设备运动时的位移信息变成连续的脉冲信号,脉冲个数表示位移量的大小。只有当设备运动的时候增量式编码器才会输出信号。编码器一般会把这些信号分为通道A和通道B 两组输出,并且这两组信号间有90° 的相位差。同时采集这两组信号就可以知道设备的运动和方向。除了通道A、通道B 以外,很多增量式编码器还会设置一个额外的通道Z 输出信号,用来表示编码器特定的参考位置,传感器转一圈Z 轴信号才会输出一个脉冲。增量式编码器只输出设备的位置变化和运动方向,不会输出设备的绝对位置。
增量式编码器都有A、B 两通道信号输出,这是因为增量式编码器的码盘上有两圈线槽,两圈线槽的之间会错开一定的角度,这个角度会使得光电检测装置输出的两相信号相差1/4 周期(90°)。码盘的具体工作方式如下图所示。图中黑色代表透光,白色代表遮光。当码盘转动时,内圈和外圈的线槽会依次透过光线,光电检测装置检测到光线通断的变化,就会相应的输出脉冲信号,因为内外圈遮光和透光时候存在时间差,所以也就有了A、B 两通道信号的相位差。
增量型编码器一般安装在电机或其他旋转机构的轴上,在码盘旋转过程中,输出两个信号称为QEPA 与QEPB,两路信号相差90°,这就是所谓的正交信号,当电机正转时,脉冲信号A 的相位超前脉冲信号 B 的相位 90°,此时逻辑电路处理后可形成高电平的方向信号 Dir。当电机反转时,脉冲信号 A 的相位滞后脉冲信号 B 的相位 90°,此时逻辑电路处理后的方向信号 Dir 为低电平。因此根据超前与滞后的关系可以确定电机的转向。其转速辩相的原理如图所示。
二、DSP28335的EQEP模块
TMS28335芯片将常用的转速测量电路集成于芯片内部,组成了增强型正交编码模块eQEP。当工作在正交计数模式下,正交解码单元可对正交信号进行4倍频处理,为位置计数器寄存器的计数提供时钟频率。
正交-计数模式: 在正交-计数模式下,正交解码器产生方向信号和时钟信号送给位置计数器。
方向解码---通过确定QEPA和QEPB两个脉冲信号,哪一个超前,来解码出旋转方向逻辑,并且将此方向逻辑更新到QEPSTS的QDF位。表10.1给出了方向解码逻辑的真值表。图10.7给出了方向解码的状态机。
时钟解码---QEP中的解码模块,会对QEPA和QEPB脉冲的上升沿及下降沿进行计数,所以最后解码的时钟频率将会是实际输入QEPA或者QEPB的4倍。
三、测速方法
(1)M法
M法又称之为测频法,其原理是先设定一定的时间,然后在这段时间TC内,对光电码盘输出的信号的个数进行计数。在测量过程中,计取的脉冲数M1不一定正好是整数,M1可能存在±1的误差。计取的M1的值越大,所得的误差越小,因此M法适用于高速转速的测量。
(2)T法
T法又称之为测周法,此测量原理是在一个脉冲周期内对高频脉冲数进行计数。为了减小误差,计得的高频脉冲M2应尽量大,因此T法测量适用于低速测量。
(3)M/T法
M/T法是将M法和T法两种方法结合起来,即在一定的时间范围TC内,计取光电码盘输出的脉冲数M1的同时,也对高频脉冲M2进行计数。TC时间到来后,再计取T时间内的高频脉冲数m,T是时间TC结束后到码盘信号下一个脉冲上升沿的时间间隔。
(4)二阶Lagrange插值多项式估值法。
其关于时间t的导数如下:
式中:t 0,t1,t 2,f (t0),f (t1),f (t2)分别为离当前时刻t最近的3个位置事件对应的时间和位置。假定在t0,t1,t2这3个时间点对应的位置增量相等,即
其中N为一个位置事件所对应的编码器脉冲数。令△t1 = t1 - t0,△t2 = t2 - t1,△t3 = t - t2,则可利用式(17)来估计当前时刻t的瞬时转速(rad/s):
(5)具体参数计算
采用双路QEP,512线光电编码器的直流减速电机 额定转速6000rpm 额定电压12V
M法:
T法、二阶Lagrange插值多项式估值法:
说明:TI的官方例程只有M、T法的相关代码,没有M、T法的,由于只是拿来用用(而且相关代码不是对照着修改的、这里就不给出了)
四、相关测速部分代码
1、头文件
#ifndef EQEP_POS_SPEED_GET_H
#define EQEP_POS_SPEED_GET_H
#include "IQmathLib.h" // Include header for IQmath library
//-----------------------------------------------------------------------------
// Define the structure of the POSSPEED Object
//-----------------------------------------------------------------------------
typedef struct {float ElecTheta; //OutPut: Motor Electrical Angle
float MechTheta; //Output: Motor Mechanical Angle
float MechTheta_Beyond; //存储超过一圈后的位置信息的变量
int DirectionQep; //output: Motor rotation direction
int PolePairs; //Parameter: 同步电机极对数
int LineEncoder; //Parameter: 码盘一周脉冲数(增量式)
int Encoder_N; //Parameter: 码盘一周脉冲数的4倍(根据倍频的倍数而定,这里用4倍频)
int CalibrateAngle;//Parameter:电机A相绕组和码盘Index信号之间的夹角,与安装精度有关
float Mech_Scaler; //Parameter:1/Encoder_N
float RawTheta; //Variable: 初始定位后,电机转子d轴和定子A相绕组之间所相差的码盘计数值
int Index_sync_flag;//Output: Index sync status
float BaseRpm; //Parameter:额定转速
float Speed_Mr_Rpm_Scaler;//Parameter:60000/(Encoder_N * T),其中T为M法测速时的时间间隔,单位ms//60/512线/(4倍频*0.001s)
float Speed_Mr_Rpm; //Output: speed int r.p.m
float Speed_Mr; //Output: speed in per-uint
float Position_k_1; //Input: Current position
float Position_k; //Input: Last position
float Speed_Tr_Rpm_Scaler;//Parameter: (UPPS * 150e6 * 60)/(Encoder_N * CCPS)//150e6*60/512
float Speed_Tr_Rpm; //Output: spedd int r.p.m
float Speed_Tr; //Output: speed int per-uint
float t1;
float t2;
float t3;
float Ts; // 采样时间
float K2; //滤波系数
float Speed_Temp;
float Speed; // Output : speed in per-unit
Uint32 K;
int cnt_old; //eQEP位置计数器寄存器中上一个时刻的计数值
int cycles; // 电机转过的圈数
float position;
void (*init)(); // Pointer to the init funcion
void (*calc)();
} EQEP_POS_SPEED_GET;
//-----------------------------------------------------------------------------
// Define a POSSPEED_handle
//-----------------------------------------------------------------------------
typedef EQEP_POS_SPEED_GET *EQEP_POS_SPEED_GET_handle;
//-----------------------------------------------------------------------------
// Default initializer for the POSSPEED Object.
//-----------------------------------------------------------------------------
#if (CPU_FRQ_150MHZ)
#define EQEP_POS_SPEED_GET_DEFAULTS {0,0,0,0,\
2,500,2000,0,1.0/2000,0,0,\
6000.0,\
9.76,0,0,0,0,\
17578125,0,0,\
0,0,0,0,0,0,0,0,0,0,0,\
(void (*)(long))eQEP_pos_speed_get_Init,\
(void (*)(long))eQEP_pos_speed_get_Calc}
//注意9.76是0.003s为周期
//#define EQEP_POS_SPEED_GET_DEFAULTS {0,0,0,0,\
// 2,500,2000,0,1.0/2000,0,0,\
// 6000.0,\
// 29.29,0,0,0,0,\
// 17578125,0,0,\
// 0,0,0,0,0,0,0,0,0,0,0,\
// (void (*)(long))eQEP_pos_speed_get_Init,\
// (void (*)(long))eQEP_pos_speed_get_Calc}
#endif
#if (CPU_FRQ_100MHZ)
#define EQEP_POS_SPEED_GET_DEFAULTS {0,0,0,\
2,500,2000,0,1.0/2000,0,0,\
6000.0,\
3.0,0,0,0,0,\
1125000,0,0,\
0,0,0,0,0,0,0,0,\
(void (*)(long))eQEP_pos_speed_get_Init,\
(void (*)(long))eQEP_pos_speed_get_Calc}
#endif
//-----------------------------------------------------------------------------
// Prototypes for the functions in posspeed.c
//-----------------------------------------------------------------------------
void eQEP_pos_speed_get_Init(EQEP_POS_SPEED_GET_handle);
void eQEP_pos_speed_get_Calc(EQEP_POS_SPEED_GET_handle);
#endif
//==============================================================
//End of file.
//==============================================================
2、主文件
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "DSP2833x_Examples.h" // DSP2833x Examples Include File #include "Example_posspeed.h"
#include "Example_posspeed.h"
#include <math.h>
//======== Definations for functions ===========================
void eQEP_pos_speed_get_Init(EQEP_POS_SPEED_GET *p)
{
#if (CPU_FRQ_150MHZ)
// EQep1Regs.QUPRD=1500000; // Unit Timer for 100Hz at 150 MHz SYSCLKOUT
//案例给的程序错误的地方 这个是单位事件使能 也就是说M法
EQep1Regs.QUPRD=150000*3; // Unit Timer for 100Hz at 150 MHz SYSCLKOUT
#endif
#if (CPU_FRQ_100MHZ)
EQep1Regs.QUPRD=1000000; // Unit Timer for 100Hz at 100 MHz SYSCLKOUT
#endif
p->Encoder_N=4*p->LineEncoder;
p->Mech_Scaler=1.0/p->Encoder_N;
EQep1Regs.QDECCTL.bit.QSRC=0x00; // QEP quadrature count mode
EQep1Regs.QEPCTL.bit.FREE_SOFT=2;
EQep1Regs.QEPCTL.bit.PCRM=0x01; // PCRM=00 mode - QPOSCNT reset on index event
EQep1Regs.QEPCTL.bit.UTE=1; // Unit Timeout Enable
EQep1Regs.QEPCTL.bit.QCLM=1; // Latch on unit time out
EQep1Regs.QPOSMAX=p->Encoder_N; // Encoder_N
EQep1Regs.QEPCTL.bit.QPEN=1; // QEP enable
EQep1Regs.QCAPCTL.bit.UPPS=1; // 1/2 for unit position
EQep1Regs.QCAPCTL.bit.CCPS=3; // 1/8 for CAP clock
EQep1Regs.QCAPCTL.bit.CEN=1; // QEP Capture Enable
// EQep1Regs.QPOSMAX=0xffffffff;
p->cycles=0;
p->Speed_Tr_Rpm_Scaler=60.0*150*1000000/4/p->Encoder_N;
p->Speed_Tr_Rpm=0;
//EQep1Regs.QEPCTL.bit.SWI=1;// software generate index pulse
}
//**********************************
void eQEP_pos_speed_get_Calc(EQEP_POS_SPEED_GET *p)
{
int cnt_inc , cnt_cur;
float tmp1;
unsigned int t2_t1;
unsigned long M1,M2,fclk;
//Check the rotational direction
p->DirectionQep = EQep1Regs.QEPSTS.bit.QDF;
cnt_cur = EQep1Regs.QPOSCNT;
//Check the position counter for EQep1
p->RawTheta = cnt_cur + p->CalibrateAngle;
if(p->RawTheta < 0)
{
p->RawTheta = p->RawTheta + EQep1Regs.QPOSMAX;
}
else if(p->RawTheta > EQep1Regs.QPOSMAX)
{
p->RawTheta = p->RawTheta - EQep1Regs.QPOSMAX;
}
//Compute the mechanical angle
p->MechTheta = p->Mech_Scaler * p->RawTheta;
/*****************超过一圈后的角度计算*************/
// Check the counter for QEP
cnt_inc = cnt_cur - p->cnt_old;
if (abs(cnt_inc) > 200) //6000 RPM, 1ms能读到的最大差值就是200, 除非转角超过1圈.
{
if (cnt_inc < 0)
p->cycles +=1;
else
p->cycles -=1;
}
p->cnt_old = cnt_cur;
//Compute the mechanical angle
tmp1=p->MechTheta_Beyond; // old position
p->MechTheta_Beyond = p->MechTheta+p->cycles;
p->Speed = 60*(p->MechTheta_Beyond-tmp1)/p->Ts; // speed in RPM values
// Check an index occurrence
if (EQep1Regs.QFLG.bit.IEL == 1)
{
p->Index_sync_flag = 0x00F0;
EQep1Regs.QCLR.bit.IEL=1; // Clear interrupt flag
}
//
// High Speed Calculation using QEP Position counter
// Check unit Time out-event for speed calculation:
// Unit Timer is configured for 100Hz in INIT function
//
if(EQep1Regs.QFLG.bit.UTO==1)// If unit timeout (one 100Hz period)
{
p->Position_k =1.0 * EQep1Regs.QPOSLAT;
if(p->DirectionQep==0) // POSCNT is counting down
{
if(p->Position_k > p->Position_k_1)
{ tmp1 = -(p->Encoder_N - (p->Position_k - p->Position_k_1)); }
else
{ tmp1 = p->Position_k - p->Position_k_1;}// x2-x1 should be negative
}
else if(p->DirectionQep==1) // POSCNT is counting up
{
if(p->Position_k < p->Position_k_1)
{ tmp1 = p->Encoder_N - (p->Position_k_1 - p->Position_k); }
else
{ tmp1 = p->Position_k - p->Position_k_1;}// x2-x1 should be positive
}
if(tmp1 > p->Encoder_N)
{ p->Speed_Mr_Rpm = p->BaseRpm; }
else if(tmp1 < -p->Encoder_N)
{ p->Speed_Mr_Rpm = -p->BaseRpm; }
else
{ p->Speed_Mr_Rpm = tmp1 * p->Speed_Mr_Rpm_Scaler; }
p->Speed_Mr = p->Speed_Mr_Rpm / p->BaseRpm;
p->Position_k_1 = p->Position_k;
EQep1Regs.QCLR.bit.UTO=1; // Clear interrupt flag
}
#ifndef Larrange_qep
// // Low-speed computation using QEP capture counter // T法
if(EQep1Regs.QEPSTS.bit.UPEVNT==1) // Unit position event
{
if(EQep1Regs.QEPSTS.bit.COEF==0) // No Capture overflow
t2_t1 = EQep1Regs.QCPRD;
else // Capture overflow, saturate the result
t2_t1 = 0xFFFF;
//
// Convert p->Speed_pr to RPM
//
if(p->DirectionQep==0)
p->Speed_Tr_Rpm = - p->Speed_Tr_Rpm_Scaler / t2_t1; //negative
else
p->Speed_Tr_Rpm = p->Speed_Tr_Rpm_Scaler / t2_t1;
// if(p->Speed_Tr_Rpm > p->BaseRpm)
// p->Speed_Tr_Rpm = p->BaseRpm;
// else if(p->Speed_Tr_Rpm < -p->BaseRpm)
// p->Speed_Tr_Rpm = -p->BaseRpm;
//
EQep1Regs.QEPSTS.all=0x88; // Clear Unit position event flag
// Clear overflow error flag
}
#else
//Lagrange
if(EQep1Regs.QEPSTS.bit.UPEVNT==1)
{
p->t1=p->t2;
p->t2=(unsigned int)EQep1Regs.QCPRD;
EQep1Regs.QEPSTS.bit.UPEVNT=1;
}
p->t3=EQep1Regs.QCTMR;
p->Speed_Tr_Rpm=(p->Speed_Tr_Rpm_Scaler)*((2*p->t3+2*p->t2+p->t1)*2/(p->t1+p->t2)/p->t2-(2*p->t3+p->t2+p->t1)/p->t2/p->t1);
if(p->DirectionQep==0)
p->Speed_Tr_Rpm=-p->Speed_Tr_Rpm;
#endif
}
//==============================================================
//End of file.
//==============================================================
说明:该文件是从ti官方例程修改的(不是我改的),搬运ing......
附录:
Example_2833xEqep_pos_speed 工程内的两个文件(对比一下)
#ifndef __POSSPEED__
#define __POSSPEED__
//
// Included Files
//
#include "IQmathLib.h" // Include header for IQmath library
//
// Typedef for the structure of the POSSPEED Object
//
typedef struct
{
int theta_elec; // Output: Motor Electrical angle (Q15)
int theta_mech; // Output: Motor Mechanical Angle (Q15)
int DirectionQep; // Output: Motor rotation direction (Q0)
int QEP_cnt_idx; // Variable: Encoder counter index (Q0)
int theta_raw; // Variable: Raw angle from Timer 2 (Q0)
//
// Parameter: 0.9999/total count, total count = 4000 (Q26)
//
int mech_scaler;
int pole_pairs; // Parameter: Number of pole pairs (Q0)
//
// Parameter: Raw angular offset between encoder and phase a (Q0)
//
int cal_angle;
int index_sync_flag; // Output: Index sync status (Q0)
//
// Parameter : Scaler converting 1/N cycles to a GLOBAL_Q speed (Q0)
// independently with global Q
//
Uint32 SpeedScaler;
_iq Speed_pr; // Output : speed in per-unit
//
// Parameter : Scaler converting GLOBAL_Q speed to rpm (Q0) speed
// independently with global Q
//
Uint32 BaseRpm;
//
// Output : speed in r.p.m. (Q0) - independently with global Q
//
int32 SpeedRpm_pr;
_iq oldpos; // Input: Electrical angle (pu)
_iq Speed_fr; // Output : speed in per-unit
//
// Output : Speed in rpm (Q0) - independently with global Q
//
int32 SpeedRpm_fr;
void (*init)(); // Pointer to the init function
void (*calc)(); // Pointer to the calc function
} POSSPEED;
//
// Typedef for the POSSPEED_handle
//
typedef POSSPEED *POSSPEED_handle;
//
// Defines for the default initializer for the POSSPEED Object.
//
#if (CPU_FRQ_150MHZ)
#define POSSPEED_DEFAULTS {0x0, 0x0,0x0,0x0,0x0,16776,2,0,0x0,\
94,0,6000,0,\
0,0,0,\
(void (*)(long))POSSPEED_Init,\
(void (*)(long))POSSPEED_Calc }
#endif
#if (CPU_FRQ_100MHZ)
#define POSSPEED_DEFAULTS {0x0, 0x0,0x0,0x0,0x0,16776,2,0,0x0,\
63,0,6000,0,\
0,0,0,\
(void (*)(long))POSSPEED_Init,\
(void (*)(long))POSSPEED_Calc }
#endif
//
// Function Prototypes
//
void POSSPEED_Init(void);
void POSSPEED_Calc(POSSPEED_handle);
#endif /* __POSSPEED__ */
//
// End of File
//
//
// Included Files
//
#include "DSP28x_Project.h" // Device Headerfile and Examples Include File
#include "Example_posspeed.h" // Example specific Include file
//
// POSSPEED_Init -
//
void
POSSPEED_Init(void)
{
#if (CPU_FRQ_150MHZ)
EQep1Regs.QUPRD=1500000; // Unit Timer for 100Hz at 150 MHz SYSCLKOUT
#endif
#if (CPU_FRQ_100MHZ)
EQep1Regs.QUPRD=1000000; // Unit Timer for 100Hz at 100 MHz SYSCLKOUT
#endif
EQep1Regs.QDECCTL.bit.QSRC=00; // QEP quadrature count mode
EQep1Regs.QEPCTL.bit.FREE_SOFT=2;
//
// PCRM=00 mode - QPOSCNT reset on index event
//
EQep1Regs.QEPCTL.bit.PCRM=00;
EQep1Regs.QEPCTL.bit.UTE=1; // Unit Timeout Enable
EQep1Regs.QEPCTL.bit.QCLM=1; // Latch on unit time out
EQep1Regs.QPOSMAX=0xffffffff;
EQep1Regs.QEPCTL.bit.QPEN=1; // QEP enable
EQep1Regs.QCAPCTL.bit.UPPS=5; // 1/32 for unit position
EQep1Regs.QCAPCTL.bit.CCPS=7; // 1/128 for CAP clock
EQep1Regs.QCAPCTL.bit.CEN=1; // QEP Capture Enable
}
//
// POSSPEED_Calc -
//
void
POSSPEED_Calc(POSSPEED *p)
{
long tmp;
unsigned int pos16bval,temp1;
_iq Tmp1,newp,oldp;
//
// Position calculation - mechanical and electrical motor angle
//
//
// Motor direction: 0=CCW/reverse, 1=CW/forward
//
p->DirectionQep = EQep1Regs.QEPSTS.bit.QDF;
//
// capture position once per QA/QB period
//
pos16bval=(unsigned int)EQep1Regs.QPOSCNT;
//
// raw theta = current pos. + ang. offset from QA
//
p->theta_raw = pos16bval+ p->cal_angle;
//
// 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;
//
// Check an index occurrence
//
if (EQep1Regs.QFLG.bit.IEL == 1)
{
p->index_sync_flag = 0x00F0;
EQep1Regs.QCLR.bit.IEL=1; // Clear interrupt flag
}
//
// High Speed Calculation using QEP Position counter
// Check unit Time out-event for speed calculation:
// Unit Timer is configured for 100Hz in INIT function
//
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
}
//
// Low-speed computation using QEP capture counter
//
if(EQep1Regs.QEPSTS.bit.UPEVNT==1) // Unit position event
{
if(EQep1Regs.QEPSTS.bit.COEF==0) // No Capture overflow
{
temp1=(unsigned long)EQep1Regs.QCPRDLAT; // temp1 = t2-t1
}
else // Capture overflow, saturate the result
{
temp1=0xFFFF;
}
//
// p->Speed_pr = p->SpeedScaler/temp1
//
p->Speed_pr = _IQdiv(p->SpeedScaler,temp1);
Tmp1=p->Speed_pr;
if (Tmp1>_IQ(1))
{
p->Speed_pr = _IQ(1);
}
else
{
p->Speed_pr = Tmp1;
}
//
// Convert p->Speed_pr to RPM
//
if (p->DirectionQep==0) // Reverse direction = negative
{
//
// Q0 = Q0*GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
//
p->SpeedRpm_pr = -_IQmpy(p->BaseRpm,p->Speed_pr);
}
else // Forward direction = positive
{
//
// Q0 = Q0*GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
//
p->SpeedRpm_pr = _IQmpy(p->BaseRpm,p->Speed_pr);
}
EQep1Regs.QEPSTS.all=0x88; // Clear Unit position event flag
// Clear overflow error flag
}
}