Shop OBEX P1 Docs P2 Docs Learn Events
Circular buffers? — Parallax Forums

Circular buffers?

HughHugh Posts: 362
edited 2013-07-04 17:03 in Propeller 1
Hi,

Is there a consensus on the best way to use circular buffers? If I want to push a byte received from a serial port through an array of bytes named 'data', is this the way to do it?
repeat         
     bytemove (@data, @data + 1, 10)                                                            
     data[10] := serial.rx

I ask because if I print to the serial terminal any one element of the array (e.g., data[0], data[1], etc.,) individually I can see the data scrolling through. If I print data[0], data[1], data[2] and data[3] as a group there are gaps (some data is missing). Is it not the done thing to use bytemove with the same source and destination?

Comments

  • jazzedjazzed Posts: 11,803
    edited 2013-07-03 14:57
    Can you use a buffer with a head and tail?
    The FullDuplexSerial object does this for you.
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-07-03 18:26
    The FDX head and tail idea works extremely well for a lot of buffers. Simplest if you use a 2^n boundary.
  • MagIO2MagIO2 Posts: 2,243
    edited 2013-07-04 12:17
    Interesting question! In other systems one might want to say that copying the buffer again and again should be avoided because it is to time consuming, but in Propeller world my guess is that bytemove is propably faster than head and tail handling if you do it in SPIN, as bytemove is in PASM

    But in your example it would maybe be an option to use the buffer of the serial driver directly, because it is already there, so why add another one?
    But maybe I simply did not understand the problem good enough because I did not understand what you wrote after the code example.
  • HughHugh Posts: 362
    edited 2013-07-04 12:46
    Thanks. FDX it is...
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2013-07-04 16:42
    The head and tail concept is (another) one of those clever things I learned from this forum :)

    Draw it on a piece of paper with (say) 16 values in a buffer and two variables, head and tail. Both start at zero. If data comes in, head moves up one. If data goes out, tail moves up one. For 16 values in the buffer, mask the top 28 bits to zero and then both head and tail automatically loop around from 15 to zero (Binary xxxx1111 to xxxx0000). If head = tail, then the buffer is empty. Buffer loop around is easiest if the number of values in the buffer is a multiple of 2, eg 2,4,8,16.
  • SRLMSRLM Posts: 5,045
    edited 2013-07-04 16:54
    As simple as using a buffer sized to a power of two is using a buffer with arbitrary size and using modulo to force the head/tail pointer wrap around:
    spin:
    head //= BUFFER_SIZE
    

    c/c++
    head = head % BUFFER_SIZE;
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-07-04 17:03
    SRLM wrote: »
    As simple as using a buffer sized to a power of two is using a buffer with arbitrary size and using modulo to force the head/tail pointer wrap around:
    spin:
    head //= BUFFER_SIZE
    

    c/c++
    head = head % BUFFER_SIZE;
    

    And in Tracy Allen's four port serial driver:
    cmpsub     tx_tail3,txsize3   wz   ' TTA for individually sized buffers, will zero at rollover
                            wrlong  tx_tail3,tx_tail_ptr3
            if_z            mov     txbuff_tail_ptr3,txbuff_ptr3 'reset tail_ptr if we wrapped
            if_nz           add     txbuff_tail_ptr3,#1   'otherwise add 1
    

    The "cmpsub" statement does for any number what "add" does for powers of two (at least in this application).

    While agree non-powers of two are easy to use, I've always used powers of two in my circular buffers (which I also learned about here on the forum).
Sign In or Register to comment.