Is there an example available for creating a motor control application with Kinetis Motor Suite that is not using the Motion Planner?

cancel
Showing results for 
Search instead for 
Did you mean: 

Is there an example available for creating a motor control application with Kinetis Motor Suite that is not using the Motion Planner?

558 Views
Contributor II

I have used KMS to identify my motor parameters and create a simple motion plan using the USER_RUN_PLAN command that I successfully downloaded and ran on my eval platform.  I now would like to create an application using the USER_RUN_SPEED command and cannot get the motor to turn.  Is there an example of creating a project for applications other than running a motion plan?

Labels (1)
8 Replies

77 Views
NXP Employee
NXP Employee

Randy,

Were you successful in creating a non -Motion Planner control interface? I found a example how one engineer use the position input from a master controller to control a 3D printer x or y position.  The same code was put into two different MCUs, one was X and the other Y.

Notice the conversion of the command to set the variables of the command. 

// Expected format: xAAAAAAAABBBBBBBBX
// Wait for initial 'x' or 'y', remember to set this in axis variable
GPIOC_PSOR=(1<<3); //set buffer ready
do {cin=UART1_CharIn();} while (cin!=axis);
// Read AAAAAAAA (dx as a float)
mem=(byte *)&f1; for (i=0;i<4;i++) {*mem=UART1_ByteIn(); mem++;}
// Read BBBBBBBB (dy as a float)
mem=(byte *)&f2; for (i=0;i<4;i++) {*mem=UART1_ByteIn(); mem++;}
// Wait for final 'X' or 'Y': if not an 'X' or 'Y', ignore message
cin=UART1_CharIn();
if (cin==axis-32)
  {
  GPIOC_PCOR=(1<<3);
 UART_CharOut(axis);
  // Valid Message, go ahead and execute
  // ROTATION: extract integer part of rotation
  value=(int)f1;
  user.command.posStepInt_mrev=value;
  //UART_TextOut("IntRotation=");
  UART_DbleOut(value,4,1);
  // ROTATION: extract fractional part of rotation
  user.command.posStepFrac_mrev=_LQ(f1-value);
  //UART_TextOut("FracRotation=");
  UART_DbleOut(f1-value,4,4);
  // SPEED: extract integer part of rotation
  f2=f2/4000.0;
  user.command.limitVel=_LQ(f2);
  //UART_TextOut("SpeedRPM=");
  UART_DbleOut(f2,4,4);
  GPIOC_PSOR=(1<<6);
  // Wait for Sync
  while (((GPIOE_PDIR&(1<<6))==0));
  GPIOC_PCOR=(1<<6);
  flag=0; mike=1; user.command.runTrajectory=1;
  while (mike==1) {UART_CharOut('1');}
  GPIOC_PSOR=(1<<6);

In drv.c of the project this was added to identify when the movement was complete.

    /* if position move has completed, turn off trajectory generator */
    if((DRV_isTrajectoryEnabled(&user)) && (trajpos.output.status == TRAJPOS_IDLE))
    {
        user.command.runTrajectory = false;
        mike=false;
    }

Philip

77 Views
Contributor II

Hello Philip,

Yes, I had found that the speed command that I was providing was incorrect.  I was providing a raw uint32_t value, instead I needed to use the defined scaling macro VEL_PU(value) when supplying a velocity.

Thank you for your help.

Randy Fields

0 Kudos

77 Views
NXP Employee
NXP Employee

I have been experimenting with exactly this for a couple of days.  I will show you what I have in hope that it helps.

In the KMS_API document the user block is defined.

The user.states definitions are in the user.h file

Before you change a user state there are parameters you can modify.

To set the user state to run speed you would write:

user.state= USER_RUN_SPEED;

if you want to idle the motor you would write:

user.state= USER_IDLE;

You then need to drive a target speed by setting user.command.targetSpeed.

The KMS code uses QMath in the calculations and the conversion of a float to a LQ type is easy to do but not obvious. Check out Chapter 24 Math for the details on the QMath library.

If you use the feedback_3ph.c as an example  piece of code the raw ADC reading is converted to a _sq with the use of the function (_sq):  tempSQ = (_sq)(adcResults->phaseBCurrent - v->calib.offsetIb);

Likewise the conversion of the RAW ADC result to LQ is accomplished using the (_lq) function.

In the following code an ADC input is used to control the speed. Before you would call the code below something like this

adc0Results[0] = (_lq) adc0RawResults[0];

conversion to type _lq would have to be done and would be dependent on the conversion you want to make.

/* code start */

// declare these as global variables in main.c

_lq adcSpeed = 0;

_lq adcSpeedAccum = 0;

_lq adcSpeedAvg = 0;

uint16_t speedUpdateCounter = 0;

 

// this code takes the ADC reading, downshifts it by 4 to remove noise and uses that as a percentage of the maximum applicaton speed (in this case 20krpm)

adcSpeed = _LQmpyLQX((adc0Results[0] >> 4), 8, _LQ(20000.0/FULL_SCALE_SPEED_RPM), 24);

adcSpeed = _LQsat(adcSpeed, _LQ(1.0), _LQ(0.0));

 

// this code averages the adc reading over 125 samples before setting it to user.command.targetSpeed

// it will also handle setting the control mode if the commanded speed is larger than 0

if(adcSpeed > _LQ(0.0))

{

                user.state= USER_RUN_SPEED;

                if(speedUpdateCounter >= 125)

                {

                                speedUpdateCounter = 0;

                                adcSpeedAvg = (adcSpeedAccum / 125) & 0x00FF0000;

                                user.command.targetSpeed = adcSpeedAvg;

                                adcSpeedAccum = 0;

                }

                else

                {

                                adcSpeedAccum = adcSpeed + adcSpeedAccum;

                                speedUpdateCounter++;

                }

}

else

{

                user.state= USER_IDLE;

                adcSpeedAccum = 0;

                adcSpeedAvg = 0;

                speedUpdateCounter = 0;

}

 

When we have a good example of this we will post a document about it.

Happy motoring,

Philip

0 Kudos

77 Views
Contributor III

Hi Philip, 

Sorry to hijack this thread, but I have a similar requirement.  I tried your suggestion, but I can't seem to get it working.  My code snippet is below:

if (GPIO_ReadPinInput(BOARD_SW2_GPIO, BOARD_SW2_GPIO_PIN) == 0) {
   running = 1;
   user.command.targetSpeed = 100;
   user.state = USER_RUN_SPEED;
   //user.state = USER_RUN_PLAN;

} else {
   if (running == 1) {
      user.state = USER_IDLE;
      running = 0;
   }
}

/* Service SoftReset Requests from KMS */

When I used USER_RUN_PLAN it works, but when I try USER_RUN_SPEED, it does not work.  What am I missing here?

Background:

Project created using KMS as Sensorless Velocity HALL, working under MCUXpresso.  Board is FRDM-KV31F with FRDM-MC-LVPMSM board.  KMS controls the motor fine, I can start and stop the motor using the KMS dashboard.  I made a motion plan with KMS, and that works fine when I set the user.state to USER_RUN_PLAN.  USER_RUN_SPEED just does not work.

Thanks.

Paul Tan.

Exadler Technologies Inc.

0 Kudos

77 Views
NXP Employee
NXP Employee

The community is made for this kind of sharing. 

Easy mistake.  The lab guide is a good place to review this kind of code. 

 

The switch on the FRDM-KV31F board is momentary. Your code, in the true state, is only executing when the button is pushed and held (the switch grounds the input). When you let off the switch is executes the else.  

The code you should create might toggle a flag when the button pushed. The first time the button is pushed the flag would set and run the plan.  The next time it could clear the flag and the plan could stop.

Philip

0 Kudos

77 Views
Contributor III

Hi Philip,

Yes, I am aware of that fact.  I was holding down the switch continuously.  In fact holding down the switch when I have the commented USER_RUN_PLAN does indeed work, and the motor stops when I release the button.

Unfortunately the same thing does not happen when I use USER_RUN_SPEED instead, which is puzzling me.  Is there anything else I need to set to use USER_RUN_SPEED instead of USER_RUN_PLAN?

Thanks.

Paul Tan.

Exadler Technologies Inc.

0 Kudos

77 Views
Senior Contributor I

Paul,

The key issue is this line:

user.command.targetSpeed = 100;

In KMS all values are normalized fixed-point.  So setting user.command.targetSpeed to 100 is setting the target speed to a very small value (<< 1RPM).

You need to convert from RPM into the normalized fixed point used by KMS.  See example below:

user.command.targetSpeed = _LQ(100.0 / FULL_SCALE_SPEED_RPM);

In the above line, FULL_SCALE_SPEED_RPM represents the maximum possible speed in the system.  Dividing the input speed of 100.0 by this value will convert your input into a normalized value.  Since KMS is also fixed point, we need to convert this normalized floating point number.  The macro _LQ will convert from floating point into fixed point at the default Qness.  The variable user.command.targetSpeed is defined as an _lq variable.  This means it is a 32-bit container with a Qness of 24 (24 bits after the decimal point).  By default all _lq variables at Q24 unless otherwise noted in the variable name.  

Hope that helps explain why you don't see any motion.

77 Views
Contributor III

Thank You!  That was indeed the problem!!

I now have a working motor controller!!

Thank you!

Paul Tan.

Exadler Technologies Inc.

0 Kudos