; ***************************************************************************
; copyright   : (C) 2024 by Martin Preuss
; email       : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome".                              *
; * Please see toplevel file COPYING of that project for license details.   *
; ***************************************************************************



; ***************************************************************************
; defines

.equ SK6812_PATTERN_NUM = 3

.equ SK6812_MODE_OFF  = 0
.equ SK6812_MODE_ON   = 1
.equ SK6812_MODE_AUTO = 2



; ***************************************************************************
; data

.dseg

sk6812DataBegin:
  sk6812NumLeds:  .byte 1
  sk6812Pattern:  .byte 5*SK6812_PATTERN_NUM ; 1 byte num leds, 4 bytes colour (RGBW)
  sk6812RGBW:     .byte 4                    ; current RGBW value
  sk6812Mode:     .byte 1
sk6812DataEnd:



; ***************************************************************************
; code

.cseg



; ---------------------------------------------------------------------------
; @routine SK6812_Init
;
; @return CFLAG set if okay, clear on error

SK6812_Init:
  ldi xh, HIGH(sk6812DataBegin)
  ldi xl, LOW(sk6812DataBegin)
  clr r16
  ldi r17, (sk6812DataEnd-sk6812DataBegin)
  rcall Utils_FillSram

  ldi r16, SK6812_MODE_AUTO
  sts sk6812Mode, r16

  sbi SK6812_DDR, SK6812_PINNUM    ; set to output
  cbi SK6812_PORT, SK6812_PINNUM   ; set LOW
  ldi r16, 150
  sts sk6812NumLeds, r16

  rcall sk6812ReadConfFromEeprom
  ldi r18, 1                       ; ON
  rcall SK6812_SetState            ; (r16, r17, r18, r19, r20, r21, r23, r24, r25)

  sec
  ret
SK6812_Init_error:
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine SK6812_Fini
;
; @return CFLAG set if okay, clear on error
; USED:

SK6812_Fini:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine sk6812SetAllColor
;
; Set all LEDs to same colour.
;
; @param r18 red value
; @param r19 green value
; @param r20 blue value
; @param r21 white value
; @clobbers r23 (r16, r17, r24, r25)

sk6812SetAllColor:
  push r15
    in r15, SREG
    cli
    ; reset
    cbi SK6812_PORT, SK6812_PINNUM
    Utils_WaitNanoSecs 80000, 0, r22            ; wait for 80us
    lds r23, sk6812NumLeds
sk6812SetAllColor_loop:
      rcall sk6812SendQuadruple ; (r16, r17, r24, r25)
      dec r23
    brne sk6812SetAllColor_loop
;    sbi SK6812_PORT, SK6812_PINNUM
    out SREG, r15
  pop r15
  ret
; @end




; ---------------------------------------------------------------------------
; @routine SK6812_SetState @global
;
; Set all LEDs to same colour according to state.
;
; @param r18 0: all LEDs off, otherwise all LEDs color stored in sk6812RGBW
; @clobbers r18, r19, r20, r21, (r16, r17, r23, r24, r25)

SK6812_SetState:
  lds r16, sk6812Mode
  cpi r16, SK6812_MODE_AUTO
  brne SK6812_SetState_end
  rjmp sk6812SetState
SK6812_SetState_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine SK6812_SetMode @global
;
; @param r18 mode (SK6812_MODE_OFF, SK6812_MODE_ON, SK6812_MODE_AUTO)
; @clobbers r18, (r16, r17, r19, r20, r21, r23, r24, r25)

SK6812_SetMode:
  sts sk6812Mode, r18
  cpi r18, SK6812_MODE_OFF
  breq SK6812_SetState_off
;  cpi r18, SK6812_MODE_ON
;  breq SK6812_SetState_on
  rjmp SK6812_SetState_on
  ret
SK6812_SetState_off:
  clr r18
  rjmp sk6812SetState       ; (r16, r17, r18, r19, r20, r21, r23, r24, r25)
