Pointer issues between AVR and ARM compilers

Shop Forum Everything Else Pointer issues between AVR and ARM compilers

Viewing 21 posts - 1 through 21 (of 21 total)
  • Author
    Posts
  • #10696
    Anonymous
    Inactive

    ### From “2 Axis CNC Demo” – downloaded from GitHub today. ###

    Been having a problem with the pointers in the processCommand() and parsenumber() routines.

    Works fine with AVR but fails when compiled for ARM on the SAMD21 M0 / M0 Pro boards (I am in Europe so these are from Arduino.org).

    I have hacked the code to get something to work (I think as I have dummy motor calls); not so elegant though šŸ™

    N.B. The Serial.print(); stuff is just to see what is going on.

    Thanks.

    /**
    * Read the input buffer and find any recognized commands. One G or M command per line.
    */
    void processCommand()
    {
    long cmd;

    if(buffer[0] == ‘;’) return; // check for a blank line

    else if(buffer[0] == ‘G’) // G Command must appear as first char on line
    {
    cmd = parsenumber(‘G’,-1);
    Serial.print(“processCommand() G”);
    Serial.println(cmd);
    switch(cmd)
    {
    case 0:
    case 1: // line
    {
    feedrate(parsenumber(‘F’,fr));
    line( parsenumber(‘X’,(mode_abs?px:0)) + (mode_abs?0:px),
    parsenumber(‘Y’,(mode_abs?py:0)) + (mode_abs?0:py) );
    break;
    }
    case 2:
    case 3: // arc
    {
    feedrate(parsenumber(‘F’,fr));
    arc(parsenumber(‘I’,(mode_abs?px:0)) + (mode_abs?0:px),
    parsenumber(‘J’,(mode_abs?py:0)) + (mode_abs?0:py),
    parsenumber(‘X’,(mode_abs?px:0)) + (mode_abs?0:px),
    parsenumber(‘Y’,(mode_abs?py:0)) + (mode_abs?0:py),
    (cmd==2) ? -1 : 1);
    break;
    }
    case 4: pause(parsenumber(‘P’,0)*1000); break; // dwell
    case 90: mode_abs=1; break; // absolute mode
    case 91: mode_abs=0; break; // relative mode
    case 92: // set logical position
    position( parsenumber(‘X’,0),
    parsenumber(‘Y’,0) );
    break;
    default:
    Serial.println(F(“Unrecognized G code!”));
    break;
    }
    }

    else if(buffer[0] == ‘M’) // M Command must appear as first char on line
    {
    cmd = parsenumber(‘M’,-1);
    Serial.print(“processCommand() M”);
    Serial.println(cmd);
    switch(cmd)
    {
    case 18: // disable motors
    disable();
    break;
    case 100: help(); break;
    case 114: where(); break;
    default:
    Serial.println(F(“Unrecognized M code!”));
    break;
    }
    }
    else
    {
    Serial.println(F(“Unrecognized Command!”));
    }
    }

    /**
    * Look for character /code/ in the buffer and read the float that immediately follows it.
    * @return the value found. If nothing is found, /val/ is returned.
    * @input code the character to look for.
    * @input val the return value if /code/ is not found.
    **/
    float parsenumber(char code, float val)
    {
    float local_vlaue;
    uint32_t whileLoopCount = 0;

    Serial.print(F(“parsenumber() start = “));
    Serial.println(code);

    char *ptr = buffer;

    while(ptr && *ptr && (ptr < buffer + sofar) )
    {
    whileLoopCount++;
    Serial.print(F(“while(…) loop = “));
    Serial.println(whileLoopCount);
    if(*ptr == code)
    {
    local_vlaue = atof(ptr+1);
    Serial.print(F(“parsenumber() end float = “));
    Serial.println(local_vlaue);
    return local_vlaue;
    }
    ptr = strchr(ptr,’ ‘) + 1;
    if(whileLoopCount >= MAX_BUF)
    break;
    }
    Serial.print(F(“parsenumber() end val = “));
    Serial.println(val);
    return val;
    }

    #10697
    Dan
    Keymaster

    I don’t have the hardware that you have and I obviously see the changes you made. Please either submit this as a github issue on the relevant project or distill this to a shorter message.

    What, exactly, is the problem?

    #10698
    Anonymous
    Inactive

    Hi Dan,

    Thanks for the quick reply.

    Problem seems to be here:

    while(ptr && *ptr && ptr < buffer + sofar)
    {

    }

    where the while loop pointer test doesn’t work properly, so the loop doesn’t terminate i.e. the program hangs.

    Sorry, I am not into the whole github thing as I am a hardware person who writes code sometimes (and my programming skills are obviously not up to snuff as far as pointers go).

    #10699
    Dan
    Keymaster

    The ptr starts at buffer and is only incrementing so it HAS to pass buffer+sofar.
    I don’t believe this is the source of the problem.

    Are you sending the newline when you talk to the board?
    What message, exactly, are you sending?
    What is the output you see in the serial window?

    #10700
    Anonymous
    Inactive

    Hi Dan,

    A bit of background:

    I have used a stripped down version of your code for over a year in a single axis stepper controller made with an Arduino Leonardo and a Arduino A000079 L298 H-bridge Motor Shield (and the TFT display).

    The whole g-code interpreter part has always worked without a hitch in this AVR setup. I have several motor controllers running 24/7 being driven from Matlab applications using the USB serial CDC.

    At the hardware end I drive the motor with a synthersised DDS waveform with a 32KHz update Sine/Cosine PWM which enables me to have low-vibration operation with speeds that can go down to 0.001 steps per second (should I so wish).

    I am now moving across to using the Arduino M0 (Pro) as I am wanting to implement more PWM channels plus faster inner code interrupt loop.

    The application for this is controlling optical positioning systems in my group’s laser labs. These setups are very sensitive to vibration and can involve scans that take hours or even days to complete.

    So yes, I am sending correct messages. What happens with the original code is that I see the > command prompt, the first command is echoed back, then the processor hangs in the while() loop.

    GcodeCNCDemo2AxisV1 1
    Commands:
    G00 [X(steps)] [Y(steps)] [F(feedrate)]; – line
    G01 [X(steps)] [Y(steps)] [F(feedrate)]; – line
    G02 [X(steps)] [Y(steps)] [I(steps)] [J(steps)] [F(feedrate)]; – clockwise arc
    G03 [X(steps)] [Y(steps)] [I(steps)] [J(steps)] [F(feedrate)]; – counter-clockwise arc
    G04 P[seconds]; – delay
    G90; – absolute mode
    G91; – relative mode
    G92 [X(steps)] [Y(steps)]; – change logical position
    M18; – disable motors
    M100; – this help message
    M114; – report position and feedrate
    All commands must end with a newline.
    >G01 X100 Y100 F20;

    ^^^ Hangs here, no further > prompt.

    Anyway, I have done a hack that works for me to get on with whilst I look at the pointers.

    Thanks.

    Best,
    Susan.

    #10701
    Dan
    Keymaster

    So it hangs on the very first command? That is almost guaranteed you are not setting “newline” in the arduino serial window. the program waits for newline (\n) before processing a message.

    #10702
    Anonymous
    Inactive

    Sorry, I am setting “newline”. I used to use Teletypes “back in the day”, I know what a CR or LF is šŸ™‚

    The pointer handling is coming adrift; probably from casting and int or long type differences.

    I am trying to reburn a Zero board with an Arduino.cc bootloader (I have the Atmel-ICE).

    As/when I will let you know if that works.

    Sorry for distraction BTW.

    Best,
    Susan.

    #10704
    Dan
    Keymaster
    #10708
    Dan
    Keymaster

    I have updated the github repository and the issue with a possible fix. please download a fresh copy and try again. If it works I will make the same change across the rest of gcodecncdemo.

    #10714
    Anonymous
    Inactive

    Hi Dan,

    Sorry, still a problem for me.

    ### Console Output from IDE ###

    In function ‘float parsenumber(char, float)’:

    error: ISO C++ forbids comparison between pointer and integer [-fpermissive]

    while(ptr > 1 && *ptr && ptr < buffer + sofar)

    ^
    exit status 1
    ISO C++ forbids comparison between pointer and integer [-fpermissive]

    ########

    For me this happens both in CC and ORG version of IDE (although having both on same machine not 100% sure they are both completely separated).

    What I have done meanwhile to get something going:

    /**
    * Look for character /code/ in the buffer and read the float that immediately follows it.
    * @return the value found. If nothing is found, /val/ is returned.
    * @input code the character to look for.
    * @input val the return value if /code/ is not found.
    **/

    float parsenumber(char code, float val)
    {
    float local_vlaue;
    uint16_t whileLoopCount = 0;

    char *ptr = buffer;

    while(whileLoopCount++ < MAX_BUF) // Default scan through entire buffer
    {
    if(*ptr == code)
    {
    local_vlaue = atof(ptr+1);
    return local_vlaue;
    }
    ptr = strchr(ptr,’ ‘) + 1;
    }
    return val;
    }

    #10717
    Dan
    Keymaster

    I guess you’ll have to cast to int and see if that makes the bug go away. It’s specific to a compiler that I don’t have installed. Can you try and let me know what works, please? I’ll then add your fix to the code permanently.

    #10722
    Anonymous
    Inactive

    Hi Dan,

    This is what I have currently. No problems with compiler.

    I am testing it with a version of code that has serial.prints for every interaction, haven’t managed to trip it up yet šŸ™‚

    
    float parsenumber(char code, float value)
      {
      float local_value;
      uint16_t whileLoopCount = 0;
    
      char *ptr = buffer;
      
      while(ptr >= buffer)  // Scan through received buffer
        {
    
        if(*ptr == ';')  // Check for start of comment, needs a space before the ; to work 
          {
          return value;
          }
    
        if(*ptr == code)   // Look for specific letter command code e.g. G, M, &c.
          {
          local_value = atof(ptr+1);
          return local_value;
          }
    
        ptr = strchr(ptr,' ') + 1;  // Hop to next blank space. Note this skips over ; if there is no space beforehand
    
        }
    
      return value;
      } 
    
    

    Also in the receive loop I stamped the NULL onto the termination LF or CR code, rather than after:

    // entire message received
    buffer[–sofar] = 0; // end the buffer, replace ‘\n’ or ‘\r’ with 0 so string functions work correctly

    #10723
    Dan
    Keymaster

    What about when you have

    G00 X10; G00 X-10

    ?

    while(ptr>=buffer)

    could be a very long time.

    #10732
    Anonymous
    Inactive

    Hi Dan,

    Added some extra input character processing; now can handle input without spaces, lower case command letters, and ; comments:

    
    bool commandFlag = true;
    
    ...
    
    void loop()
      {
      // listen for serial commands
      while(SerialUSB.available() > 0)  // is something available in receive buffer?
        {
        char c = SerialUSB.read();  // get it
        SerialUSB.print(c);  // repeat it back so I know you got the message
    
        if(commandFlag == true)
          {
          if(sofar < MAX_BUF-1)
            {
            if(c == ';')     // check for a comment - ignore rest of line
              {
              commandFlag = false;
              }
            else
              {
              if(c > 96)    // Check for lower case letter           
                c = c - 32; // Turn into upper case
              if(sofar)     // Not for first character in buffer, don't index outside array !!!
                {
                if((c >= 64) && (buffer[sofar -1] < 58) && (buffer[sofar -1] > 45))   // if letter and previous was a number          
                  {
                  buffer[sofar++] = ' ';  // add space, then post incriment sofar count            
                  }
                }
              buffer[sofar++] = c;  // store it, then post incriment sofar count
              }
            }
          else
            {
            SerialUSB.println(F("E002 ;Command String Too Long."));  // complain to human
            }
          }
    
        if((c == '\n') || (c == '\r'))  // entire message received
          {
          if(commandFlag)         // look to see if command... 
            buffer[--sofar] = 0;  // end the buffer, replace '\n' or '\r' with 0 so string functions work correctly
          else                    // ... or a comment, when we do not want to back space onto previous command info
            buffer[sofar] = 0;    // end the buffer, replace '\n' or '\r' with 0 so string functions work correctly
    
          SerialUSB.print(F("\r\n"));  // echo a return character for humans
    
          if(sofar)   // If we have something in buffer to process
            {
            processCommand();       // do something with the command
            }
    
    // The following tidy-up was in ready() - moved here for clarity of operation
          commandFlag = true;       // Allow for new command processing - add to ready() if not here
          sofar = 0;                // clear input buffer index pointer
          SerialUSB.print(F(">"));  // signal ready to receive input
          }
    
        }  // End: while(SerialUSB.available() > 0)
      }    // End: loop()
    

    Which gives for example the following input string: g1x-10y-10;g1x10y10;hdhddh

    >g1x-10y-10;g1x10y10;hdhddh

    String ready to process. buffer [G1 X-10 Y-10] & sofar = 12
    parsenumber() start = G
    while(…) loop = 1 : G : G1 X-10 Y-10
    parsenumber() found code – return new value = 1.00
    processCommand() G1
    parsenumber() start = F
    while(…) loop = 1 : G : G1 X-10 Y-10
    while(…) loop = 2 : X : X-10 Y-10
    while(…) loop = 3 : Y : Y-10
    parsenumber() return original value = 50.00
    parsenumber() start = X
    while(…) loop = 1 : G : G1 X-10 Y-10
    while(…) loop = 2 : X : X-10 Y-10
    parsenumber() found code – return new value = -10.00
    parsenumber() start = Y
    while(…) loop = 1 : G : G1 X-10 Y-10
    while(…) loop = 2 : X : X-10 Y-10
    while(…) loop = 3 : Y : Y-10
    parsenumber() found code – return new value = -10.00
    ^<^<^<^<^<^<^<^<^<^<^<^<^<^<^<^<^<^<^<^<*
    Finished command.

    >

    and this for M codes:

    >m114;dfff

    String ready to process. buffer [M114] & sofar = 4
    parsenumber() start = M
    while(…) loop = 1 : M : M114
    parsenumber() found code – return new value = 114.00
    processCommand() M114
    X10.00
    Y10.00
    F50.00
    ABS
    Finished command.

    >

    The processing still assumes a certain sanity of input, this just covers the basic variations.

    Best,
    Susan.

    #10735
    Dan
    Keymaster

    The things you have added are not in the specs for GCODE.

    Please limit your fix to the actual problem.

    What do you bet there’s a compiler option that makes your system process the arduino code without error?

    #10736
    Anonymous
    Inactive

    My apologies, sorry to trouble you. I will go away now.
    S.
    =====================================================

    reprap.org/wiki/G-code

    Comments: Gcode comments begin at a semicolon, and end at the end of the line:

    N3 T0*57 ; This is a comment
    N4 G92 E0*67
    ; So is this
    N5 G28*22

    Comments and white space will be ignored by your RepRap Printer. It’s better to strip these out on the host computer before sending the Gcode to your printer, as this saves bandwidth.

    #10738
    Dan
    Keymaster

    You are correct that they end with a semicolon. Does anyone put gcode AFTER the semicolon? Programmer error.

    Does Gcode come with lower-case letter or no-space-before letters? No, so no need to cover those cases.

    I love that you’re making an effort, that’s rare and special. I want to encourage you. How? The fixes you’re proposing create new problems. By pointing them out I’m sending the “you’re doing it wrong” message instead of the “good try!” message.

    #10741
    Anonymous
    Inactive

    I appreciate the G-code spec may be nice and tidy, real world examples less so. E.g. the following G-code line as per webpage from: http://www.daycounter.com/Calculators/GCode/GCode-Circle.phtml generated G-code with default settings.

    G2 X0.5625Y1.0000 i0.4375j0 z-0.0625

    Note mix of upper and lower case letters, and no spaces or multiple spaces between each code/number section.

    With the additional input processing (and I have also now added a test to remove multiple spaces) the command input will be processed as follows:

    >G2 X0.5625Y1.0000 i0.4375j0 z-0.0625

    String ready to process. buffer [G2 X0.5625 Y1.0000 I0.4375 J0 Z-0.0625] & sofar = 38
    parsenumber() start = G
    while(…) loop = 1 : G : G2 X0.5625 Y1.0000 I0.4375 J0 Z-0.0625
    parsenumber() found code – return new value = 2.00
    processCommand() G2
    parsenumber() start = F
    while(…) loop = 1 : G : G2 X0.5625 Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 2 : X : X0.5625 Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 3 : Y : Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 4 : I : I0.4375 J0 Z-0.0625
    while(…) loop = 5 : J : J0 Z-0.0625
    while(…) loop = 6 : Z : Z-0.0625
    parsenumber() return original value = 50.00
    parsenumber() start = I
    while(…) loop = 1 : G : G2 X0.5625 Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 2 : X : X0.5625 Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 3 : Y : Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 4 : I : I0.4375 J0 Z-0.0625
    parsenumber() found code – return new value = 0.44
    parsenumber() start = J
    while(…) loop = 1 : G : G2 X0.5625 Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 2 : X : X0.5625 Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 3 : Y : Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 4 : I : I0.4375 J0 Z-0.0625
    while(…) loop = 5 : J : J0 Z-0.0625
    parsenumber() found code – return new value = 0.00
    parsenumber() start = X
    while(…) loop = 1 : G : G2 X0.5625 Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 2 : X : X0.5625 Y1.0000 I0.4375 J0 Z-0.0625
    parsenumber() found code – return new value = 0.56
    parsenumber() start = Y
    while(…) loop = 1 : G : G2 X0.5625 Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 2 : X : X0.5625 Y1.0000 I0.4375 J0 Z-0.0625
    while(…) loop = 3 : Y : Y1.0000 I0.4375 J0 Z-0.0625
    parsenumber() found code – return new value = 1.00
    *
    Finished command.

    >

    The situation for myself is that I am using the Arduino platform and IDE so I can hand over projects to our group’s PhD students to do further work on and install and maintain on various experiments running in our laser labs. To that end I have to keep the gribly electronics and firmware coding bits as simple as possible to make it accessible to people who don’t have an electronics and hardware level coding background. They are expected to use MatLab (or LabView) so the step across to Arduino coding isn’t as steep as it might otherwise be once they get the point about the hardware not being abstracted behind high level API’s. Amongst other things I also get to teach them how to solder and use my workshop’s drilling machine safely.

    Anyway, I reiterate my apologies for these digressions.

    #10742
    Anonymous
    Inactive

    P.S. Multiple spaces not being displayed by the web page formatting šŸ™‚

    
    G2 X0.5625Y1.0000   i0.4375j0   z-0.0625
    

    May display this properly?

    #10971
    Dan
    Keymaster

    Checked in a fix for both firmwares that eliminates C++ error by typecasting pointers to long.

    #10974
    Anonymous
    Inactive

    Hi Dan,
    Awesome; thanks for update / letting me know.
    Best,
    Susan.

Viewing 21 posts - 1 through 21 (of 21 total)
  • You must be logged in to reply to this topic.