;; LED Matrix driver
;;
.set MATRIX_DDR = DDRB
.set MATRIX_PORT = PORTB
.set MATRIX_OE = PORTB1 ; Output Enable, active low, externally pulled high
.dseg
.set MATRIX_COLS = 8 ; number of physical columns (XXX: fixed to 8)
.set MATRIX_BUF_COLS = 16 ; number of columns in frame buffer
matrix_colbit: .byte 1 ; scan column bit
matrix_colbuf: .byte MATRIX_BUF_COLS ; row bits by column
matrix_colshift: .byte 1 ; left column offset
.cseg
;; Normalize the outputs, enable the matrix, an set up buffers
Matrix_Init:
; Setup ENable port
sbi MATRIX_PORT, MATRIX_OE ; high -> disabled
sbi MATRIX_DDR, MATRIX_OE ; out
; blank hardware
ldi r16, 0
sts spi_outbuf + 0, r16 ; column sinks
sts spi_outbuf + 1, r16 ; row drivers
; write out
rcall SPI_SendRecv
; enable
cbi MATRIX_PORT, MATRIX_OE ; low -> enabled
; init buffers
ldi r16, 0b1
sts matrix_colbit, r16
ldi r16, 0
sts matrix_colshift, r16
ldi r16, 0
ldi r17, MATRIX_BUF_COLS
ldi YL, low(matrix_colbuf)
ldi YH, high(matrix_colbuf)
m_init_mzero:
st Y+, r16
; loop until zero
dec r17
brne m_init_mzero
; Use Timer0, 32k cycles -> 500Hz scan rate
ldi r16, 32
rcall Timer0_Start
; done
ret
;; Scan the next column
;; Interrupt-driven
Matrix_ScanCol:
; Save registers
push r16
push r17
; Column bit
; load
lds r16, matrix_colbit
; start packet
cbi SPI_PORT, SPI_SS
; output
out SPDR, r16
; Compute col index
ldi r17, 0
m_sc_colidx:
; shift
lsr r16
; done if we shifted the bit out
brcs m_sc_row
; count shifts
inc r17
rjmp m_sc_colidx
m_sc_row:
; Column shift
; load
lds r16, matrix_colshift
; add to col index
add r17, r16
; Row mask
; base
ldi XL, low(matrix_colbuf)
ldi XH, high(matrix_colbuf)
; offset
ldi r16, 0
add XL, r17
adc XH, r16
; load
ld r16, X
; output
rcall SPI_Wait
out SPDR, r16
; Update col bit
lds r16, matrix_colbit
; shift left
lsl r16
brcc m_sc_colout
; overflow, take bit from C
rol r16
m_sc_colout:
; store
sts matrix_colbit, r16
; End of packet
rcall SPI_Wait
sbi SPI_PORT, SPI_SS
; Done
pop r17
pop r16
ret
;; Scan the matrix once in one go
Matrix_ScanFull:
; Row index
ldi ZL, low(matrix_colbuf)
ldi ZH, high(matrix_colbuf)
; Column bit
ldi r25, 0
sec ; set C
m_pulse_col:
; rotate bit left from C
rol r25
; overflow
brcs m_pulse_end
; store in output buffer
sts spi_outbuf + 1, r25
; Row mask
ld r16, Z+
sts spi_outbuf + 0, r16
; Display
rcall SPI_SendRecv
; Next column
rjmp m_pulse_col
m_pulse_end:
; Done
ret
;; Shift the matrix output one column to the right, looping around
Matrix_ShiftRight:
; Decrement-loop current value
; current value
lds r16, matrix_colshift
; shift window left
dec r16
; test for underflow -> MSB/N set
brpl Matrix_ShiftSet
; reset window to right edge
ldi r16, MATRIX_BUF_COLS - MATRIX_COLS
;; Continue
;; Set the matrix output left shift
;; Input: r16
Matrix_ShiftSet:
; store new value
sts matrix_colshift, r16
; done
ret