Shop OBEX P1 Docs P2 Docs Learn Events
Problem with serial comms in FDS — Parallax Forums

Problem with serial comms in FDS

Don MDon M Posts: 1,647
edited 2015-06-24 11:49 in Propeller 1
I am communicating with a coin hopper. Seems rather simple- you send it a serial command, it responds with a message and dispenses coins.

In my routine I reset the communication message counter, pause then issue a coin dispense command for 1 coin, pause then issue a second coin dispense command for 4 coins.

For some reason my code won't receive the response from the hopper the second time through the dispense routine. I can see it on the logic analyzer and it looks exactly as expected. I've tried putting rxflush in several different spots and using rxtime or just rx but to no avail. It dispenses the coins fine and as I said the analyzer shows the response every time.

Here's the screen shot of PST:

attachment.php?attachmentid=114535&d=1435118453

Here's my code:
con

  _clkmode        = xtal1 + pll16x                      ' Feedback and PLL multiplier
  _xinfreq        = 5_000_000                           ' External oscillator = 5 MHz

  MS_001   = 80_000_000 / 1_000                         ' ticks in 1ms
  
  HOPRX = 0                     ' to pin 6 on Hopper
  HOPTX = 1                     ' to pin 5 on Hopper
  HOPADD = $00                  ' Hopper address number 0 - 7 for daisy chaining additional hoppers                         

obj

  term  : "fullduplexserialplus"
  hopper: "fullduplexserialplus"

var

  byte  Ser_Num                 ' used to keep track of each dispense command so as not to repeat
  byte  ACK_Message[12]         ' buffer for the ACK messages
  word  Num                     ' Number of coins

pub main | d

  term.start(31, 30, %0000, 115_200)                    ' start terminal (use PST)
  hopper.start(HOPRX, HOPTX, %0000, 9600)               ' start hopper serial port  

  pause(600)

  term.str(@ver)

  pause(1000)

  Reset_Dispenser

  pause(1000)  

  'Ser_Num := 1                                          ' set serial number

  Num := 1
  Dispense

  pause(1000)

  Num := 4
  Dispense

pub Dispense | chk1, chk2, d, idx

  idx := 0

  hopper.rxflush

  hopper.tx($ED)                ' Header
  hopper.tx($08)                ' Number of bytes in message. Always 8
  hopper.tx(Ser_Num)            ' Command serial number. Should increment after each successful command
  hopper.tx($50)                ' Dispense command
  hopper.tx(HOPADD)                
  hopper.tx(Num & $FF)          ' Lower byte of quantity to dispense
  hopper.tx(Num >> 8)           ' Higher byte of quantity to dispense
  chk1 := $ED ^ $08 ^ Ser_Num ^ $50 ^ HOPADD ^ (Num & $FF) ^ (Num >> 8)
  hopper.tx(chk1)

  hopper.rxflush 

  d := hopper.rxtime(200)                   
  if d == $FD
    byte[ACK_Message][idx] := d
    chk1 := d
    idx++

    repeat 4
      d := hopper.rx 
      byte[ACK_Message][idx] := d
      chk1 := chk1 ^ d
      idx++
      
    chk2 := hopper.rx
    byte[ACK_Message][idx] := chk2
    
    if chk1 == chk2  
      Ser_Num++
      repeat idx from 0 to 5
        term.hex(byte[ACK_Message][idx], 2)
        term.tx(32)
      term.str(string("Hopper Dispense ACK OK", 13))   
    else
      term.str(string("Bad Checksum for Hopper Dispense...", 13))
    
  else
    term.hex(d, 2)
    term.tx(32)
    term.str(string("No comms for Hopper Dispense...", 13))  
  
  
