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.

Loading non compile-time constant motor parameters and various other questions

Other Parts Discussed in Thread: TMS320F28069

Hey all,

I'm currently struggling with implementing an insta-spin solution for the TMS320F28069 using the DRV8301-HC kit; I'm mostly having issues with the API for controlling system behavior.

My current setup is primarly based on the insta-spin labs (Lab 2a/3a), with changes to allow me to set the motor parameters (Rs, Ls, flux, etc) in run-time. The goal of this is to allow the finished device to perform an identification, saving parameters to nonvolatile memory. Afterwards the device uses the saved parameters for operation. An alternate use case is externally input parameters, such as over an UART interface. So far, what I have done is set all the motor parameters in user.h to 0/NULL, in other words;

  • I_A/B/C_offset = 0
  • V_A/B/C_offset = 0
  • USER_MOTOR_TYPE = NULL
  • USER_MOTOR_NUM_POLE_PAIRS = NULL
  • ... down to ...
  • USER_MOTOR_MAX_CURRENT = NULL
  • USER_MOTOR_FLUX_EST_FREQ_Hz = NULL
  • ... along with the dependant defines ...
  • USER_MAX_CURRENT_SLOPE_EPL <removed>
  • USER_MAX_CURRENT_SLOPE <removed>

I've written a function

void motorcontrol_load_motorparams(unsigned int polepairs, float i_max, float rs, float ls, float flux, float i_rs){
    CTRL_Obj *obj = (CTRL_Obj *)ctrlHandle;
    gUserParams.motor_type = MOTOR_Type_Pm;
    gUserParams.motor_numPolePairs = polepairs;
    gUserParams.motor_Rr = NULL;
    gUserParams.motor_Rs = rs;
    gUserParams.motor_Ls_d = ls;
    gUserParams.motor_Ls_q = ls;
    gUserParams.motor_ratedFlux = flux;
    gUserParams.maxCurrent = i_max;
    gUserParams.maxCurrent_resEst = i_rs;
    /* Set derived motor parameters based on input.
     * Normally these are calculated in a #define in user.h */
    gUserParams.maxCurrentSlope = (i_rs/USER_IQ_FULL_SCALE_CURRENT_A/USER_TRAJ_FREQ_Hz);
    gUserParams.maxCurrentSlope_epl = (0.3*i_rs/USER_IQ_FULL_SCALE_CURRENT_A/USER_TRAJ_FREQ_Hz);

    CTRL_setParams(ctrlHandle,&gUserParams);                // Seed gUserParams struct with new values
    CTRL_setFlag_enableUserMotorParams(ctrlHandle, true);    // Enable loading parameters from gUserParams struct
    CTRL_setFlag_enableOffset(ctrlHandle, true);            // Enable offset ...
    gMotorVars.Flag_enableOffsetcalc = true;
    EST_setFlag_enableRsRecalc(obj->estHandle, true);        // ... and Rs recalculation for a short calibration ...
    gMotorVars.Flag_Run_Identify = true;                    // ... which is run ...
    motorcontrol_set_en();                                    // ... now.
}

Which I intend to run on startup. From what I've understood of the seeding function USER_setParams(&gUserParams) this should fully define the motor parameters and calculate the relevant offset currents/voltages as well as the motor winding resistance Rs.

On startup I run the same initialization routines as done in the labs, ie;

USER_setParams(&gUserParams);
    DRV_setParams(drvHandle,&gUserParams);
#ifdef FAST_ROM_V1p6
    ctrlHandle = CTRL_initCtrl(CTRLNUM, ESTNUM);          //v1p6 format (06xF and 06xM devices)
    controller_obj = (CTRL_Obj *)ctrlHandle;
#else
    ctrlHandle = CTRL_initCtrl(ESTNUM,&ctrl,sizeof(ctrl));    //v1p7 format default
#endif
    CTRL_setParams(ctrlHandle,&gUserParams);
    DRV_setupFaults(drvHandle);
    DRV_initIntVectorTable(drvHandle);
    DRV_enableAdcInts(drvHandle);
    DRV_enableDebugInt(drvHandle);
    DRV_disablePwm(drvHandle);
    CTRL_setFlag_enableDcBusComp(ctrlHandle, true);

