Go Down

Topic: Two's Complement (Read 490 times) previous topic - next topic

jremington

#15
Aug 10, 2019, 07:20 pm Last Edit: Aug 10, 2019, 07:27 pm by jremington
Quote
What you geniuses can't seem to grasp is that after getting the two's as in reply #1, I can't pass it as an unsigned value because the msb does not lose its significance as a sign bit.
Your explanation makes no sense. By definition, an unsigned value has no sign bit. I repeat: by definition.

It is pretty hard to imagine what you are doing wrong, given that you failed to post an example demonstrating the problem.


AWOL

Did you read reply #8?

johnwasser

If I send a "MoveAbs,0,20000,800" command, the motor moves to position 20000.  The "GetPos,0" command returns 20000 as expected.

If I send a "MoveAbs,0,-20000,800" command, the motor does not move at all but interestingly enough, the "GetPos,0" command returns 4194303.
I don't know why the motor doesn't move but I do know why the new position is 4194303 (0x3FFFFF).  When you put -20,000 (0xFFFFB1E0) into an unsigned long you get the value 4,294,947,296.  The _GoTo() function (see reply #10) in the library finds that is larger than the maximum allowed absolute position (0x3FFFFF == 4,194,303) and sets the target position to the maximum. 
Maybe the library and the motor controller disagree on what the maximum allowed absolute coordinate is and the driver rejects the command.
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

johnwasser

Did you wait for the previous command to complete?  The datasheet says "Any attempt to perform a GoTo command when a previous command is under execution (BUSY low) causes the command to be ignored and the NOTPERF_CMD flag to rise (see Section 9.1.22)"

I think I see part of the problem.  The function takes a 32-bit unsigned argument but the ABS_POS register is a SIGNED 22-bit number.  You can't use a 32-bit negative number directly.  You have to mask off the upper bits. That is BAD design on the part of the library writer.

To move to position -20,000 you can't pass -20,000 to the function.  Since the function takes a 32-bit unsigned number it will appear to be a very large positive number: 4,294,947,296, greater than the allowed maximum of 4,194,303.  To get the number you want you have to mask off the upper bits:

-20,000 & 0x3FFFFFUL == 0x003FB1E0 == 4,174,304 

Since 0x3FB1E0 is less than 0x3FFFFF it will be passed to the driver unchanged.
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

Chevelle

#19
Aug 10, 2019, 10:21 pm Last Edit: Aug 10, 2019, 10:36 pm by Chevelle
John,

Thanks very much for you thoughtful and supportive help.

Yes, the writer of the library didn't to a particularly good job of it.  On that subject for a moment, I have tried several libraries for this device (which is one hell of a device, very powerful) but there doesn't seem to be a simple straightforward library from anyone, including STI.  They are all loaded up with board specific configurations (such as the Sparkfun and STI libraries) or are combined in a mega library such as from Adafruit.

This one at least is geared for the IC level which is great for my application.  It isn't a bad library but it isn't great either.  This is one example.  The purpose of a library is to provide the user with a command set that gives him/her a frame of reference to the real word and makes the translation of real world units to machine level units.  If people think in + and - terms with reference to home, then accept + and - parameters.  Why require the stall current in hex when it can be converted to milliamps in the library?

Anyway, back to this issue.  It was indeed simple.

My mistake was keeping everything as a signed long (as was pointed out).  I was taking a negative signed long, taking the two's complement, and then assigning that back to a signed long.  That "preserved" the msb as a sign bit.

The right way is to take the two's complement for negative position values and assign that back to an unsigned long.  And John, you are dead right.  The upper bits have to be masked off.  Viola, it works.

Code: [Select]


long valLong;
unsigned long valULong;

if (valLong < 0) {
  valULong = (~abs(valLong) + B01) & 0x3FFFFF;
  }
  else {
  valULong = valLong;
  }

Axis0._GoTo(valULong);


johnwasser

Code: [Select]
(~abs(valLong) + B01)
Since the absolute value of a negative number is the negative of that negative number and the 2's Compliment of a number is the negative of that number, your expression is functionally equivalent to:
Code: [Select]
(-(-valLong))
Which is equivalent to:
Code: [Select]
(valLong)

For values in the allowed range of 0x200000 (-2,097,152) to 0x1FFFFF (+2,097,151) the code:
Code: [Select]
if (valLong < 0) {
  valULong = (~abs(valLong) + B01) & 0x3FFFFF;
  }
  else {
  valULong = valLong;
  }

Axis0._GoTo(valULong);


Is equivalent to:
Code: [Select]
Axis0._GoTo(valLong & 0x3FFFFFUL);
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

Chevelle

I will give that try.  Looks much more efficient.

Thanks very much.

(I'll be back when it comes to reading the x_ABS_POS value.)

Chevelle

#22
Sep 17, 2019, 05:50 pm Last Edit: Sep 17, 2019, 06:15 pm by Chevelle
It has been a while since this thread has been active because I have been making great progress, especially thanks to John.  Things are moving and there are now plenty of functional feature that are working very well.

By way off background, the board controls two stepper motors (A and B).  These motors move a CoreXY stage (very similar to many 3D printers.)  Any X,Y movement of the carriage requires movement by motors A and B.  For example, moving just motor A move the carriage in both X and Y in a diagonal direction.

There are two routines that do not seem to jive.  One is to send the carriage to an X,Y location.  (MoveAbsXY)  The next two parameters that are sent are the desired X and Y positions.  Optionally, velocities for the A and B motor can be sent.

The other routine is to read the position of the A and B motor.  (GetPos)  The A and B positions are then translated into the X and Y coordinates of the carriage.

Here are the two routines....

Code: [Select]

 if (Cmd[0] == "moveabsxy") {
     // Syntax MoveAbsXY, X, Y, vel0*, vel1*
     PosX = Cmd[1].toInt(); // This the desired X position to move to
     if (abs(PosX) > 0x1FFFFF) { CmdOK = false; } // Check to be sure that X is within acceptable range
 
     PosY = Cmd[2].toInt(); // This is the desired Y position to move to
     if (abs(PosY) > 0x1FFFFF) { CmdOK = false; } // Check to be sure that Y is within acceptable range

     SpeedA = Cmd[3].toInt(); // This the speed at which the A motor will move
     if ((Cmd[3] != "") && (SpeedA != constrain(SpeedA, 1, 0x3CFA))) { CmdOK = false; }
 
     SpeedB = Cmd[4].toInt(); // This is the speed at which the B motor will move
     if ((Cmd[4] != "") && (SpeedB != constrain(SpeedB, 1, 0x3CFA))) { CmdOK = false; }
 
     if (CmdOK == false) { Serial.println("err:Command"); }
 
     else { Serial.println("OK"); }
 
     if (CmdOK == true) {
         if (Cmd[3] != "") { AxisA._SetParam(x_MAX_SPEED, AxisA._MaxSpdCalc(SpeedA)); }
         if (Cmd[4] != "") { AxisB._SetParam(x_MAX_SPEED, AxisB._MaxSpdCalc(SpeedB)); }
 
         uPosA = (PosX + PosY) & 0x3FFFFFUL; // Calculate the position for the A motor so that the carriage move to the desired X, Y position
         AxisA._GoTo(uPosA); // Send the A motor on its way
 
         uPosB = (PosX - PosY) & 0x3FFFFFUL; // Calculate the position for the B motor so that the carriage moves to the desired X,Y position
         AxisB._GoTo(uPosB); // Send the B motor on its way
     }
 }


and

Code: [Select]

if (Cmd[0] == "getpos") {
    // Syntax GetPos
    valULong = AxisA._GetParam(x_ABS_POS); // Get the position for the A motor in 2's comp

    if (valULong > 0x1FFFFF) {  // Convert from 2's comp to signed long
        valLong = (~valULong + B01) & 0x1FFFFF; valLong = -valLong;}
    else {
        valLong = valULong;
    }

    strCmd = String(valLong) + ",";

    valULong = AxisB._GetParam(x_ABS_POS); // Get the position for the B motor in 2's comp
   
    if (valULong > 0x1FFFFF) {   // Convert from 2's comp to signed long
        valLong = (~valULong + B01) & 0x1FFFFF; valLong = -valLong;
        }
    else {
        valLong = valULong;
    }

    strCmd = strCmd + String(valLong);
    Serial.println(strCmd);
}




Something is wrong because when I command the carriage to an X,Y location and use the GetPos routine to get the A and B position and calculate X and Y, they do not match.

I am assuming that there is something wrong with how I am dealing with the 2's complements of the values.

Any assistance is greatly appreciated.

johnwasser

Things are very confusing because the library takes (and returns?) positions as unsigned 32-bit values BUT the hardware requires 22-bit SIGNED numbers.  This is what we in the programming business call "a major design mistake". 

It it were me, I would modify the library.  I would have the library always use 32-bit signed integers (int32_t) for positions.  The library would truncate to 22 bits  (Position & 0x3FFFFF) when sending the position to the hardware and sign-extend any 22-bit position it gets from the hardware:
Code: [Select]
int32_t pos = (Position & 0x200000) ? Position | 0xFFC00000 : Position;
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

Chevelle

John, you are a life saver.

Yes, confusing to say the least.  I am confident enough to modify the library but only up to a point.  (I already modified it to comment out some unwanted serial print statements.) 

This is a professional application but it is not in my main area of expertise.  We are a low budget affair but I am sure we can engage you for a few hours of your time if you are interested.

Please let me know.

gfvalvo

I wouldn't modify the library because you'd have to do so again every time it is updated by the author. Instead, just write a wrapper class around it.
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

Chevelle

I think a new library is in order here, completely separate from the original.

Chevelle

#27
Sep 18, 2019, 03:00 pm Last Edit: Sep 18, 2019, 03:01 pm by Chevelle
John,

My C++ isn't good enough to completely rely on the use of the (better) short handed way of doing things.  Am I correct in my long handed interpretation of your code snippet?

Code: [Select]
int32_t pos = Position;

if (Position & 0x200000) {Position | 0xFFC00000;}

johnwasser

Am I correct in my long handed interpretation of your code snippet?
Code: [Select]
int32_t pos = Position;
if (Position & 0x200000) {Position | 0xFFC00000;}
Not quite.  The "{Position | 0xFFC00000;}" doesn't store a value anywhere.  It calculates a value and throws the result away.  If your warning levels are high enough the compiler should warn you that the statement has no side effects.

A full expansion would be more like:
Code: [Select]
int32_t pos = Position;
  if (Position & 0x200000)
  {
    pos = Position | 0xFFC00000;
  }


The result of the Ternary Operator "A ? B : C" is the value B if A is 'true' (non-zero) and the value C if A is 'false' (zero).

Another way to expand it is:
Code: [Select]
int32_t pos = Position;
  if (pos & 0x00200000)
  {
    pos |= 0xFFC00000;
  }


Similar to +=, -=, *=... the "pos |=" is a C shortcut for "pos = pos |".
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

gfvalvo

I think a new library is in order here, completely separate from the original.
OK, then it's on you to fix any problems that arise with the base library and are subsequently fixed in that repository. You'll need to implement similar fixes in your custom version. Not a problem if your coding skills are up to it.
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

Go Up