pub Reset_Dispenser | chk1, chk2, d, idx

  hopper.rxflush

  hopper.tx($ED)                ' Header
  hopper.tx($08)                ' Number of bytes in message. Always 8
  hopper.tx($00)                ' Command serial number. Sending a "0" will reset the counter in hopper
  hopper.tx($50)                ' Dispense command
  hopper.tx(HOPADD)                
  hopper.tx($00)                ' Lower byte of quantity to dispense
  hopper.tx($00)                ' Higher byte of quantity to dispense
  chk1 := $ED ^ $08 ^ Ser_Num ^ $50 ^ HOPADD ^ (Num & $FF) ^ (Num >> 8)
  hopper.tx(chk1)

  idx := 0
  chk1 := 0
  chk2 := 0

  d := hopper.rxtime(200)                   
  if d == $FD
    byte[ACK_Message][idx] := d
    chk1 := d
    idx++

    repeat 4
      d := hopper.rx 
      byte[ACK_Message][idx] := d
      chk1 := chk1 ^ d
      idx++
      
    chk2 := hopper.rx
    byte[ACK_Message][idx] := chk2
    
    if chk1 == chk2  
      Ser_Num++
      repeat idx from 0 to 5
        term.hex(byte[ACK_Message][idx], 2)
        term.tx(32)
      term.str(string("Reset Hopper ACK OK", 13))   
    else
      term.str(string("Bad Checksum for Hopper Reset...", 13))
  else
    term.str(string("No comms for Hopper Reset...", 13))  

   
pub pause(ms) | t

'' Delays program for ms milliseconds

  t := cnt
  repeat ms
    waitcnt(t += MS_001)

Any suggestions?
741 x 460 - 20K

