Working font rendering on LED matrix
authorTero Marttila <terom@fixme.fi>
Sun, 29 Aug 2010 22:49:32 +0300
changeset 38 f430b507a885
parent 37 df0bdaf0eb08
child 39 d7eac199d323
Working font rendering on LED matrix
Makefile
font-compile.py
font.inc
matrix.inc
matrix.s
--- a/Makefile	Mon Aug 23 01:07:45 2010 +0300
+++ b/Makefile	Sun Aug 29 22:49:32 2010 +0300
@@ -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
 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:$<
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/font-compile.py	Sun Aug 29 22:49:32 2010 +0300
@@ -0,0 +1,182 @@
+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
+        
+            print 'read_charblock', 'ascii', 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(
+            ("; '%s'\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
+    
+    font_start = '0'
+    font_end = '9'
+
+    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 ; '%s'\n" % (ord(font_start), font_start))
+        +  (".equ FONT_8x6_END = %d ; '%s'\n" % (ord(font_end), font_end))
+        +  (".equ FONT_8x6_COLS = %d\n" % (6, ))
+        +  (".equ FONT_8x6_ROWS = %d\n" % (8, ))
+        +   "FONT_8x6:\n"
+            "\n"
+    )
+
+    for char in xrange(ord('0'), ord('9') + 1) :
+        ascii = chr(char)
+        cols = charmap[ascii]
+
+        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()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/font.inc	Sun Aug 29 22:49:32 2010 +0300
@@ -0,0 +1,65 @@
+;; 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    f_render_invalid
+
+        ; test over-range
+        ldi     r17, FONT_END
+        cp      r17, r16
+        brlt    f_render_invalid
+
+    ; Locate font char
+        ; compute offset in chars (r16)
+        subi    r16, FONT_START
+
+        ; 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
+f_render_cpy:
+        
+        ; copy via r17
+        lpm     r17, Z+
+        st      Y+, r17
+
+        dec     r16
+        brne    f_render_cpy
+
+f_render_invalid:
+
+    ; Done
+        ret
+
--- a/matrix.inc	Mon Aug 23 01:07:45 2010 +0300
+++ b/matrix.inc	Sun Aug 29 22:49:32 2010 +0300
@@ -1,5 +1,5 @@
 ;; LED Matrix driver
-;;
+;; vim: set ft=avr:
 
 .dseg
 ;; I/O addresses
--- a/matrix.s	Mon Aug 23 01:07:45 2010 +0300
+++ b/matrix.s	Sun Aug 29 22:49:32 2010 +0300
@@ -1,3 +1,5 @@
+;; vim: set ft=avr:
+
 .nolist
 .include "m168def.inc"      ; Same family as 328P
 .list
@@ -38,6 +40,9 @@
 ;; Utils
 .include "delay.inc"
 
+;; Font rendering
+.include "font.inc"
+
 ;; Scan through each pixel
 Main_ScanRaw:
 	; init
@@ -195,6 +200,36 @@
     ; Load next frame, and animate
         rjmp        sc_next
 
+Main_ScanText:
+
+stxt_start:
+        ; char to render
+        ldi         r24, 48 ; '0'
+
+    ; Render char
+stxt_loop:
+        ; target buffer
+        ldi         YL, low(matrix_colbuf + 0)
+        ldi         YH, high(matrix_colbuf + 0)
+        
+        ; render r24 to Y
+        mov         r16, r24
+        rcall       Font_Render
+
+    ; Wait
+        ldi         XH, high(10 * 1024)
+        ldi         XL, low(10 * 1024)
+
+        rcall       Timer_Sleep
+
+    ; Next char
+        inc         r24
+        cpi         r24, 57 + 1 ; '9'
+        brsh        stxt_start        ; start again from zero
+
+        ; render this char
+        rjmp        stxt_loop
+
 Main:
 init:
     ; Stack
@@ -222,8 +257,8 @@
     ; Run
 		; rcall		Main_ScanRaw
 		; rcall		Main_ScanTest
-
-        rcall       Main_ScanCode
+        ; rcall       Main_ScanCode
+        rcall       Main_ScanText
 
 end:
         rjmp        end