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.

关于Example_posspeed.c的一些思考

//###########################################################################
//
// FILE:    Example_posspeed.c
//
// TITLE:    Pos/speed measurement using EQEP peripheral
//
// DESCRIPTION:
//
// This file includes the EQEP initialization and position and speed calcuation
// functions called by Example_2833xEqep_posspeed.c.  The position and
// speed calculation steps performed by POSSPEED_Calc() at  SYSCLKOUT = 150 MHz
// and 100 MHz are described in detail below:
//
// For 150 MHz Operation:
// ----------------------
//
// 1. This program calculates: **theta_mech**
//    
//    theta_mech = QPOSCNT/mech_Scaler = QPOSCNT/4000, where 4000 is the number of
//                 counts in 1 revolution.(4000/4 = 1000 line/rev. quadrature encoder)
//
// 2. This program calculates: **theta_elec**
//
//    theta_elec = (# pole pairs) * theta_mech = 2*QPOSCNT/4000 for this example
//
// 3. This program calculates: **SpeedRpm_fr**
//
//    SpeedRpm_fr = [(x2-x1)/4000]/T                                             - Equation 1
//    Note (x2-x1) = difference in number of QPOSCNT counts. Dividing (x2-x1) by
//    4000 gives position relative to Index in one revolution.
// If base RPM  = 6000 rpm:   6000 rpm = [(x2-x1)/4000]/10ms                     - Equation 2
//                                     = [(x2-x1)/4000]/(.01s*1 min/60 sec)
//                                     = [(x2-x1)/4000]/(1/6000) min
//                         max (x2-x1) = 4000 counts, or 1 revolution in 10 ms
//
//
// If both sides of Equation 2 are divided by 6000 rpm, then:
//                            1 = [(x2-x1)/4000] rev./[(1/6000) min * 6000rpm]
//                            Because (x2-x1) must be <4000 (max) for QPOSCNT increment,
//                            (x2-x1)/4000 < 1 for CW rotation
//                          And because (x2-x1) must be >-4000 for QPOSCNT decrement,
//                          (x2-x1)/4000>-1  for CCW rotation
//                            speed_fr = [(x2-x1)/4000]/[(1/6000) min * 6000rpm]
//                                   = (x2-x1)/4000                              - Equation 3
//
// To convert speed_fr to RPM, multiply Equation 3 by 6000 rpm
//                           SpeedRpm_fr = 6000rpm *(x2-x1)/4000                 - Final Equation
//                                                  
//
// 2. **min rpm ** = selected at 10 rpm based on CCPS prescaler options available (128 is greatest)
//
// 3. **SpeedRpm_pr**
//    SpeedRpm_pr = X/(t2-t1)                                                    - Equation 4
//    where X = QCAPCTL [UPPS]/4000 rev. (position relative to Index in 1 revolution)
// If max/base speed = 6000 rpm:  6000 = (32/4000)/[(t2-t1)/(150MHz/128)]  
//    where 32 = QCAPCTL [UPPS] (Unit timeout - once every 32 edges)
//          32/4000 = position in 1 revolution (position as a fraction of 1 revolution)
//          t2-t1/(150MHz/128),  t2-t1= # of QCAPCLK cycles, and
//                          1 QCAPCLK cycle = 1/(150MHz/128)
//                                          = QCPRDLAT
//
//                So: 6000 rpm = [32(150MHz/128)*60s/min]/[4000(t2-t1)]
//                     t2-t1 = [32(150MHz/128)*60 s/min]/(4000*6000rpm)           - Equation 5
//                           = 94 CAPCLK cycles = maximum (t2-t1) = SpeedScaler
//
// Divide both sides by (t2-t1), and:
//                   1 = 94/(t2-t1) = [32(150MHz/128)*60 s/min]/(4000*6000rpm)]/(t2-t1)
//                     Because (t2-t1) must be < 94 for QPOSCNT increment:
//                     94/(t2-t1) < 1 for CW rotation
//                   And because (t2-t1) must be >-94 for QPOSCNT decrement:
//                     94/(t2-t1)> -1 for CCW rotation
//
//                     speed_pr = 94/(t2-t1)
//                      or [32(150MHz/128)*60 s/min]/(4000*6000rpm)]/(t2-t1)  - Equation 6
//
// To convert speed_pr to RPM:
// Multiply Equation 6 by 6000rpm:
//                  SpeedRpm_fr  = 6000rpm * [32(150MHz/128)*60 s/min]/[4000*6000rpm*(t2-t1)]
//                                            = [32(150MHz/128)*60 s/min]/[4000*(t2-t1)]
//                                        or [(32/4000)rev * 60 s/min]/[(t2-t1)(QCPRDLAT)]- Final Equation
//
//
// For 100 MHz Operation:
// ----------------------
//
// The same calculations as above are performed, but with 100 MHz
// instead of 150MHz when calculating SpeedRpm_pr.
// The value for freqScaler_pr becomes: [32*(100MHz/128)*60s/min]/(4000*6000rpm) = 63
// More detailed calculation results can be found in the Example_freqcal.xls
// spreadsheet included in the example folder.
//
//
//
// This file contains source for the posspeed module
//
//###########################################################################
// Original Author: SD
//
// $TI Release: 2833x/2823x Header Files and Peripheral Examples V133 $
// $Release Date: June 8, 2012 $
//###########################################################################