SK6812_SetState_on:
  ldi r18, 1
  rjmp sk6812SetState       ; (r16, r17, r18, r19, r20, r21, r23, r24, r25)
; @end



; ---------------------------------------------------------------------------
; @routine SK6812_SetRGBW @global
;
; Store new color for all LEDs.
;
; @param r18 red value
; @param r19 green value
; @param r20 blue color
; @param r21 white color
; @clobbers (r16, r17, r18, r19, r20, r21, r23, r24, r25, X)

SK6812_SetRGBW:
  push r15
    in r15, SREG
    cli
    sts sk6812RGBW, r18
    sts sk6812RGBW+1, r19
    sts sk6812RGBW+2, r20
    sts sk6812RGBW+3, r21
    rcall sk6812WriteConfToEeprom ; (R16, R17, X)
; no longer set state
;    ldi r18, 1                    ; ON
;    rcall SK6812_SetState         ; (r16, r17, r18, r19, r20, r21, r23, r24, r25)
    out SREG, r15
  pop r15
  ret
; @end



; ---------------------------------------------------------------------------
; @routine sk6812SetState
;
; Set all LEDs to same colour according to state.
;
; @param r18 0: all LEDs off, otherwise all LEDs color stored in sk6812RGBW
; @clobbers r18, r19, r20, r21, (r16, r17, r23, r24, r25)

sk6812SetState:
  tst r18
  breq sk6812SetState_off
  lds r18, sk6812RGBW
  lds r19, sk6812RGBW+1
  lds r20, sk6812RGBW+2
  lds r21, sk6812RGBW+3
  rjmp sk6812SetState_haveValue
sk6812SetState_off:
  clr r18
  clr r19
  clr r20
  clr r21
sk6812SetState_haveValue:
  rjmp sk6812SetAllColor               ; (r16, r17, r23, r24, r25)
; @end


; ---------------------------------------------------------------------------
; @routine sk6812SendPattern
;
; Send given pattern to LEDs.
;
; @param R22 number of 5-byte subpatterns
; @param X pointer to pattern (numleds, R, G, B, W)*SK6812_PATTERN_NUM
; @clobbers R18, R19, R20, R21, R22, R23, X (R16, R17)

sk6812SendPattern:
sk6812SendPattern_loop1:
    ld r23, X+ ; num leds
    tst r23
    brne sk6812SendPattern_readandset
    ret                        ; no leds, done
sk6812SendPattern_readandset:
    ld r18, X+ ; R
    ld r19, X+ ; G
    ld r20, X+ ; B
    ld r21, X+ ; W
sk6812SendPattern_loop2:
      rcall sk6812SendQuadruple ; (r16, r17)
      dec r23
    brne sk6812SendPattern_loop2
    dec r22
  brne sk6812SendPattern_loop1
  ret
; @end



; ---------------------------------------------------------------------------
; @routine sk6812SendQuadruple
;
; Sends GRBW
; @param r18 red value
; @param r19 green value
; @param r20 blue value
; @param r21 white value
; @clobbers r16 (r17, r24, r25)

sk6812SendQuadruple:
  mov r16, r19     ; G
  rcall sk6812SendByte4  ; (r16, r17, r24, r25)
  mov r16, r18     ; R
  rcall sk6812SendByte4  ; (r16, r17, r24, r25)
  mov r16, r20     ; B
  rcall sk6812SendByte4  ; (r16, r17, r24, r25)
  mov r16, r21     ; W
  rcall sk6812SendByte4  ; (r16, r17, r24, r25)
  ret
; @end




; ---------------------------------------------------------------------------
; @macro M_SK6812_HANDLEBIT

.macro M_SK6812_HANDLEBIT
  lsl r16                          ; +1
  brcs l_sendOne                   ; +1 if false, +2 if branch taken
  ; send ZERO
  nop                              ; +1
  out SK6812_PORT, r25             ; +1   HIGH
  nop
  nop
  nop
  out SK6812_PORT, r24             ; +1
  nop                              ; +1
  rjmp l_end                       ; +2