Followed a while later by my seeding function (for now with compile-time constants);

motorcontrol_load_motorparams(7, 25, 9.8e-9, 19.8e-6, 0.01608, 5);

When I run my own function and I view the real-time watch window in CCS I can see that the estimation of Rs tends towards very small values (around 3e-8, IE. tiny) on startup.  If I try to run the motor using a dummy motor_run function along the lines of;

CTRL_setFlag_enableOffset(ctrlHandle, true);
CTRL_setFlag_enableSpeedCtrl(ctrlHandle, true);
gMotorVars.Flag_enableSys = true;
DRV_enablePwm(drvHandle);
CTRL_setFlag_enableCtrl(ctrlHandle, true);

The motor only moves a few degrees in each direction and the estimated motor speeds (in the watch window) jumps all over the place. Question 1 Does anyone have any pointers as to what to look for or what to try next?


I've written an identification function, for identifying an arbitrary motor which seems to work completely;

void motorcontrol_start_identify(unsigned int polepairs, unsigned int roverl_f, float i_max, float i_rs, float i_ls, float vph_f){
    if(mc_init == 0 || motorcontrol_get_error()){
        gMotorVars.Flag_Run_Identify = false;
        return;
    }
    motorcontrol_set_dis();
    // Set motor parameters to the input
    gUserParams.motor_type = MOTOR_Type_Pm;
    gUserParams.motor_numPolePairs = polepairs;
    gUserParams.RoverL_estFreq_Hz = roverl_f;
    gUserParams.maxCurrent_resEst = i_rs;
    gUserParams.maxCurrent_indEst = i_ls;
    gUserParams.maxCurrent = i_max;
    gUserParams.fluxEstFreq_Hz = vph_f;
    /* Set derived motor parameters based on input.
     * Normally these are calculated in a #define in user.h */
    gUserParams.maxCurrentSlope = (i_rs/USER_IQ_FULL_SCALE_CURRENT_A/USER_TRAJ_FREQ_Hz);
    gUserParams.maxCurrentSlope_epl = (0.3*i_rs/USER_IQ_FULL_SCALE_CURRENT_A/USER_TRAJ_FREQ_Hz);
    gUserParams.estWaitTime[EST_State_RampUp] = (uint_least32_t)((5.0 + vph_f / USER_MAX_ACCEL_EST_Hzps) * USER_EST_FREQ_Hz);
    // Zero out any old parameters to ensure they are all gone through
    gUserParams.motor_Rr = NULL;
    gUserParams.motor_Rs = NULL;
    gUserParams.motor_Ls_d = NULL;
    gUserParams.motor_Ls_q = NULL;
    gUserParams.motor_ratedFlux = NULL;
    gUserParams.IdRated = NULL;
    // Update the controller and set other flags
    gMotorVars.Flag_MotorIdentified = false;
    CTRL_setParams(ctrlHandle,&gUserParams);
    CTRL_setFlag_enableUserMotorParams(ctrlHandle, false);
    gMotorVars.Flag_enableOffsetcalc = true;
    gMotorVars.Flag_Run_Identify = true;
    gMotorVars.Flag_enableSys = true;
  DRV_enablePwm(drvHandle);
  CTRL_setFlag_enableCtrl(ctrlHandle, true);
}

If I run this function (and fully go through an identification routine) I can then run the motor exactly as expected using the previously mentioned dummy motor_run function. Question 2: What is the automatic motor identification routine doing that I'm not seeding in my motorcontrol_load_motorparams() function? In the previously described motorcontrol_load_motorparams() I am seeding gUserParsms.maxCurrent and gUserParams.maxCurrent_resEst...

There a few more points that I'm struggling with at the moment that seem ill defined in the insta-spin manual that could also use clarification;

Question 3: How is the gMaxCurrentSlope global variable related to the EST_setMaxCurrentSlope_pu () and EST_getMaxCurrentSlope_pu() functions? (And more generally, how are the parameters in the gMotorVars global variable associated with their equivalent get() and set() functions (both for CTRL_ and EST_) in the insta-spin API?)