#include "DSP28x_Project.h"     // Device Headerfile and Examples Include File
#include "Example_posspeed.h"   // Example specific Include file

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;
    EQep1Regs.QEPCTL.bit.PCRM=00;        // 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=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
}

void POSSPEED_Calc(POSSPEED *p)
{
    long tmp;
  unsigned int pos16bval,temp1;
  _iq Tmp1,newp,oldp;
    //**** Position calculation - mechanical and electrical motor angle  ****//     
  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;                      

// 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 Calcultation 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 = _IQdiv(p->SpeedScaler,temp1);    // p->Speed_pr = 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
            p->SpeedRpm_pr = -_IQmpy(p->BaseRpm,p->Speed_pr);     // Q0 = Q0*GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
        else                                                    // Forward direction = positive
            p->SpeedRpm_pr = _IQmpy(p->BaseRpm,p->Speed_pr);     // Q0 = Q0*GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
        EQep1Regs.QEPSTS.all=0x88;                    // Clear Unit position event flag    
                                                                  // Clear overflow error flag
    }
}

关于Example_posspeed.c的理解
在高速下每10ms进入if(EQep1Regs.QFLG.bit.UTO==1)这个case计算转速。
此时由于转速很高,故QCLK 32分频之后的时间远小于10ms,所以UPEVNT即Unit positon event的发生几率
很高,会频繁进入if(EQep1Regs.QEPSTS.bit.UPEVNT==1)这个case即低速模式计算,转速越高进入次数越多。
虽然频繁进入,但是只有当Unit time out即10ms单位时间到了以后才会更新QCPRDLAT的值。
利用更新后的QCPRDLAT同样会计算转速,将高速模式计算出的值覆盖。
所以高速模式下的工作过程是会同时在高速模式和低速模式下计算转速,会多次进入低速模式,但是实际起
作用的还是高速模式,低速模式计算出的值覆盖高速模式的数值,高速模式主导;

在低速下,32分频后的QCLK时间很长,所以会频繁进入高速模式计算,因为Unit time out 是10ms,在UTO的触发下,
会频繁更新QCPRDLAT的值,实现在低速时的转速计算,即频繁进入高速模式触发QCPRDLAT的更新,此时高速模式处于辅助
地位,低速模式主导。

疑问
开头的注释中有以下内容
“ 2. **min rpm ** = selected at 10 rpm based on CCPS prescaler options available (128 is greatest) ”
觉得最小转速不是10 rpm
由以下式子计算
                    1 X 32
————————————                                         128         
Speed(rpm)                                 ==         ————————————  X QCPRDLAT       
——————    X        4000                                  150000000
   60
当QCPRDLAT取最大值0xFFFF时,可以计算出Speed = 8.58rpm,约为9rpm,不知道我的这个计算对不对。

