Shop OBEX P1 Docs P2 Docs Learn Events
GE color effects assembly driver — Parallax Forums

GE color effects assembly driver

EE351EE351 Posts: 81
edited 2014-11-25 17:55 in Propeller 1
Hello all!

I am working on an assembly driver for the GE color effects rgb lights. This is my first attempt at writing any assembly code, so be kind please! I modified the driver originally written by Robert Quattlebaum so that a byte array could be passed to the assembly code. I have the code working but there is one part of the assembly routine that I can't figure out. I am passing the first address of a byte array to the assembly program; however, I have to offset the address by 4 to get it to align correctly with the first byte. Can anyone tell me what I'm doing wrong?
{{
TOP OBJECT FILE
}}
CON
  _clkmode = xtal1 + pll16x
  _xinfreq = 6_250_000
CON


  ON_PIN                = 12

OBJ

  xmas          : "xmas2"


VAR

     long set_array
     byte rgb_array[108]

PUB start
           
    xmas.start(ON_PIN)
    'xmas.set_standard_enum
    'xmas.stand_alone_test 
    set_array := @rgb_array


 repeat 
    BYTEFILL(@rgb_array,$FF,108)
    xmas.send_raw_command(@set_array)
    waitcnt(6250000 + cnt)
    
    BYTEFILL(@rgb_array,$00,108)
    xmas.send_raw_command(@set_array)
    waitcnt(6250000 + cnt) 


{{
**  GE Color Effects Control Object
**  Version 0.2, 2010-12-02
**  by Robert Quattlebaum <darco@deepdarc.com>
**
**  http://www.deepdarc.com/2010/11/27/hacking-christmas-lights/
**  
**  This object file is public domain. You may use it as you see fit.
MODIFIED BY PAUL MATOS
}}
CON { General constants }
           
    CMD_LEN             = 26
    MAX_COLOR_VALUE     = $F
    MAX_HUE             = (MAX_COLOR_VALUE+1)*6-1

    DEFAULT_INTENSITY   = $CC   ' Default controler never uses larger than this.
    MAX_INTENSITY       = $FF
    
    MAX_BULB            = $3E   ' Theoretical last possible bulb address. Note
                                ' that normally there are only 50 bulbs on a string.
    BROADCAST_BULB      = $3F   ' Intensity set on this bulb affects all bulbs.
    
CON { Color convenience constants }
    COLOR_MASK          = $FFF
    COLOR_BLACK         = $000
    COLOR_WHITE         = $DDD  ' Default controler uses this value for white.
    COLOR_BLUE          = $F00
    COLOR_GREEN         = $0F0
    COLOR_RED           = $00F
    COLOR_CYAN          = COLOR_GREEN|COLOR_BLUE
    COLOR_MAGENTA       = COLOR_RED|COLOR_BLUE
    COLOR_YELLOW        = COLOR_RED|COLOR_GREEN

CON { These constants are needed only for the stand alone test mode. }
    _clkmode    = xtal1 + pll16x
    _xinfreq    = 5_000_000

VAR
    long cog
    long command

