Hello!

Every system needs lights. Well With this driver there can be a lot of lights. 
I wrote this against 5.5
Today's big merge might make this useless or at least require some tweaking.

While writing this driver I spend some time finding from .lst file which 
registers are allowed to use and how the word should be written. I think there 
should be some documentation or tips at least how to write assembler words. I 
wrote some notes.

It was my own stupidity to try sbiw temp0, 1 but took a while to notice and 
then think the alternative for it. Link to this kind of quirks and workarounds 
would be nice to have in docs too.

I wrote some kind of ugly delay macro. Something like that could be useful.

I noticed make doesn't start compilation if words/foo.asm is edited in project. 
Might be good idea to add that to the line.

The driver can be used like this:

21 buffer: rgb
: rgb-test 21 0 do rgb i + c! loop rgb 21 ws2811.write ;

$ff $ff $ff
$ff $ff 0
$ff 0 $ff
0 $ff $ff
$ff 0 0 
0 $ff 0
0 0 $ff
rgb-test 5000 ms 21 ws2811.off


ws2811.asm:


; Hannu Vuolasaho 2014

;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 2 of the License, or
;    (at your option) any later version.

;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.
;
;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/>.

; Device driver for WS2811 LED drivers.

; Rather stupid delay system.
; The 1000ns macro takes care the half clocks from 250ns delay.
eight_cycle_delay:
    ret

.macro wait_cycles
  .set busy_wait =  ((@0 * @1) / F_CPU)
  .if (busy_wait & 16)
    call eight_cycle_delay
    call eight_cycle_delay
    .message "16 cycle"
  .endif
  .if   (busy_wait & 8)
    call eight_cycle_delay
    .message "8 cycle"
  .endif
  .if   (busy_wait & 4)
    .message "4 cycle"
    rjmp  pc+1
    rjmp  pc+1
  .endif
  .if   (busy_wait & 2)
    .message "2 cycle"
    rjmp  pc+1
  .endif
  .if   (busy_wait & 1)
    .message "1 cycle"
    nop
  .endif
.endmacro

.if (F_CPU < 8000000)
    .error "WS2811 800 kHz driver can't work. F_CPU too slow"
.endif
.macro ns250_fill_delay
  wait_cycles 3, 20000000
.endmacro
.macro ns1000_fill_delay
  wait_cycles 9, 20000000
.endmacro

.set WS2811_DDR=(WS2811_PORT-1)

;****f* WS2811.WRITE
; NAME
;   WS2811.WRITE
; SYNOPSIS
;   WS2811.WRITE ( addr len -- ) Write array of 8-bit RGB values to LED chain.
; DESCRIPTION
;   Write RGB values to WS2811 device chain.
;******
; ( addr len -- )
; Hardware
; Write to WS2811 devices

VE_WS2811_WRITE:
    .dw $ff0c
    .db "ws2811.write"
    .dw VE_HEAD
    .set VE_HEAD = VE_WS2811_WRITE
XT_WS2811_WRITE:
    .dw PFA_WS2811_WRITE
PFA_WS2811_WRITE:
    ; Reset WS2811
    sbi WS2811_DDR, WS2811_BIT; Set as output
    ;Write zero
    cbi WS2811_PORT, WS2811_BIT; Write zero
    delay 50 ; Wait 50 usec
    ; Prepare to write
    movw temp0, tosl ; len
    loadtos
    movw Z, tosl ; addr
    in temp2, WS2811_PORT ; zero value
    cbr temp2, (1 << WS2811_BIT)
    mov temp3, temp2 ; one value
    sbr temp3, (1 << WS2811_BIT)
    ; Save status register and disable interrupts. This is critical.
    in temp4, SREG
    cli
PFA_WS2811_WRITE_START:
    ldi temp6, 7             ; Bit counter to 7
     ld temp5, Z+              ; Load from Z to 
PFA_WS2811_WRITE_CLK_6:
    nop 
PFA_WS2811_WRITE_CLK_7:
    nop
     ns1000_fill_delay
    out WS2811_PORT, temp2   ; Pin DOWN 
PFA_WS2811_WRITE_CLK_9: 
    lsl temp5                 ; MSB to carry
    ns250_fill_delay
