Shop OBEX P1 Docs P2 Docs Learn Events
DEMO - Serial FIFO buffer - data handling — Parallax Forums

DEMO - Serial FIFO buffer - data handling

Beau SchwabeBeau Schwabe Posts: 6,568
edited 2009-05-05 20:26 in Propeller 1
This is just a small·application to demonstrate decision making based on an incoming serial stream using a small FIFO·buffer approach.
·
After loading this program into a Propeller Demo Board, run the Parallax Serial Terminal (PST.exe) and enter ...
·
+++········· in the window to turn ON all of the Demo-Board LEDs
---········· in the window to turn OFF all of the Demo-Board LEDs
blink········· in the window to blink on and off·all of the Demo-Board LEDs
·
·
... A similar approach can be used to decode a sequence of keypad entries, or hierarchical command style serial control.
·


CON
    _clkmode = xtal1 + pll16x                           
    _xinfreq = 5_000_000
FIFOBufferSize = 16
 
OBJ
  COMport       : "FullDuplexSerial.spin"
 
VAR
byte    StringBuffer[noparse][[/noparse]FIFOBufferSize],Null
 
PUB  FIFOBuffer_DEMO
 
     COMport.start(31, 30, 0, 38400)                    '' Initialize serial communication to the PC
 
     Null := 0                                          '' Initialize 'Null' String Buffer.  This value
                                                        '' must always be Zero.
     repeat                                                   

 
       Serial_FIFO

       if Command(string("+++"))
          dira[noparse][[/noparse]16..23]~~
          outa[noparse][[/noparse]16..23]~~

       if Command(string("---"))
          dira[noparse][[/noparse]16..23]~
          outa[noparse][[/noparse]16..23]~

       if Command(string("blink"))
          repeat 5
            repeat 100000          
            dira[noparse][[/noparse]16..23]~~
            outa[noparse][[/noparse]16..23]~~
            repeat 100000
            dira[noparse][[/noparse]16..23]~
            outa[noparse][[/noparse]16..23]~
                  
PUB Serial_FIFO                                'receive new byte, shift old byte out
    bytemove(@StringBuffer[noparse][[/noparse]0],@StringBuffer[noparse][[/noparse]1],FIFOBufferSize-1)
    StringBuffer[noparse][[/noparse]FIFOBufferSize-1] := COMport.rx
 
PUB Command(Compare)|Length                    'compare FIFO contents to compare string 
    Length := StrSize(Compare)
    if StrComp(Compare,@StringBuffer[noparse][[/noparse]FIFOBufferSize-Length])
       result := 1
    else
       result := 0




▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe

IC Layout Engineer
Parallax, Inc.

Post Edited (Beau Schwabe (Parallax)) : 5/5/2009 5:12:00 AM GMT

