GE color effects assembly driver
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?
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
Any other cool tips you'd like to share?
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.
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.
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????)
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.
https://www.youtube.com/watch?v=YR-itnatUJY&feature=youtu.be
These are the ge color effects rgb leds. The array is 16 strings with 36 leds each.