l_sendOne:
  out SK6812_PORT, r25             ; +1   HIGH
  nop                              ; +1
  nop                              ; +1
  nop                              ; +1
  nop                              ; +1
  nop                              ; +1
  out SK6812_PORT, r24             ; +1
  nop                              ; +1
l_end:
.endmacro
; @end



; ---------------------------------------------------------------------------
; @routine sk6812SendByte4
;
; reset: 80microsecs
; 0: 500ns high, 750ns low  (+/- 150ns)
; 1: 750ns high, 500ns low  (+/- 150ns)
; 8 ticks per cycle
;
; @param r16 value
; @clobbers r16, r17, r24, r25

sk6812SendByte4:
  ldi r24, 0
  ldi r25, (1<<SK6812_PINNUM)

  M_SK6812_HANDLEBIT
  M_SK6812_HANDLEBIT
  M_SK6812_HANDLEBIT
  M_SK6812_HANDLEBIT

  M_SK6812_HANDLEBIT
  M_SK6812_HANDLEBIT
  M_SK6812_HANDLEBIT
  M_SK6812_HANDLEBIT
  ret                                ; +4
; @end




; ---------------------------------------------------------------------------
; @routine sk6812WriteConfToEeprom
;
; @clobbers r16, r17, X

sk6812WriteConfToEeprom:
  push r15
    in r15, SREG
    cli
    ldi xl, LOW(EEPROM_OFFS_SK6812_RGBW)
    ldi xh, HIGH(EEPROM_OFFS_SK6812_RGBW)
    lds r16, sk6812RGBW
    rcall Eeprom_WriteByteIfChanged        ; (R17)
    adiw xh:xl, 1
    lds r16, sk6812RGBW+1
    rcall Eeprom_WriteByteIfChanged        ; (R17)
    adiw xh:xl, 1
    lds r16, sk6812RGBW+2
    rcall Eeprom_WriteByteIfChanged        ; (R17)
    adiw xh:xl, 1
    lds r16, sk6812RGBW+3
    rcall Eeprom_WriteByteIfChanged        ; (R17)
    out SREG, r15
  pop r15
  ret
; @end



; ---------------------------------------------------------------------------
; @routine sk6812ReadConfFromEeprom
;
; @clobbers r16, r17, X

sk6812ReadConfFromEeprom:
  push r15
    in r15, SREG
    cli
    ldi xl, LOW(EEPROM_OFFS_SK6812_RGBW)
    ldi xh, HIGH(EEPROM_OFFS_SK6812_RGBW)
    rcall Eeprom_ReadByte
    brcc sk6812ReadConfFromEeprom_default
    mov r18, r16
    adiw xh:xl, 1

    rcall Eeprom_ReadByte
    brcc sk6812ReadConfFromEeprom_default
    mov r19, r16
    adiw xh:xl, 1

    rcall Eeprom_ReadByte
    brcc sk6812ReadConfFromEeprom_default
    mov r20, r16
    adiw xh:xl, 1

    rcall Eeprom_ReadByte
    brcc sk6812ReadConfFromEeprom_default
    mov r21, r16
    
    mov r16, r18
    and r16, r19
    and r16, r20
    and r16, r21
    inc r16
    brne sk6812ReadConfFromEeprom_storeColor
    ; fall through if value from EEPROM is 0xffffffff
sk6812ReadConfFromEeprom_default:
    ldi r18, 128            ; R
    ldi r19, 69             ; G
    ldi r20, 0              ; B
    ldi r21, 0              ; W
sk6812ReadConfFromEeprom_storeColor:
    sts sk6812RGBW, r18
    sts sk6812RGBW+1, r19
    sts sk6812RGBW+2, r20
    sts sk6812RGBW+3, r21
sk6812ReadConfFromEeprom_end:
    out SREG, r15
  pop r15
  ret
; @end