Comments

  • MagIO2MagIO2 Posts: 2,243
    edited 2009-05-05 05:52
    Hmmm .... a command buffer is not what I'd use a FIFO for. A FIFO in my oppinion is good for decoupling data read from data processing - where read speed, as well as processing speed can vary, but are still close. (Otherwise you tend to have FIFO overruns or the FIFO is most likely empty).
    Usually FIFOs don't shift the buffer-bytes, as this is a slow solution. Instead they use two pointers into the FIFO buffer, one showing the last element and one showing the first element. Incrementing the pointers (including wraparound) is a bit faster than moving all the data. If both pointer point to the same memory location the buffer is empty. If end is one byte before start, the buffer is full.

    Is this example meant to be a user interface? Then I see the following disatvantages:
    - You don't have a chance to correct input. Of course you can simply retype it - but you have to get used to that.
    - Command execution starts with the last letter implicitly. Usual command line interfaces use RETURN-key.
    - How would you then parse a parameter? This would then be business of each command itself? Possible, but overhead.
    - Having the command "+++" and "++++" is not possible as well as having "+++" and "-+++" is depending on the order of the·if statements.

    Is this example meant to be a interface to another device which sends the commands?
    - With all the bytemove and stringcompare, you limit the max. speed
    - Why transfer long strings if machines talk with each other? Command byte/word/long would be enough and can be 'decoded' much easier.




    Post Edited (MagIO2) : 5/5/2009 6:36:57 AM GMT
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2009-05-05 06:58
    MagIO2,
    ·
    This is meant more for·a user interface where you have a hierarchical structure and·a parsing of the string check that leads to other decision elements.

    An example where this might be used is with the communication protocal used to communicate with the PWMPAL, or the Parallax Servo Controller.
    ·
    The functionality of this example is not·far removed from·the WAIT command used in the BS2, the main difference is that you can check
    multiple scenarios at once as the data streams in rather than in a single threaded mode.
    ·

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 5/5/2009 7:06:38 AM GMT
  • MagIO2MagIO2 Posts: 2,243
    edited 2009-05-05 09:30
    Hi Beau,

    so, what you mean is something like that?
    PUB  FIFOBuffer_DEMO
     
         COMport.start(31, 30, 0, 38400)                    '' Initialize serial communication to the PC
     
         Null := 0                                          '' Initialize 'Null' String Buffer.  This value
                                                            '' must always be Zero.
         repeat                                                   
     
           Serial_FIFO
    
           if Command(string("+++"))
              ...
    
           if Command(string("---"))
              ...
    
           if Command(string("blink"))
              ...
     
           if Command(string("pwm"))
              doPWM
     
    PUB doPWM
       repeat
         Serial_FIFO
         if command( string("on") )
              ...
         if command( string("off") )
              ...
         if command( string("back") )
              return
     
    
    

    Nice.

    But dangerous implementation as well because of it's robustness. You can send anything you want and it won't recognize as long as it does not match with a known command. So, in case of a transmission error you would not recognize that as well and miss a command. It very much depends on the usecase if this is good or bad. Having a big motor running where the controller misses the slowdown command before changing direction might be a bad idea.

    E.G.:
    Say we have the commands: IOon, IOoff and off
    If you have an transmission error in IOoff (IDoff)·then it would be interpreted as an off. Hmm...
    IOoff (IOonf) -> IOon
    IOoff (IOoxf) -> skipped

    IOoff correctly transferred will execute IOoff AND off because the FIFO does not flush things already processed.

    That's why it's a good idea to have an end of command character like RETURN. You know when you have to parse and you can then detect such easy transmission problems because you will not find a command then. If you don't get an RETURN until the buffer is completely filled up you have a transmission problem as well and you can do·some meaningful·error-handling instead of ignoring. Of course for an environment where you have to expect transmission problems you would have checksums.

    I only want to make you and potential users aware of this, because this also depends on the environment. During development everything works fine, but when you go out with your helicopter and the RC of others put some noise on your TX line it might cause your heli to crash.



    Post Edited (MagIO2) : 5/5/2009 9:43:25 AM GMT
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2009-05-05 16:04
    MagIO2,
    I understand your point, and perhaps it was a bad example on my part. Typically you don't structure your commands as "IOon, IOoff and off" or else your just asking for trouble. What you want is a "core" function like....

    !IOPwr
    !IOCyc

    If·and only if the 'core' matches, then you read in other data corresponding to that command.

    For example....

    !IOPwrA + CR                  turns on all Demo Board LEDs 
    !IOPwrC + {Channel} + CR      only turns on a specific channel 
    !IOPwrF + CR                  turns off all Demo Board LEDs 
    !IOCycU + CR                  cycles the Demo Board LEDs from Low to High 
    !IOCycD + CR                  cycles the Demo Board LEDs from High to Low
    


    ...again, this is just an example, and the implemented structure is always up to the user. I just thought this might be an easy way to Parse an incoming data string with a relatively small and easy to use amount of code.



    [b]CON[/b]
        [b]_clkmode[/b] = [b]xtal1[/b] + [b]pll16x[/b]                           
        [b]_xinfreq[/b] = 5_000_000
    
    FIFOBufferSize = 16
    
     
    [b]OBJ[/b]
      COMport       : "FullDuplexSerial.spin"
    
    [b]VAR[/b]
    [b]byte[/b]    StringBuffer[noparse][[/noparse]­FIFOBufferSize],Null
    
    
    [b]PUB[/b]  FIFOBuffer_DEMO|Ch
    
         COMport.start(31, 30, 0, 38400)                    '' Initialize serial communication to the PC
    
         Null := 0                                          '' Initialize 'Null' String Buffer.  This value
                                                            '' must always be Zero.
    
         [b]repeat[/b]                                                   
           Serial_FIFO
           [b]If[/b] Command([b]string[/b]("!IOPwr"))
              [b]Case[/b] COMport.rx
                "A" : [b]If[/b] COMport.rx == 13
                         [b]dira[/b][noparse][[/noparse]­16..23]~~
                         [b]outa[/b][noparse][[/noparse]­16..23]~~
                "C" : Ch := 0 #> COMport.rx - 48 <# 7
                      [b]If[/b] COMport.rx == 13
                         [b]dira[/b][noparse][[/noparse]&shy;16+Ch]~~
                         [b]outa[/b][noparse][[/noparse]&shy;16+Ch]~~
                "F" : [b]If[/b] COMport.rx == 13
                         [b]dira[/b][noparse][[/noparse]&shy;16..23]~~
                         [b]outa[/b][noparse][[/noparse]&shy;16..23]~
           [b]If[/b] Command([b]string[/b]("!IOCyc"))
              [b]Case[/b] COMport.rx
                "U" : [b]If[/b] COMport.rx == 13
                         [b]dira[/b][noparse][[/noparse]&shy;16..23]~~
                         [b]repeat[/b] 10
                          [b]repeat[/b] Ch [b]from[/b] 0 to 7
                           [b]outa[/b][noparse][[/noparse]&shy;16+Ch]~~
                           [b]repeat[/b] 10000
                          [b]repeat[/b] Ch [b]from[/b] 0 to 7
                           [b]outa[/b][noparse][[/noparse]&shy;16+Ch]~
                           [b]repeat[/b] 10000
                "D" : [b]If[/b] COMport.rx == 13
                         [b]dira[/b][noparse][[/noparse]&shy;16..23]~~
                         [b]repeat[/b] 10
                          [b]repeat[/b] Ch [b]from[/b] 7 to 0
                           [b]outa[/b][noparse][[/noparse]&shy;16+Ch]~~
                           [b]repeat[/b] 10000
                          [b]repeat[/b] Ch [b]from[/b] 7 to 0
                           [b]outa[/b][noparse][[/noparse]&shy;16+Ch]~
                           [b]repeat[/b] 10000
                           
                         
    
    
    [b]PUB[/b] Serial_FIFO                                'receive new byte, shift old byte out
        [b]bytemove[/b](@StringBuffer[noparse][[/noparse]&shy;0],@StringBuffer[noparse][[/noparse]&shy;1],FIFOBufferSize-1)
        StringBuffer[noparse][[/noparse]&shy;FIFOBufferSize-1] := COMport.rx
    
    [b]PUB[/b] Command(Compare)|Length                    'compare FIFO contents to compare string 
        Length := [b]StrSize[/b](Compare)
        [b]if[/b] [b]StrComp[/b](Compare,@StringBuffer[noparse][[/noparse]&shy;FIFOBufferSize-Length])
           [b]result[/b] := 1
        [b]else[/b]
           [b]result[/b] := 0
    
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.
  • simonlsimonl Posts: 866
    edited 2009-05-05 18:17
    Hey Beau: At last I can (humbly!) count myself amongst the greats! (Yeah I wish!). I posted something similar to your code in a reply to nomad last week smile.gif

    I like the way you've "packaged" the command comparison; you'll see my version has this in a "case" structure, which looks real messy, but I'm also attempting to handle case-insensitive commands.

    And just for MagIO2; I also have the ability to send a parameter too!

    This code is ALPHA: I'm actually trying to put together a guide to Propeller communications, but you guys keep posting stuff that I'm still working on! Anyways; I'll try to tidy-up my examples and get something published "real soon now".

    <edit>Sorry Beau; Your post is about FIFO buffering - whilst my code doesn't do FIFO buffering. Still; it might be useful to some, so I'll leave it here anyway. Back to you...</edit>

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Cheers,
    Simon

    www.norfolkhelicopterclub.com

    “Before you criticize someone, you should walk a mile in their shoes. That way when you criticize them, you are a mile away from them and you have their shoes.” - Jack Handey.

    Post Edited (simonl) : 5/5/2009 6:35:05 PM GMT
  • MagIO2MagIO2 Posts: 2,243
    edited 2009-05-05 20:26
    Hi Beau,

    first of all thank you for your patience and the further efford. This forum is one of the best forums I know and I appreciate·the work that·Parallax staff is doing for us hobbyists very much. But that seems to be a problem as well.
    Beau said...
    I understand your point, and perhaps it was a bad example on my part. Typically you don't structure your commands as "IOon, IOoff and off" or else your just asking for trouble. What you want is a "core" function like....
    If pofis talk about a piece of code they simply don't talk about a lot of assumptions/facts they know about. But looking on some members here makes me feel that not all of them will be aware of what the posted code will do in other environments.

    E.G. obviously you had some hidden knowledge about the structure of the commands but you did not tell in the original post.
    You and me know that garbage will simply be skipped, but do the beginners know that either?

    Your last example is - in my personal opinion - close to the maximum number of·different commands·where your code is handsome. Because each command is split up in serveral branches, each command branch does it's own comPort.rx and additional comparisons. If you need parameters each command which uses parameters has to add more code to fetch these. Of course, your code of the initial read (what you called FIFO) is small, but the rest blows up more and more with each additional command.

    Maybe we should simply point to some other code like FemtoBasic or PropCMD which do command-line parsing as well - on a different way - which might for other usecases be the better choice.





    Post Edited (MagIO2) : 5/5/2009 8:36:24 PM GMT
Sign In or Register to comment.