Comments

  • StefanL38StefanL38 Posts: 2,292
    edited 2015-06-23 21:18
    dispensing 4 coins needs more time. is it possible that your device throws away serial data that was rushing in to soon?

    did you try some debugging by sending all the raw bytes to the terminal that come in from the device?

    or did you try to send debug-info after each received byte?

    best regards

    Stefan
  • Don MDon M Posts: 1,647
    edited 2015-06-23 21:38
    StefanL38 wrote: »
    dispensing 4 coins needs more time. is it possible that your device throws away serial data that was rushing in to soon?

    I even tried changing the pause time between dispenses to 5 seconds which is way more time than needed to wait for dispensing coins and no difference.
    StefanL38 wrote: »
    did you try some debugging by sending all the raw bytes to the terminal that come in from the device?

    Yes. See the screen shot of PST. It shows the message buffer from the hopper. The last line shows an FF for some reason. That is what I'm trying to solve.
    StefanL38 wrote: »
    or did you try to send debug-info after each received byte?

    Yes.

    Here are the screen shots from the Logic Analyzer. The first one shows the command from the Prop to reset the hopper and the hoppers immediate response:

    attachment.php?attachmentid=114537&d=1435120313


    The second one shows the command to dispense 1 coin and the immediate response back from hopper:

    attachment.php?attachmentid=114538&d=1435120518

    The third shows the command to dispense 4 coins then the immediate response back from the hopper:

    attachment.php?attachmentid=114539&d=1435120529
    906 x 280 - 17K
    1024 x 274 - 163K
    1024 x 252 - 72K
  • Don MDon M Posts: 1,647
    edited 2015-06-23 21:44
    So I waited 5 seconds after the 1 coin dispense and then send the 4 coin dispense command. I can't seem to pickup the initial byte of the response- the "0xFD" as I do the first 2 times. This stumps me...
  • SapphireSapphire Posts: 496
    edited 2015-06-23 22:14
    FDS is somewhat sensitive to noise on the RX pin. I've seen this same thing on my devices. An $FF is in the buffer before the actual response data. To confirm that, write out everything you get, and see if the rest of the response is okay after the $FF. If so, you could choose to ignore the $FF and proceed with the rest of the data, or you could try to eliminate the problem. For example, I've put a .001uF cap on the RX pin and it went away.
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-06-24 04:54
    $FF is just the lower 8-bits of -1. rxtime returns a value of -1 if it times out. Remove the second call to rxflush in the Dispense method. It is not needed. You traces show that the response doesn't overlap the request, but maybe it does in some cases.
  • Don MDon M Posts: 1,647
    edited 2015-06-24 05:06
    Dave- I tried adding / deleting / moving around the rxflush. Made no difference. When I get back to it this morning I'm going to look at the signal with a scope. It may be that I need a pull up / down resistor. The fact that it works 2 out of 3 times every time tells me that maybe the code isn't the problem. What is confusing is that the logic analyzer ALWAYS sees it but maybe it has a different threshold tolerance. I'll let you know.

    And btw.. I am NOT sharing the same power supply for the hopper with the prop board. It is a separate supply.
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-06-24 06:03
    Add the following method to your top object and replace the calls to hopper.rxflush with rxdump. This will tell you if you're flushing anything useful.
    PUB rxdump
      repeat while (result := hopper.rxcheck) <> -1
        term.str(string("Dumping $")
        term.hex(result, 2)
        term.tx(13)
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-06-24 06:54
    Don M wrote: »
    And btw.. I am NOT sharing the same power supply for the hopper with the prop board. It is a separate supply.

    Do they share a common ground? If not they need to unless you're using something like RS-485.

    I also think you're dumping the RX buffer too frequently. Dump it once before you start to send data but the way the code reads now you dump it again after the send which could easily throw away the first character.

    I definitely think you should be checking to see what's getting dumped from the RX buffer as Dave suggested.

    Edit: It looks like the two devices are communicating elsewhere in the code so I bet you're already using a common ground.
  • Don MDon M Posts: 1,647
    edited 2015-06-24 07:01
    Ok I got it to work. I ignored storing anything in the ACK_Message and just displayed the bytes as received. Works every time. It appears that my problems have to do with storing the values in the ACK_Message buffer. I must have a problem with my indexing logic I don't know. I'll have to look into that further. I'll need the buffer later because I'll want to test some bits in a certain byte of that buffer.

    Here's my latest working code. I made quite a few changes from the original.
    con
    
      _clkmode        = xtal1 + pll16x                      ' Feedback and PLL multiplier
      _xinfreq        = 5_000_000                           ' External oscillator = 5 MHz
    
      MS_001   = 80_000_000 / 1_000                         ' ticks in 1ms
      
      HOPRX = 0                     ' to pin 6 on Hopper
      HOPTX = 1                     ' to pin 5 on Hopper
      HOPADD = $00                  ' Hopper address number 0 - 7 for daisy chaining additional hoppers                         
    
    obj
    
      term  : "FullDuplexSerial256"
      hopper: "FullDuplexSerial256"
    
    var
    
      byte  Ser_Num                 ' used to keep track of each dispense command so as not to repeat
      byte  ACK_Message[12]         ' buffer for the ACK messages
      word  Num                     ' Number of coins
    
    pub main | d, idx
    
      term.start(31, 30, %0000, 115_200)                    ' start terminal (use PST)
      hopper.start(HOPRX, HOPTX, %0000, 9600)                  ' start hopper serial port
    
      pause(600)
    
      term.str(@ver)
    
      hopper.rxflush
    
      Ser_Num := 0
      
      pause(1000)
     
      Dispense(0, 1, HOPADD)        ' Reset hopper 
    
      pause(500)
    
      Num := 1
      Dispense(Num, 0, HOPADD)      ' Dispense 1 coin
      Inquire
    
      pause(2000)
    
      Dispense(12, 0, HOPADD)        ' Dispense 12 coins
      Inquire
    
    pub Dispense(Coins, Reset, Addr) | chk1, chk2, d, idx
    
      if Reset == 1
        term.str(string("Sending Hopper Reset Command     "))      
      else
        term.str(string("Sending Hopper Dispense Command  "))  
    
      idx := 0
    
      hopper.rxflush
    
      hopper.tx($ED)                ' Header
      hopper.tx($08)                ' Number of bytes in message. Always 8
      if Reset == 0
        hopper.tx(Ser_Num)          ' Command serial number. Should increment after each successful dispense
      elseif Reset == 1
        hopper.tx($00)
        Ser_Num := 0
      hopper.tx($50)                ' Dispense command
      hopper.tx(addr)                
      hopper.tx(Coins & $FF)        ' Lower byte of quantity to dispense
      hopper.tx(Coins >> 8)         ' Higher byte of quantity to dispense
      chk1 := $ED ^ $08 ^ Ser_Num ^ $50 ^ Addr ^ (Coins & $FF) ^ (Coins >> 8)
      hopper.tx(chk1)
    
      if Reset == 1
        Ser_Num := 1
    
      d := hopper.rxtime(200) 
      if d == $FD
        term.hex(d, 2)
        term.tx(32)
        chk1 := d
        idx++
    
        repeat 4
          d := hopper.rx 
          chk1 := chk1 ^ d
          term.hex(d, 2)
          term.tx(32)
          idx++
    
        chk2 := hopper.rx    
        term.hex(chk2, 2)
        term.tx(32)
        
        if chk1 == chk2
          if Reset == 0 
            term.str(string("      Hopper Dispense ACK OK", 13))
          else
            term.str(string("      Hopper Reset ACK OK", 13))   
        else
          term.str(string("      Bad Checksum for Hopper Dispense...", 13))
        
      else
        term.str(string("      No comms for Hopper Dispense...", 13))  
      
      
    pub Inquire | chk1, chk2, d, idx
    
      term.str(string("Sending Hopper Inquire Command   ")) 
    
      hopper.tx($ED)                ' Header
      hopper.tx($08)                ' Number of bytes in message. Always 8
      hopper.tx(Ser_Num)            ' Command serial number. 
      hopper.tx($51)                ' Inquire command
      hopper.tx(HOPADD)                
      hopper.tx($00)                ' Lower byte of quantity to dispense
      hopper.tx($00)                ' Higher byte of quantity to dispense
      chk1 := $ED ^ $08 ^ Ser_Num ^ $50 ^ HOPADD ^ (Num & $FF) ^ (Num >> 8)
      hopper.tx(chk1)
    
      idx := 0
      chk1 := 0
      chk2 := 0
    
      d := hopper.rxtime(500) 
      if d == $FD
        chk1 := d
        term.hex(d, 2)
        term.tx(32)
        idx++
    
        repeat 6
          d := hopper.rx 
          chk1 := chk1 ^ d
          term.hex(d, 2)
          term.tx(32)
          idx++
    
        chk2 := hopper.rx  
        term.hex(chk2, 2)
        term.tx(32)
        
        if chk1 == chk2  
          Ser_Num++
          term.str(string("Inquire Hopper ACK OK", 13))   
        else
          term.str(string("Bad Checksum for Hopper Inquire...", 13))
      else
        term.str(string("No comms for Hopper Inquire...", 13))
        Inquire  
    
       
    pub pause(ms) | t
    
    '' Delays program for ms milliseconds
    
      t := cnt
      repeat ms
        waitcnt(t += MS_001)
    


    What's odd is that in the spec sheet it shows an INQUIRE command and one of the flags shows whether the hopper is busy or not. But the hopper won't reply at all while it's busy dispensing so I just continue to poll it till it responds. I'll need to add some sort of time out so that it doesn't hang there forever if there is a problem.

    Here's a screen shot of PST showing it working:

    attachment.php?attachmentid=114542&d=1435154454
    710 x 461 - 27K
  • Don MDon M Posts: 1,647
    edited 2015-06-24 07:26
    Duane Degn wrote: »
    Do they share a common ground? If not they need to unless you're using something like RS-485.

    I also think you're dumping the RX buffer too frequently. Dump it once before you start to send data but the way the code reads now you dump it again after the send which could easily throw away the first character.

    I definitely think you should be checking to see what's getting dumped from the RX buffer as Dave suggested.

    Edit: It looks like the two devices are communicating elsewhere in the code so I bet you're already using a common ground.

    Yes. Common ground.
  • Don MDon M Posts: 1,647
    edited 2015-06-24 07:36
    Duane Degn wrote: »
    I also think you're dumping the RX buffer too frequently. Dump it once before you start to send data but the way the code reads now you dump it again after the send which could easily throw away the first character.

    I definitely think you should be checking to see what's getting dumped from the RX buffer as Dave suggested.

    ???? Did you look at my latest code post? I flush the rx buffer once during initialization and only 1 other time before the dispense command is sent. You think that is too often? I figured I'd flush it right before the command was sent so the buffer is clean and waiting for the reply.

    Anyway that seems to work fine. As I mentioned earlier I need to study the proper way to store the data received into an indexed buffer so that I may test certain bytes/bits for flags.

    This is a Chinese made hopper and the command responses documented aren't the same that is experienced. Anyway I'll mark this as solved for now. Please continue to add any comments as you wish.

    Thanks for everyone's help. :-)
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-06-24 11:49
    Don M wrote: »
    ???? Did you look at my latest code post?

    Probably not. The code I saw before commenting had a rxFlush after the command was sent (or so I remember).

    I'm glad it's working. Sounds like a fun project.
Sign In or Register to comment.