Question 4: How should the gMaxCurrentSlope variable be set? In the labs, this value is set after a motor identification, is this the cannonical method? (Perform identification, store calibrated value?)

Question 5: Is there any functional difference between

Question 6: Are all parameters in the global variables (gUserParams, gMotorVars, gMaxCurrentSlope) guaranteed to be atomically written/read?

I realize this is a pretty extensive post with a lot of questions; I would very much appreciate any help I could get with any/all of these questions. In the event that this goes beyond the range of what is reasonable to ask in "for free" I would appreciate where/who to get in touch with for commercial help... Cheers!

  • Oh, there are two utility functions in the larger functions referenced in the post above that are of relevance, they are;

    void motorcontrol_set_en(void){
        gMotorVars.Flag_enableSys = true;
        DRV_enablePwm(drvHandle);
        CTRL_setFlag_enableCtrl(ctrlHandle, true);
    }

    void motorcontrol_set_dis(void){
        gMotorVars.Flag_enableSys = false;
        //TODO: this should ramp down the motor and eventually short the output!
        DRV_disablePwm(drvHandle);
        CTRL_setFlag_enableCtrl(ctrlHandle, false);    //Turns off the entire insta-spin subsystem
    }

  • This is a really long post, it's going to take me some time to go through it all.

     

    A couple things for you to look at in the meantime:

    Are you calling the software update for F2806xM/F silicon?

                  if(CTRL_getFlag_enableUserMotorParams(ctrlHandle) == true)
                  {
                      // call this function to fix 1p6
                      softwareUpdate1p6(ctrlHandle);
                  }

     

    Here is what we did with the GUI project (which we haven't released source to, it's too messy/complicated to support): to support the Reset All GUI button and to allow some interface to the userParams structure.  The same should be done if you are taking in new User data (our structure for this is Gui.gUserData) and want to update the control system (USER_Params).

        if (Gui.gRestartAll)
        {
       DRV_disablePwm(drvHandle);
       CTRL_setFlag_enableCtrl(ctrlHandle,FALSE); //CTRL goes IDLE, causing EST to go IDLE
       USER_setParams((USER_Params *)&Gui.gUserData);

       Gui.gRestartAll = 0;
          Gui.gEnableFlag = 0;
          offsetCalCompleteFlag = 0;
        }

              // initialize global variables
              InitGlobalVariables(); 

           CTRL_setParams(ctrlHandle,(USER_Params *)&Gui.gUserData);
           CTRL_setFlag_enableUserMotorParams(ctrlHandle, Gui.gUserParms);
           CTRL_setFlag_enableCtrl(ctrlHandle, TRUE);

              if(CTRL_getFlag_enableUserMotorParams(ctrlHandle) == TRUE)
              {
                  // call this function to fix 1p6
                  softwareUpdate1p6(ctrlHandle);
              }

              calcPIgains(ctrlHandle);

  • Heh, I realize the questions I've asked are quite extensive, thanks for the help so far! Is there any chance for live (IE. telephone, VOIP, or similar) communications with you or someone else at TI? That would reduce the latency between our posts very significantly and be a real help! (Of course, I'll be glad to post the results here in this thread for future reference).

    I'm not quite clear as to how often, when, or in which context to call the softwareUpdate1p6() function, based on what I've seen in the lab source code it seems that calling it once (and only once), from a low priority after motor identification (IE. potentially after several mainISR calls) is acceptable. Is this the case?

    I've made some slight modifications to my function to load motor parameters, as it is now I'm doing;

    void motorcontrol_load_motorparams(unsigned int polepairs, float i_max, float rs, float ls, float flux, float i_rs){
        CTRL_Obj *obj = (CTRL_Obj *)ctrlHandle;
        if(mc_init == 0){
            return;
        }
        motorcontrol_set_dis();
        gUserParams.motor_type = MOTOR_Type_Pm;
        gUserParams.motor_numPolePairs = polepairs;
        gUserParams.motor_Rr = NULL;
        gUserParams.motor_Rs = rs;
        gUserParams.motor_Ls_d = ls;
        gUserParams.motor_Ls_q = ls;
        gUserParams.motor_ratedFlux = flux;
        gUserParams.maxCurrent = i_max;
        gUserParams.maxCurrent_resEst = i_rs;
        gMotorVars.RsOnLineCurrent_A = i_rs;
        /* Set derived motor parameters based on input.
         * Normally these are calculated in a #define in user.h */
        gUserParams.maxCurrentSlope = (i_rs/USER_IQ_FULL_SCALE_CURRENT_A/USER_TRAJ_FREQ_Hz);
        gUserParams.maxCurrentSlope_epl = (0.3*i_rs/USER_IQ_FULL_SCALE_CURRENT_A/USER_TRAJ_FREQ_Hz);

        CTRL_setParams(ctrlHandle,&gUserParams);                // Seed gUserParams struct with new values
        CTRL_setFlag_enableUserMotorParams(ctrlHandle, true);    // Enable loading parameters from gUserParams struct
        CTRL_setFlag_enableOffset(ctrlHandle, true);            // Enable offset ...
        gMotorVars.Flag_enableOffsetcalc = true;
        EST_setFlag_enableRsRecalc(obj->estHandle, true);        // ... and Rs recalculation before operation.
        softwareUpdate1p6(ctrlHandle);                            // Finally, fix bug in instaspin implementation...
        calcPIgains(ctrlHandle);                                // ...and update PI controller gains
    }

    Once on startup before global interrupts are enabled. I'm not sure that my calling of softwareUpdate1p6 and/or calcPIgains is done at the right time or in the right context however...

    In my main loop I'm running an idle function equivalent to that done in the main loop in the labs, with content;

    void motorcontrol_idle(void){
        if(mc_init == 0){
            return;
        }
        CTRL_Obj *obj = (CTRL_Obj *)ctrlHandle;

        if(motorcontrol_get_en()){
            updateGlobalVariables_motor(ctrlHandle);
            if(motorcontrol_get_error()){
                CTRL_setFlag_enableCtrl(ctrlHandle,false);
                gMotorVars.Flag_enableSys = false;
                DRV_disablePwm(drvHandle);
            }
        }

        if(CTRL_updateState(ctrlHandle)){
            CTRL_State_e ctrlState = CTRL_getState(ctrlHandle);

            if(ctrlState == CTRL_State_OffLine){
                // enable the PWM
                DRV_enablePwm(drvHandle);
            }else if(ctrlState == CTRL_State_OnLine){
                // update the ADC bias values
                DRV_updateAdcBias(drvHandle);

                // Return the bias value for currents
                gMotorVars.I_bias.value[0] = DRV_getBias(drvHandle,DRV_SensorType_Current,0);
                gMotorVars.I_bias.value[1] = DRV_getBias(drvHandle,DRV_SensorType_Current,1);
                gMotorVars.I_bias.value[2] = DRV_getBias(drvHandle,DRV_SensorType_Current,2);

                // Return the bias value for voltages
                gMotorVars.V_bias.value[0] = DRV_getBias(drvHandle,DRV_SensorType_Voltage,0);
                gMotorVars.V_bias.value[1] = DRV_getBias(drvHandle,DRV_SensorType_Voltage,1);
                gMotorVars.V_bias.value[2] = DRV_getBias(drvHandle,DRV_SensorType_Voltage,2);

                // enable the PWM
                DRV_enablePwm(drvHandle);
            }else if(ctrlState == CTRL_State_Idle){
                // disable the PWM
                DRV_disablePwm(drvHandle);
                gMotorVars.Flag_Run_Identify = false;
            }
        }

        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));

            if(Flag_Latch_softwareUpdate)
            {
              Flag_Latch_softwareUpdate = false;

    #ifdef FAST_ROM_V1p6
              if(CTRL_getFlag_enableUserMotorParams(ctrlHandle) == true)
              {
                  // call this function to fix 1p6
                  softwareUpdate1p6(ctrlHandle);
              }
    #endif

              calcPIgains(ctrlHandle);
            }
        }else{
            Flag_Latch_softwareUpdate = true;
            // the estimator sets the maximum current slope during identification
            gMaxCurrentSlope = EST_getMaxCurrentSlope_pu(obj->estHandle);
        }
    }


    As it is now, when I run motorcontrol_load_motorparams() the CCS watch window doesn't show the seeded parameters (gMotorVars.Rs_Ohm et. al. are all 0.0 or equivalent). If I add

        gMotorVars.Flag_enableSys = true;
        DRV_enablePwm(drvHandle);
        CTRL_setFlag_enableCtrl(ctrlHandle, true);

    to the motorcontrol_load_motorparams() the motor starts squealing and the board brings my lab PSU into current saturation (~5A), along with displaying invalid (0.0) motor parameter values. In short; what do I need to write to / do to signal to instaspin that the motor is identified and seed it with the correct motor parameters?

    Again, thank you very much for your help!

  • Real quick, I will try to answer further this afternoon

    "I'm not quite clear as to how often, when, or in which context to call the softwareUpdate1p6() function, based on what I've seen in the lab source code it seems that calling it once (and only once), from a low priority after motor identification (IE. potentially after several mainISR calls) is acceptable. Is this the case?"

    softwareUpdate1p6() needs to be run after every time you load a new set of Ls values into the estimator. This would normally just be done once at start-up (for the identified motor), but it looks like you are trying to allow UART updates of the motor parameters, so you would want to run it after every time a new set of motor parameters is presented.

     

     

  • Thanks for the clarification. I'm not planning for updates over UART per se, but rather for the ability to load motor parameters from an external nonvolatile memory (eg. SPI EEPROM). In this application the motor driver (with power electronics and a 2806x) will be used to drive several different motors depending on the final application -- making it elegant to be able to load parameters from a removable memory that is associated with the motor in question.

    As of now I'm still not able to load parameters during run-time (motorcontrol_load_motorparams), any help would be very appreciated!

  • Ok, I've managed to mangle something together that seems to work reasonably well!

    For future reference here's what I've done in order to load variable but known motor parameters. Again, many thanks for your help Chris, I'll mark this thread as answered as the primary question has been resolved.

    Assume some setup function along the lines of

    void motorcontrol_init(void){
        /* Dummy values, seed in some suitable way */
        int32_t polepairs = 7;
        float imax = 10;
        float rs = 10e-3;
        float ls = 20e-3;
        float flux = 0.015;
        float rs_est_i = 10;
        CTRL_setFlag_enableDcBusComp(ctrlHandle, true);
        /* If motor is identified (valid values stored somewhere) update insta-spin accordingly */
        if(polepairs != 0 && rs != 0 &&    ls != 0 && flux != 0){
            motorcontrol_load_motorparams(polepairs, imax, rs, ls, flux, rs_est_i);

            // Run post-setup identification
            gMotorVars.Flag_Run_Identify = true;
            motorcontrol_set_en();
            while(motorcontrol_get_identify_complete() != true){
                board_kick_wdt();
                motorcontrol_idle();
            }
            motorcontrol_set_dis();
        }

    }

    where the functions motorcontrol_set_en(), motorcontrol_set_dis(), motorcontrol_idle(), motorcontrol_get_identify_complete(), and motorcontrol_load_motorparams() are defined as;

    void motorcontrol_set_en(void){
        gMotorVars.Flag_enableSys = true;
        DRV_enablePwm(drvHandle);
        CTRL_setFlag_enableCtrl(ctrlHandle, true);
    }

    void motorcontrol_set_dis(void){
        gMotorVars.Flag_enableSys = false;
        DRV_disablePwm(drvHandle);
        CTRL_setFlag_enableCtrl(ctrlHandle, false);    //Turns off the entire insta-spin subsystem
    }

    void motorcontrol_load_motorparams(unsigned int polepairs, float i_max, float rs, float ls, float flux, float i_rs){
        CTRL_Obj *obj = (CTRL_Obj *)ctrlHandle;
        motorcontrol_set_dis();
        gUserParams.motor_type = MOTOR_Type_Pm;
        gUserParams.motor_numPolePairs = polepairs;
        gUserParams.motor_Rr = NULL;
        gUserParams.motor_Rs = rs;
        gUserParams.motor_Ls_d = ls;
        gUserParams.motor_Ls_q = ls;
        gUserParams.motor_ratedFlux = flux;
        gUserParams.maxCurrent = i_max;
        gUserParams.maxCurrent_resEst = i_rs;
        gMotorVars.RsOnLineCurrent_A = i_rs;
        /* Set derived motor parameters based on input.
         * Normally these are calculated in a #define in user.h */
        gUserParams.maxCurrentSlope = (i_rs/USER_IQ_FULL_SCALE_CURRENT_A/USER_TRAJ_FREQ_Hz);
        gUserParams.maxCurrentSlope_epl = (0.3*i_rs/USER_IQ_FULL_SCALE_CURRENT_A/USER_TRAJ_FREQ_Hz);
        gMotorVars.Flag_MotorIdentified = true;

        CTRL_setParams(ctrlHandle,&gUserParams);                // Seed gUserParams struct with new values
        CTRL_setFlag_enableUserMotorParams(ctrlHandle, true);    // Enable loading parameters from gUserParams struct
        CTRL_setFlag_enableOffset(ctrlHandle, true);            // Enable offset ...
        gMotorVars.Flag_enableOffsetcalc = true;
        EST_setFlag_enableRsRecalc(obj->estHandle, true);        // ... and Rs recalculation before operation.
        softwareUpdate1p6(ctrlHandle);                            // Finally, fix bug in instaspin implementation...
        calcPIgains(ctrlHandle);                                // ...and update PI controller gains
    }

    bool motorcontrol_get_identify_complete(void){
        CTRL_Obj *obj = (CTRL_Obj *)ctrlHandle;
        return EST_isMotorIdentified(obj->estHandle) ? true : false;
    }

    void motorcontrol_idle(void){
        if(mc_init == 0){
            return;
        }
        CTRL_Obj *obj = (CTRL_Obj *)ctrlHandle;

        updateGlobalVariables_motor(ctrlHandle); // See labs, used verbatim

        if(motorcontrol_get_error()){
            //TODO: do something here, log error and maybe reset instaspin system?
            CTRL_setFlag_enableCtrl(ctrlHandle,false);
            gMotorVars.Flag_enableSys = false;
            DRV_disablePwm(drvHandle);
        }

        if(CTRL_updateState(ctrlHandle)){
            CTRL_State_e ctrlState = CTRL_getState(ctrlHandle);

            if(ctrlState == CTRL_State_OffLine){
                // enable the PWM
                DRV_enablePwm(drvHandle);
            }else if(ctrlState == CTRL_State_OnLine){
                // update the ADC bias values
                DRV_updateAdcBias(drvHandle);

                // Return the bias value for currents
                gMotorVars.I_bias.value[0] = DRV_getBias(drvHandle,DRV_SensorType_Current,0);
                gMotorVars.I_bias.value[1] = DRV_getBias(drvHandle,DRV_SensorType_Current,1);
                gMotorVars.I_bias.value[2] = DRV_getBias(drvHandle,DRV_SensorType_Current,2);

                // Return the bias value for voltages
                gMotorVars.V_bias.value[0] = DRV_getBias(drvHandle,DRV_SensorType_Voltage,0);
                gMotorVars.V_bias.value[1] = DRV_getBias(drvHandle,DRV_SensorType_Voltage,1);
                gMotorVars.V_bias.value[2] = DRV_getBias(drvHandle,DRV_SensorType_Voltage,2);

                // enable the PWM
                DRV_enablePwm(drvHandle);
            }else if(ctrlState == CTRL_State_Idle){
                // disable the PWM
                DRV_disablePwm(drvHandle);
                gMotorVars.Flag_Run_Identify = false;
            }
        }

        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));

            if(Flag_Latch_softwareUpdate)
            {
              Flag_Latch_softwareUpdate = false;

    #ifdef FAST_ROM_V1p6
              if(CTRL_getFlag_enableUserMotorParams(ctrlHandle) == true)
              {
                  // call this function to fix 1p6
                  softwareUpdate1p6(ctrlHandle);
              }
    #endif

              calcPIgains(ctrlHandle);
            }
        }else{
            Flag_Latch_softwareUpdate = true;
            // the estimator sets the maximum current slope during identification
            gMaxCurrentSlope = EST_getMaxCurrentSlope_pu(obj->estHandle);
        }
    }