Shop OBEX P1 Docs P2 Docs Learn Events
How do I sequence LEDs? Plus other ?'s — Parallax Forums

How do I sequence LEDs? Plus other ?'s

PoundSign2PoundSign2 Posts: 129
edited 2013-10-06 10:23 in Propeller 1
Hello friends,

I have downloaded some very interesting LED .spin files from the OBEX, but I do not understand how they generate patterns or sequences. I would like to start off small and do this for example, blink all odd pinned LEDs, and while the odd LEDs are on, the evens are off, and vice verse. I was thinking a not statement would suffice paired with an if statement. However, I do not know how to conveniently assign a variable/constant for even numbered pins, or for that matter, how to assign multiple pins in general. Forgive me for my lack of knowledge I've only had this Propeller chip for a few days! Anyways, any help is appreciated!

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2013-09-28 22:24
    Below are examples for even/odd and combined (QS/demoboard). Does this help in any way?
    PUB even
    
      dira[23..16] := %01010101
      repeat
        outa[23..16] ^= %01010101                           ' even pins
        waitcnt(clkfreq/2 + cnt)
    
    PUB odd
    
      dira[23..16] := %10101010
      repeat
        outa[23..16] ^= %10101010                           ' odd pins
        waitcnt(clkfreq/2 + cnt)
    
    PUB even_odd
    
      dira[23..16]~~
      outa[23..16] := %01010101                             ' preset
      repeat
        outa[23..16] ^= %11111111                           ' toggle even/odd
        waitcnt(clkfreq/2 + cnt)
    
    Given that each cog has its own direction register you can also run one cog only dealing with the odd pins and another handling the even pins. If both signal groups are related timing wise it would be more sensible to keep processing in one cog though which basically means your patterns have to be combined before being displayed.
  • StefanL38StefanL38 Posts: 2,292
    edited 2013-09-29 09:33
    the "not"-operator is a logical not. If you want to inverse more than one bit you have to use the bitwise not-operator.

    another way could be to start with &01010101 shift the bits one to the left and then shift back one to the right etc. etc.

    best regards Stefan
  • PoundSign2PoundSign2 Posts: 129
    edited 2013-09-29 19:20
    kuroneko wrote: »
    Below are examples for even/odd and combined (QS/demoboard). Does this help in any way?
    PUB even
    
      dira[23..16] := 010101
      repeat
        outa[23..16] ^= 010101                           ' even pins
        waitcnt(clkfreq/2 + cnt)
    
    PUB odd
    
      dira[23..16] := 101010
      repeat
        outa[23..16] ^= 101010                           ' odd pins
        waitcnt(clkfreq/2 + cnt)
    
    PUB even_odd
    
      dira[23..16]~~
      outa[23..16] := 010101                             ' preset
      repeat
        outa[23..16] ^= 111111                           ' toggle even/odd
        waitcnt(clkfreq/2 + cnt)
    
    Given that each cog has its own direction register you can also run one cog only dealing with the odd pins and another handling the even pins. If both signal groups are related timing wise it would be more sensible to keep processing in one cog though which basically means your patterns have to be combined before being displayed.

    This is very helpful. Although when I uploaded you supplied code only the odd numbered pins lit up; and it never switched back and forth between even/odd. I do not know how to debug it but I tried, and got no results. I understand now that [xx..yy] is how you list multiple pins. This will be very helpful in the future! One thing I am still confused about is how to shift the bits left/right. I know you use a << operator and specify how many bits to shift but how do you tell the dira to now turn on those pins? So if I had 001111 and shifted left 1111, how does it know that dira[xx..yy] := 001111 is now dira[xx..yy] := 110000 ?
  • kuronekokuroneko Posts: 3,623
    edited 2013-09-29 19:52
    PoundSign2 wrote: »
    This is very helpful. Although when I uploaded you supplied code only the odd numbered pins lit up; and it never switched back and forth between even/odd. I do not know how to debug it but I tried, and got no results. I understand now that [xx..yy] is how you list multiple pins. This will be very helpful in the future! One thing I am still confused about is how to shift the bits left/right. I know you use a << operator and specify how many bits to shift but how do you tell the dira to now turn on those pins? So if I had 001111 and shifted left 1111, how does it know that dira[xx..yy] := 001111 is now dira[xx..yy] := 110000 ?
    Looks like you're using the broken editor (eats % prefixes). Can you (in future) switch to the standard editor (not WYSIWYG)?

    As for your code only lighting odd LEDs, I don't know. Can you attach your program so I/we can have a look? re: shifting, ususally you setup dira once and do the rest with outa, e.g.
    dira[23..16]~~       ' all 8 LEDs are output
      outa := $01010101    ' preset
      repeat
        outa <-= 1
        waitcnt(clkfreq/10 + cnt)
    
    In general a cog should only ever touch pins (dira) it is going to drive (outa).

    What's the final goal here?
  • PoundSign2PoundSign2 Posts: 129
    edited 2013-09-29 20:01
    kuroneko wrote: »
    Looks like you're using the broken editor (eats % prefixes). Can you (in future) switch to the standard editor (not WYSIWYG)?

    As for your code only lighting odd LEDs, I don't know. Can you attach your program so I/we can have a look? re: shifting, ususally you setup dira once and do the rest with outa, e.g.
    dira[23..16]~~       ' all 8 LEDs are output
      outa := $01010101    ' preset
      repeat
        outa <-= 1
        waitcnt(clkfreq/10 + cnt)
    
    In general a cog should only ever touch pins (dira) it is going to drive (outa).

    What's the final goal here?

    So do I use the % symbol for binary or the $ symbol for binary? I thought the manual suggested % for indicating binary? Also I do not know what "WYSIWYG" means, sorry. The end goal of all of this is being able to com up with sequences for LEDs. Essentially patterns for lights that I can incorporate into projects for my kids. I thought the propeller would be ideally suited for this task.
  • kuronekokuroneko Posts: 3,623
    edited 2013-09-29 20:12
    PoundSign2 wrote: »
    So do I use the % symbol for binary or the $ symbol for binary? I thought the manual suggested % for indicating binary? Also I do not know what "WYSIWYG" means, sorry.
    % is for binary patterns, I used $ (hex) deliberately so that while I rotate outa its 1 count (4x) stays intact. $01010101 is %00000001_00000001_00000001_00000001. Imagine that dira has opened a window on pins 16..23 and outa is simply projected through this hole. Try $0103070F to see the difference.

    WYSIWYG, What You See Is What You Get. This is one of three editor types you can chose to edit your postings. Unfortunately this particular one is broken (I use the standard version). This can be changed in your profile settings.
  • PoundSign2PoundSign2 Posts: 129
    edited 2013-10-01 01:24
    kuroneko wrote: »
    % is for binary patterns, I used $ (hex) deliberately so that while I rotate outa its 1 count (4x) stays intact. $01010101 is %00000001_00000001_00000001_00000001. Imagine that dira has opened a window on pins 16..23 and outa is simply projected through this hole. Try $0103070F to see the difference.

    WYSIWYG, What You See Is What You Get. This is one of three editor types you can chose to edit your postings. Unfortunately this particular one is broken (I use the standard version). This can be changed in your profile settings.

    Oh well that clears some things up! I have tried to bit shift, which I got to work a couple times, but overall not so much. So what I did was use binary for every outa call. Please examine my code and tell me what you think. Please don't be too harsh!
    {{
    
    
    
    
            ######################################################################################################## 
            ########################################################################################################
            ********************************        LED SEQUENCE: BIT by BIT        ********************************
            ********************************        By Justin A. McGillivary        ********************************
            ********************************        Date: September 30th, 2013      ********************************
            ######################################################################################################## 
            ########################################################################################################
    
    
            
                                                            - OVERVIEW -
     
    - This program demonstrates, bit by bit, an LED sequence for the Propeller P8X32A QuickStart Board (Rev.A 40000).
    - Here is a demonstration of how to control an LED sequence by turning off/on bits in some given order for effect.
    - This code, while some might consider 'sloppy' is easy to understand and grasp. By looking at each line of code with a "%" prefix,
      which indicates the LED(s) to be lit in Binary (ex. %10101010), it is easy to visualise what is taking place.
    - There is also a display of how to incorporate a system of repeats to generate a sequence of events. The infinite loop cycles the entire
      program while each "portion" is repeated a set number of times. Within these sets are the patterns to execute. The sequence of LEDs being
      lit can be changed by adjusting the repeat counts for each repeating loop.
    
    
      So what is taking place/actually happening?
    
    
    - We'll start by looking at the intial REPEAT loop. Since this has no whole number associated with it, it cycles forever. Each additional
      repeat is a PORTION or a PATTERN. The patterns make up the portions, and they repeat individually, while the portions repeat the patterns
      and make up the guts of the program. In the first pattern the LEDs will cycle with ODD/EVEN pins by turning every other bit on, or off,
      and vice verse. The second pattern turns on the LEDs from the outside bits to the inside bits, e.g pins 23/16 to 22/17 to 21/18 to 20/19.
      The next pattern turns 4 bits on, shifts them left, and repeats. So while 4 bits are on, 4 are off, and it alternates. The fourth pattern
      turns individual bits on or off in a left to right then right to left order. If you notice the binary symbol, "%" you can clearly see how
      the pattern unfolds.
    
    
    - Try changing any of the code to see how it affects the patterns!
    
    
    
    
            $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
            $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$                                                                                                                        
            $$$$$$$$$$$$$$$$$$$$$$       This program is uploaded in accordance with                       $$$$$$$$$$$$$$$$$$$$$$
            $$$$$$$$$$$$$$$$$$$$$$       Parallax's Terms & Conditions on the Object Exchange.             $$$$$$$$$$$$$$$$$$$$$$
            $$$$$$$$$$$$$$$$$$$$$$       Please refer to  http://obex.parallax.com/  for more details.     $$$$$$$$$$$$$$$$$$$$$$
            $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$                        
            $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
    
    
            
    }}
    
    
    PUB main                                                
      dira[23..16] := %11111111
      repeat                                                ' Set up infinite loop
      
        repeat 3                                            ' Repeat this PORTION 3 times
        
          repeat 5                                          ' Repeat this pattern 5 times
          
            outa[23..16] := %10101010                       ' ALTERNATING
            waitcnt(clkfreq/4 + cnt)
            !outa[23..16]
            waitcnt(clkfreq/4 + cnt)
                                                            
          repeat 3                                          ' Repeat this pattern 5 times
          
            outa[23..16] := %00000000                       ' OUTSIDE to INSIDE
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %10000001
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %11000011
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %11100111
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %11111111
            waitcnt(clkfreq/4 + cnt)
              
          repeat 5                                          ' Repeat this pattern 5 times
          
            outa[23..16] := %00001111                       ' 4 ON/OFF
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] <<= 4
            waitcnt(clkfreq/4 + cnt)                        
        
          repeat 3                                          ' Repeat this pattern 5 times
          
            outa[23..16] := %10000000                       ' LEFT to RIGHT
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %11000000
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %11100000
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %11110000
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %11111000
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %11111100
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %11111110
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %11111111
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %00000001                        ' RIGHT TO LEFT
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %00000011
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %00000111
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %00001111
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %00011111
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %00111111
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %01111111
            waitcnt(clkfreq/4 + cnt)
            outa[23..16] := %11111111
            
    ' END OF LINE
    
    



    EDIT:

    I also uploaded it to the OBEX, it can be found at...

    http://obex.parallax.com/object/714
  • PoundSign2PoundSign2 Posts: 129
    edited 2013-10-03 16:26
    Any chance I could get someone to look at the above code?
  • kuronekokuroneko Posts: 3,623
    edited 2013-10-03 16:41
    What are you after? I mean it works just fine. The last part could be done more compact:
    repeat 5                                          ' Repeat this pattern 5 times
          
            outa[31..16] := %11111111_00000000              ' LEFT to RIGHT
            repeat 8
              outa[31..16] >>= 1
              waitcnt(clkfreq/4 + cnt)
    
            outa[23..8] := %00000000_11111111               ' RIGHT TO LEFT
            repeat 8
              outa[23..8] <<= 1
              waitcnt(clkfreq/4 + cnt)
    
    Your next step could be to e.g. replay patterns from DAT memory (rather than hardcode the sequence in you method).
  • PoundSign2PoundSign2 Posts: 129
    edited 2013-10-03 17:10
    kuroneko wrote: »
    What are you after? I mean it works just fine. The last part could be done more compact:
    repeat 5                                          ' Repeat this pattern 5 times
          
            outa[31..16] := 111111_00000000              ' LEFT to RIGHT
            repeat 8
              outa[31..16] >>= 1
              waitcnt(clkfreq/4 + cnt)
    
            outa[23..8] := 000000_11111111               ' RIGHT TO LEFT
            repeat 8
              outa[23..8] <<= 1
              waitcnt(clkfreq/4 + cnt)
    
    Your next step could be to e.g. replay patterns from DAT memory (rather than hardcode the sequence in you method).


    How do I use the DAT object to do this? Any tutorials you could possibly point me to?

    The thing I am after is learning how to generate these patterns more accurately and using less code (more efficient).
  • kuronekokuroneko Posts: 3,623
    edited 2013-10-03 17:25
    This is just a quick and dirty version to give you an idea re: DAT section access:
    CON
      #0, PLAY, REWIND
      
    PUB null | indx
    
      dira[16..23]~~
    
      indx := 0                                             ' first position
      repeat
        case sequence[indx++]                               ' read command and advance index
          PLAY:   indx := display(indx)                     ' play pattern sequence
          REWIND: indx := 0                                 ' restart
    
    PRI display(indx)
    
      repeat sequence[indx++]                               ' number of patterns
        outa[16..23] := sequence[indx++]                    ' display
        waitcnt(clkfreq/4 + cnt)                            ' hardwired delay (could be part of sequence)
    
      return indx
        
    DAT
    
    sequence        byte    PLAY, 2, %11000011, %00111100   ' PLAY, pattern count, P0, P1, ... PN
                    byte    PLAY, 2, %11110000, %00001111
                    byte    PLAY, 4, %10101010, %01010101, %11001100, %00110011
                    byte    REWIND
                    
    DAT
    
    This command interface can be easily expanded to include repeat counts, delays and data manipulations (e.g. rotate the following pattern n times) ...
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-10-03 18:59
    While your original program does work, that is not the way you want to do this. I live and work in Hollywood where producers, directors, and actors love shiny things -- these means I write a lot of code to control LEDs. Just yesterday I finished coding a costume for Limp Bizkit guitarist, Wes Borland. It has nearly 300 RGB pixels (that's almost 900 individual LEDs). It was a bear to program, but everyone was happy in the end. I'll post pictures after tomorrow's event (for those in Los Angeles attending the Riot Games event you'll see it for yourself).

    I've also written a lot of LED sequencer code for places like Disneyland.

    If I could put you into a time machine and take you forward a year, I think you'd write a program more like the one that's attached.

    Keys:
    -- make your code flexible
    -- make your code easy to read
    -- make your code easy to modify

    All keys involve not embedding "magic numbers" into your code. Changing the timing of your program would be a lot of work because of the style. Please trust me that when you're writing code for others they will want changes -- sometimes before you've finished implementing the last request.

    What Marko suggested is something I do with LED sequencers: put the patterns and sequences into tables (in DAT sections). Here's how I translated your patterns (not all shown).
    dat { patterns }
    
      ' patterns are simple; all elements use same timing
    
      Alternate             byte    2                               ' # elements in pattern
                            byte    %01010101                       ' element #1
                            byte    %10101010
    
      OutsideIn             byte    5
                            byte    %00000000
                            byte    %10000001
                            byte    %11000011
                            byte    %11100111        
                            byte    %11111111 
    
      FlipFlop              byte    2
                            byte    %00001111
                            byte    %11110000
    


    Honestly, the hardest part is coming up with meaningful names for the patterns! Note that my table includes the number of elements in the pattern -- this lets me use any value in the table. We can play a the pattern with this bit of code Updated: 04 OCT):
    pub play_pattern(p_pat, msb, lsb, ms, cycles, p_flag) | elements, p_leds
    
    '' Play pattern
    '' -- p_pat is pointer to pattern
    '' -- msb and lsb define output range (limit to eight bits)
    '' -- ms is timing (milliseconds) for each step
    '' -- cycles is # of times to play pattern
    '' -- p_flag is pointer to flag variable (set to false when done)
    ''    * use -1 for no flag
    
      if (p_flag > 0)                                               ' using flag?
        long[p_flag] := true                                        '  mark busy
    
      repeat cycles
        elements := byte[p_pat]                                     ' get elements in pattern
        p_leds := p_pat + 1                                         ' set working pointer to 1st element
        repeat elements                                             ' loop through elements
          set_leds(msb, lsb, byte[p_leds++])                        ' display, point to next
          pause(ms)                                                 ' hold 
    
      if (p_flag > 0)                                               ' using flag?
        long[p_flag] := false                                       '  mark done
    


    It's a little involved, but gives you a lot of flexibility. You pass the address of the pattern, the pins to use, the timing for each element, and the number of cycles to play. By passing the pins as parameters you can use the same table on different sets of outputs. You could even play them in different cogs (I did this on a Disney project).

    LED output is easy:
    pub set_leds(msb, lsb, pattern)
    
    '' Set LED outputs to low bits of pattern
    '' -- msb and lsb define output range (limit to eight bits)
    
      outa[msb..lsb] := pattern
      dira[msb..lsb] := $FFFF_FFFF                                  ' all outputs
    


    This sets your outputs -- wherever they are and makes sure that the IO pins are in output mode.

    Here's my translation of your program (Updated: 04 OCT.
    pub main | scale
    
      set_leds(LED7, LED0, %0000_0000)                              ' start off
    
      repeat 3
        play_pattern(@Alternate, LED7, LED0, 250, 5, -1)            ' play in main cog
        play_pattern(@OutsideIn, LED7, LED0, 250, 3, -1)
        play_pattern(@FlipFlop,  LED7, LED0, 250, 5, -1)
        play_pattern(@RightLeft, LED7, LED0, 250, 3, -1)
    


    Easy, right? Need to speed up a pattern? No problem, change the timing parameter in the call. Done!

    There's an additional feature in my program that let's you define sequences that include the timing for each step. I'll leave you to explore that.

    Have fun!


    WHOOPS! When I updated the program to allow pins as parameters I neglected to fully edit the set play_pattern() and play_sequence() methods. Those are fixed now and the demo updated to show you how to launch them into their own cogs so you can have patterns running in the background while your foreground code is doing other things. To facilitate this feature, I added a flag to each method that gets set to true when the pattern or sequence is playing, and then to false when done. By using a flag we don't have to calculate the duration of a pattern or sequence (steps * ms * cycles).

    I realize that some of this code looks a little funky, but that's only because you're new. There will be an "Aha!" moment where it all comes together and you're free to be as creative as you like.
  • PoundSign2PoundSign2 Posts: 129
    edited 2013-10-06 02:42
    Well that certainly was a lot to digest! Great information guys! A lot more than I really expected to get, I guess the Propeller community really does live up to the reputation! In any case. I think I understand most, or at least the jist of what your code is doing. It appears to me that you are basically setting up certain parameters, and within those parameters you are telling each cog to fetch code from the DAT obj. The DAT obj serves as kind of like a library, or a reference, so that you can quickly call sets of code and execute them in some organised way. But, thats just how I am seeing/interpreting it. I could be totally wrong, who knows? :thumb:
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-10-06 10:23
    Strictly speaking, you're not fetching code, you're fetching data.

    At some point you may want to add a little more pizazz while keeping the simplicity of storing patterns in DAT tables. This is what we did for the Disneyland Buzz Lightyear hand stamper. That device plays audio and a little light show at the same time. When the trigger is activated one of the audio clips is selected along with the pattern that goes with it. The main loop launches the WAV player cog, then the light show cog. The main loop waits monitors the audio and when finished, kills the light show cog if it has not already stopped.

    We achieved a small level of brightness control by using quarternary numbers instead of binary patterns. For eight outputs this gives us four levels of brightness, though one assumes 0 will always be one of those levels. Have a look at the attached program.

    BTW... did you see this post:
    -- http://forums.parallax.com/showthread.php/150675-For-League-of-Legend-and-Wes-Borland-(Limp-Bizkit)-Fans?p=1211781#post1211781

    I'm still worn out from several late nights (after my day job) programming that costume.
Sign In or Register to comment.