再有就是因为UTO = 10ms , ccps设置为32分频,所以高速模式和低速模式应该有个切换点
计算如下:
32 X P = 10ms,其中P是正交编码脉冲A序列或B序列的四分之一周期,
因此可以计算出A或B序列的频率是800Hz,对应的转速为48rpm,这个是否正确请大家帮我看下,谢谢!

  • 不好意思 有个地方看错了

    高速时对应的转速存储到SpeedRpm_fr

    低速时对应的转速存储到SpeedRpm_pr

    是应该以上述计算中的转速点48rmp作为分界点吗?

  •   不错的帖子。很好。

  • 应该是UPPS32分频,意思是说32个位置计数脉冲产生一个UPEVENT事件,此时捕获计数器的值锁存到周期寄存器中,同时捕获计数器清零,此时完成了T法测速流程,即单位位移量除以单位位移量所用的时间。

  • 这个程序在低速时需要改变配置吗?我修改了pwm的频率依旧看不到低速时的测量结果

  • 不需要 

    但是用这个C文件需要需要配置一下 有个Example_posspeed.xls文件,如下图

    比如:

    我的主频是120MHz,编码器是2048线,4倍频以后是8192,我用的异步机是2极电机

    你是不是配置的不对呢,按照表格里计算的结果要修改一些寄存器的设定

  • 您好,刚才看了您的程序,请问最大速度值BaseRpm是怎么计算的?谢谢您

  • 你好 明天我抽时间给你看下啊 这个帖子发的比较早 有些记不得了 不好意思

  • 嗯嗯,谢谢啦。我在做永磁同步电机矢量控制的研究,有一些问题搞不定,麻烦您了。

  • 你好 我看了下 BaseRPM不是算出来的 是自己设定的

    你有Example_posspeed.xls这个文件吗,如下图所示:

    我的平台最先开发的鼠笼机算法,鼠笼机是2对极,所以同步磁场旋转转速最大是1500RPM,后续开发的无刷直流、有刷直流、永磁同步都控制在了最大转速1500,所以我的BaseRPM设置为1500

  • 谢谢您的回复,就是60*f/p算出的吧。那这个与Q格式是怎么对应起来的,我在这个地方比较晕。麻烦您了

  • 对 就是这个公式算的 书本上有

    关于Q格式,建议你在官网搜索一些资料来了解下

    不要把这个想的很难,就是数据处理方式

    北航出的《电动机的DSP控制:TI公司》一书中也有讲解,可以看看,网上有该书的PDF版本

  • 谢谢您,我找一下电子版看一下,非常感谢。

  • 近期调试过程中发现 BaseRpm(Maximum Required Speed (RPM))设置成1500不合理

    闭环系统调试过程中会有超调现象,当转速大于1500rpm时,Example_posspeed计算的转速值不正确,从而造成闭环控制出现问题

    因此在设定BaseRpm时,应注意考虑系统超调问题,这个大家使用的时候注意下。

  • 我按照这个配置但是还是测不出速度,您能帮我看一下程序吗?是我的程序配置的不对吗?

    //###########################################################################
    
    
    /*-----------------------------------------------------------------------------
    Default initializer for the POSSPEED Object.!! mech_scaler = 16384 (Q26格式) 1024线程
    -----------------------------------------------------------------------------*/
      #define POSSPEED_DEFAULTS {0x0, 0x0,0x0,0x0,0x0,16384,2,0,0x0,\
            220,0,3000,0,\
            0,0,0,\
            (void (*)(long))POSSPEED_Init,\
            (void (*)(long))POSSPEED_Calc }
    
    
    
    
    void  POSSPEED_Init(void)
    {
    
        EQep1Regs.QUPRD=1800000;         // Unit Timer for 50Hz at 90 MHz SYSCLKOUT
    
        EQep1Regs.QDECCTL.bit.QSRC=00;      // QEP quadrature count mode
    
        EQep1Regs.QEPCTL.bit.FREE_SOFT=2;
        EQep1Regs.QEPCTL.bit.PCRM=00;       // 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=0xffffffff;
        EQep1Regs.QEPCTL.bit.QPEN=1;        // QEP enable
    
        EQep1Regs.QCAPCTL.bit.UPPS=5;       // 1/32 for unit position
        EQep1Regs.QCAPCTL.bit.CCPS=6;       // 1/64 for CAP clock
        EQep1Regs.QCAPCTL.bit.CEN=1;        // QEP Capture Enable
    
    //    EQep1Regs.QDECCTL.all = QDECCTL_INIT_STATE;
    //    EQep1Regs.QEPCTL.all = QEPCTL_INIT_STATE;
    //    EQep1Regs.QPOSCTL.all = QPOSCTL_INIT_STATE;
    //    EQep1Regs.QUPRD = 900000;	/* Unit Timer for 100Hz = sysclock/900000*/
    //    EQep1Regs.QCAPCTL.all = QCAPCTL_INIT_STATE;
    //    EQep1Regs.QPOSMAX = 4096;                //4*LineEncoder 1024线程
    
    //    EALLOW;                       /* Enable EALLOW*/
    //    GpioCtrlRegs.GPAMUX2.bit.GPIO20 = 1;  /* GPIO20 is EQEP1A */
    //    GpioCtrlRegs.GPAMUX2.bit.GPIO21 = 1;  /* GPIO21 is EQEP1B */
    //    GpioCtrlRegs.GPAMUX2.bit.GPIO23 = 1;  /* GPIO23 is EQEP1I  */
    //    EDIS;                         /* Disable EALLOW*/
    
    }
    
    void POSSPEED_Calc(POSSPEED *p)
    {
         long tmp;
         unsigned int pos16bval,temp1;
    //     unsigned int pos16bval;
         _iq Tmp1,newp,oldp;
    
    //**** Position calculation - mechanical and electrical motor angle  ****//
         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;
    
    // 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 Calcultation 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 = _IQdiv(p->SpeedScaler,temp1);    // p->Speed_pr = 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
    //            p->SpeedRpm_pr = -_IQmpy(p->BaseRpm,p->Speed_pr);   // Q0 = Q0*GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
    //        else                                                    // Forward direction = positive
    //            p->SpeedRpm_pr = _IQmpy(p->BaseRpm,p->Speed_pr);    // Q0 = Q0*GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
    //
    //
    //        EQep1Regs.QEPSTS.all=0x88;                  // Clear Unit position event flag
    //                                                    // Clear overflow error flag
    //    }
    
    }