Shop OBEX P1 Docs P2 Docs Learn Events
Question about use of ON ... GOTO/GOSUB and LOOKDOWN in SX/B — Parallax Forums

Question about use of ON ... GOTO/GOSUB and LOOKDOWN in SX/B

RobotWorkshopRobotWorkshop Posts: 2,307
edited 2008-02-29 15:35 in General Discussion
Ok, i'm not 100% clear on the way that the ON..GOTO/GOSUB will work when it gets values other than the ones in the list. Below is the example from the SX/B help file:
FROM SX/B Help File... said...

A variant of the ON... instruction essentially combines LOOKDOWN into the syntax. For example:

Main:
cmd = RX_BYTE
LOOKDOWN cmd, "L", R", "S", cmd
ON cmd GOSUB Robot_Left, Robot_Right, Robot_Stop
GOTO Main

... can be combined to:

Main:
cmd = RX_BYTE
ON cmd = "L", R", "S" GOSUB Robot_Left, Robot_Right, Robot_Stop
GOTO Main

It is pretty obvious what will happen when RX_BYTE returns an L, R, or S. However it isn't so clear what it may do if any other characters are received. Part of this is due to that the LOOKDOWN command states that the value of cmd is unchanged if it doesn't match any of the characters. I see a potential problem with this in the code above. What happens if RX_BYTE happens to return a 0, 1, or 2? Wouldn't that also send it to the subroutines too? That being the case wouldn't you need to check to see if cmd was less than 3 and then skip over the next line if it was?

Has anyone else run into this?

Robert

Post Edited (RobotWorkshop) : 2/27/2008 10:09:03 PM GMT

