Interactively test WS2812 NeoPixel LEDs with Tachyon and serial terminal +Matrix demo
Peter Jakacki
Posts: 10,193
Yesterday I was asked if I could interface to the WS2812 LEDs so I had a look at the datasheet and implemented a very simple addition to the kernel. I've checked all the timing and it is very good and all that is needed is a byte array in memory and a size which is passed to the WS2818 driver. In fact the driver doesn't use a special cog, it will just run from whichever it is called from, normally the console cog. So this allows for interacting with LEDs and that is exactly what I would be doing now, ..... if I had any that is.
So if you are game, if you adventurous, if you want to see what you can do then grab the latest Tachyon binary, F11 it into your favorite board, hook up a serial terminal at 230,400 baud and go over to this code document where you can just select all, copy, then paste it in through the terminal.
I have included a demo called CHASE where you can tell it to go faster or slower (FASTER or SLOWER) or whatever speed as it is running.
If you are cluey though you can change the pattern on the fly as well and even modulate the brightness as it moves but the code isn't there for that although it is extremely simply to do.
Terminal settings are 230,400 8N1 and 15ms LINE delay (not character delay). Recommended terminals are TeraTerm or minicom.
Latest Tachyon binary (works automatically with 5 or 10MHz crystals)
TACHYON.binary
BTW, although I can't see the LEDs I can look at the buffer while it is running (using ^X to repeat the last operation each time):
rgbleds $40 DUMP
0000_45D4: 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 ................
0000_45E4: 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 ................
0000_45F4: FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 ................
0000_4604: FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 ................ ok
0000_45D4: 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF ................
0000_45E4: 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 ................
0000_45F4: 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 ................
0000_4604: 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 ................ ok
0000_45D4: 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF ................
0000_45E4: 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 ................
0000_45F4: 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 ................
0000_4604: 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 ................ ok
0000_45D4: 00 FF 00 FF 00 00 00 FF 00 FF 00 00 00 00 FF 00 ................
0000_45E4: FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 ................
0000_45F4: FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 ................
0000_4604: 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF ................ ok
0000_45D4: 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF ................
0000_45E4: 00 00 00 00 FF 00 FF 00 FF 00 00 00 FF 00 FF 00 ................
0000_45F4: 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 ................
0000_4604: FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 ................ ok
0000_45D4: FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 ................
0000_45E4: FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 ................
0000_45F4: 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF ................
0000_4604: 00 00 00 00 FF 00 FF 00 FF 00 00 00 FF 00 FF 00 ................ ok
0000_45D4: 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 ................
0000_45E4: 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 ................
0000_45F4: 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 ................
0000_4604: FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 ................ ok
So if you are game, if you adventurous, if you want to see what you can do then grab the latest Tachyon binary, F11 it into your favorite board, hook up a serial terminal at 230,400 baud and go over to this code document where you can just select all, copy, then paste it in through the terminal.
I have included a demo called CHASE where you can tell it to go faster or slower (FASTER or SLOWER) or whatever speed as it is running.
If you are cluey though you can change the pattern on the fly as well and even modulate the brightness as it moves but the code isn't there for that although it is extremely simply to do.
Terminal settings are 230,400 8N1 and 15ms LINE delay (not character delay). Recommended terminals are TeraTerm or minicom.
Latest Tachyon binary (works automatically with 5 or 10MHz crystals)
TACHYON.binary
BTW, although I can't see the LEDs I can look at the buffer while it is running (using ^X to repeat the last operation each time):
rgbleds $40 DUMP
0000_45D4: 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 ................
0000_45E4: 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 ................
0000_45F4: FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 ................
0000_4604: FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 ................ ok
0000_45D4: 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF ................
0000_45E4: 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 ................
0000_45F4: 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 ................
0000_4604: 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 ................ ok
0000_45D4: 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF ................
0000_45E4: 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 ................
0000_45F4: 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 ................
0000_4604: 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 ................ ok
0000_45D4: 00 FF 00 FF 00 00 00 FF 00 FF 00 00 00 00 FF 00 ................
0000_45E4: FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 ................
0000_45F4: FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 ................
0000_4604: 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF ................ ok
0000_45D4: 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF ................
0000_45E4: 00 00 00 00 FF 00 FF 00 FF 00 00 00 FF 00 FF 00 ................
0000_45F4: 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 ................
0000_4604: FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 ................ ok
0000_45D4: FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 ................
0000_45E4: FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 ................
0000_45F4: 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF ................
0000_4604: 00 00 00 00 FF 00 FF 00 FF 00 00 00 FF 00 FF 00 ................ ok
0000_45D4: 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 ................
0000_45E4: 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 FF 00 ................
0000_45F4: 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 FF 00 ................
0000_4604: FF 00 00 00 00 FF 00 FF 00 FF 00 00 00 00 FF 00 ................ ok