PFA_WS2811_WRITE_CLK_0: 
    out WS2811_PORT, temp3   ; Pin UP
      ns250_fill_delay
    brcs PFA_WS2811_WRITE_CLK_3; If carry set jump CLK_3
    out WS2811_PORT, temp2   ; Carry not set, pin DOWN
PFA_WS2811_WRITE_CLK_3: 
    dec temp6                ; Bit counter -1 
    brne PFA_WS2811_WRITE_CLK_6 ; Jump CLK_6 if bits  left to send from byte.
    lsl temp5                 ; MSB to carry
     brcc PFA_WS2811_WRITE_LOAD_CLOCK_8 ; If carry clear jump
     ldi temp6, 7             ; Bit counter to 7
    ns1000_fill_delay
     out WS2811_PORT, temp2   ; Pin DOWN
    nop
    ns250_fill_delay
     out WS2811_PORT, temp3   ; Pin UP
    ns250_fill_delay
    subi temp0, 1            ; Byte counter -1
    sbci temp1, 0            ; sbiw equivalent
     ;sbiw temp0, 1            ; Byte counter -1
     ld temp5, Z+              ; Next byte
     brne PFA_WS2811_WRITE_CLK_7 ; Jump CLK_7 if byte counter 0
    ns1000_fill_delay
     rjmp PFA_WS2811_WRITE_EXIT_CLK_8 ; jump to end
PFA_WS2811_WRITE_LOAD_CLOCK_8:
    ns1000_fill_delay 
    out WS2811_PORT, temp2   ; Pin DOWN 
     ldi temp6, 7
    ns250_fill_delay
     out WS2811_PORT, temp3   ; Pin UP
     nop
    
    ns250_fill_delay
     out WS2811_PORT, temp2   ; Pin DOWN
    ns1000_fill_delay 
    subi temp0, 1            ; Byte counter -1
    sbci temp1, 0            ; sbiw equivalent;
     ;sbiw temp0, 1            ; Byte counter -1
     ld temp5, Z+              ; Next byte
     brne PFA_WS2811_WRITE_CLK_9 ; Jump CLK_9 if byte count != 0
PFA_WS2811_WRITE_EXIT_CLK_8:
    out WS2811_PORT, temp2   ; Pin DOWN
    nop 
    ns250_fill_delay
    out WS2811_PORT, temp3   ; Pin UP
    out SREG, temp4
    loadtos ; dump the temp value from tos
    jmp_ DO_NEXT


;****f* WS2811.OFF
; NAME
;   WS2811.OFF
; SYNOPSIS
;   WS2811.OFF ( n -- ) shut off n ws2811 leds.
; DESCRIPTION
;   Simple loop which write zero to chain n * 3 times.
;******
; ( n -- )
; Hardware
; Write to WS2811 devices

VE_WS2811_OFF:
    .dw $ff0a
    .db "ws2811.off"
    .dw VE_HEAD
    .set VE_HEAD = VE_WS2811_OFF
XT_WS2811_OFF:
    .dw PFA_WS2811_OFF
PFA_WS2811_OFF:
     ; Reset WS2811
    sbi WS2811_DDR, WS2811_BIT; Set as output
    ;Write zero
    cbi WS2811_PORT, WS2811_BIT; Write zero
    delay 50 ; Wait 50 usec
    in temp4, SREG
    cli
PFA_WS2811_RESET_LOOP:
    sbi WS2811_PORT, WS2811_BIT; Write one
    ns250_fill_delay
    cbi WS2811_PORT, WS2811_BIT; Write zero
    sbiw tosl, 1
    wait_cycles 15, 20000000
    brne PFA_WS2811_RESET_LOOP
    out SREG, temp0
    loadtos ; dump the temp value from tos
    jmp_ DO_NEXT
                                          
------------------------------------------------------------------------------
Comprehensive Server Monitoring with Site24x7.
Monitor 10 servers for $9/Month.
Get alerted through email, SMS, voice calls or mobile push notifications.
Take corrective actions from your mobile device.
http://pubads.g.doubleclick.net/gampad/clk?id=154624111&iu=/4140/ostg.clktrk
_______________________________________________
Amforth-devel mailing list for http://amforth.sf.net/
Amforth-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/amforth-devel

Reply via email to