PUB stand_alone_test | i,j,b
{{ Stand alone test. Used only when you run this object directly. }}
    ' Start up our cog.
    start(12)

    ' Give the attached string the default bulb enumeration.
    set_standard_enum

    ' This code makes an animated rainbow.
    repeat
        repeat until ina[16]
            i++
            repeat j from 0 to MAX_BULB
                set_bulb(j,b,make_color_hue((i+j)//constant(MAX_HUE+1)))
            repeat 4
                if b<DEFAULT_INTENSITY
                    b++
                    set_bulb(BROADCAST_BULB,b,0)        
        repeat while b
            i++
            b--
            set_bulb(BROADCAST_BULB,b,0)        
            repeat 100
        repeat while ina[16]
        repeat until ina[16]
        repeat while ina[16]

PUB start(pin)
{{ Starts the cog for the object using the given pin index for output. }}
    ' Stop the object if it is already running
    stop
    
    ' Set up pin
    output_pin_mask := |< pin

    ' Set up # of bulbs         added by PJM
    no_bulbs := 36              'length of string

    ' ***FOR YBOX2 ONLY***
    ' output_pin_mask := (|< 12)|(|< 13)|(|< 14)
    
    ' Set the timing information
    period_10_us := (clkfreq/1_000_000) * 10
    
    ' Start the cog
    cog := cognew(@cog_init, @command) + 1
    
    ' Make sure the cog actually started
    ifnot cog
        abort -1
        
    return cog

PUB stop
{{ Stops the object. You must call start again before you can use this object. }}
    if cog
        cogstop(cog~ - 1)       ' cog variable is cleared by this statement
    command~

PUB set_standard_enum | i
{{ Performs the standard bulb enumeration for individual bulb control. }} 
    repeat i from 0 to MAX_BULB
        set_bulb(i,DEFAULT_INTENSITY,COLOR_BLACK)

PUB flush
{{ Wait for any prior command to finish. }}
    repeat while command AND (cog > 0)
    
PUB send_raw_command(x)
{{ Sends a single command, consisting of the 26 least significant bits of x. }} 
    ' We use the most significant bit
    ' to help us determine when the command
    ' has been sent by the cog. This works
    ' because the command length is only 26
    ' bits, and the most significant bit won't
    ' be used otherwise.
    'x |= constant(|<31)   'commented out by PJM

    ' Wait for any previous command to finish. 
    flush                  'commented out by PJM

    ' Set the command. The cog will notice this
    ' and send it automatically, clearing
    ' the command variable when it is finished.
    command := x

PUB make_color_rgb(r,g,b)
{{ Helper function for converting discrete red, green, and blue values into a single 12 bit color value. }} 
    return (r&MAX_COLOR_VALUE)|((g&MAX_COLOR_VALUE)<<4)|((b&MAX_COLOR_VALUE)<<8)

PUB make_color_hue(hue)
{{ Helper function for creating a color based on a hue value. hue must be between 0 and MAX_HUE. }} 
    case (hue>>4)
        0:  hue &= MAX_COLOR_VALUE
            return make_color_rgb(MAX_COLOR_VALUE,hue,0)
        1:  hue &= MAX_COLOR_VALUE
            return make_color_rgb((MAX_COLOR_VALUE-hue),MAX_COLOR_VALUE,0)
        2:  hue &= MAX_COLOR_VALUE
            return make_color_rgb(0,MAX_COLOR_VALUE,hue)
        3:  hue &= MAX_COLOR_VALUE
            return make_color_rgb(0,(MAX_COLOR_VALUE-hue),MAX_COLOR_VALUE)
        4:  hue &= MAX_COLOR_VALUE
            return make_color_rgb(hue,0,MAX_COLOR_VALUE)
        5:  hue &= MAX_COLOR_VALUE
            return make_color_rgb(MAX_COLOR_VALUE,0,(MAX_COLOR_VALUE-hue))
    return 0

PUB set_bulb(bulb,intensity,color) | x
{{ Convenience function for setting the intensity and color for the specified bulb index. }}
    send_raw_command(((bulb&$3F)<<20)|((intensity&$FF)<<12)|(color&COLOR_MASK))

DAT { Cog Implementation }
            org

cog_init    or      dira, output_pin_mask   ' Initialize output pin direction

loop        wrlong  zero,par                ' Clear out the previous command

:waitforcmd rdlong  array_ready,par wz  ' Wait for next command
    if_z    jmp     #:waitforcmd

            mov     array_ptr,array_ready           
            mov     bulb_counter,no_bulbs           
            
            add     array_ptr,#4        'offset byte array by 4 to get first byte?  
bulb_loop 

            rdbyte  red_byte,array_ptr  'read red byte
        
            and     red_byte,#$f0     'mask the upper 4 bits 
 
            add     array_ptr,#1        'move the arrary pointer to get green byte

            rdbyte  green_byte,array_ptr 'read green byte

            and     green_byte,#$f0     'mask the upper 4 bits 

            add     array_ptr,#1        'move the arrary pointer to get blue byte

            rdbyte  blue_byte,array_ptr 'read blue byte

            and     blue_byte,#$f0    'mask the upper 4 bits

            shl     blue_byte,#4        'shift the blue bits into the correct position
            
            shr     green_byte,#4       'shift the green bits into the correct position

            or      current_command,blue_byte   'place blue bits into current command

            or      current_command,red_byte    'place red bits into current command

            or      current_command,green_byte   'place green bits into current command

            or      current_command,bulb_intensity      'place bulb intensisty bits into current command
        
            


            mov     bulb_address,no_bulbs           'calculate bulb address
            sub     bulb_address,bulb_counter

            shl     bulb_address,#20    'shift the bulb address bits into the correct position

            or      current_command,bulb_address        'place bulb address bits into current command


            ' Send the command
            'call    #send_cmd
send_cmd    ' Load number of data bits
            mov     current_bit, #CMD_LEN

            ' Rotate the bits so that the next bit is at bit zero.
            rol     current_command, #(32-CMD_LEN)

            ' Set up next_cnt
            mov     next_cnt, period_10_us
            add     next_cnt, cnt

            ' Send start pulse
            or      outa, output_pin_mask   ' Output high
            waitcnt next_cnt, period_10_us  ' Wait 10 uSeconds
            andn    outa, output_pin_mask   ' Output low

:output_loop
            rol     current_command, #1 wc

    if_c    waitcnt next_cnt, period_10_us  ' Wait extra 10 uSeconds if one bit
            waitcnt next_cnt, period_10_us  ' Wait 10 uSeconds
            or      outa, output_pin_mask   ' Output high
    if_nc   waitcnt next_cnt, period_10_us  ' Wait extra 10 uSeconds if zero bit
            waitcnt next_cnt, period_10_us  ' Wait 10 uSeconds
            andn    outa, output_pin_mask   ' Output low
        
            ' Decrement current_bit ; jump if not Zero
            djnz    current_bit,    #:output_loop

            ' Finish up the frame with 30 uSecond quiet period.
            waitcnt     next_cnt, period_10_us  ' Wait 10 uSeconds
            waitcnt     next_cnt, period_10_us  ' Wait 10 uSeconds
            waitcnt     next_cnt, period_10_us  ' Wait 10 uSeconds
            waitcnt     next_cnt, period_10_us  ' Wait 10 uSeconds
            waitcnt     next_cnt, period_10_us  ' Wait 10 uSeconds
            waitcnt     next_cnt, period_10_us  ' Wait 10 uSeconds
            
            mov    current_command,zero             'clear current command

            add     array_ptr,#1        ''move the arrary pointer to get red byte 

            djnz    bulb_counter,    #bulb_loop        'repeat for the next bulb

            ' Jump back to the start and wait for next command
            jmp     #loop

'DAT { Send Command Subroutine }


'send_cmd_ret ret

DAT { Constants }
period_10_us    long    0       ' Set at init time
output_pin_mask long    0       ' Set at init time
no_bulbs        long    0       ' Set at init time
zero            long    0
bulb_intensity  long    %00000000_00001100_00110000_00000000 

DAT { Variables }
next_cnt        res     1
current_command res     1
current_bit     res     1
array_ptr       res     1       
red_byte        res     1       
green_byte      res     1       
blue_byte       res     1       
bulb_counter    res     1      
array_ready     res     1      
bulb_address    res     1

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2014-10-01 19:07
    EE351 wrote: »
    I am passing the first address of a byte array to the assembly program; however, I have to offset the address by 4 to get it to align correctly with the first byte.
    You're not. You do pass the address of set_array which is at -4 relative to rgb_array. Either pass the address of the latter (make sure it's 4n aligned) or keep the code as is and do an extra rdlong inside the driver (confusing though).
  • EE351EE351 Posts: 81
    edited 2014-10-01 19:15
    kuroneko wrote: »
    You're not. You do pass the address of set_array which is at -4 relative to rgb_array. Either pass the address of the latter (make sure it's 4n aligned) or keep the code as is and do an extra rdlong inside the driver (confusing though).
    Thanks for the reply. I thought I was passing the address of rgb_array through set_array but apparently not. How would I correctly pass the first address of a byte array to the assembly routine?
  • kuronekokuroneko Posts: 3,623
    edited 2014-10-01 19:21
    EE351 wrote: »
    I thought I was passing the address of rgb_array through set_array but apparently not. How would I correctly pass the first address of a byte array to the assembly routine?
    In this case use set_array instead of @set_array (you initialised set_array with @rgb_array) or pass @rgb_array instead (to send_raw_command)
  • EE351EE351 Posts: 81
    edited 2014-10-01 19:34
    kuroneko wrote: »
    In this case use set_array instead of @set_array (you initialised set_array with @rgb_array) or pass @rgb_array instead (to send_raw_command)
    Removing the @ from set_array worked! Thank you for your help!

    Any other cool tips you'd like to share?
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-10-01 21:14
    Is this just a learning exercise or is there some specific way you want the code to behave?

    It looks like you're using the same starting code I used with my project using these LEDs. I arranged my LEDs in a 2D grid and wrote some code to scroll text on the array. In hindsight, I don't think a 2D array is the best use for these LEDs but it was still fun.

    I'm thinking of programming in some Halloween messages and have the LEDs lit with orange hues (or any other colors I can think of to make Xmas lights look like Halloween lights).

    In the off chance you're interested in using these LEDs in a 2D grid (I can't say I recommend it), I attached my code to this post.
  • EE351EE351 Posts: 81
    edited 2014-10-01 21:32
    Duane Degn wrote: »
    Is this just a learning exercise or is there some specific way you want the code to behave?

    It looks like you're using the same starting code I used with my project using these LEDs. I arranged my LEDs in a 2D grid and wrote some code to scroll text on the array. In hindsight, I don't think a 2D array is the best use for these LEDs but it was still fun.

    I'm thinking of programming in some Halloween messages and have the LEDs lit with orange hues (or any other colors I can think of to make Xmas lights look like Halloween lights).

    In the off chance you're interested in using these LEDs in a 2D grid (I can't say I recommend it), I attached my code to this post.
    I plan on driving 12 strings of 36 lights with a program called xlights. The final code will include a driver for an enc28j60 Ethernet module to receive e1.31 data. This is why I needed to modify the gece driver to accept a byte array.

    BTW, xlights is pretty neat. It can do scrolling text, a bunch of cool patterns, and you can set the rgb colors very easily without modifying the prop code.
  • WurlitzerWurlitzer Posts: 237
    edited 2014-10-02 06:49
    Thanks to all for this teaser. I think you just scheduled all my time until Christmas. Starting from ground zero the learning curve looks like Mt. Everest but I am sure that will level out quickly.
  • WurlitzerWurlitzer Posts: 237
    edited 2014-10-03 06:29
    Assuming I were to use only the pre-configured effects in xLights, is there any need for Vixen?

    Based upon everything I read/watched yesterday, Vixen seemed to be involved but I don't understand why. Is it used to create pixel by pixel effects by hand from the ground up?

    I would like to use xLights on my laptop direct to a controller (Prop/DMX or other) via USB or preferably ethernet E131 and I am open to any reasonable cost ethernet module (ESP8266????)
  • EE351EE351 Posts: 81
    edited 2014-10-03 08:03
    Wurlitzer wrote: »
    Assuming I were to use only the pre-configured effects in xLights, is there any need for Vixen?

    Based upon everything I read/watched yesterday, Vixen seemed to be involved but I don't understand why. Is it used to create pixel by pixel effects by hand from the ground up?

    I would like to use xLights on my laptop direct to a controller (Prop/DMX or other) via USB or preferably ethernet E131 and I am open to any reasonable cost ethernet module (ESP8266????)
    Unless you want to sync the lights to music or create a unique effect by hand there is no need to use vixen.

    As far as ethernet goes, the enc28j60 module on ebay is about as cheap as it gets for adding ethernet to a project.
  • WurlitzerWurlitzer Posts: 237
    edited 2014-10-03 13:47
    EE351 wrote: »
    Unless you want to sync the lights to music or create a unique effect by hand there is no need to use vixen.

    As far as ethernet goes, the enc28j60 module on ebay is about as cheap as it gets for adding ethernet to a project.

    xLights does synchronize music with effects. I know they were/are working on or have the ability to import label files (for timing so you don't have to manually type in the start time for each event) from Audacity in addition to importing the mp3 file. I was just watching a video today where they imported Stars and Stripes Forever.

    Yes it is hard to beat that price but my only concern is my ability (or lack thereof) to program for it as I can only spell "C", never liked it, but I will have to look at the trade off from cost to ability to program for it.
  • EE351EE351 Posts: 81
    edited 2014-11-24 21:01
    I finally finished the controller and display. Here's a video, sorry for the poor quality.

    https://www.youtube.com/watch?v=YR-itnatUJY&feature=youtu.be
  • Little-endianLittle-endian Posts: 91
    edited 2014-11-25 08:05
    Looks great. Good job!
  • NWCCTVNWCCTV Posts: 3,629
    edited 2014-11-25 17:47
    That is quite cool. Which GE Strips did you use and how many?
  • EE351EE351 Posts: 81
    edited 2014-11-25 17:55
    NWCCTV wrote: »
    That is quite cool. Which GE Strips did you use and how many?

    These are the ge color effects rgb leds. The array is 16 strings with 36 leds each.
Sign In or Register to comment.