Comments
I have some 2812's on a slow boat from China.
It looks like I deprecated KEYPOLL at some stage and forgot about it as most of the time the "keypoll" variable is set directly from user code rather than from user console. In code you would say:
' myfunction keypoll W!
to set keypoll with the address of the function.
You can call CHASE directly as that is basically what happens in the background with "keypoll" when it's waiting for user input from the console.
I was unable to make the CHASE word work. If I just enter CHASE, it does nothing but goes off to hang my system. If I enter
' CHASE keypoll W!
it goes off to do it's thing but I don't see anything on the LED.
Just to make sure the hardware was OK (and a WS2811 worked like a WS2812), I loaded up JonnyMac's WS2812 demo and it worked just fine with the WS2811.
I haven't had a chance to play more with the Tachyon version yet.
When I set WSTXD outset, my led comes on.
When I use LED, my led goes off.
I can follow the change in the array with ^x on the terminal, but there is nothing on the output?
Could it be something with to use or is there someting else to setup.
I can not test it, since I don't have the LEDs, but from reading the code
you need this line first the ' kind of returns the function pointer of the LEDS word which is them stored in the keypoll word variable.
This is a vector that if not 0 is called in the keypoll loop.
KEYPOLL is not defined, and keypoll is just a variable (its address actually) - therefore written in lowercase by convention.
inside CHASE here the alarm callback is installed, which is the function pointer to (CHASE)
- looks strange, but a function name surounded by () is just to mark an internal function (by convention)
the () really is part of the function / WORD name. Everything separated by a SPACE is a word ...
an other option to having the keypoll set up would be to put LEDS directly at the end of (CHASE) - so it is automatically called,
when the colors have been changed.
The benefit of the keypoll variant is that you can then interactively just modify colors and speed
and the update is automatic.
so do not see, why it is not working for you.
yes the RUNMOD needs a MASK, and the OUTCLR as well so
looks correct
too bad without HW ...
http://youtu.be/os6fUiua4D8
First then Thanks to @David and @MJB
and the color words just modify the colors array directly.
@fride - whoops, faster should be less of a delay. LEDs could be called from CHASE if you want and then just keypoll CHASE.
The main thing was to make sure that bit of code was handling the bit timing correctly, then it should be possible to do some fancy stuff at the high level interactively, even just typing in one line loops without actually creating a definition, as is possible in Tachyon.
This started off when I noticed that as I played with the colors and the CHASE, I was seeing colors introduced in my LED string that I had never entered.
I looked at the (CHASE) routine and saw that it is shifting the color array 1 byte with each iteration, NOT 3 bytes. This means that each time through, the BLUE value for the last LED becomes the GREEN value for the first LED, GREEN for LED1 become RED for LED1 and RED for LED1 goes to BLUE for LED1. So if you have a 2 LED setup with LED 1 set to green ($FF0000) and LED2 set to blue ($0000FF)
$FF0000 0000FF after one trip through (CHASE) will look like this: $FFFF00 000000; trip 2 will yield $$00FFFF 000000 and finally, trip #3 will yield $0000FF FF0000 - at this point, you have really moved the color from LED1 to LED2 and LED2 to LED1. I've called the 8 bit shift a (SLIDE) and fixed (CHASE) to do a 24 bit shift.
(CHASE) contains my changes, (SLIDE) is Peter's original (CHASE) just renamed.
pub (CHASE) --- perform a single chase step (move to next LED ) spd W@ ledtimer TIMEOUT --- reload timer with current speed rgbactv rgbsz + 1- C@ --- save last LEDs BLUE value on stack rgbactv rgbsz + 2- C@ --- save RED value onto STACK rgbactv rgbsz + 3 - C@ --- save GREEN value onto stack ( bvalue rvalue gvalue ) rgbactv rgbactv 3 + rgbsz 3 - <CMOVE --- shift them all along (in reverse) rgbactv C! --- write saved GREEN byte into LED #1 GREEN rgbactv 1+ C! --- write saved RED byte into LED #1 RED rgbactv 2+ C! --- write saved BLUE byte into LED #1 BLUE ; pub (SLIDE) --- perform a single chase step (move to next LED ) spd W@ ledtimer TIMEOUT --- reload timer with current speed rgbactv rgbsz + 1- C@ --- save last LEDs BLUE value on stack rgbactv rgbactv 1+ rgbsz 1 - <CMOVE --- shift them all along (in reverse) rgbactv C! --- write saved GREEN byte into LED #1 GREEN ;Once I did this, I was hooked and started playing (darn LEDs and Tachyon!!!!)
I set up a staging array so you can set a pattern in the staging area and then move it to the active array (RELOAD).
Created some TIMER controls: STARTLEDS, STOPLEDS, STEP8 and STEP24. The last two do a 8 bit or 24 bit single step.
Added words to set the colors into the two arrays: STGLED and ACTLED. These will plug the GRB values into the proper array based on an LED# offset, for example,
will place the proper G ($FF) R($FF) and B(0) values for YELLOW into the staging array for LED #2.
After this , I needed colors so I could RED and get $00 $FF $00 on the stack and YELLOW would put $FF $FF $00 on the stack.
I borrowed more complex colors from Jonny Mac's WS2812 spin program - most of them need to be tuned for my WS2811 8MM diffused LEDs ( I think the need for a color tools has arisen so you can play with the GRB values to find values for your LEDs.)
This all led to a highly modified (and butchered) version of Peter's original demo code. It still uses the roots of his work but is hacked up a bit. I've attached it for you playtime if you so desire. (it's good for a laugh, just to see how I write Tachyon!)
Once you load it, you still need to do
to get things started. From there you can try CHASE or SLIDE or stop the CHASE with STOPLEDS and then go manipulate the colors with ACTLED or STGLED and RELOAD and then start it all again with STARTLEDS.
If you want to see the difference between Peter's (CHASE) and mine, get to the point where CHASE is running and then STOPLEDS, look at the pattern, do STEP24 and you should see each LED color move to the right, then do a STEP8 and you will see some new colors introduced.
It's all good fun and blinking lights!
Thanks Peter for getting me started in Tachyon again!!
( WS2812.fth ) TACHYON [~ FORGET WS2812.fth pub WS2812.fth PRINT" WS2812 Intelligent RGB LED driver V1.0 1410131230.0000 " ; IFNDEF [WS2812] --- use newer name for module in case this had the old name ALIAS [TXRGB] [WS2812] } { WS2812 timing Screenshot from 2014-10-13 12:41:48.png A zero is transmitted as 350ns high by 800ns low (+/-150ns) A one is transmitted as 700ns high by 600ns low RET is >50us Screenshot from 2014-10-13 12:51:03.png Screenshot from 2014-10-13 12:51:52.png A zero is transmitted as 350ns high by 800ns low (+/-150ns) A one is transmitted as 700ns high by 600ns low RET is >50us } --- UPDATE - WS2812 RUNMOD added to current 2.4 kernel, this is all that is needed now to run it ( define I/O and an array ) #P14 MASK == WSTXD --- define our transmit pin #12 == rgbsz --- specify the size of the array as a constant --- set to 3 * the number of LEDs (e.g. 4 LEDS, rgbsx = 12) rgbsz BYTES rgbactv --- define our byte array rgbsz BYTES rgbstage --- define BYTE array for staging LED colors pub LEDS WSTXD OUTCLR --- Start a RET to synch the chips WSTXD 4 COGREG! --- setup the I/O pin for the RUNMOD to use [WS2812] --- select the WS2812 module for RUNMOD (needs to be renamed yet to [WS2812] ) #10 us --- still need a bit of extra delay for a minimum RET op while txd is low rgbactv rgbsz RUNMOD --- pass the address of the array and the byte count to the RUNMOD ; { executing LEDS will update a 96 byte display in 1 ms The WS2812 LEDs can also be updated in the background of the main console cog this way: KEYPOLL LEDS or ' LEDS keypoll W! Or called regularly from the timer cog this way: TIMER ledtimer --- create a timer for the LEDs #20 ledtimer TIMEOUT --- set it to trip in 20 ms (add this method to LEDS as well to reload) ' LEDS ledtimer ALARM --- what to do when the timer expires } --- COLOR Definitions { From JonnyMac's jm_ws2812.spin demo borrowed from Gavin Garner's TM1804 LED driver -- additional colors by Lachlan -- some alterations by JM } --- the more complex colors really need to be tuned --- they are way off for the WS2811 8MM LEDs I'm using pub RED ( -- gvalue rvalue bvalue) 0 $FF 0 ; pub GREEN ( -- gvalue rvalue bvalue) $FF 0 0 ; pub BLUE ( -- gvalue rvalue bvalue) 0 0 $FF ; pub YELLOW ( --- gvalue rvalue bvalue) $FF $FF 0 ; pub CYAN ( -- gvalue rvalue bvalue) $FF 0 $FF ; pub MAGENTA ( -- gvalue rvalue bvalue) 0 $FF $FF ; pub CHARTREUSE ( --- gv rv bv ) $FF $7F 0 ; pub ORANGE ( --- gv rv bv ) $60 $FF 0 ; pub AQUAMARINE ( --- gv rv bv ) $FF $7F $D4 ; pub PINK ( --- gv rv bv ) $5F $FF $5F ; pub TURQUOISE ( --- gv rv bv ) $E0 $3F $C0 ; pub REALWHITE ( --- gv rv bv ) $FF $C8 $FF ; pub INDIGO ( --- gv rv bv ) $00 $3F $7F ; pub VIOLET ( --- gv rv bv ) $7F $BF $BF ; pub MAROON ( --- gv rv bv ) $00 $32 $10 ; pub BROWN ( --- gv rv bv ) $06 $0E 0 ; pub CRIMSON ( --- gv rv bv ) $28 $DC $3C ; pub PURPLE ( --- gv rv bv ) 0 $8C $FF ; pub WHITE ( -- gvalue rvalue bvalue ) $FF $FF $FF ; pub BLACK ( --- gvalue rvalue bvalue ) 0 0 0 ; --- words to work with the two LED color arrays - ACTxxxx affects the ACTive array --- STGxxx affects teh staging array pub STGCLEAR rgbstage rgbsz ERASE ; pub ACTCLEAR rgbactv rgbsz ERASE ; pub STGDUMP rgbstage rgbsz DUMP ; pub ACTDUMP rgbactv rgbsz DUMP ; pub STGLED ( gvalue rvalue bvalue lednum -- ) 1- 3 * rgbstage + --- calc address of array plus offset for LED DUP ROT SWAP --- ( gv rv bv addr --- gv rv addr bv addr) 2+ C! --- put TOS into BLUE byte for this LED DUP ROT SWAP --- ( vg rv addr --- gv addr rv addr ) 1+ C! --- put TOS into RED byte for this LED C! --- put TOS into GREEN byte for this LED ; pub ACTLED ( gvalue rvalue bvalue lednum -- ) 1- 3 * rgbactv + --- calc address of array plus offset for LED DUP ROT SWAP --- ( gv rv bv addr --- gv rv addr bv addr) 2+ C! --- put TOS into BLUE byte for this LED DUP ROT SWAP --- ( vg rv addr --- gv addr rv addr ) 1+ C! --- put TOS into RED byte for this LED C! --- put TOS into GREEN byte for this LED ; pub RELOAD rgbstage rgbactv rgbsz CMOVE ; --- copy the staging byte array to the active byte array ( Simple DEMO code to make RGB LEDs chase each other ) DECIMAL TIMER ledtimer --- create a timer structure variable WORD spd --- allow for the speed to be varied interactively pub (CHASE) --- perform a single chase step (move to next LED ) spd W@ ledtimer TIMEOUT --- reload timer with current speed rgbactv rgbsz + 1- C@ --- save last LEDs BLUE value on stack rgbactv rgbsz + 2- C@ --- save RED value onto STACK rgbactv rgbsz + 3 - C@ --- save GREEN value onto stack ( bvalue rvalue gvalue ) rgbactv rgbactv 3 + rgbsz 3 - <CMOVE --- shift them all along (in reverse) rgbactv C! --- write saved GREEN byte into LED #1 GREEN rgbactv 1+ C! --- write saved RED byte into LED #1 RED rgbactv 2+ C! --- write saved BLUE byte into LED #1 BLUE ; pub (SLIDE) --- perform a single chase step (move to next LED ) spd W@ ledtimer TIMEOUT --- reload timer with current speed rgbactv rgbsz + 1- C@ --- save last LEDs BLUE value on stack rgbactv rgbactv 1+ rgbsz 1 - <CMOVE --- shift them all along (in reverse) rgbactv C! --- write saved GREEN byte into LED #1 GREEN ; --- DEBUG words to manipulate the timer and single step through the ACTIVE array pub STARTLEDS spd W@ ledtimer TIMEOUT ; --- set the ledtimer to the curent speed pub STOPLEDS 0 ledtimer TIMEOUT ; --- set the ledtimer to 0 (stop the chase) pub SPEED ( n -- ) 1 MAX 10000 MIN spd W! ; pub FAST 50 SPEED ; pub SLOW 500 SPEED ; pub FASTER spd W@ 80 * 100 / SPEED ; pub SLOWER spd W@ 120 * 100 / SPEED ; pub STEP8 --- shift the array 1 color BYTE (like a (SLIDE) ) spd W@ --- save the current speed to the stack 0 spd W! --- set speed to 0 so you can use (SLIDE) (SLIDE) spd W! --- restore the current speed ; pub STEP24 --- shift the active array 3 bytes (an LED's worth) spd W@ --- save the current speed to the stack 0 spd W! --- set speed to 0 so you can use (CHASE) (CHASE) spd W! --- restore teh current speed ; --- demo words --- pub CHASE ( --- ) GREEN 1 STGLED --- set first LED to GREEN RED 2 STGLED --- set second LED to RED BLUE 3 STGLED --- set third LED to BLUE WHITE 4 STGLED --- set fourth LED to WHITE RELOAD --- move the staging array to the active array ' (CHASE) ledtimer ALARM --- set action for timer 10 ledtimer TIMEOUT --- kick off timer 100 SPEED --- set initial chase speed ; pub SLIDE ( --- ) GREEN 1 STGLED --- set first LED to GREEN RED 2 STGLED --- set second LED to RED BLUE 3 STGLED --- set third LED to BLUE WHITE 4 STGLED --- set fourth LED to WHITE RELOAD --- move the staging array to the active array ' (SLIDE) ledtimer ALARM --- set action for timer 10 ledtimer TIMEOUT --- kick off timer 100 SPEED --- set initial chase speed ; ]~ ENDInefficient or not, it works and it's fun
Without making too many real changes here is a slight optimization as a suggestion. I mainly put the colors into a long but there are other subtle changes too but beware I've written it blind so it may be full of little bugs!
[FONT=courier new]TACHYON [~ FORGET WS2812.fth pub WS2812.fth PRINT" WS2812 Intelligent RGB LED driver V1.0 1410131230.0000 " ; IFNDEF [WS2812] --- use newer name for module in case this had the old name ALIAS [TXRGB] [WS2812] } ( define I/O and an array ) #P14 MASK == WSTXD --- define our transmit pin #12 == rgbsz --- specify the size of the array as a constant --- set to 3 * the number of LEDs (e.g. 4 LEDS, rgbsz = 12) rgbsz BYTES rgbactv --- define our byte array rgbsz BYTES rgbstage --- define BYTE array for staging LED colors pub !LEDS WSTXD OUTCLR --- Start a RET to synch the chips WSTXD 4 COGREG! --- setup the I/O pin for the RUNMOD to use [WS2812] --- select the WS2812 module for RUNMOD (needs to be renamed yet to [WS2812] ) ; pub SHOW rgbactv rgbsz RUNMOD --- pass the address of the array and the byte count to the RUNMOD #50 us --- the RET is specified at 50us so this will ensure it is synched even if LEDS is called again without delay ; --- COLOR Definitions \ G R B $00FF00 == RED $FF0000 == GREEN $0000FF == BLUE $FFFF00 == YELLOW $FF00FF == CYAN $00FFFF == MAGENTA $FF7F00 == CHARTREUSE $60FF00 == ORANGE $FF7FD4 == AQUAMARINE $5FFF5F == PINK $E03FC0 == TURQUIOSE $FFC8FF == REALWHITE $003F7F == INDIGO $7FBFBF == VIOLET $003210 == MAROON $060E00 == BROWN $28DC3C == CRIMSON $008CFF == PURPLE $FFFFFF == WHITE $000000 == BLACK --- words to work with the two LED color arrays - ACTxxxx affects the ACTive array --- STGxxx affects teh staging array pub STGCLEAR rgbstage rgbsz ERASE ; pub ACTCLEAR rgbactv rgbsz ERASE ; pub STGDUMP rgbstage rgbsz DUMP ; pub ACTDUMP rgbactv rgbsz DUMP ; [COLOR=#FF0000]{ alternative is just to say ACT DUMP or STG DUMP or STG ERASE or ACT ERASE pub STG rgbstage rgbsz ; pub ACT rgbactv rgbsz ; }[/COLOR] [COLOR=#FF0000]--- perhaps a CMOVE method could work better by first storing the long in a variable LONG rgb pub STGLED ( gvalue.rvalue.bvalue lednum -- ) rgbstage pub SETLED ( g.r.b lednum buffer --- ) SWAP 1- 3 * + --- calc address of array plus offset for LED SWAP rgb ! rgb SWAP 3 CMOVE ; pub ACTLED ( gvalue rvalue bvalue lednum -- ) rgbactv SETLED ; [/COLOR] pub RELOAD rgbstage rgbactv rgbsz CMOVE ; --- copy the staging byte array to the active byte array ( Simple DEMO code to make RGB LEDs chase each other ) DECIMAL TIMER ledtimer --- create a timer structure variable WORD spd --- allow for the speed to be varied interactively pub (CHASE) --- perform a single chase step (move to next LED ) spd W@ ledtimer TIMEOUT --- reload timer with current speed [COLOR=#FF0000]\ pub STEP24[/COLOR] rgbactv rgbsz + 1- C@ --- save last LEDs BLUE value on stack rgbactv rgbsz + 2- C@ --- save RED value onto STACK rgbactv rgbsz + 3 - C@ --- save GREEN value onto stack ( bvalue rvalue gvalue ) rgbactv rgbactv 3 + rgbsz 3 - <CMOVE --- shift them all along (in reverse) rgbactv C! --- write saved GREEN byte into LED #1 GREEN rgbactv 1+ C! --- write saved RED byte into LED #1 RED rgbactv 2+ C! --- write saved BLUE byte into LED #1 BLUE [COLOR=#ff0000]SHOW --- just write this all to the LEDs right now rather than using KEYPOLL[/COLOR] ; pub (SLIDE) --- perform a single chase step (move to next LED ) spd W@ ledtimer TIMEOUT --- reload timer with current speed [COLOR=#FF0000]\ pub STEP8[/COLOR] rgbactv rgbsz + 1- C@ --- save last LEDs BLUE value on stack rgbactv rgbactv 1+ rgbsz 1 - <CMOVE --- shift them all along (in reverse) rgbactv C! --- write saved GREEN byte into LED #1 GREEN [FONT=courier new] [COLOR=#ff0000]SHOW --- just write this all to the LEDs right now[FONT=courier new][COLOR=#ff0000] rather than using KEYPOLL[/COLOR][/FONT] [/COLOR][/FONT] ; --- DEBUG words to manipulate the timer and single step through the ACTIVE array pub STARTLEDS spd W@ ledtimer TIMEOUT ; --- set the ledtimer to the curent speed pub STOPLEDS 0 ledtimer TIMEOUT ; --- set the ledtimer to 0 (stop the chase) [COLOR=#FF0000]{ Or use this: pub LEDS ( on/off -- ) spd W@ AND ledtimer TIMEOUT !LEDS ; \ so you can say ON LEDS or OFF LEDS } [/COLOR] --- percentage of speed increase or decrease so 80 % will run 80% of the current speed pub % ( n -- ) 200 SWAP - spd W@ * 100 / pub SPEED ( n -- ) 1 MAX 10000 MIN spd W! ; pub FAST 50 SPEED ; pub SLOW 500 SPEED ; pub FASTER 120 % ; pub SLOWER 80 % ; [COLOR=#800080] ( NOTE: could STEP8 and STEP24 just be an entry into (CHASE) and (SLIDE) as shown above ? ) pub STEP8 --- shift the array 1 color BYTE (like a (SLIDE) ) spd W@ --- save the current speed to the stack 0 spd W! --- set speed to 0 so you can use (SLIDE) (SLIDE) spd W! --- restore the current speed ; pub STEP24 --- shift the active array 3 bytes (an LED's worth) spd W@ --- save the current speed to the stack 0 spd W! --- set speed to 0 so you can use (CHASE) (CHASE) spd W! --- restore teh current speed ; [/COLOR] --- demo words --- pub CHASE ( --- ) ' (CHASE) ledtimer ALARM --- set action for timer pub LIGHT GREEN 1 STGLED --- set first LED to GREEN RED 2 STGLED --- set second LED to RED BLUE 3 STGLED --- set third LED to BLUE WHITE 4 STGLED --- set fourth LED to WHITE RELOAD --- move the staging array to the active array 100 SPEED --- set initial chase speed [COLOR=#ff0000] ON LEDS [/COLOR] ; pub SLIDE ( --- ) ' (SLIDE) ledtimer ALARM --- set action for timer LIGHT ; ]~ END[/FONT]It could be cleaner too by reducing STGLEDS to STG LEDS so that it works likes STG DUMP and STG ERASE etc. The idea is to create a language that is more like English rather than concatenatedtechnicalgerman.
Tachyon has grown up so much since I last played. I forgot things like the entry into a pub for step8 and step24.
I'm brute forcing my way back in here with my old brute Forth mentality. I need to dig through the Tachyon thread and code to pick up on all the elegance you have added.
You'll really start having fun if you ever get any LEDs!!
I've just ordered 5 sticks of 8 LEDs locally although I wanted more and some discrete component LEDs as well but I'm looking at the flexible strips on eBay and thinking that I could line these up in rows of 7 or so to form a really long flexible message board that I might even use to read email on from my Prop! Howzat!
OK, I appear to have broken the timer somehow. I implemented your improvements (maybe modified and added a few) but I think I captured the needed parts.
The individual pieces all seem to work. I can load the arrays with colors for the specified LEDs, SHOW them on the LEDs, (CHASE) them or (SLIDE) them. The only thing not working is the timer. It appears to stop or run very, very slowly. It looks good when you do .TIMERS but it never fires. I've been staring at it for a couple hours now, and trying things to no avail. Obviously, I'm missing something and/or don't understand the timers (they seemed pretty straight forward).
I'm open to suggestions from anyone.
( WS2812.fth ) TACHYON [~ FORGET WS2812.fth pub WS2812.fth PRINT" WS2812 Intelligent RGB LED driver V1.0 1410131230.0000 " ; IFNDEF [WS2812] --- use newer name for module in case this had the old name ALIAS [TXRGB] [WS2812] } { WS2812 timing Screenshot from 2014-10-13 12:41:48.png A zero is transmitted as 350ns high by 800ns low (+/-150ns) A one is transmitted as 700ns high by 600ns low RET is >50us Screenshot from 2014-10-13 12:51:03.png Screenshot from 2014-10-13 12:51:52.png A zero is transmitted as 350ns high by 800ns low (+/-150ns) A one is transmitted as 700ns high by 600ns low RET is >50us } --- UPDATE - WS2812 RUNMOD added to current 2.4 kernel, this is all that is needed now to run it ( define I/O and an array ) #P14 MASK == WSTXD --- define our transmit pin #12 == rgbsz --- specify the size of the array as a constant --- set to 3 * the number of LEDs (e.g. 4 LEDS, rgbsx = 12) rgbsz BYTES rgbactv --- define our byte array rgbsz BYTES rgbstage --- define BYTE array for staging LED colors pub !LEDS WSTXD OUTCLR --- Start a RET to synch the chips WSTXD 4 COGREG! --- setup the I/O pin for the RUNMOD to use [WS2812] --- select the WS2812 module for RUNMOD (needs to be renamed yet to [WS2812] ) ; pub SHOW rgbactv rgbsz RUNMOD --- pass the address of the array and the byte count to the RUNMOD #50 us ; \ executing !LEDS will update a 96 byte display in 1 ms --- COLOR Definitions { From JonnyMac's jm_ws2812.spin demo borrowed from Gavin Garner's TM1804 LED driver -- additional colors by Lachlan -- some alterations by JM } --- the more complex colors really need to be tuned --- they are way off for the WS2811 8MM LEDs I'm using \ G R B $00FF00 == RED $FF0000 == GREEN $0000FF == BLUE $FFFF00 == YELLOW $FF00FF == CYAN $00FFFF == MAGENTA $FF7F00 == CHARTREUSE $60FF00 == ORANGE $FF7FD4 == AQUAMARINE $5FFF5F == PINK $E03FC0 == TURQUIOSE $FFC8FF == REALWHITE $003F7F == INDIGO $7FBFBF == VIOLET $003210 == MAROON $060E00 == BROWN $28DC3C == CRIMSON $008CFF == PURPLE $FFFFFF == WHITE $000000 == BLACK --- words to work with the two LED color arrays: ACTIVE and STAGE pub ACTIVE rgbactv rgbsz ; pub STAGE rgbstage rgbsz ; --- SETLED places color values for a specific LED into ACTIVE or STAGE color arrays --- example: RED 3 ACTIVE SETLED --- sets LED#3 in ACTIVE buffer to RED LONG rgb --- create a long to save the passed G.R.B into for CMOVE pub SETLED ( g.r.b lednum buffer bufsz --- ) DROP --- extra parameter from using ACTIVE/STAGE words SWAP 1- 3 * + --- calc address of array plus offset for LED SWAP rgb ! rgb SWAP 3 CMOVE ; pub RELOAD rgbstage rgbactv rgbsz CMOVE ; --- copy the staging byte array to the active byte array ( Simple DEMO code to make RGB LEDs chase each other ) DECIMAL TIMER ledtimer --- create a timer structure variable WORD spd --- allow for the speed to be varied interactively pub (CHASE) --- perform a single chase step (move to next LED ) spd W@ ledtimer TIMEOUT --- reload timer with current speed pub STEP24 rgbactv rgbsz + 1- C@ --- save last LEDs BLUE value on stack rgbactv rgbsz + 2- C@ --- save RED value onto STACK rgbactv rgbsz + 3 - C@ --- save GREEN value onto stack ( bvalue rvalue gvalue ) rgbactv rgbactv 3 + rgbsz 3 - <CMOVE --- shift them all along (in reverse) rgbactv C! --- write saved GREEN byte into LED #1 GREEN rgbactv 1+ C! --- write saved RED byte into LED #1 RED rgbactv 2+ C! --- write saved BLUE byte into LED #1 BLUE SHOW ; pub (SLIDE) --- slide the colors through the array one byte each time spd W@ ledtimer TIMEOUT --- reload timer with current speed pub STEP8 rgbactv rgbsz + 1- C@ --- save last LEDs BLUE value on stack rgbactv rgbactv 1+ rgbsz 1 - <CMOVE --- shift them all along (in reverse) rgbactv C! --- write saved GREEN byte into LED #1 GREEN SHOW ; --- DEBUG words to manipulate the timer and single step through the ACTIVE array pub LEDS ( on/off -- ) spd W@ AND ledtimer TIMEOUT !LEDS ; pub SPEED ( n -- ) 1 MAX 10000 MIN spd W! ; pub FAST 50 SPEED ; pub SLOW 500 SPEED ; pub FASTER spd W@ 80 * 100 / SPEED ; pub SLOWER spd W@ 120 * 100 / SPEED ; --- demo words --- pub CHASE ( --- ) ' (CHASE) ledtimer ALARM pub LIGHT GREEN 1 STAGE SETLED --- set first LED to GREEN RED 2 STAGE SETLED --- set second LED to RED BLUE 3 STAGE SETLED --- set third LED to BLUE WHITE 4 STAGE SETLED --- set fourth LED to WHITE RELOAD --- move the staging array to the active array SLOW --- set initial chase speed ON LEDS ; pub SLIDE ( --- ) ' (SLIDE) ledtimer ALARM --- set action for timer LIGHT ; ]~ ENDThere are a couple of conflicts that I discovered, first off the TIMERTASK cog is the one that needs to initialize the LED pin and RUNMOD for it to be able to call it automatically. However the WAITCNT used by the RUNMOD also upsets the WAITCNT that is used to synch the TIMERTASK so the RUNMOD either has to be changed or just get it to run under keypoll. Here's code that will work ( just type CHASE or SLIDE to start ):
[FONT=courier new]TACHYON [~ FORGET WS2812.fth pub WS2812.fth PRINT" WS2812 Intelligent RGB LED driver V1.0 1410131230.0000 " ; IFNDEF [WS2812] --- use newer name for module in case this had the old name ALIAS [TXRGB] [WS2812] } DECIMAL ( define I/O and an array ) #P8 MASK == WSTXD --- define our transmit pin 8 == ledcnt --- number of LEDs in string ledcnt 3 * == rgbsz --- specify the size of the array as a constant --- set to 3 * the number of LEDs (e.g. 4 LEDS, rgbsx = 12) rgbsz BYTES rgbactv --- define our byte array rgbsz BYTES rgbstage --- define BYTE array for staging LED colors --- Setup the RUNMOD and port for the WS2812 pub !LEDS WSTXD OUTCLR --- Start a RET to synch the chips WSTXD 4 COGREG! --- setup the I/O pin for the RUNMOD to use [WS2812] --- select the WS2812 module for RUNMOD (needs to be renamed yet to [WS2812] ) ; --- Show the contents of rgbactv on the LEDs pub SHOW rgbactv rgbsz RUNMOD --- pass the address of the array and the byte count to the RUNMOD #50 us --- the RET is specified at 50us so this will ensure it is synched even if SHOW is called again without delay ; --- COLOR Definitions \ G R B $00FF00 == RED $FF0000 == GREEN $0000FF == BLUE $FFFF00 == YELLOW $FF00FF == CYAN $00FFFF == MAGENTA $FF7F00 == CHARTREUSE $60FF00 == ORANGE $FF7FD4 == AQUAMARINE $5FFF5F == PINK $E03FC0 == TURQUIOSE $FFC8FF == REALWHITE $003F7F == INDIGO $7FBFBF == VIOLET $003210 == MAROON $060E00 == BROWN $28DC3C == CRIMSON $008CFF == PURPLE $FFFFFF == WHITE $000000 == BLACK --- words to work with the two LED color arrays --- ACT affects the ACTive array --- STG affects teh staging array \ alternative is just to say ACT DUMP or STG DUMP or STG ERASE or ACT ERASE pub STG rgbstage rgbsz ; pub ACT rgbactv rgbsz ; LONG rgb --- holding variable to allow CMOVE into array \ Usage: <color> led STG LED or <color> led ACT LED \ Example: RED 4 STG LED \ note: sz is redundant but passed as part of buffer parameter pub LED! ( g.r.b lednum buffer sz --- ) DROP SWAP 1- 3 * + --- calc address of array plus offset for LED SWAP rgb ! rgb SWAP 3 CMOVE ; pub RELOAD rgbstage rgbactv rgbsz CMOVE ; --- copy the staging byte array to the active byte array ( Simple DEMO code to make RGB LEDs chase each other ) TIMER ledtimer --- create a timer structure variable WORD spd --- allow for the speed to be varied interactively pub (CHASE) --- perform a single chase step (move to next LED ) spd W@ ledtimer TIMEOUT --- reload timer with current speed pub STEP3 rgbactv rgbsz + 3 - U@ rgb ! --- read last 3 colors unaligned as a long (only use 3 ls bytes) rgbactv rgbactv 3 + rgbsz 3 - <CMOVE --- shift them all along (in reverse) rgb rgbactv 3 CMOVE --- write last 3 bytes back to start (wrap) ; pub (SLIDE) --- perform a single chase step (move to next LED ) spd W@ ledtimer TIMEOUT --- reload timer with current speed pub STEP1 rgbactv rgbsz + 1- C@ --- save last LEDs BLUE value on stack rgbactv rgbactv 1+ rgbsz 1 - <CMOVE --- shift them all along (in reverse) rgbactv C! --- write saved GREEN byte into LED #1 GREEN ; --- DEBUG words to manipulate the timer and single step through the ACTIVE array --- starts or stops the timer using current speed : LEDS ( on/off -- ) spd W@ AND ledtimer TIMEOUT ; --- percentage of speed increase or decrease so 80 % will run 80% of the current speed pub % ( n -- ) 200 SWAP - spd W@ * 100 / pub SPEED ( n -- ) 1 MAX 10000 MIN spd W! ; pub FAST 50 SPEED ; pub SLOW 500 SPEED ; pub FASTER 120 % ; pub SLOWER 80 % ; --- demo words --- pub CHASE ( --- ) ' (CHASE) ledtimer ALARM --- set alarm action for timer pub LIGHT !LEDS --- allow timer task to setup the I/O and WS2812 RUNMOD STG ERASE GREEN 1 STG LED! --- set first LED to GREEN RED 2 STG LED! --- set second LED to RED BLUE 3 STG LED! --- set third LED to BLUE WHITE 4 STG LED! --- set fourth LED to WHITE RELOAD --- move the staging array to the active array SLOW --- set initial chase speed ON LEDS ' SHOW keypoll W! ; pub SLIDE ( --- ) ' (SLIDE) ledtimer ALARM --- set action for timer LIGHT ; ]~ END [/FONT]I'll give this a try with some LEDs when my morning rolls around.
I like how you refine and tune the code as you iterate through the versions. I may accidently learn something yet!
There's lots of things that can be added very easily, such as synchronizing the updates for instance, just change this and add the REFRESH word to the end of the (CHASE) and (SLIDE) defs:
BYTE refresh --- create a flag to indicate a synchronized refresh is requested pub REFRESH refresh C~~ ; --- Show the contents of rgbactv on the LEDs pub SHOW refresh C@ 0EXIT refresh C~ --- clear refresh flag and uppdate the LEDs rgbactv rgbsz RUNMOD --- pass the address of the array and the byte count to the RUNMOD #50 us --- the RET is specified at 50us so this will ensure it is synched even if SHOW is called again without delay ;So the other thing I would add would be brightness modulation at the refresh level so that you can run a timer and extract it's current value, convert it to triangle or sine even and use that value to vary the brightness every time the LEDs are refreshed which would also not need to be synchronized then. To keep it efficient the modulation index could just be applied either as a shift or added/subtracted and clipped, this will produce some stroboscopic effects on top of the current pattern with some LEDs appearing to glow brighter before fading etc.
Also of course we can just have a file or an array set up to produce any pattern that we want. Once I get enough rows and columns I will do that scrolling message display or a digital clock at least!.
BTW, even though I don't have any LEDs yet I just tie the transmit pin to the scope through a 1M resistor to analog the signal (RC filter) so I can see the pattern moving across the screen.
The code cleanup was mainly because I decided to check it and then I discovered the WAITCNT problem so I tidied everything up in the process. So you can change individual LEDs on the fly of course with the <color> <led> ACT LED! command etc or just stop it with OFF LEDS and use STEP1 or STEP3 etc.
(I noticed a bit more streamlining - cool tricks!)
I'll get to play if I ever finish my chores today.
Great, and remembering that it's interactive means you could type $C000 ACT CMOVE while it's running to introduce a pattern from ROM for instance. In fact RELOAD could be redone as a more general load with:
pub RELOAD rgbstage pub LOAD ( src -- ) ACT CMOVE --- copy the source array to the active byte array ;So now you just say 0 LOAD or $C000 LOAD etc. In the next couple of days I when I get to actually try this stuff hands on I will add modulation and twinkling to the standard chaser. Can't wait to get those long strings though to do a message display, but I'm still selecting suitable ones, thinking maybe the higher density 2m strips in rows of 7 or 8 would be awesome.
The # prefix forces a number to decimal and I allow symbols to be mixed with digits so you could say 13 or #13 or #P13 or even #PORT13 as it all resolves the same way but #P13 is much more recognizable as a port number rather than just some constant.
Here are a few commented examples:
$13120520 .LONG 1312.0520 ok
force hex
$13.12.05.20 .LONG 1312.0520 ok
force hex with separators
$13:12:05:20 .LONG 1312.0520 ok
force hex
#13:12:05:20 .LONG 0D0C.0514 ok
force decimal with BCD clock style notation
$1312_0520 .LONG 1312.0520 ok
force hex with separators
#13120520 .LONG 00C8.3408 ok
force decimal
#1312_0520 .LONG 00C8.3408 ok
force decimal with separators
#80,000,000 .LONG 04C4.B400 ok
force decimal with commas etc
01_1100 .BYTE 9C ok
force binary
&192.168.16.1 .LONG C0A8.1001 ok
specify an IP address
#P26 .BYTE 1A ok
specify a port number
#P26:25:19:18 .LONG 1A19.1312 ok
packing pin numbers into a long (used to set device pinouts)
BTW, I looked at getting the 8x8 matrix but figured that 8 of the 8 way strips were better value and more flexible, although they only had 5 left in stock. If I am going to do a matrix I am better off with rows of the LED ropes.
EDIT: Just created a blog entry in regards to numeric notation