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.

TMS320F28069: DRV8301-HC-Kit - problem with encoder in lab 12

Part Number: TMS320F28069

Hello,

We are facing some issues with lab 12 ( encoder).

1- At start up, the motor starts some back and forth movements for few seconds and then starts moving normally. How can we fix this issue? RS calib and force_angle are both enabled.

2- Sometimes when we start spinning using CCS, motor spins in the reverse direction. why is that happening?

3- Also we have noticed that sometimes the motor does not respond to the speed commands or it takes a long time to change the speed after setting commands.

4- there is a huge overshoot when we change the speed. Does this have to do with PID coefficient? We have already tuned the bandwidth.

We have used the appropriate labs to detect motor parameters and configure the encoder. Any suggestions/comments on these issues will be highly appreciated.

  • The noise on encoder feedback can make this kind of issue if the bandwidth has turned well with a proper system inertia and friction.
    The input qualification feature is already enabled for the encoder GPIOs. But some user cases, It might not be enough for noise filtering without additional signal filtering like RC passive filter and ferrite core on encoder cable.

    So you need to check encoder feedback signal first by oscilloscope while motor is running.
    You can also adjust sampling window by using GPIO_setQualificationPeriod() function in hal.c

    This issue is not resolved with my comment, please attach the oscilloscope waveform of encoder feedback.
  • The one more thing you need to check is an align current. In this lab, rotor is aligned to zero angle position before start up by using Rs recalculation function. But if rotor does not move to zero position due to the high load torque, PMSM motors do not work properly.
    Please check if the Rs recalculation current(USER_MOTOR_RES_EST_CURRENT) is enough for moving rotor to electrically zero angle position.

  • Hi Steve,

    Thank you for your input. Please see the waveforms for the encoder which seems to be clean and also the phase current at max speed. also increasing USER_MOTOR_RES_EST_CURRENT didn't help. 

    Let me just add that we have one more issue which is the maximum speed which is limited to about 400 rpm where and does not go beyond that number. Actually we have tested this motor before with sensorless and we were able to go to 700 rpm. ( the rated speed of our motor is 800 rpm, voltage: 48VDC, bldc  motor, 8 pair poles). What's happening at high speed is that the motor phase current goes to high (40 arms)and motor gets  too hot  and stops working. The input current from power supply at max speed is about 12A at 48 VDC which is really high for the little  load that we have which makes think that we are injecting some unnecessary id  due to wrong offset of encoders maybe? note that enable force angle and enableoffsetcalc were both enabled in our testing.  Also we tried to increase the input DC voltage to see if it makes change but it didn't help. Any idea why our motor speeds is limited? we are trying to find out why the motor is drawing too much power! We areally appreciate your URGENT help on this.

  • I think the encoder setting "USER_MOTOR_ENCODER_LINES" in user.h has some issue. You need to carefully check the encoder resolution spec. For example, the resolution in the encoder below is the value of post-quadrature. It means that the resolution(counts/rev) should be divide by 4 for USER_MOTOR_ENCODER_LINES because this lab use eQEP feature with quadrature enabled.

    Could you share your encoder specification?

    What the value of USER_MOTOR_ENCODER_LINES ?

  • Hi Steve, Thanks again for your support and helping us on this issue which is highly appreciated. We agree that there might be an issue with the encoder settings but not with the USER_MOTOR_ENCODER_LINES. The motor encoder CPR is 2500. So are you saying that we should try 2500/4 =625  ? 

    What about encoder offset? How do we make sure the encoder offset is correct?

    And what does this number stand for ? #define ENC_ZERO_OFFSET 3813071.0     

    Thanks

  • if there is no any comment like "post-quad" on encoder , USER_MOTOR_ENCODER_LINES is 2500.
    The define "ENC_ZERO_OFFSET" is not used as a constant in code. Instead, the enc zero offset is calculated by forcing align to electrical zero posing through Rs recalculation function such as the code in mainISR() below.

    // if we are forcing alignment, using the Rs Recalculation, align the eQEP angle with the rotor angle
    if((EST_getState(obj->estHandle) == EST_State_Rs) && (USER_MOTOR_TYPE == MOTOR_Type_Pm))
    {
    ENC_setZeroOffset(encHandle, (uint32_t)(HAL_getQepPosnMaximum(halHandle) - HAL_getQepPosnCounts(halHandle)));
    gEnc_offset = HAL_getQepPosnCounts(halHandle);
    }

    I’m still doubt if encoder feedback does not have noise.
    Could you check an encoder feedback again by running open-loop angle generation? You can add some data logging module, and can check encode count value or electrical angle while running motor with constant frequency and constant current with open-loop angle generation.

    Did you check the encoder count direction by any chance?

  • 1- which lab should we use for open loop generation?

    2- can you elaborate on how to check encoder counts?

    3- How do we ensure that the offset is correct for the encoder?

    Thanks again for your urgent support.

  • Also does lab12b uses the index signal of the encoder?
  • A1 A2) . the attached file is for open loop test, and has data logging module for graph. please find the following test procedure.

     step1. replace the origital proj_lab12b.c file with the attached file (proj_lab_12b_1.c).

     step2. add "sw\modules\angle_gen\src\32\bangle_gen.c" file into the project 

     step3. compile & downloading & run

     step4. gMotorVars.Flag_enableSys = 1 

     step5. gMotorVars.Flag_Run_Identify = 1

     step6. Set target speed (gMotorVars.SpeedRef_krpm)

    if gMotorVars.EstState becomes EST_State_Online, set the target speed with "gMotorVars.SpeedRef_krpm". 10~30% of rated speed is enough for this encoder test. You can increase Id current with gMotorVars.IdRef_A if you need more current for more high speed. The default value of Id reference current is same with Rs estimation current.

     step6. Data logging enable (Graph_Flag_Enable_update = 1) once rotor reach to the target speed.

     step7. Check encoder data by using CCS graph tool. Graph setting is as following.

    The sample of test result should be like this. Electrical angle from encoder have to be positive direction and same frequency with phase current. 

    A3) Because an incremental encoder is just used in this lab, the offset is automatically calculated through forced align to zero angle. Generally, if encoder angle is not aligned with rotor angle, more current will need for torque output. 

     A4) Index signal is not used in this lab.

     

    /* --COPYRIGHT--,BSD
     * Copyright (c) 2012, LineStream Technologies Incorporated
     * Copyright (c) 2012, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
    * *  Neither the names of Texas Instruments Incorporated, LineStream
     *    Technologies Incorporated, nor the names of its contributors may be
     *    used to endorse or promote products derived from this software without
     *    specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * --/COPYRIGHT--*/
    //! \file   solutions/instaspin_motion/src/proj_lab12b.c
    //! \brief  SpinTAC Velocity Controller using a quadrature encoder for feedback
    //!
    //! (C) Copyright 2012, LineStream Technologies, Inc.
    //! (C) Copyright 2011, Texas Instruments, Inc.
    
    //! \defgroup PROJ_LAB12b PROJ_LAB12b
    //@{
    
    //! \defgroup PROJ_LAB12b_OVERVIEW Project Overview
    //!
    //! SpinTAC Velocity Controller using a quadrature encoder for feedback
    //!
    
    // **************************************************************************
    // the includes
    
    // system includes
    #include <math.h>
    #include "main.h"
    
    #ifdef FLASH
    #pragma CODE_SECTION(mainISR,"ramfuncs");
    #endif
    
    // Include header files used in the main function
    
    
    // **************************************************************************
    // the defines
    
    #define LED_BLINK_FREQ_Hz   5
    
    #define _GRAPH_
    // **************************************************************************
    // the globals
    
    uint_least16_t gCounter_updateGlobals = 0;
    
    bool Flag_Latch_softwareUpdate = true;
    
    CTRL_Handle ctrlHandle;
    
    HAL_Handle halHandle;
    
    USER_Params gUserParams;
    
    HAL_PwmData_t gPwmData = {_IQ(0.0), _IQ(0.0), _IQ(0.0)};
    
    HAL_AdcData_t gAdcData;
    
    _iq gMaxCurrentSlope = _IQ(0.0);
    
    #ifdef FAST_ROM_V1p6
    CTRL_Obj *controller_obj;
    #else
    CTRL_Obj ctrl;				//v1p7 format
    #endif
    
    ENC_Handle encHandle;
    ENC_Obj enc;
    
    SLIP_Handle slipHandle;
    SLIP_Obj slip;
    
    ST_Obj st_obj;
    ST_Handle stHandle;
    
    uint16_t gLEDcnt = 0;
    
    volatile MOTOR_Vars_t gMotorVars = MOTOR_Vars_INIT;
    
    #ifdef FLASH
    // Used for running BackGround in flash, and ISR in RAM
    extern uint16_t *RamfuncsLoadStart, *RamfuncsLoadEnd, *RamfuncsRunStart;
    #endif
    
    
    #ifdef DRV8301_SPI
    // Watch window interface to the 8301 SPI
    DRV_SPI_8301_Vars_t gDrvSpi8301Vars;
    #endif
    #ifdef DRV8305_SPI
    // Watch window interface to the 8305 SPI
    DRV_SPI_8305_Vars_t gDrvSpi8305Vars;
    #endif
    
    _iq gFlux_pu_to_Wb_sf;
    
    _iq gFlux_pu_to_VpHz_sf;
    
    _iq gTorque_Ls_Id_Iq_pu_to_Nm_sf;
    
    _iq gTorque_Flux_Iq_pu_to_Nm_sf;
    
    uint32_t gEnc_offset = 0;
    
    #ifdef _GRAPH_
    // Graphing Variables
    volatile bool Graph_Flag_Enable_update = false;
    volatile unsigned int Graph_Counter = 0;
    volatile unsigned short Graph_Tick_Counter = 0;
    volatile unsigned short Graph_Tick_Counter_Value = 5;
    #define GRAPH_SIZE 500
    volatile _iq Graph_Data1[GRAPH_SIZE];
    volatile _iq Graph_Data2[GRAPH_SIZE];
    volatile _iq SpeedRef_krpm_previous = _IQ(0.0);
    #endif
    
    // define Angle Generate
    ANGLE_GEN_Handle angle_genHandle;
    ANGLE_GEN_Obj    angle_gen;
    
    bool gFlag_IF_OpenLoop = false;
    
    // **************************************************************************
    // the functions
    
    void main(void)
    {
      uint_least8_t estNumber = 0;
    
    #ifdef FAST_ROM_V1p6
      uint_least8_t ctrlNumber = 0;
    #endif
    
      // Only used if running from FLASH
      // Note that the variable FLASH is defined by the project
      #ifdef FLASH
      // Copy time critical code and Flash setup code to RAM
      // The RamfuncsLoadStart, RamfuncsLoadEnd, and RamfuncsRunStart
      // symbols are created by the linker. Refer to the linker files.
      memCopy((uint16_t *)&RamfuncsLoadStart,(uint16_t *)&RamfuncsLoadEnd,(uint16_t *)&RamfuncsRunStart);
      #endif
    
      // initialize the hardware abstraction layer
      halHandle = HAL_init(&hal,sizeof(hal));
    
    
      // check for errors in user parameters
      USER_checkForErrors(&gUserParams);
    
    
      // store user parameter error in global variable
      gMotorVars.UserErrorCode = USER_getErrorCode(&gUserParams);
    
    
      // do not allow code execution if there is a user parameter error
      if(gMotorVars.UserErrorCode != USER_ErrorCode_NoError)
        {
          for(;;)
            {
              gMotorVars.Flag_enableSys = false;
            }
        }
    
    
      // initialize the user parameters
      USER_setParams(&gUserParams);
    
    
      // set the hardware abstraction layer parameters
      HAL_setParams(halHandle,&gUserParams);
    
    
      // initialize the controller
    #ifdef FAST_ROM_V1p6
      ctrlHandle = CTRL_initCtrl(ctrlNumber, estNumber);  		//v1p6 format (06xF and 06xM devices)
      controller_obj = (CTRL_Obj *)ctrlHandle;
    #else
      ctrlHandle = CTRL_initCtrl(estNumber,&ctrl,sizeof(ctrl));	//v1p7 format default
    #endif
    
    
      {
        CTRL_Version version;
    
        // get the version number
        CTRL_getVersion(ctrlHandle,&version);
    
        gMotorVars.CtrlVersion = version;
      }
    
    
      // set the default controller parameters
      CTRL_setParams(ctrlHandle,&gUserParams);
    
    
      // setup faults
      HAL_setupFaults(halHandle);
    
    
      // initialize the interrupt vector table
      HAL_initIntVectorTable(halHandle);
    
    
      // enable the ADC interrupts
      HAL_enableAdcInts(halHandle);
    
    
      // disable the PWM
      HAL_disablePwm(halHandle);
    
    
      // initialize the ENC module
      encHandle = ENC_init(&enc, sizeof(enc));
    
    
      // setup the ENC module
      ENC_setup(encHandle, 1, USER_MOTOR_NUM_POLE_PAIRS, USER_MOTOR_ENCODER_LINES, 0, USER_IQ_FULL_SCALE_FREQ_Hz, USER_ISR_FREQ_Hz, 8000.0);
    
    
      // initialize the SLIP module
      slipHandle = SLIP_init(&slip, sizeof(slip));
    
    
      // setup the SLIP module
      SLIP_setup(slipHandle, _IQ(gUserParams.ctrlPeriod_sec));
    
    
      // initialize the SpinTAC Components
      stHandle = ST_init(&st_obj, sizeof(st_obj));
      
      // initialize the angle generate module
      angle_genHandle = ANGLE_GEN_init(&angle_gen,sizeof(angle_gen));
      ANGLE_GEN_setParams(angle_genHandle, gUserParams.iqFullScaleFreq_Hz, gUserParams.ctrlPeriod_sec);
      
      // setup the SpinTAC Components
      ST_setupVelCtl(stHandle);
      ST_setupPosConv(stHandle);
    
    #ifdef DRV8301_SPI
      // turn on the DRV8301 if present
      HAL_enableDrv(halHandle);
      // initialize the DRV8301 interface
      HAL_setupDrvSpi(halHandle,&gDrvSpi8301Vars);
    #endif
    
    #ifdef DRV8305_SPI
      // turn on the DRV8305 if present
      HAL_enableDrv(halHandle);
      // initialize the DRV8305 interface
      HAL_setupDrvSpi(halHandle,&gDrvSpi8305Vars);
    #endif
    
      // enable DC bus compensation
      CTRL_setFlag_enableDcBusComp(ctrlHandle, true);
    
    
      // compute scaling factors for flux and torque calculations
      gFlux_pu_to_Wb_sf = USER_computeFlux_pu_to_Wb_sf();
      gFlux_pu_to_VpHz_sf = USER_computeFlux_pu_to_VpHz_sf();
      gTorque_Ls_Id_Iq_pu_to_Nm_sf = USER_computeTorque_Ls_Id_Iq_pu_to_Nm_sf();
      gTorque_Flux_Iq_pu_to_Nm_sf = USER_computeTorque_Flux_Iq_pu_to_Nm_sf();
    
      // init variables
      gMotorVars.SpeedRef_krpm = _IQ(0.0);
      gMotorVars.IdRef_A = _IQ(USER_MOTOR_RES_EST_CURRENT);
    
      // enable global interrupts
      HAL_enableGlobalInts(halHandle);
    
      // enable debug interrupts
      HAL_enableDebugInt(halHandle);
    
      for(;;)
      {
        // Waiting for enable system flag to be set
        while(!(gMotorVars.Flag_enableSys));
    
        // Dis-able the Library internal PI.  Iq has no reference now
        CTRL_setFlag_enableSpeedCtrl(ctrlHandle, false);
    
        // loop while the enable system flag is true
        while(gMotorVars.Flag_enableSys)
          {
            CTRL_Obj *obj = (CTRL_Obj *)ctrlHandle;
            ST_Obj *stObj = (ST_Obj *)stHandle;
    
            // increment counters
            gCounter_updateGlobals++;
    
            // enable/disable the use of motor parameters being loaded from user.h
            CTRL_setFlag_enableUserMotorParams(ctrlHandle,gMotorVars.Flag_enableUserParams);
    
            // enable/disable Rs recalibration during motor startup
            EST_setFlag_enableRsRecalc(obj->estHandle,gMotorVars.Flag_enableRsRecalc);
    
            // enable/disable automatic calculation of bias values
            CTRL_setFlag_enableOffset(ctrlHandle,gMotorVars.Flag_enableOffsetcalc);
    
    
            if(CTRL_isError(ctrlHandle))
              {
                // set the enable controller flag to false
                CTRL_setFlag_enableCtrl(ctrlHandle,false);
    
                // set the enable system flag to false
                gMotorVars.Flag_enableSys = false;
    
                // disable the PWM
                HAL_disablePwm(halHandle);
              }
            else
              {
                // update the controller state
                bool flag_ctrlStateChanged = CTRL_updateState(ctrlHandle);
    
                // enable or disable the control
                CTRL_setFlag_enableCtrl(ctrlHandle, gMotorVars.Flag_Run_Identify);
    
                if(flag_ctrlStateChanged)
                  {
                    CTRL_State_e ctrlState = CTRL_getState(ctrlHandle);
    
                    if(ctrlState == CTRL_State_OffLine)
                      {
                        // enable the PWM
                        HAL_enablePwm(halHandle);
                      }
                    else if(ctrlState == CTRL_State_OnLine)
                      {
                        if(gMotorVars.Flag_enableOffsetcalc == true)
                        {
                          // update the ADC bias values
                          HAL_updateAdcBias(halHandle);
                        }
                        else
                        {
                          // set the current bias
                          HAL_setBias(halHandle,HAL_SensorType_Current,0,_IQ(I_A_offset));
                          HAL_setBias(halHandle,HAL_SensorType_Current,1,_IQ(I_B_offset));
                          HAL_setBias(halHandle,HAL_SensorType_Current,2,_IQ(I_C_offset));
    
                          // set the voltage bias
                          HAL_setBias(halHandle,HAL_SensorType_Voltage,0,_IQ(V_A_offset));
                          HAL_setBias(halHandle,HAL_SensorType_Voltage,1,_IQ(V_B_offset));
                          HAL_setBias(halHandle,HAL_SensorType_Voltage,2,_IQ(V_C_offset));
                        }
    
                        // Return the bias value for currents
                        gMotorVars.I_bias.value[0] = HAL_getBias(halHandle,HAL_SensorType_Current,0);
                        gMotorVars.I_bias.value[1] = HAL_getBias(halHandle,HAL_SensorType_Current,1);
                        gMotorVars.I_bias.value[2] = HAL_getBias(halHandle,HAL_SensorType_Current,2);
    
                        // Return the bias value for voltages
                        gMotorVars.V_bias.value[0] = HAL_getBias(halHandle,HAL_SensorType_Voltage,0);
                        gMotorVars.V_bias.value[1] = HAL_getBias(halHandle,HAL_SensorType_Voltage,1);
                        gMotorVars.V_bias.value[2] = HAL_getBias(halHandle,HAL_SensorType_Voltage,2);
    
                        // enable the PWM
                        HAL_enablePwm(halHandle);
                      }
                    else if(ctrlState == CTRL_State_Idle)
                      {
                        // disable the PWM
                        HAL_disablePwm(halHandle);
                        gMotorVars.Flag_Run_Identify = false;
    
                        gFlag_IF_OpenLoop = false;
                        angle_gen.Angle_pu = _IQ(0.0);
                      }
    
                    if((CTRL_getFlag_enableUserMotorParams(ctrlHandle) == true) &&
                      (ctrlState > CTRL_State_Idle) &&
                      (gMotorVars.CtrlVersion.minor == 6))
                      {
                        // call this function to fix 1p6
                        USER_softwareUpdate1p6(ctrlHandle);
                      }
    
                  }
              }
    
    
            if(EST_isMotorIdentified(obj->estHandle))
              {
                // set the current ramp
                EST_setMaxCurrentSlope_pu(obj->estHandle,gMaxCurrentSlope);
                gMotorVars.Flag_MotorIdentified = true;
    
                // set the speed reference
                CTRL_setSpd_ref_krpm(ctrlHandle,gMotorVars.SpeedRef_krpm);
    
                // set the speed acceleration
                CTRL_setMaxAccel_pu(ctrlHandle,_IQmpy(MAX_ACCEL_KRPMPS_SF,gMotorVars.MaxAccel_krpmps));
    
                // enable the SpinTAC Speed Controller
                STVELCTL_setEnable(stObj->velCtlHandle, true);
    
                if(EST_getState(obj->estHandle) != EST_State_OnLine)
                {
                	// if the estimator is not running, place SpinTAC into reset
                	STVELCTL_setEnable(stObj->velCtlHandle, false);
                }
                else
                {
                    gFlag_IF_OpenLoop = true;
                }
    
                if(Flag_Latch_softwareUpdate)
                {
                  Flag_Latch_softwareUpdate = false;
    
                  USER_calcPIgains(ctrlHandle);
    			  
                  // initialize the watch window kp and ki current values with pre-calculated values
                  gMotorVars.Kp_Idq = CTRL_getKp(ctrlHandle,CTRL_Type_PID_Id);
                  gMotorVars.Ki_Idq = CTRL_getKi(ctrlHandle,CTRL_Type_PID_Id);
    
    			  // initialize the watch window Bw value with the default value
                  gMotorVars.SpinTAC.VelCtlBw_radps = STVELCTL_getBandwidth_radps(stObj->velCtlHandle);
    
                  // initialize the watch window with maximum and minimum Iq reference
                  gMotorVars.SpinTAC.VelCtlOutputMax_A = _IQmpy(STVELCTL_getOutputMaximum(stObj->velCtlHandle), _IQ(USER_IQ_FULL_SCALE_CURRENT_A));
                  gMotorVars.SpinTAC.VelCtlOutputMin_A = _IQmpy(STVELCTL_getOutputMinimum(stObj->velCtlHandle), _IQ(USER_IQ_FULL_SCALE_CURRENT_A));
                }
    
              }
            else
              {
                Flag_Latch_softwareUpdate = true;
    
                // the estimator sets the maximum current slope during identification
                gMaxCurrentSlope = EST_getMaxCurrentSlope_pu(obj->estHandle);
              }
    
    
            // when appropriate, update the global variables
            if(gCounter_updateGlobals >= NUM_MAIN_TICKS_FOR_GLOBAL_VARIABLE_UPDATE)
              {
                // reset the counter
                gCounter_updateGlobals = 0;
    
                updateGlobalVariables_motor(ctrlHandle, stHandle);
              }
    
    
            // update Kp and Ki gains
            updateKpKiGains(ctrlHandle);
    
            // set the SpinTAC (ST) bandwidth scale
            STVELCTL_setBandwidth_radps(stObj->velCtlHandle, gMotorVars.SpinTAC.VelCtlBw_radps);
    
            // set the maximum and minimum values for Iq reference
            STVELCTL_setOutputMaximums(stObj->velCtlHandle, _IQmpy(gMotorVars.SpinTAC.VelCtlOutputMax_A, _IQ(1.0/USER_IQ_FULL_SCALE_CURRENT_A)), _IQmpy(gMotorVars.SpinTAC.VelCtlOutputMin_A, _IQ(1.0/USER_IQ_FULL_SCALE_CURRENT_A)));
    
            // enable/disable the forced angle
            EST_setFlag_enableForceAngle(obj->estHandle,gMotorVars.Flag_enableForceAngle);
    
            // enable or disable power warp
            CTRL_setFlag_enablePowerWarp(ctrlHandle,gMotorVars.Flag_enablePowerWarp);
    
    #ifdef DRV8301_SPI
            HAL_writeDrvData(halHandle,&gDrvSpi8301Vars);
    
            HAL_readDrvData(halHandle,&gDrvSpi8301Vars);
    #endif
    #ifdef DRV8305_SPI
            HAL_writeDrvData(halHandle,&gDrvSpi8305Vars);
    
            HAL_readDrvData(halHandle,&gDrvSpi8305Vars);
    #endif
          } // end of while(gFlag_enableSys) loop
    
    
        // disable the PWM
        HAL_disablePwm(halHandle);
    
        // set the default controller parameters (Reset the control to re-identify the motor)
        CTRL_setParams(ctrlHandle,&gUserParams);
        gMotorVars.Flag_Run_Identify = false;
    	
        // setup the SpinTAC Components
        ST_setupVelCtl(stHandle);
        ST_setupPosConv(stHandle);
    
      } // end of for(;;) loop
    
    } // end of main() function
    
    
    interrupt void mainISR(void)
    {
    
      static uint16_t stCnt = 0;
      CTRL_Obj *obj = (CTRL_Obj *)ctrlHandle;
      _iq angle_pu = 0;
    
      // toggle status LED
      if(++gLEDcnt >= (uint_least32_t)(USER_ISR_FREQ_Hz / LED_BLINK_FREQ_Hz))
      {
        HAL_toggleLed(halHandle,(GPIO_Number_e)HAL_Gpio_LED2);
        gLEDcnt = 0;
      }
    
    
      // compute the electrical angle
      ENC_calcElecAngle(encHandle, HAL_getQepPosnCounts(halHandle));
    
    
      // acknowledge the ADC interrupt
      HAL_acqAdcInt(halHandle,ADC_IntNumber_1);
    
    
      // convert the ADC data
      HAL_readAdcData(halHandle,&gAdcData);
    
    
      // Run the SpinTAC Components
      if(stCnt++ >= ISR_TICKS_PER_SPINTAC_TICK) {
    	  ST_runPosConv(stHandle, encHandle, ctrlHandle);
    	  ST_runVelCtl(stHandle, ctrlHandle);
    	  stCnt = 1;
      }
    
    
      // open-loop angle generation
      if(gFlag_IF_OpenLoop)
      {
          _iq speed_ref_pu = TRAJ_getIntValue(controller_obj->trajHandle_spd);
    
          ANGLE_GEN_run(angle_genHandle, speed_ref_pu);
    
          // generate the motor electrical angle
          angle_pu = ANGLE_GEN_getAngle_pu(angle_genHandle);
      }
    
    
      if(USER_MOTOR_TYPE == MOTOR_Type_Induction)
      {
          // update the electrical angle for the SLIP module
          SLIP_setElectricalAngle(slipHandle, ENC_getElecAngle(encHandle));
          // compute the amount of slip
          SLIP_run(slipHandle);
    
          // run the controller
          CTRL_run(ctrlHandle,halHandle,&gAdcData,&gPwmData,SLIP_getMagneticAngle(slipHandle));
      }
      else
      {
          if(gFlag_IF_OpenLoop)
          {
              // run the controller with open-loop angle
              CTRL_run(ctrlHandle,halHandle,&gAdcData,&gPwmData,angle_pu);
    
          }
          else
          {
              // run the controller
              CTRL_run(ctrlHandle,halHandle,&gAdcData,&gPwmData,ENC_getElecAngle(encHandle));
          }
      }
    
      // write the PWM compare values
      HAL_writePwmData(halHandle,&gPwmData);
    
    
      // setup the controller
      CTRL_setup(ctrlHandle);
    
      // if we are forcing alignment, using the Rs Recalculation, align the eQEP angle with the rotor angle
      if((EST_getState(obj->estHandle) == EST_State_Rs) && (USER_MOTOR_TYPE == MOTOR_Type_Pm))
      {
    	  ENC_setZeroOffset(encHandle, (uint32_t)(HAL_getQepPosnMaximum(halHandle) - HAL_getQepPosnCounts(halHandle)));
    	  gEnc_offset = HAL_getQepPosnCounts(halHandle);
      }
    
    #ifdef _GRAPH_
      // store graph value
      if(Graph_Flag_Enable_update) {
          if(Graph_Counter >= GRAPH_SIZE) {
              Graph_Flag_Enable_update = false;
              Graph_Counter = 0;
          }
          else {
              if(Graph_Tick_Counter == 0) {
                  Graph_Data1[Graph_Counter] = gAdcData.I.value[0];  //Ia
                  Graph_Data2[Graph_Counter] = ENC_getElecAngle(encHandle); //elec_angle;
                  Graph_Counter++;
                  Graph_Tick_Counter = Graph_Tick_Counter_Value - 1;
              }
              else {
                  Graph_Tick_Counter--;
              }
          }
       }
    #endif
    
      return;
    } // end of mainISR() function
    
    
    void updateGlobalVariables_motor(CTRL_Handle handle, ST_Handle sthandle)
    {
      CTRL_Obj *obj = (CTRL_Obj *)handle;
      ST_Obj *stObj = (ST_Obj *)sthandle;
    
    
      // get the speed estimate
      gMotorVars.Speed_krpm = EST_getSpeed_krpm(obj->estHandle);
    
      // get the speed from eQEP
      gMotorVars.SpeedQEP_krpm = _IQmpy(STPOSCONV_getVelocityFiltered(stObj->posConvHandle), _IQ(ST_SPEED_KRPM_PER_PU));
    
      // get the real time speed reference coming out of the speed trajectory generator
      gMotorVars.SpeedTraj_krpm = _IQmpy(CTRL_getSpd_int_ref_pu(handle),EST_get_pu_to_krpm_sf(obj->estHandle));
    
      // get the torque estimate
      gMotorVars.Torque_Nm = USER_computeTorque_Nm(handle, gTorque_Flux_Iq_pu_to_Nm_sf, gTorque_Ls_Id_Iq_pu_to_Nm_sf);
    
      // get the magnetizing current
      gMotorVars.MagnCurr_A = EST_getIdRated(obj->estHandle);
    
      // get the rotor resistance
      gMotorVars.Rr_Ohm = EST_getRr_Ohm(obj->estHandle);
    
      // get the stator resistance
      gMotorVars.Rs_Ohm = EST_getRs_Ohm(obj->estHandle);
    
      // get the stator inductance in the direct coordinate direction
      gMotorVars.Lsd_H = EST_getLs_d_H(obj->estHandle);
    
      // get the stator inductance in the quadrature coordinate direction
      gMotorVars.Lsq_H = EST_getLs_q_H(obj->estHandle);
    
      // get the flux in V/Hz in floating point
      gMotorVars.Flux_VpHz = EST_getFlux_VpHz(obj->estHandle);
    
      // get the flux in Wb in fixed point
      gMotorVars.Flux_Wb = USER_computeFlux(handle, gFlux_pu_to_Wb_sf);
    
      // get the controller state
      gMotorVars.CtrlState = CTRL_getState(handle);
    
      // get the estimator state
      gMotorVars.EstState = EST_getState(obj->estHandle);
    
      // Get the DC buss voltage
      gMotorVars.VdcBus_kV = _IQmpy(gAdcData.dcBus,_IQ(USER_IQ_FULL_SCALE_VOLTAGE_V/1000.0));
    
      // get the Iq reference from the speed controller
      gMotorVars.IqRef_A = _IQmpy(STVELCTL_getTorqueReference(stObj->velCtlHandle), _IQ(USER_IQ_FULL_SCALE_CURRENT_A));
    
      // read Id and Iq vectors in amps
      gMotorVars.Id_A = _IQmpy(CTRL_getId_in_pu(ctrlHandle), _IQ(USER_IQ_FULL_SCALE_CURRENT_A));
      gMotorVars.Iq_A = _IQmpy(CTRL_getIq_in_pu(ctrlHandle), _IQ(USER_IQ_FULL_SCALE_CURRENT_A));
    
      // calculate vector Is in amps
      gMotorVars.Is_A = _IQsqrt(_IQmpy(gMotorVars.Id_A, gMotorVars.Id_A) + _IQmpy(gMotorVars.Iq_A, gMotorVars.Iq_A));
    
      // gets the Velocity Controller status
      gMotorVars.SpinTAC.VelCtlStatus = STVELCTL_getStatus(stObj->velCtlHandle);
    
      // get the inertia setting
      gMotorVars.SpinTAC.InertiaEstimate_Aperkrpm = _IQmpy(STVELCTL_getInertia(stObj->velCtlHandle), _IQ(ST_SPEED_PU_PER_KRPM * USER_IQ_FULL_SCALE_CURRENT_A));
    
      // get the friction setting
      gMotorVars.SpinTAC.FrictionEstimate_Aperkrpm = _IQmpy(STVELCTL_getFriction(stObj->velCtlHandle), _IQ(ST_SPEED_PU_PER_KRPM * USER_IQ_FULL_SCALE_CURRENT_A));
    
      // get the Velocity Controller error
      gMotorVars.SpinTAC.VelCtlErrorID = STVELCTL_getErrorID(stObj->velCtlHandle);
    
      // get the Position Converter error
      gMotorVars.SpinTAC.PosConvErrorID = STPOSCONV_getErrorID(stObj->posConvHandle);
    
      return;
    } // end of updateGlobalVariables_motor() function
    
    
    void updateKpKiGains(CTRL_Handle handle)
    {
      if((gMotorVars.CtrlState == CTRL_State_OnLine) && (gMotorVars.Flag_MotorIdentified == true) && (Flag_Latch_softwareUpdate == false))
        {
          // set the kp and ki speed values from the watch window
          CTRL_setKp(handle,CTRL_Type_PID_spd,gMotorVars.Kp_spd);
          CTRL_setKi(handle,CTRL_Type_PID_spd,gMotorVars.Ki_spd);
    
          // set the kp and ki current values for Id and Iq from the watch window
          CTRL_setKp(handle,CTRL_Type_PID_Id,gMotorVars.Kp_Idq);
          CTRL_setKi(handle,CTRL_Type_PID_Id,gMotorVars.Ki_Idq);
          CTRL_setKp(handle,CTRL_Type_PID_Iq,gMotorVars.Kp_Idq);
          CTRL_setKi(handle,CTRL_Type_PID_Iq,gMotorVars.Ki_Idq);
    	}
    
      return;
    } // end of updateKpKiGains() function
    
    
    void ST_runPosConv(ST_Handle handle, ENC_Handle encHandle, CTRL_Handle ctrlHandle)
    {
    	ST_Obj *stObj = (ST_Obj *)handle;
    
    	// get the electrical angle from the ENC module
        STPOSCONV_setElecAngle_erev(stObj->posConvHandle, ENC_getElecAngle(encHandle));
    
        if(USER_MOTOR_TYPE ==  MOTOR_Type_Induction) {
          // The CurrentVector feedback is only needed for ACIM
          // get the vector of the direct/quadrature current input vector values from CTRL
          STPOSCONV_setCurrentVector(stObj->posConvHandle, CTRL_getIdq_in_addr(ctrlHandle));
        }
    
    	// run the SpinTAC Position Converter
    	STPOSCONV_run(stObj->posConvHandle);
    
    	if(USER_MOTOR_TYPE ==  MOTOR_Type_Induction) {
    	  // The Slip Velocity is only needed for ACIM
    	  // update the slip velocity in electrical angle per second, Q24
    	  SLIP_setSlipVelocity(slipHandle, STPOSCONV_getSlipVelocity(stObj->posConvHandle));
    	}
    }
    
    
    void ST_runVelCtl(ST_Handle handle, CTRL_Handle ctrlHandle)
    {
        _iq speedFeedback, iqReference;
        ST_Obj *stObj = (ST_Obj *)handle;
        CTRL_Obj *ctrlObj = (CTRL_Obj *)ctrlHandle;
    
        // Get the mechanical speed in pu
        speedFeedback = STPOSCONV_getVelocityFiltered(stObj->posConvHandle);
    
    	// Run the SpinTAC Controller
    	// Note that the library internal ramp generator is used to set the speed reference
        STVELCTL_setVelocityReference(stObj->velCtlHandle, TRAJ_getIntValue(ctrlObj->trajHandle_spd));
    	STVELCTL_setAccelerationReference(stObj->velCtlHandle, _IQ(0.0));	// Internal ramp generator does not provide Acceleration Reference
    	STVELCTL_setVelocityFeedback(stObj->velCtlHandle, speedFeedback);
    	STVELCTL_run(stObj->velCtlHandle);
    
    	// select SpinTAC Velocity Controller
    	iqReference = STVELCTL_getTorqueReference(stObj->velCtlHandle);
    
    	// Set the Iq reference that came out of SpinTAC Velocity Control
    	CTRL_setIq_ref_pu(ctrlHandle, iqReference);
    
    	if(gFlag_IF_OpenLoop)
    	{
    	    CTRL_setIq_ref_pu(ctrlHandle, _IQ(0.0));
    	    CTRL_setId_ref_pu(ctrlHandle, _IQmpy(gMotorVars.IdRef_A, _IQ(1.0/USER_IQ_FULL_SCALE_CURRENT_A)));
    	}
    
    }
    
    
    //@} //defgroup
    // end of file
    

  • Hi Steve,

    The code is not working. The motor does not start spinning with this code. Also, Graph_Flag_Enable_update cannot be get updated using CCS and stays zero all the time. Any idea? Can we just add the data logging code to the encoder code and run this test instead of trying to run it in open loop using your code?
  • Also can you advise how to reverse the direction of the motor in lab12b ? We tried negative speed commands but after a couple of times, it killed the driver . (Not sure that was the reason for damaging driver tho)
  • I'm wondering why the open-loop test code is not working. If you are using DRV8301-69M-KIT without any modification, this code should work. Did you check the motor A-phase current with oscilloscope? If you’ve done setup properly your motor parameters in user.h, you can see an align current at start-up and rotating current while motor running.

     

    The reason I provided the test code is that I wanted to confirm two things: the encoder signal integrity, the direction of encoder angle. The encoder angle have to be same direction with rotating flux. For example, if rotor is running with positive torque, the direction of encoder angle have to be up-count like the waveform I attached on above answer.

     

    Can you attach your user.h file for checking your motor parameters and board settings?

  • I'm sorry to hear that your kit was damaged. I hope you have extra new one.

    I think your motor phase and encoder pulse direction are not matched. If so, you can resolve the issue by simply exchanging the encoder A/B connection each other or exchanging only the motor two phases connection(U<->V or U<->W or V<->W).

  • 4278.user.h

    User.h attached. Thanks for your help!

  • phase current waveform.docxI have also attached the Phase current waveform at max speed when it starts oscillating

  • Steve,

    I have also attached the motor datasheet for your reference. The maximum speed is 800rpm. As mentioned, we cannot go above almost 500 rpm. we don't have the motor characteristics to see what we should expect from the motor at high speed. Is this normal that for a rated 800 rpm motor, you can't get a higher rpm than 500 ?

  • The waveforms you requested (phase current and elec angle) are also attached

    encoder.docx

    FL80SV80-48V-800 (Rev. A), 10.9.2018.pdf

  • The encoder angle with phase current waveform looks good.
    There is something suspicious about motor parameters in user.h you shared.
    If the rated input voltage(40V) and max speed(800rpm) are correct, some motor parameters like inductance and flux should be more high. For example, BEMF voltage at max speed is about 2V.
    BEMF_V = RPM/60*PP*FLUX = 800/60*8*0.02 = 2.13V
    The max output voltage at 40V input is about 21V (=40V/sqrt(3)). Compare to 21V, the BEMF 2V at max speed is too small even if ignoring resistance and inductance voltage drop.

    Also the inductance is too small for low speed motor. Generally, low speed motors have relatively high value of inductance and flux than high speed motors.
    I think you need to check your motor parameters again with lab2c.

    I found out your motor datasheet show the motor parameters. When you run lab2c, the parameter results should be reasonably similar with the following values. If the motor identification lab does not work well, You can manually calculate them by using the values in motor datasheet. Please use the following motor parameters for testing.

    #define USER_MOTOR_Rs (0.35) //0.7 ohm line-line
    #define USER_MOTOR_Ls_d (0.00075) //1.5mH line-line
    #define USER_MOTOR_Ls_q (0.00075) //1.5mH line-line

    #define USER_MOTOR_RATED_FLUX (0.22654) //37Vrms/krpm (=37*1.414/sqrt(3)/(1000/60*8)). 37Vrms will be line-line voltage, not phase voltage for 40V rated input.
    #define USER_MOTOR_RES_EST_CURRENT (2.0) // About 20% of rated current is enough for general applications
    #define USER_MOTOR_MAX_CURRENT (10.0) //Rated Torque: 4N.m

    The rated toque of your motor is 4N.m. It mean that the Iq current will be about 10A for the rated torque if encoder interface does not have issue for a rotor angle calculation.
    Iq_A = RATED_TORQUE/(1.5*PP*FLUX(Wb)) = 4/(1.5*8*0.22654/(2*pi)) = 9.25A

    I hope my answer does help to resolve your issue.

    Thank,
    Steve
  • Hi Steve,

    We had tried the values that you suggested. In fact, we started with those values and the maximum rpm we get using those values is 360 rpm. increasing  #define USER_MOTOR_MAX_CURRENT from 10 to 20 helped to get up to 500 rpm. After that, increasing max current didn't help. Increasing input voltage didn't help either. Do you think we are hitting the maximum power this motor can handle? also do you think field weakening or over modulation might help?

  • Hi Eduardo,

    If the max speed was not changed when you increased input voltage, field weakening does not help for getting up max speed.
    The no load current in your motor should be very small (< 0.5A) according to the motor datasheet. I think rotor angle calculation with encoder feedback still does not work properly.
    I think It would be better to contact TI local FAE in your site if the issue was not resolved yet.