Comments

  • JonnyMacJonnyMac Posts: 9,216
    edited 2008-02-25 22:59
    You're right, which is why I don't use the LOOKDOWN approach in my programs that accept serial commands. While it may seem verbose, checking the characters as they come in prevents issues. For example, our FC-4 fader board uses this bit of code for processing commands from the master:

    Main:
      char = RX_BYTE                                ' wait for header
      IF char <> "!" THEN Main
      char = RX_BYTE ToUpper
      IF char <> "F" THEN Main
      char = RX_BYTE ToUpper
      IF char <> "C" THEN Main
      char = RX_BYTE
      IF char <> "4" THEN Main
    
    
    Check_Addr:
      char = RX_BYTE                                ' rx addr
      allCall = No                                  ' clear global request
      IF char = CmdAll THEN                         ' all call requested?
        allCall = Yes                               '   yes, set flag
        GOTO Get_Cmd                                '   and skip ID check    
      ENDIF
      addr = %00                                    ' clear old setting
      addr.0 = ~A0                                  ' get low bit of address
      addr.1 = ~A1                                  ' get high bit of address
      IF char <> addr THEN Main                     ' validate
    
    
    Get_Cmd:
      char = RX_BYTE ToUpper                        ' rx command byte
      IF char = "V" THEN Show_Version
      IF char = "G" THEN Get_Status
      IF char = "L" THEN Set_Level
      IF char = "S" THEN Set_All
      IF char = "P" THEN Preset_All
      IF char = "A" THEN All_On
      IF char = "D" THEN Digital_Control
      IF char = "X" THEN Reset_All 
      IF char = "F" THEN Fader
      IF char = "C" THEN Cross_Fade
      GOTO Main
    


    It may seem verbose, but as the style is close to the assembly output it doesn't take a lot of code, and it's quite easy to add or disable features while testing.
  • RobotWorkshopRobotWorkshop Posts: 2,307
    edited 2008-02-27 21:26
    Doesn't seem verbose at all. Actually it's a lot shorter than what i'm trying to use in the program i'm working on at the moment. I'm going to go through and optimize mine a bit but want to get it working right first. I'm having a problem with it and I think I just need to take a step back or have another set of eyes look over the main routine. It is for an SX48 acting as a co-processor for my robot project. The ISR in the background is handing the reception of the serial commands which it buffers using your serial code. It also keeps track of an encoder for the motor.

    What i'm trying to do which is a bit different from your code above is to check to see if any serial data has been received. If some has come in then it will deal with it and then go on to do some other processing before going back to the main loop again. If no data has been received then it goes ahead and does it's processing and loops back to main to check for serial data and so on. If this works out the way I hope it may be a useful technique to incorporate into other SX/B programs. Any suggestions for the code below?

    The weird part is that it doesn't seem to fall through and do the regular processing unless I keep sending some data to it. It's almost as if my check to see if any characters are in the Serial receive buffer isn't working right. Besides that all the other routines seem to work ok. If I uncomment the first couple lines in the main routine it just displays the serial data on a set of 8 leds as a test. That part works perfect now. I had problems with that which were caused by extra Timer Interrupts but that was fixed by masking them off at the beginning of the program. Below is a section of the code where the main loop is:



    ' Enable ISR to start accepting serial data
    active = True ' Serial RX disabled until this flag is True
    HdBsy = False ' Tell Host we're ready for action!
    actmov = False ' We're not in an active movement.

    Main:

    'Leds=RX_BYTE
    'GOTO Main

    IF rxBufCnt = 0 THEN
    GOTO Motor_Control
    ENDIF


    ' cmdChar should be non-zero for a mutli-byte command
    IF cmdChar = 0 THEN
    char = RX_BYTE
    IF char = "C" THEN ' 67
    GOSUB CLR_FLAGS
    GOTO Motor_Control
    ENDIF
    IF char = "G" THEN ' 71
    GOSUB GO_TARGET ' Goto position encTarget
    GOTO Motor_Control
    ENDIF
    IF char = "H" THEN ' 72
    GOSUB HOME_HEAD ' Set encTarget to 0 then set actmov = True
    GOTO Motor_Control
    ENDIF
    IF char = "I" THEN ' 73
    GOSUB INIT_HEAD ' Initialize head to point forward and set encValue & encTarget to 0
    GOTO Motor_Control
    ENDIF
    IF char = "F" THEN ' 70
    cmdChar = char ' Set LEDs near wheels
    GOTO Motor_Control
    ENDIF
    IF char = "L" THEN ' 76
    cmdChar = char ' Set LED's on front (8 LEDs)
    GOTO Motor_Control
    ENDIF
    IF char = "S" THEN ' 83
    cmdChar = char ' Set new encTarget postion
    GOTO Motor_Control
    ENDIF

    ' We must have received a junk command. Just flash the LEDs for now.
    GOSUB COMM_ERR

    ELSE
    IF rxBufCnt > 0 THEN
    IF char = "F" THEN ' 70
    GOSUB FOOT_LEDS
    cmdChar = 0
    GOTO Motor_Control
    ENDIF
    IF char = "L" THEN ' 76
    GOSUB Chest_Leds
    cmdChar = 0
    GOTO Motor_Control
    ENDIF
    ENDIF

    IF rxBufCnt > 1 THEN
    char = RX_CHKBYTE
    IF char = "S" THEN ' 83
    GOSUB Set_Target
    cmdChar = 0
    GOTO Motor_Control
    ENDIF
    ENDIF
    ENDIF

    Motor_Control:

    IF actmov = True THEN

    ' Check encoder & remaining distance.
    ' Reached position? If so, stop movement and reset flag
    encOffset = encTarget - encValue
    IF encOffset.15 = True THEN
    encOffset = -encOffset ' Use 2's complement to get ABS value
    ENDIF

    IF encOffset < 2 THEN
    actmov = False
    TIMER1 TIMER
    LOW Spd1
    HdBsy = False
    ELSE

    ' We should be moving so adjust and update pwm
    encOffset = encTarget - encValue
    IF encOffset.15 = True THEN
    Dir1 = 1 ' If the difference is negative we should move left
    encOffset = -encOffset ' Use 2's complement to get ABS value
    ELSE
    Dir1 = 0
    ENDIF

    ' Figure out how fast to move
    m1Spd = 750 ' Slow

    IF encOffset > 50 THEN
    m1Spd = 925 ' Medium
    ENDIF
    IF encOffset > 100 THEN
    m1Spd = 1110 ' Fast
    ENDIF

    TIMER1 PWM, m1Spd, dutyCycle

    ENDIF

    ENDIF

    ' check to see if head moved on it's own (or someone moved it)
    ' if more that allowable variance
    ' setup new movement and set flag

    ' IF encTarget = encValue THEN
    ' GOTO Done
    ' ENDIF
    encOffset = encTarget - encValue
    IF encOffset.15 = True THEN
    encOffset = -encOffset
    ENDIF
    IF encOffset > 10 THEN
    actmov = True
    HdBsy = True
    ENDIF

    Done:
    GOTO Main ' Go back and do it all again.

    Post Edited (RobotWorkshop) : 2/27/2008 10:09:23 PM GMT
  • JonnyMacJonnyMac Posts: 9,216
    edited 2008-02-27 23:04
    I've attached a program (16 channel, MiniSSC-compatible servo controller for the Prop-SX) that uses a minor update to my serial stuff. Whenever there is anything in the receive buffer a flag called rxReady is set. If you need to wait for a specific number of bytes you can always check rxBufCnt.
  • RobotWorkshopRobotWorkshop Posts: 2,307
    edited 2008-02-27 23:26
    Thanks for the example!

    I can see the flag you added for rxReady. Do you see any reason why the just checking rxBufCnt wouldn't do the same thing?

    For some reason this latest program i've been working on is just fighting me all the way. I'm sure it's just me overlooking something simple and it will stand out soon.

    Robert
  • JonnyMacJonnyMac Posts: 9,216
    edited 2008-02-28 00:20
    Theoretically, could just check rxBufCnt; using the flag saves a few cycles, though, as the flag is in the global variable space and you can do a JB or JNB with it.
  • RobotWorkshopRobotWorkshop Posts: 2,307
    edited 2008-02-28 01:29
    That makes sense and is a good reason to use that instead. One thing that i've noticed in your latest program is the way you've got the variables split up in different arrays. I would have thought that all the servo values could have been all in a single array and the counts in another. It looks like you've got it structured the way due to the way the SX handles the banks and you can just keep working in a single bank at a time. That is something i'll need to be wary of in my own programs. Does SX/B only automatically handle the banking when doing basic statements and ignore it when it encounters a section of ASM code? If so, a section that covers the best practices for the layout of variables and banks would make an excellent addition to your book.

    I can see that you've been primarily using the SX28 chips. Have you done much on the SX48 with your code? Bean mentioned in another thread that instead of using a BANK 0 that a BANK $10 should probably be used on the SX48. If that is the case than perhaps another topic for the help file (or book) could be using conditional compiling so that the same code can assemble for both the SX28 and SX48. It seems that for many programs there isn't much difference but for some it may help.
  • JonnyMacJonnyMac Posts: 9,216
    edited 2008-02-28 01:34
    Arrays and banks are handled differently on the SX28 and SX48, and for this program it made sense -- so that others could follow the code -- just to drop in a second virtual servo controller, especially since it would have been complicated to have the timers in one bank and the pin pointers in another (if you look closely I'm running two pins at once, one on each port). Most of my programs are for the SX28 and I tend to declare 16-byte arrays so that individual banks are used; that keeps things easy to track when working with those variables in assembly.

    In high-level SX/B, there are no worries, all the gory bank-pointing stuff is automated -- in fact, I have learned a ton by looking at the output created by SX/B.
  • RobotWorkshopRobotWorkshop Posts: 2,307
    edited 2008-02-29 03:03
    JonnyMac said...
    I've attached a program (16 channel, MiniSSC-compatible servo controller for the Prop-SX) that uses a minor update to my serial stuff. Whenever there is anything in the receive buffer a flag called rxReady is set. If you need to wait for a specific number of bytes you can always check rxBufCnt.

    Hello Jon,

    I'm confused. I've looked at the Serial code in your 16 Channel servo controller and compared it to the previous code for the ISR based serial routines. It appears that both are using an 8 byte circular buffer to store incoming serial characters. The part that is confusing is that you check a different bit in the latest code to see if the buffer is full:

    JB rxBufCnt.4, RX_Done

    and in the older version you have:

    JB rxBufCnt.3, RX_Done

    From looking at the code I would expect that the .3 is correct for an 8 byte buffer and the .4 would be for a 16 byte buffer. If that is the case I can see where some of your variables might get overwritten if the data came in fast enough. Is there another reason for this?

    Robert
  • JonnyMacJonnyMac Posts: 9,216
    edited 2008-02-29 15:35
    Whoops! Error on my part -- I was going to implement a 16-byte buffer (in another bank) and then changed my mind; I forgot to fix that bit. Thanks for the catch.
Sign In or Register to comment.