;; 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 columns
matrix_colbit: .byte 1 ; column bit
matrix_rowbuf: .byte MATRIX_COLS ; row bitmask by column
.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
ldi r17, MATRIX_COLS
ldi YL, low(matrix_rowbuf)
ldi YH, high(matrix_rowbuf)
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:
; 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:
; Row mask
; base
ldi XL, low(matrix_rowbuf)
ldi XH, high(matrix_rowbuf)
; offset
ldi r18, 0
add XL, r17
adc XH, r18
; 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
ret
;; Scan the matrix once in one go
Matrix_ScanFull:
; Row index
ldi ZL, low(matrix_rowbuf)
ldi ZH, high(matrix_rowbuf)
; 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