matrix.inc
author Tero Marttila <terom@paivola.fi>
Sun, 20 Apr 2014 22:47:41 +0300
changeset 78 504c2310cddb
parent 42 99a179b64e6a
permissions -rw-r--r--
timer: continuous interval-sleep operation
;; LED Matrix driver
;; vim: set ft=avr:

.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

;; Text
; Maximum length of message
.set TEXT_MAXLENGTH = 64

; Scrolling speed (kiloticks per frame)
.set TEXT_SPEED = 1

text_buffer:        .byte TEXT_MAXLENGTH    ; display message buffer
text_offset:        .byte 1                 ; current offset in text

.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

;; Rewinds the currently visible viewport to the beginning of the framebuffer
; This copies the currently visible viewport data to the beginning of the framebuffer and resets the offset
Matrix_ShiftRewind:
        ; current view offset
        ldi         XL, low(matrix_colbuf)
        ldi         XH, high(matrix_colbuf)

        ; offset
        lds         r16, matrix_colshift

        ; add
        ldi         r17, 0
        add         XL, r16
        adc         XH, r17

        ; start of framebuffer
        ldi         YL, low(matrix_colbuf + 0)
        ldi         YH, high(matrix_colbuf + 0)

        ; viewport width
        ldi         r17, MATRIX_COLS

matrix_shiftrew_loop:
        ; copy
        ld          r16, X+
        st          Y+, r16

        ; count
        dec         r17
        brne        matrix_shiftrew_loop

        ; done, reset offset
        rjmp        MAtrix_ShiftZero


;; Load a NUL-terminated ASCII string from PGM into the text buffer
; Input:    Z       - Address of NUL-terminated ASCII string in PGM        
Text_LoadString:
    ; Setup
        ; storage buffer
        ldi         YL, low(text_buffer)
        ldi         YH, high(text_buffer)

        ; max. length
        ldi         r18, TEXT_MAXLENGTH

text_loads_loop:
    ; Test max length
        ; count and check for overflow
        dec         r18
        brne        text_loads_char

    ; Load char
        ; force NUL
        ldi         r16, 0x00

text_loads_char:
        ; load next char
        lpm         r16, Z+

text_loads_store:
    ; Store and test NUL
        ; store it
        st          Y+, r16

        ; test for NUL
        tst         r16
        brne        text_loads_loop

    ; Update scroll offset
        ; reset offset
        ldi         r17, 0
        sts         text_offset, r17

        ; done
        ret

;; Shows the loaded string of ASCII text on the display, scrolling it horizontally
; Uses font.inc for rendering
; XXX: uses blocking timer sleeps and doesn't return until done
Text_ShowString:
    ; Load initial char
        ldi         XL, low(text_buffer + 0)
        ldi         XH, high(text_buffer + 0)

        ; load char
        ld          r16, X+
        push        XL
        push        XH

        ; one column spacing
        ldi         YL, low(matrix_colbuf + 1)
        ldi         YH, high(matrix_colbuf + 1)

        ; render to framebuffer
        rcall       Font_Render

        ; reset viewport
        rcall       Matrix_ShiftZero
       
   ; Load next char
text_shows_next:        
        ; next char
        pop         XH
        pop         XL
        ld          r16, X+
        push        XL
        push        XH

        ; test NUL
        tst         r16
        breq        text_shows_end

        ; offscreen
        ldi         YL, low(matrix_colbuf + 1 + 6 + 1)
        ldi         YH, high(matrix_colbuf + 1 + 6 + 1)

        ; render
        rcall       Font_Render

    ; Animate to next char
        ldi         r20, 7
        
text_shows_animloop:
        ; sleep
        ldi         XH, high(TEXT_SPEED * 1024)
        ldi         XL, low(TEXT_SPEED * 1024)

        rcall       Timer_Sleep

        ; shift
        rcall       Matrix_ShiftRight

        ; count
        dec         r20
        brne        text_shows_animloop

    ; Rewind to next char
        rcall       Matrix_ShiftRewind

        sbi         PIND, PIND7

        ; load next char and animate it in
        rjmp        text_shows_next

text_shows_end:
    ; Done
        pop         XH
        pop         XL


    ; Clear second frame
        ldi         YL, low(matrix_colbuf + 16)
        ldi         YH, high(matrix_colbuf + 16)
        ldi         r16, MATRIX_COLS
        ldi         r17, 0

text_shows_end2:        
        st          -Y, r17

        dec         r16
        brne        text_shows_end2

    ; Blink
text_shows_end3:        
        ; on
        ldi         r16, 0
        rcall       Matrix_ShiftSet

        ; sleep
        ldi         XH, high(TEXT_SPEED * 6 * 1024)
        ldi         XL, low(TEXT_SPEED * 6 * 1024)

        rcall       Timer_Sleep

        ; off        
        ldi         r16, 8
        rcall       Matrix_ShiftSet

        ; sleep
        ldi         XH, high(TEXT_SPEED * 6 * 1024)
        ldi         XL, low(TEXT_SPEED * 6 * 1024)

        rcall       Timer_Sleep

        ; loop
        rjmp        text_shows_end3

    ; XXX: end
        ret