# HG changeset patch # User Tero Marttila # Date 1311709439 -10800 # Node ID fe9f354dddd43f7987dce7102b4e89bf74bd0071 # Parent 3803c0b40a9c7b294c9c7f5bb61e6450ba95decf# Parent d7eac199d323fd0dd815f591073662f6aff12cd0 merge diff -r 3803c0b40a9c -r fe9f354dddd4 Makefile --- a/Makefile Tue Jul 26 22:43:41 2011 +0300 +++ b/Makefile Tue Jul 26 22:43:59 2011 +0300 @@ -4,7 +4,7 @@ AD_PART = m328p AD_PROG = arduino AD_BAUD = 57600 -AD_PORT = /dev/ttyUSB0 +AD_PORT = /dev/ttyUSB1 AD = avrdude ADFLAGS = -p $(AD_PART) -c $(AD_PROG) -b $(AD_BAUD) -P $(AD_PORT) @@ -13,14 +13,19 @@ all: $(PROG).hex -matrix.hex: spi.inc matrix.inc timer.inc delay.inc macros.inc +matrix.hex: spi.inc matrix.inc timer.inc delay.inc macros.inc font.inc font.def led7seg.hex: spi.inc led7seg.inc adc.inc timer.inc delay.inc macros.inc timer.hex: timer.inc macros.inc +font.inc: font.def + %.hex: %.s $(AS) $(ASFLAGS) $< mv $<.hex $@ +font.def: font.txt font-compile.py + python font-compile.py $< $@ > /dev/null + upload: $(PROG).hex $(AD) $(ADFLAGS) -U flash:w:$< diff -r 3803c0b40a9c -r fe9f354dddd4 font-compile.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/font-compile.py Tue Jul 26 22:43:59 2011 +0300 @@ -0,0 +1,193 @@ +def read_block (fh) : + """ + Yield a series of non-empty lines from the given file, ignoring any leading empty lines, and stopping after the first empty line + """ + + leading = True + + for line in fh : + if line.strip() : + leading = False + + # yield non-empty + yield line + + elif leading : + # skip leading empty + continue + + else : + # stop on empty + return + + else : + # EOF + return + +def read_charblock (lines) : + """ + Read in a char from the given lines, returning an + (ascii, rows) + + tuple, or None, if there weren't any more blocks + """ + + # the ascii code as a char + ascii = '' + + # row data as ints + rows = [] + + for line in lines : + line = line.strip() + + if line.startswith(';') : + # set ascii code + ascii = line.replace(';', '').strip() + + if not ascii : + print 'read_charblock', 'empty' + + # skip + return None + + elif len(ascii) == 1 : + print 'read_charblock', 'simplechar', ascii + + else : + ascii = ascii.decode('string_escape') + + print 'read_charblock', 'decodechar', ascii + + assert len(ascii) == 1 + + else : + # convert + row = line.replace('#', '1').replace('-', '0') + + print 'read_charblock', 'row', row + + # 6 columns + assert len(row) == 6 + + # from binary + row = int(row, 2) + + rows.append(row) + + # got data? + if ascii and rows : + # 8 rows + assert len(rows) == 8 + + return ascii, rows + + else : + # nope, empty block, EOF + return None + +def read_charblocks (fh) : + """ + Read in all char blocks as (ascii, rows) tuples from given file + """ + + while True : + out = read_charblock(read_block(fh)) + + if out : + yield out + + else : + break + +def decode_rows (inrows) : + """ + Decode char def data from its 6x8 row format into the format we need (6x8 col format) + """ + + outcols = [0x00] * 6 + + for rowidx, row in enumerate(inrows) : + + for colidx, col in enumerate(outcols) : + # get bit from row + bit = (row >> (5 - colidx)) & 1 + + # set bit into column + outcols[colidx] |= (bit << rowidx) + + # ok... + return outcols + +def write_chardef (fh, ascii, cols) : + """ + Write out character definition block to given .def file, using given char code and column data + """ + + fh.write( + ("; %r\n" % ascii) + + (".db %s\n" % (', '.join(bin(col) for col in cols))) + + ("\n") + ) + +def compile_fonts (infh, outf) : + """ + Compile char blocks from infh, writing out definitions to outf + """ + + charmap = dict() + + # decode in + for charblock in read_charblocks(infh) : + # unpack + ascii, rows = charblock + + # convert + cols = decode_rows(rows) + + # map + charmap[ascii] = cols + + print 'compile_fonts', 'read', ascii + + # detect min/max syms + syms = charmap.keys() + font_start = min(syms) + font_end = max(syms) + + assert(ord(font_start) < ord(font_end)) + + # write out + outf.write( + ";; AUTOMATICALLY GENERATED - Do not edit!\n" + ";; 8x6 font, '0' - '1', rows-by-col format\n" + + (".equ FONT_8x6_START = %d ; %r\n" % (ord(font_start), font_start)) + + (".equ FONT_8x6_END = %d ; %r\n" % (ord(font_end), font_end)) + + (".equ FONT_8x6_COLS = %d\n" % (6, )) + + (".equ FONT_8x6_ROWS = %d\n" % (8, )) + + "FONT_8x6:\n" + "\n" + ) + + # default symbol for unknown chars + defsym = charmap['\0'] + + for char in xrange(ord(font_start), ord(font_end) + 1) : + ascii = chr(char) + cols = charmap.get(ascii, defsym) + + write_chardef(outf, ascii, cols) + +def main () : + import sys, getopt + + opts, args = getopt.getopt(sys.argv[1:], '') + + inpath, outpath = args + + # run + compile_fonts(open(inpath, 'r'), open(outpath, 'w')) + +if __name__ == '__main__' : + main() + diff -r 3803c0b40a9c -r fe9f354dddd4 font.inc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/font.inc Tue Jul 26 22:43:59 2011 +0300 @@ -0,0 +1,73 @@ +;; Basic LED matrix fonts +;; vim: set ft=avr: + +.cseg + +;; Font definition +; Defines FONT_8x6 symbol +.include "font.def" + +; Font to use +.set FONT_TABLE = FONT_8x6 +.set FONT_START = FONT_8x6_START +.set FONT_END = FONT_8x6_END +.set FONT_COLS = FONT_8x6_COLS +.set FONT_ROWS = FONT_8x6_ROWS ; XXX: fixed to 8 + +;; Render the given ASCII char into the given buffer +; Input: r16 - ASCII char code +; Y - dest buf (Wx8 column data) +Font_Render: + ; Test char index + + ; test under-range + ldi r17, FONT_START + cp r16, r17 + brlt font_r_invalid + + ; test over-range + ldi r17, FONT_END + cp r17, r16 + brlt font_r_invalid + + ; compute offset in chars (r16) + subi r16, FONT_START + + ; ok + rjmp font_r_render + +font_r_invalid: + ; use first sym + ldi r16, 0 + + cbi PORTD, PIND7 + +font_r_render: + ; compute offset in bytes (r1:r0) + ldi r17, FONT_COLS + mul r16, r17 + + ; font table start offset from words + ldi ZL, low(FONT_TABLE * 2) + ldi ZH, high(FONT_TABLE * 2) + + ; apply offset + add ZL, r0 + adc ZH, r1 + + ; Copy column pixel data + ; count columns + ldi r16, FONT_COLS +font_r_cpy: + + ; copy via r17 + lpm r17, Z+ + st Y+, r17 + + dec r16 + brne font_r_cpy + + + ; Done + ret + diff -r 3803c0b40a9c -r fe9f354dddd4 font.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/font.txt Tue Jul 26 22:43:59 2011 +0300 @@ -0,0 +1,210 @@ +; \x00 +------ +-####- +-#--#- +-#--#- +-#--#- +-#--#- +-####- +------ + +; \x20 +------ +------ +------ +------ +------ +------ +------ +------ + +; ! +--##-- +--##-- +--##-- +--##-- +--##-- +------ +--##-- +--##-- + +; 0 +-####- +#----# +#----# +#----# +#----# +#----# +#----# +-####- + +; 1 +---#-- +--##-- +-#-#-- +#--#-- +---#-- +---#-- +---#-- +-##### + +; 2 +--###- +-#---# +-#---# +----#- +---#-- +--#--- +-#---- +###### + +; 3 +-###-- +#---#- +-----# +----#- +---##- +-----# +#---#- +-###-- + +; 4 +----#- +-#--#- +##--#- +#---#- +###### +----#- +----#- +----#- + +; 5 +###### +#----- +#----- +#####- +-----# +-----# +#----# +-####- + +; 6 +------ +-####- +##---# +#----- +#####- +#----# +#----# +-####- + +; 7 +-##### +#----# +----#- +---#-- +--#--- +-#---- +#----- +------ + +; 8 +------ +-####- +#----# +#----# +-####- +#----# +#----# +-####- + +; 9 +-####- +#----# +#----# +#----# +-##### +-----# +-----# +----#- + +; h +#----- +#----- +#----- +#----- +#-###- +##---# +#----# +#----# + +; e +--##-- +-#--#- +#----# +###### +#----- +#---#- +-###-- +------ + +; l +--##-- +---#-- +---#-- +---#-- +---#-- +---#-- +---#-- +--###- + +; o +------ +------ +------ +-####- +#----# +#----# +#----# +-####- + +; w +------ +------ +------ +#---#- +#---#- +#---#- +#-#-#- +-#-#-- + +; r +------ +------ +#----- +#-##-- +##---- +#----- +#----- +#----- + +; d +----#- +----#- +----#- +-####- +#---#- +#---#- +#---#- +-###-- + +; +------ +------ +------ +------ +------ +------ +------ +------ + diff -r 3803c0b40a9c -r fe9f354dddd4 matrix.inc --- a/matrix.inc Tue Jul 26 22:43:41 2011 +0300 +++ b/matrix.inc Tue Jul 26 22:43:59 2011 +0300 @@ -1,19 +1,58 @@ ;; 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 -.dseg -.set MATRIX_COLS = 8 ; number of columns +;; Matrix properties +; Matrix width in columns +.set MATRIX_COLS = 8 ; physical columns -matrix_colbit: .byte 1 ; column bit -matrix_rowbuf: .byte MATRIX_COLS ; row bitmask by column +; 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, an set up buffers +;; Normalize the outputs, enable the matrix, and set up buffers Matrix_Init: ; Setup ENable port sbi MATRIX_PORT, MATRIX_OE ; high -> disabled @@ -35,10 +74,13 @@ ldi r16, 0b1 sts matrix_colbit, r16 + ldi r16, 0 + sts matrix_colshift, r16 + ldi r16, 0 - ldi r17, MATRIX_COLS - ldi YL, low(matrix_rowbuf) - ldi YH, high(matrix_rowbuf) + ldi r17, MATRIX_BUF_COLS + ldi YL, low(matrix_colbuf) + ldi YH, high(matrix_colbuf) m_init_mzero: st Y+, r16 @@ -54,9 +96,13 @@ ; done ret -;; Scan the next column +;; 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 @@ -64,7 +110,7 @@ ; start packet cbi SPI_PORT, SPI_SS - ; output + ; output single column-enable bit out SPDR, r16 ; Compute col index @@ -82,21 +128,28 @@ 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_rowbuf) - ldi XH, high(matrix_rowbuf) + ldi XL, low(matrix_colbuf) + ldi XH, high(matrix_colbuf) ; offset - ldi r18, 0 + ldi r16, 0 add XL, r17 - adc XH, r18 + adc XH, r16 ; load ld r16, X - ; output + ; output full row-enable bitmask rcall SPI_Wait out SPDR, r16 @@ -119,13 +172,17 @@ 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_rowbuf) - ldi ZH, high(matrix_rowbuf) + ldi ZL, low(matrix_colbuf) + ldi ZH, high(matrix_colbuf) ; Column bit ldi r25, 0 @@ -156,3 +213,211 @@ ; 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 + + ret + diff -r 3803c0b40a9c -r fe9f354dddd4 matrix.s --- a/matrix.s Tue Jul 26 22:43:41 2011 +0300 +++ b/matrix.s Tue Jul 26 22:43:59 2011 +0300 @@ -1,3 +1,5 @@ +;; vim: set ft=avr: + .nolist .include "m168def.inc" ; Same family as 328P .list @@ -18,7 +20,8 @@ .org SPIaddr rjmp SPI_Interrupt -.org 0x40 + +cseg0: .org 0x40 ;; Syntax .include "macros.inc" @@ -37,6 +40,9 @@ ;; Utils .include "delay.inc" +;; Font rendering +.include "font.inc" + ;; Scan through each pixel Main_ScanRaw: ; init @@ -73,15 +79,15 @@ Main_ScanTest: ; Generate pattern ; end of buffer - ldi r17, MATRIX_COLS - ldi XL, low(matrix_rowbuf + MATRIX_COLS) - ldi XH, high(matrix_rowbuf + MATRIX_COLS) + ldi r17, MATRIX_BUF_COLS + ldi XL, low(matrix_colbuf + MATRIX_BUF_COLS) + ldi XH, high(matrix_colbuf + MATRIX_BUF_COLS) ; bit pattern ldi r16, 0b11 st_loop: - ; put + ; put, pre-decrement st -X, r16 ; flip @@ -91,10 +97,128 @@ dec r17 brne st_loop -st_scan: - ; Scan repeatedly - ;rcall Matrix_ScanCol - rjmp st_scan +st_animate: + ; Animate + ; shift right + rcall Matrix_ShiftLeft + + ; wait for X/16th of a second + ldi XH, high(8 * 1024) + ldi XL, low(8 * 1024) + + rcall Timer_Sleep + + ; loop + rjmp st_animate + + +;; Display device code memory +Main_ScanCode: + ; Code start + ldi ZL, low(cseg0 * 2) ; word addr + ldi ZH, high(cseg0 * 2) ; word addr + + ; Pause refresh + cli + + ; Load initial frame + ; to first frame, starting from right edge + ldi r17, 8 + ldi XL, low(matrix_colbuf + 16) + ldi XH, high(matrix_colbuf + 16) + +sc_load_initial: + ; one byte + lpm r16, Z+ + st -X, r16 + + ; loop until zero + dec r17 + brne sc_load_initial + + ; the first ShiftLeft below will jump to the end of the framebuffer + +sc_next: + ; Show this frame + rcall Matrix_ShiftLeft + + ; Load next frame + ldi r17, 8 + ldi XL, low(matrix_colbuf + 8) + ldi XH, high(matrix_colbuf + 8) + +sc_load_next: + ; one byte + lpm r16, Z+ + st -X, r16 + + ; loop until zero + dec r17 + brne sc_load_next + + ; Enable refresh + sei + + ; Animate from this -> next frame + ldi r17, 8 ; 8 columns + +sc_anim: + ; wait for X/16th of a second + ldi XH, high(2 * 1024) + ldi XL, low(2 * 1024) + + rcall Timer_Sleep + + ; shift + rcall Matrix_ShiftLeft + + ; loop until zero + dec r17 + brne sc_anim + + ; Pause refresh + cli + + ; Move next -> this + ldi r17, 8 + ldi XL, low(matrix_colbuf + 16) + ldi XH, high(matrix_colbuf + 16) + ldi YL, low(matrix_colbuf + 8) + ldi YH, high(matrix_colbuf + 8) + +sc_move_this: + ; one byte + ld r16, -Y + st -X, r16 + + ; loop until zero + dec r17 + brne sc_move_this + +sbi PIND, PIND7 + + ; Load next frame, and animate + rjmp sc_next + +Main_ScanText: + + ; Constants +stxt_message: ; text to render + .db 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0 ; 'hello world!' + + ; Load into buffer + ldi ZL, low(stxt_message * 2) + ldi ZH, high(stxt_message * 2) + + rcall Text_LoadString + +sbi PORTD, PIND7 + + ; Display + rcall Text_ShowString + + ; Done + ret Main: init: @@ -122,8 +246,9 @@ ; Run ; rcall Main_ScanRaw - - rcall Main_ScanTest + ; rcall Main_ScanTest + ; rcall Main_ScanCode + rcall Main_ScanText end: rjmp end diff -r 3803c0b40a9c -r fe9f354dddd4 spi.inc --- a/spi.inc Tue Jul 26 22:43:41 2011 +0300 +++ b/spi.inc Tue Jul 26 22:43:59 2011 +0300 @@ -106,6 +106,7 @@ rjmp spi_sr_wait ; Read + ; XXX: wrong, should be head byte? ; read+store tail byte in r1, SPDR st -X, r1