Solved: Error with EV3 Motors using BricxCC

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
endojo
Posts: 12
Joined: 06 Oct 2013, 16:48

Re: Error with EV3 Motors using BricxCC

Post by endojo »

I tested the same thing with RotateMotorNoWaitEx, and again, it works with OUT_AB but doesn't with OUT_A.

RobotcxCC only bundles the tools for communicating with the ev3, right?
Because than codesourcery just compiles to native linux opcode which runs on the brick's OS - I boot it with the standard firmware on it and I can start the program directly from the brick's interface.
(Well, I am a Noob on low-level stuff like that, but I'm willing to learn more if necessary)
totokan
Posts: 45
Joined: 28 Sep 2013, 04:22

Re: Error with EV3 Motors using BricxCC

Post by totokan »

// currently no way to turn off syncronization (Sync parameter is ignored)
So sayeth ev3_output.c. Looks like 1-motor isn't implemented yet. It waits for the second motor to sync, indefinitely, then the counter probably overflows and crashes the kernel, if I had to guess.
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: Error with EV3 Motors using BricxCC

Post by afanofosc »

So I am very curious how RotateMotorNoWait works or does not work. It does not wait until the motors finish their rotation so an additional wait after the call would be appropriate.

Here's the very simple code for RotateMotor:

Code: Select all

void RotateMotorEx(byte Outputs, char Speed, int Angle, short Turn, bool Sync, bool Stop)
{
  // currently no way to turn off syncronization (Sync parameter is ignored)
  OutputStepSyncEx(Outputs, Speed, Turn, Angle, Stop, OWNER_NONE);
  bool busy;
  while (true)
  {
    usleep(2); // 2ms between checks
    busy = false;
    OutputTest(Outputs, &busy);
    if (!busy) break;
  }
}
I think the usleep(2) is probably bad. It should sleep more than 2 microseconds (and the comment is clearly wrong). Replacing that with Wait(2) might be worth trying.

Here's the code for OutputTest:

Code: Select all

bool OutputTest(byte Outputs, bool * isBusy)
{
  if (!OutputInitialized())
    return false;

  byte Layer;
  int  test;
  int  test2;
  char busyReturn[20]; // Busy mask
  bool result = false;

  *isBusy = false;
  test2 = 0;
  DecodeOutputs(&Outputs, &Layer);
  if (Layer == LAYER_MASTER)
  {
    if (OutputInstance.PwmFile >= 0)
    {
      size_t bytes_read = read(OutputInstance.PwmFile, busyReturn, 10);
      result = bytes_read > 0;
      sscanf(busyReturn, "%u %u", &test, &test2);
      *isBusy = ((Outputs & (byte)test2) != 0);
    }
  }
  else
  {
//    if cDaisyCheckBusyBit(Layer, Outputs) )
      *isBusy = false;
  }
  return result;
}
This is essentially identical to the OUTPUT_TEST opcode implementation in the LMS firmware. The code for OutputStepSyncEx is here:

Code: Select all

bool OutputStepSyncEx(byte Outputs, char Speed, short Turn, int Step, bool useBrake, byte Owner)
{
  if (!OutputInitialized())
    return false;

  int cmdLen = sizeof(StepOrTimeSync);
  byte Layer;
  StepOrTimeSync cmd;
  // opOutputStepSync (outputs, speed, turn, step, brake?)
  DecodeOutputs(&Outputs, &Layer);
  cmd.Cmd = opOutputStepSync;
  cmd.Outputs = Outputs;
  cmd.Speed = Speed;
  cmd.Turn = Turn;
  cmd.StepOrTime = Step;
  cmd.Brake = (byte)useBrake;
  if (Layer == LAYER_MASTER)
  {
    bool result = WriteToPWMDevice((char*)&(cmd.Cmd), cmdLen) == cmdLen;
    if (result)
    {
      int i;
      for (i = 0; i < NUM_OUTPUTS; i++)
      {
        if (Outputs & (0x01 << i))
          OutputInstance.Owners[i] = Owner;
      }
    }
    return result;
  }
  else
  {
    return false;
/*
      if (cDaisyReady() != BUSY)
      {
        DaisyBuf[Len++]  =  0;
        DaisyBuf[Len++]  =  0;
        DaisyBuf[Len++]  =  opOUTPUT_STEP_SYNC;
        Len             +=  cOutputPackParam((DATA32)0, &(DaisyBuf[Len]));
        Len             +=  cOutputPackParam((DATA32)StepSync.Nos,   &(DaisyBuf[Len]));
        Len             +=  cOutputPackParam((DATA32)StepSync.Speed, &(DaisyBuf[Len]));
        Len             +=  cOutputPackParam((DATA32)StepSync.Turn,  &(DaisyBuf[Len]));
        Len             +=  cOutputPackParam((DATA32)StepSync.Step,  &(DaisyBuf[Len]));
        Len             +=  cOutputPackParam((DATA32)StepSync.Brake, &(DaisyBuf[Len]));
        if(OK != cDaisyMotorDownStream(DaisyBuf, Len, Layer, StepSync.Nos))
        {
          SetObjectIp(TmpIp - 1);
          DspStat  =  BUSYBREAK;
        }
        //cDaisyMotorDownStream(DaisyBuf, Len, Layer, StepSync.Nos);
      }
      else
      {
        SetObjectIp(TmpIp - 1);
        DspStat  =  BUSYBREAK;
      }
*/
  }
}
If RotateMotorNoWait works then it must be a bug in the API layer.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: Error with EV3 Motors using BricxCC

Post by afanofosc »

endojo wrote:I tested the same thing with RotateMotorNoWaitEx, and again, it works with OUT_AB but doesn't with OUT_A.

RobotcxCC only bundles the tools for communicating with the ev3, right?
Because than codesourcery just compiles to native linux opcode which runs on the brick's OS - I boot it with the standard firmware on it and I can start the program directly from the brick's interface.
(Well, I am a Noob on low-level stuff like that, but I'm willing to learn more if necessary)
I assume you are talking about BricxCC. I am not sure what RobotcxCC is.

It is definitely not possible to execute a native arm-linux executable from the EV3's on-brick user interface. They do not even show up as files with the on-brick interface. The only way to execute a native-arm linux executable is via the BricxCC IDE with the Explorer tool or with the Run button on the compile toolbar. Or you can run it from a command prompt if you have shut down the VM or connected to it via telnet or SSH while the VM is still running.

If you have a native arm-linux executable running from a command prompt then I think it is likely that it could be killed with Ctrl+Break or Ctrl+C but perhaps not. I'm not in a position to test that right now.

Perhaps it would be preferable for BricxCC to launch executables as a background process by sticking " &" on the end of the command. That way the process could be killed if it hangs without completely hanging the VM executable. But if it is actually crashing a kernel module then it is a much more serious problem.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: Error with EV3 Motors using BricxCC

Post by afanofosc »

Code: Select all

 *  opOUTPUT_STEP_SYNC:   Runs two motors regulated and syncronized, duration as specified by tacho cnts
 *  opOUTPUT_TIME_SYNC:   Runs two motors regulated and syncronized, duration as specified by time
Hmmm. It does look like d_pwm.c expects that these two opcodes are only used with multiple motors.

Code: Select all

 *  opOUTPUT_POSITION:    Runs the motor to the absolute tacho positon - Starts the motor
This looks promising until you look a little further:

Code: Select all

    case opOUTPUT_POSITION:
    {
    }
    break;
It looks like I need to use opOUTPUT_STEP_SPEED or opOUTPUT_STEP_POWER

Code: Select all

 *  opOUTPUT_STEP_POWER:  Runs the motor un-regulated with ramp up const and down according to the tacho
 *  opOUTPUT_STEP_SPEED:  Runs the motor regulated with ramp up const and down according to the tacho
I will correct this later today and upload a fix tonight.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
totokan
Posts: 45
Joined: 28 Sep 2013, 04:22

Re: Error with EV3 Motors using BricxCC

Post by totokan »

Sorry for the misinformation, in my haste to figure out the problem I should've mentioned I didn't have an EV3 in front of me to check everything! Seems like it's figured out now though.
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: Error with EV3 Motors using BricxCC

Post by afanofosc »

Try these changes:

Code: Select all

void RotateMotorNoWaitEx(byte Outputs, char Speed, int Angle, short Turn, bool Sync, bool Stop)
{
  if (Sync)
  {
    byte Layer, tmpOuts;
    tmpOuts = Outputs;
    DecodeOutputs(&tmpOuts, &Layer);
    if (tmpOuts == OUT_AB || tmpOuts == OUT_AC || tmpOuts == OUT_AD ||
        tmpOuts == OUT_BC || tmpOuts == OUT_BD || tmpOuts == OUT_CD)
    {
      OutputStepSyncEx(Outputs, Speed, Turn, Angle, Stop, OWNER_NONE);
      return;
    }
  }
  // otherwise use a non-synchronized API call
  int s1 = Angle / 10, s2 = Angle - (2*s1), s3 = s1;
  OutputStepSpeedEx(Outputs, Speed, s1, s2, s3, Stop, OWNER_NONE);
}

void RotateMotorEx(byte Outputs, char Speed, int Angle, short Turn, bool Sync, bool Stop)
{
  RotateMotorNoWaitEx(Outputs, Speed, Turn, Angle, Stop, OWNER_NONE);
  bool busy;
  while (true)
  {
    Wait(MS_2); // 2ms between checks
    busy = false;
    OutputTest(Outputs, &busy);
    if (!busy) break;
  }
}
And in that same file insert this code after DecodeOutputs in OutputStepSyncEx (line 866) and OutputTimeSyncEx (line 932):

Code: Select all

  // it is invalid to call this function with anything other than 2 motors:
  // i.e., OUT_AB, OUT_AC, OUT_AD, OUT_BC, OUT_BD, or OUT_CD
  if (!(Outputs == OUT_AB || Outputs == OUT_AC || Outputs == OUT_AD ||
        Outputs == OUT_BC || Outputs == OUT_BD || Outputs == OUT_CD))
    return false;
Please test this and let me know how it works (or does not work). This line

Code: Select all

  int s1 = Angle / 10, s2 = Angle - (2*s1), s3 = s1;
is, in theory, setting a 10% ramp up, 80% steady state, and 10% ramp down to the originally specified tachometer limit. I have not tested the 3 step API functions at all so I am not 100% sure how they are supposed to work. In theory, 0, Angle, 0 would work without any ramp up or ramp down.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
endojo
Posts: 12
Joined: 06 Oct 2013, 16:48

Re: Error with EV3 Motors using BricxCC

Post by endojo »

I'm afraid that didn't fix it.

Trying to rotate only one motor does nothing, and two motors now crash. First it's rotating for a short amount of time fast, then it crashes and the motor continues rotating really slow.

Wait... doesn't there belong an "else" after the "if(Sync)"?
-> I tried it out, it only solved that the motor doesn't continue moving after the crash - it stops after the stuttering
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: Error with EV3 Motors using BricxCC

Post by afanofosc »

It is not clear to me which API function you are testing, RotateMotor or RotateMotorNoWait. The best thing to do is try to get the low level routines working. Try OutputStepSyncEx with one motor. It should return false now, no motors turning, no crashing. Try OutputStepSyncEx with three or more motors. It should do the same. Try it with OUT_AB, OUT_AC, OUT_AD, OUT_BC, OUT_BD, and OUT_CD and verify whether it works correctly in all of these cases. This function did not change aside from an if statement to return false if the Outputs passed into the routine were not one of the 2 motor combinations I just listed. So it should still work correctly.

There is no need for an else clause in RotateMotorNoWaitEx because if the code calls OutputStepSyncEx it exits the routine immediately thereafter. The lack of an else is so that if you pass in TRUE for the Sync parameter but you pass in a set of invalid Outputs then it will fall through in either case to the unsynchronized motor movement function call, i.e., OutputStepSpeedEx.

I just now have noticed a bug. I scewed up on the parameters being passed from RotateMotorEx to RotateMotorNoWaitEx. It used to call OutputStepSyncEx there which has a different set of parameters (the same number, though) so it compiled but the values are wrong in a few places.

Try this for RotateMotorEx:

Code: Select all

void RotateMotorEx(byte Outputs, char Speed, int Angle, short Turn, bool Sync, bool Stop)
{
  RotateMotorNoWaitEx(Outputs, Speed, Angle, Turn, Sync, Stop);
  bool busy;
  while (true)
  {
    Wait(MS_2); // 2ms between checks
    busy = false;
    OutputTest(Outputs, &busy);
    if (!busy) break;
  }
}
John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
endojo
Posts: 12
Joined: 06 Oct 2013, 16:48

Re: Error with EV3 Motors using BricxCC

Post by endojo »

I'm sorry that I were not precise.

So right now I tried out "OutputStepSyncEx(OUT_XX, 50, 0, 360, true, OWNER_NONE);"
If i use something else than two motors, it returns false. (Which was obvious)

I have a setup of one medium motor at port A and two large ones at Port B and C.
OUT_AB: Only B makes a little stuttering.
OUT_AC: Only C makes a little stuttering.
OUT_AD: Nothing happens.
OUT_BC: Both motors do a full 360° like they're supposed to do.
OUT_BD: B makes a little stuttering.
OUT_CD: C makes a little stuttering.

(If I only tried it earlyer, I'd propably saved you some time)
Conclusion:
The medium motor doesn't get dedected; And if a big one gets paired with "nothing", a bug or whatever appears.

I really appreciate your effords and I'll keep my fingers crossed for you to get this nasty bug fixed!
Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests