Shop OBEX P1 Docs P2 Docs Learn Events
FullDuplexSerial Object - Detecting when data has been sent? — Parallax Forums

FullDuplexSerial Object - Detecting when data has been sent?

DavidMDavidM Posts: 630
edited 2011-02-22 23:24 in Propeller 1
HI,

Is there a way to detect when the data has been sent fully, after you use the TX command?

I would like the current method ( the tx methods) to STOP/PAUSE until the data has been sent.

I need to disable the 485 driver AFTER data has been sent, my data size varies, So far I am doing this with a time out, but this means that I am not transmitting as fast as I want to?

SO..

Q1) Is there any variable in the assembly code that I can READ and check via SPIN code?


Thanks

David M

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2011-02-21 18:28
    ATM tx only makes sure that it doesn't overwrite buffer content, i.e. it blocks until there is free space. Even with a one char buffer you'd only detect that the PASM part has grabbed the character but it may not have left the chip yet.

    If that's sufficient (transmit buffer empty) then you'd need to add a method checking for tx_tail == tx_head. OTOH, if off-chip is required you could use transmit-buffer-empty + a single character delay (bit time * 10) or modify the PASM section to tell you it's gone (although that may be overkill).
  • Mike GreenMike Green Posts: 23,101
    edited 2011-02-21 18:30
    I've used what kuroneko suggested ... check for buffer empty, then one additional character time's delay.
  • DavidMDavidM Posts: 630
    edited 2011-02-21 19:38
    Thanks Kuroneko,

    I will try out what you have suggested, I am reading a tutorial on PASM, I might even use this as an excuse to learn something about assembly!

    thanks

    Dave M
  • DavidMDavidM Posts: 630
    edited 2011-02-21 23:58
    HI,

    Ok, I managed to get this idea working ( thanks kuroneko!) . I am running the serial at 57600 baud ( works at 115200 as well)

    I modified ( a copy ) of FullDuplexSerial.spin

    I added a method ( a copy of TX ) called..

    PUB TXWait(TxByte)

    I added to this the following lines at the end of the method
    REPEAT UNTIL TX_TAIL == TX_HEAD
    WAITCNT(10_000+CNT)
    

    I then modified ( temporarily) the STR call to use TXWait instead of TX

    I experimented with differnt values, I.e 5000, 7000, and 10,000 worked out best.

    BUT, I would prefer to use some indicator from the Assembly routine to tell me that the "bits" sent have finished, Just in case I trample on some of the message. I would then add a fixed delay after that, so the driver has time to switch.

    Is this possible
  • kuronekokuroneko Posts: 3,623
    edited 2011-02-22 00:10
    DavidM wrote: »
    I experimented with differnt values, I.e 5000, 7000, and 10,000 worked out best.
    Note that lower baudrates will need higher values, e.g. 19200 baud requires about 42k cycles (@80MHz) to get 10 bits out. So in that respect you might want to base your delay on the bit rate.
    DavidM wrote: »
    BUT, I would prefer to use some indicator from the Assembly routine to tell me that the "bits" sent have finished, Just in case I trample on some of the message. I would then add a fixed delay after that, so the driver has time to switch.
    It's certainly doable. I don't have time right now to look at it (off-line stuff) but maybe someone else chimes in. For the time being the approach you have now should work well enough.
  • DavidMDavidM Posts: 630
    edited 2011-02-22 00:20
    HI Kuroneko,

    Its close enough now, I can live with this , So I can get on with the other this I need to do regarding a RS485 Object, I want to be the first to make this!

    Dave M
  • Tracy AllenTracy Allen Posts: 6,666
    edited 2011-02-22 01:26
    Marko, Is this a situation where a lock could serve? The transmit code sets the lock when it detects data in the buffer, and clears the lock when it finishes sending the last bit and the buffer is empty. The spin code executes the usual
    repeat until not lockset
    transmit                jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,par                'check for head <> tail
                            add     t1,#2 << 2
                            rdlong  t2,t1
                            add     t1,#1 << 2
                            rdlong  t3,t1
                            cmp     t2,t3           wz      ' if z, the buffer is empty
            if_z            cmp     flag,#1         wc  '<<<< have we just finished sending?
            if_c            mov     flag,#1                 '<<<< yes reset flag to not sending
            if_c           lockclr 7                          '<<<<clear the lock (ID can be passed)
            if_z            jmp     #transmit
                            mov     flag, #0                ' <<<<sending, set the flag=0
                            lockset 7                         ' <<<set the lock (could be done from spin)
                            add     t3,txbuff             'get byte and inc tail
          ' ... and so on remainder of tx code ...
    
  • kuronekokuroneko Posts: 3,623
    edited 2011-02-22 05:12
    I was more thinking along the lines of only updating the tail pointer after the byte has been sent. This way transmit-buffer-empty also means off-chip. Downside is that the buffer is considered full sightly longer (for the time of one character). From a quick glance it only takes one more temporary (receiverA and transmitterB share them) and one instruction has to be relocated (untested).

    A lock will do the job as well. What do you need the extra flag for? I have the odd feeling that you get a free race condition with your purchase (I could be wrong though). This needs some more thought in case you take that approach.
    transmit                jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     [COLOR="red"]t4[/COLOR],par                'check for head <> tail
                            add     [COLOR="red"]t4[/COLOR],#2 << 2
                            rdlong  t2,[COLOR="red"]t4[/COLOR]
                            add     [COLOR="red"]t4[/COLOR],#1 << 2
                            rdlong  t3,[COLOR="red"]t4[/COLOR]
                            cmp     t2,t3           wz
            if_z            jmp     #transmit
    
                            add     t3,txbuff             'get byte and inc tail
                            rdbyte  txdata,t3
                            sub     t3,txbuff
                            add     t3,#1
                            and     t3,#$0F
    [COLOR="red"]' {disabled}            wrlong  t3,t4                 'deferred until after byte is gone[/COLOR]
    
                            or      txdata,#$100          'ready byte to transmit
                            shl     txdata,#2
                            or      txdata,#1
                            mov     txbits,#11
                            mov     txcnt,cnt
    
    :bit                    test    rxtxmode,#%100  wz    'output bit on tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_and_c      xor     txdata,#1
                            shr     txdata,#1       wc
            if_z            muxc    outa,txmask        
            if_nz           muxnc   dira,txmask
                            add     txcnt,bitticks        'ready next cnt
    
    :wait                   jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,txcnt              'check if bit transmit period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
                            djnz    txbits,#:bit          'another bit to transmit?
    
                            [COLOR="red"]wrlong  t3,t4                 'update tail[/COLOR]
                            jmp     #transmit             'byte done, transmit next byte
    '
    '
    ' Uninitialized data
    '
    t1                      res     1
    t2                      res     1
    t3                      res     1
    [COLOR="red"]t4                      res     1[/COLOR]
    
    A t1, t2
    B t1, t2, t3 (now t4 instead of t1)
  • Mike HuseltonMike Huselton Posts: 746
    edited 2011-02-22 07:16
    The recipient should send an ACK/NAK to signify full receipt of the message. This is the only way to be certain. Leave the transmitter code alone. My professional experience writing AGV and general robotics drivers taught me this lesson. This will generally require a simple state machine between the transmitter and receive units and it works rock solid every time. It handles recovery and retransmission of lost messages, to boot.

    Let me know how the RS485 network is coming, as I have considerable experience with this code, as well.
  • Tracy AllenTracy Allen Posts: 6,666
    edited 2011-02-22 10:20
    Marko,

    I think you are right about not needing the flag variable. It was there to clear the lock only when the buffer makes the transition from busy to empty. But that doesn't matter in the context of blocking tx until the transmission finishes. I don't see a race condition in it. It is an example maybe to use a lock as a simple resource-busy flag.

    Your solution changing the position where the tail pointer is updated is neat, so that the extra character delay is not needed when the spin code tests the difference head-tail.

    I'm interested in the RS485 code too. Mike, it is ideal to have an ack/nack handshake, but that is often wishful thinking unless you control both ends of the channel. I wish!
  • kuronekokuroneko Posts: 3,623
    edited 2011-02-22 17:10
    I don't see a race condition in it. It is an example maybe to use a lock as a simple resource-busy flag.
    Let's say the buffer is empty and the lock is clear. Also, the lock gets locked when sending and is cleared once the last bit is gone (assuming correct understanding of your code/intention). If I now place a byte into the buffer there is a small gap until the PASM part picks up the new byte and sets the lock. If I query the lock during that time I'd get a wrong off-chip indication. Doing the latter in SPIN may just be slow enough to not be an issue but it feels wrong.
  • DavidMDavidM Posts: 630
    edited 2011-02-22 20:17
    Hi,

    Q1) Can I READ the "Flag" Status in SPIN code say within the FDSerial object (Modified as suggested by kuroneko? If so How?

    To Mike Huselton..
    Q2) You say to leave the code alone, ! Why, All that is happening is that a flag is being sent, Are you also assuming that the messages sent are of the same length? If you have done lots of work with RS485, then how about sharing some code ideas?



    Please excuse my ignorance regarding Assembly.

    Thanks

    Dave M
  • kuronekokuroneko Posts: 3,623
    edited 2011-02-22 20:23
    DavidM wrote: »
    Q1) Can I READ the "Flag" Status in SPIN code say within the FDSerial object (Modified as suggested by kuroneko? If so How?
    If you use my modification then you don't need a flag. Transmit-buffer-empty also means off-chip in this case (but you still need the txwait method, without post delay).
  • DavidMDavidM Posts: 630
    edited 2011-02-22 20:27
    So this..
    REPEAT UNTIL TX_TAIL == TX_HEAD
    WAITCNT(10_000+CNT)
    
    becomes just
    REPEAT UNTIL TX_TAIL == TX_HEAD
    

    Along with the code you modified?

    because I can test this out now .

    Dave M
  • kuronekokuroneko Posts: 3,623
    edited 2011-02-22 20:42
    That's it. The delay is now hidden in the PASM part.
  • Tracy AllenTracy Allen Posts: 6,666
    edited 2011-02-22 21:05
    Marko, I did think of that, that there is a sliver of time between putting the data into the buffer and pasm setting the lock. That should not cause a problem if spin is supplying data to the buffer.

    My original thought had been to let the supplier set the lock and then wait until the lock is cleared. I probably over-complicated the issue by introducing the busy-flag and by putting the lockset in the pasm code.

    It should really be quite simple. Supplier hands over the data, sets lock and waits for it to be cleared, either blocking or between executing other tasks. Pasm clears the lock when finished through to the last bit of the last byte.
  • DavidMDavidM Posts: 630
    edited 2011-02-22 21:55
    HI,

    OK!


    I did the mods as suggested, ( I was also using a 128byte RX & TX buffers , when I reverted back to a 16 byte buffer, I got a mess, ( I may not have made the changes properly, so I when back to my 128 byte buffers)

    Seems to work! with 128 byte buffers!

    I was sending a long message AT 57600 i.e

    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

    Well done kuroneko!!!!!!!! and others

    Also, as a test

    I checked my normal SERIAL-RS232 via USB coms to an application I wrote on the MAC using REALBASIC, and I CAN communicate with that AT THE SAME TIME, So the MAC is reading continuous data from my RS485 connection, AND sending and receiving data via Serial RS232 !!!

    both are using the same MODIFIED FULLDUPLEXSERIAL Objects!!!!

    Sweeet!!!

    regards

    Dave M
  • kuronekokuroneko Posts: 3,623
    edited 2011-02-22 22:03
    DavidM wrote: »
    I was also using a 128byte RX & TX buffers , when I reverted back to a 16 byte buffer, I got a mess, ( I may not have made the changes properly, so I when back to my 128 byte buffers)
    Glad you got it working. As for changing buffer sizes, it's a bit more involved than simply changing the size of the arrays in the VAR section. In case you knew, maybe you just overlooked a mask somewhere.
  • DavidMDavidM Posts: 630
    edited 2011-02-22 22:24
    Hi kuroneko,

    Yes I know, I had to change in about 8 places including the asm code. I got the info ages ago form somewhere here! can;t remember where.

    I really should test back on 16 byte buffer, to see any difference, I want to know if I send say 40 bytes in one string, and the buffer is only 16 bytes, what will happen?


    thanks

    Dave M
  • kuronekokuroneko Posts: 3,623
    edited 2011-02-22 22:32
    Just grab the original FDS from the PropTool library directory (that's what I used for the patch). IIRC it's also in the OBEX. re: testing, you should pick a low baudrate and measure the time it takes to send a single character, e.g. at 9600 baud I expect at least 83k cycles (@80MHz).
  • Cluso99Cluso99 Posts: 18,069
    edited 2011-02-22 23:24
    FYI: I put a version of FDX (or FDS) in the obex that uses the buffer size as declared, so it is a simple change to vary the size. IIRC is is FullDuplexSerial_rr004.spin
Sign In or Register to comment.