Shop OBEX P1 Docs P2 Docs Learn Events
ser.rxtime Discovery — Parallax Forums

ser.rxtime Discovery

T ChapT Chap Posts: 4,223
edited 2014-12-06 09:30 in Propeller 1
I like to read data by setting the first read as an .rx so the loop just sits there till something arrives. Then any reads after that are rxtime. In the past I have rarely used speeds of 115200, but needed to on this project. After a number of hours I finally figured out the problem of why rx would would but rxtime would not on the same data stream. Even at 57600, the rxtime was taking too much time doing it's math.

repeat until (rxbyte := rxcheck) => 0 or (cnt - t) / (clkfreq / 1000) > ms

So I tried a different method shown below that still allows the rx to time out, but it works perfectly so far.

  Repeat
     ser.rxflush
     ms := 1
     i :=0  ' Start loop here for Data in
     SerialReadBuffer[i++] := ser.rx                     ' waits here for first PACKET HEADER byte  (1F) from USB
     SerialReadBuffer[i++] := ser.rxtime(ms)        ' waits here for second byte OPCODE
     SerialReadBuffer[i++] := ser.rxtime(ms)        ' waits here for second byte LENGTH
     Repeat 199 'SerialReadBuffer[2]  + 10       
          SerialReadBuffer[i++] := ser.rxtime(ms) 


PUB rxtime(ms) : rxbyte | t
'' Wait ms milliseconds for a byte to be received
'' returns -1 if no byte received, $00..$FF if byte

' added these next 5

  repeat while (rxbyte := rxcheck) < 0
     rxtimeout++
     if rxtimeout++ >  10000    'prevent getting stuck in a rx
        rxtimeout~
        return -1

  't := cnt
  'repeat until (rxbyte := rxcheck) => 0 or (cnt - t) / (clkfreq / 1000) > ms   ' original

Comments

  • dbpagedbpage Posts: 217
    edited 2014-12-01 15:15
    I like your strategy. Thank you for sharing. I don't see how ms is used to change the timeout period.
  • JDatJDat Posts: 103
    edited 2014-12-01 16:38
    dbpage! ms aren't used.

    Right now I have code with .rxtime(ms) instead of .rx, but I am using "PBnJ_serial" library.
    Tested with short data packets (20 bytes) ad 115_200. No problems.
    Current FIFO buffer length 32 bytes.

    Tomorrow I will test with 400- 24_000 byte packets.
    var
       long parser
    PUB event
      if (something==true)
        parsermode:=true
      else
       parsermode:=false
    PUB parser | inchar,inlong,incount,outlong,
    'run in new cog
    repeat
      dataport.rxflush
      inlong:=0
      outlong:=-1
      incount:=0
      repeat while parsermode==true
        inchar:=dataport.rxtime(10)    'wait 10 mSec
        if (inchar==-1)
          inlong:=0
          incount:=0
          outlong:=-1
        else
          inlong <<= 8
          inlong += inchar
          incount++
          if incount>3
            outlong:=inlong
            inlong:=0
            incount-=4
    
        case outlong & $FF_00_00_00
          $00_00_00_00:             'parse data
                'do something
          $01_00_00_00:             'command 1
                'do something
          $02_00_00_00:             'command 2
                'do something
          other:                          'garbage
                'do anything
       outlong:=-1    ' !!!reset after parse!!!
    
  • T ChapT Chap Posts: 4,223
    edited 2014-12-01 16:40
    dbpage wrote: »
    I like your strategy. Thank you for sharing. I don't see how ms is used to change the timeout period.

    In the new method ms does nothing. The main idea is that you send data and it cannot lock up ever. It is not required to specify the length of data as long as you have enough repeats to capture the max data stream. If the data is less it doesn't matter
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-12-01 22:38
    It's not clear to me how the original is locking up. The timeout is set for an instant in the future, which at 80MHz clock may be as much as 26.8 seconds. What value in ms are you using for the timeout?

    One possibility is that your incoming data at 115k is overrunning the serial data buffer. The Spin code is slow and isn't removing characters from the serial data buffer as fast as they appear there. The solution to that is to either increase the size of the buffer, or (as you have done) increase the speed of de-buffering the data.

    Note that computations within a repeat loop are recalculated every time around the loop, and this applies to the costly division of clkfreq/1000.
    repeat until (rxbyte := rxcheck) => 0 or (cnt - t) / [b](clkfreq / 1000)[/b] > ms
    
    That could be precomputed and taken out of the loop. pre-compute clk1000 := clkfreq/1000, then
    t := cnt
    repeat until (rxbyte := rxcheck) => 0 or cnt-t > clk1000 * ms
    
    That takes the the division out of the repeat loop, but still a long expression with a multiply to evaluate before returning a value. The following is more like your approach, so it can respond quickly if a character is already in the buffer.
    t := cnt
    repeat while (rxbyte := rxcheck) < 0
        if cnt - t > clk1000 * ms
          quit
    


    I'm uncomfortable with your approach in that its timeout is not based on clkfreq. Just a number of spin loops, good enough for escape but hard to figure the definite time allowed.
  • T ChapT Chap Posts: 4,223
    edited 2014-12-02 05:37
    Tracy, I think that is the issue with the original repeat loop, it was taking too long to compute and the data was passing by. I did not mean to imply "locking up" with the old version. The example you posted works perfectly too, I swapped that in mine.

    Below is the actual code. This allows any length of data to be sent and can never hang up.
      Repeat
         ser.rxflush
         ms := 5
         i :=0  ' Start loop here for Data in
         SerialReadBuffer[i++] := ser.rx             ' 0 waits here for first PACKET HEADER byte  (1F) from USB
         SerialReadBuffer[i++] := ser.rxtime(ms)        ' 1 second byte OPCODE
         SerialReadBuffer[i++] := ser.rxtime(ms)        ' 2 third byte LENGTH of Data to receive
         SerialReadBuffer[i++] := ser.rxtime(ms)        ' 3 Chksum
         SerialReadBuffer[i++] := ser.rxtime(ms)        ' 4 Target Single  number  to Update  
         SerialReadBuffer[i++] := ser.rxtime(ms)        ' 5 Transfer Range Start  number
         SerialReadBuffer[i++] := ser.rxtime(ms)        ' 6 Transfer Range END  number 
         SerialReadBuffer[i++] := ser.rxtime(ms)        ' 7 Future
         SerialReadBuffer[i++] := ser.rxtime(ms)        ' 8 Future
         SerialReadBuffer[i++] := ser.rxtime(ms)        ' 9 Future 
    
         Repeat SerialReadBuffer[2]              
              SerialReadBuffer[i++] := ser.rxtime(ms) 
    
    
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-12-06 09:30
    By the way, in my serial port routines I've modified the condition for the NOECHO mode to use RxTime instead of plain RX, and have a default and a settable timeout. The Tx method implements NOECHO in Spin by receiving and discarding one char for each char that it transmits.

    As a bug this bit me in a situation using RS485 (or SPI), where occasionally and unpredictably the device on the other end did not echo, and that caused the Prop to lock up waiting for an Rx that never came. There'd be other workarounds, but NOECHO is the most convenient way to separate commands from returned data.
Sign In or Register to comment.