;; LED Matrix driver
;;
.dseg
;; I/O addresses
; Control port
.set MATRIX_DDR = DDRB
.set MATRIX_PORT = PORTB
; Pin for matrix driver Output Enable
.set MATRIX_OE = PORTB1 ; Output Enable, active low, externally pulled high
;; Matrix properties
; Matrix width in columns
.set MATRIX_COLS = 8 ; physical columns
; Framebuffer width in columns
.set MATRIX_BUF_COLS = 16 ; framebuffer columns
;; SPI addresses
; row drivers (8 bits)
.set MATRIX_SPI_ROW = 0 ; row mask source
; column sinks (8 bits)
.set MATRIX_SPI_COL = 1 ; column scan sink
;; Matrix state
; Matrix framebuffer
; this holds the columns data as a 1 byte bitmask of row data per column (8 bits -> 8 rows)
matrix_colbuf: .byte MATRIX_BUF_COLS ; framebuffer (row data by column)
; Column scan bit
; in the matrix refresh loop, we push out each column's row data in turn
; this bit tracks the currently refreshing column
matrix_colbit: .byte 1 ; column scan bit
; Matrix viewport offset
; the visible matrix data is taken directly from the framebuffer, but it can be taken at an arbitrary offset
; this determines the starting offset for the visible viewport's left edge from the start of the framebuffer in columns
matrix_colshift: .byte 1 ; viewport left column offset
.cseg
;; Normalize the outputs, enable the matrix, and 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 matrix's next column from the viewport
;; 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 single column-enable bit
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 full row-enable bitmask
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
;; XXX: doesn't support colshift
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
;; Reset the viewport to the start (left edge) of the framebuffer
Matrix_ShiftZero:
; Constant offset
ldi r16, 0
; Set
rjmp Matrix_ShiftSet
;; Shift the viewport one column to the left in the framebuffer, looping around to the end of the framebuffer
; This moves the visible output one column to the right
Matrix_ShiftLeft:
; Decrement-loop current value
; current value
lds r16, matrix_colshift
; shift window left
dec r16
; test for underflow (MSB/N set) -> don't skip reset
brpl Matrix_ShiftSet
; reset window to right edge
ldi r16, MATRIX_BUF_COLS - MATRIX_COLS
; Set
rjmp Matrix_ShiftSet
;; Shift the viewport one column to the right in the framebuffer, looping around to the start of the FB
; This moves the visible output one column to the left
Matrix_ShiftRight:
; Increment-loop current value
; current value
lds r16, matrix_colshift
; shift window right
inc r16
; test for overflow -> don't skip reset
cpi r16, MATRIX_BUF_COLS - MATRIX_COLS
brlt Matrix_ShiftSet
; reset window to left edge
ldi r16, 0
; Set
rjmp Matrix_ShiftSet
;; Set the matrix viewport offset
;; Input: r16
Matrix_ShiftSet:
; store new value
sts matrix_colshift, r16
; done
ret