;****** Multiplex7Seg 1.2 (c) Oliver Mezger 15.12.2010

.include "m16def.inc"

;*** Zuordnung zu den Ports ***
.equ SegmentIn      = PINA
.equ SegmentOut     = PORTA
.equ SegmentDdr     = DDRA
.equ TastaturIn     = PINB
.equ TastaturOut    = PORTB
.equ TastaturDdr    = DDRB
.equ StelleIn      = PINC
.equ StelleOut     = PORTC
.equ StelleDdr     = DDRC

.def null = R23     ;Register mit 0
.def tmp = R16      ;tmp-Register zum Arbeiten
.def tmp2 = R17
.def spalte = R18   ;Spalte fuer Darstellung
.def zeichen = R19  ;Index des Zeichens
.def durchlauf = R20;Zaehler fuer Zeichenwechsel
.def itmp = R21     ;tmp-Register fuer Interrupt
.def led = R22      ;RGB-LED animieren

    jmp reset       ;Einsprung nach Reset
    .org OVF0addr   ;Einsprung nach Timer0 Ueberlauf
    jmp isr_timer   ;nach ISR
    .org $2a        ;folgender Code ab Adresse $2a

isr_timer:
    in itmp,SREG    ;SREG retten
    push itmp
    ldi itmp, 150   ;Interrupttimer einstellen
    out TCNT0,itmp
    cpi spalte,0b00010000   ;wenn nicht Spalte 5
    brlo nspalte            ;dann naechste Spalte
    ldi spalte,1            ;Spalte1 initialisieren
    ldi XH, high(anzeige)   ;Datenzeiger initialisieren
    ldi XL, low(anzeige)
nspalte:                    ;naechste Spalte
    ld itmp,x+              ;lade Wert aus anzeige und erhoehe x
    ;wandle Wert in 7-Segmentmuster um
    ldi ZH, high(bcd_7*2)   ;Datenzeiger im Programmspeicher initialisieren
    ldi ZL, low(bcd_7*2)
    add ZL,itmp     ;Addiere Wert um zum Eintrag zu kommen
    adc ZH,null
    lpm itmp, z     ;lade Daten aus Flash-Programmspeicher
    out SegmentOut,itmp ;gib 7-Segmentmuster aus
    in itmp, StelleOut  ;schalte die Stelle ein
    andi itmp,0b11110000
    or itmp,spalte
    out StelleOut,itmp
    lsl spalte          ;naechste Stelle
    pop itmp            ;SREG wieder herstellen
    out SREG,itmp
    reti            ;return from Interrupt

reset:                  ;Hauptprogramm
    ldi tmp,high(RAMEND);Oberste RAM-Adresse holen
    out SPH, tmp        ;Stack-Pointer initialisieren
    ldi tmp,low(RAMEND) 
    out SPL, tmp
    ldi tmp,$ff
    out SegmentDdr,tmp  ;SegmentPort als Ausgang
    out StelleDdr,tmp   ;StellenPort als Ausgang
    ldi tmp,0b11101111  ;Tastatur init
    out TastaturOut,tmp
    ldi tmp,0b00010000
    out TastaturDdr,tmp
    ldi tmp,3       ;Systemtakt / 64
    out TCCR0,tmp   ;Timer setzen
    in tmp,TIMSK    ;Timerinterrupt einschalten
    ori tmp, 1 << TOIE0
    out TIMSK,tmp
    ldi null,0
    ldi XH, high(anzeige)   ;Datenzeiger initialisieren
    ldi XL, low(anzeige)
    st X+,null              ;Feld mit 0 initialisieren
    st X+,null
    st X+,null
    st X+,null
    sei             ;globale Interruptfreigabe
loop:               ;Schleife fuer Tastaturabfrage
    sbis TastaturIn,0 ;wurde Taste gedrueckt?
    rjmp taste
    inc led         ;Animation der FarbLED
    andi led,7      ;nur von 0 bis 7
    mov tmp,led
    swap tmp        ;vertausche Nibble
    lsl tmp         ;und noch einmal schieben
    in tmp2,StelleOut  ;schreibe auf StelleOut
    andi tmp2,0b00011111
    or tmp2,tmp
    out StelleOut,tmp2
    rcall wait
    rjmp loop
taste:
    sbis TastaturIn,0 ;Taste immer noch gedrueckt
    rjmp taste
    rcall zaehle      ;wenn losgelassen erhoehe zaehler um 1
    rjmp loop

zaehle:     ;der Zaehler wird um 1 erhoeht
    ldi YH, high(anzeige)   ;Datenzeiger initialisieren
    ldi YL, low(anzeige)
nextstelle:
    ld tmp,y        ;lade Stelle
    cpi tmp,9       ;ist sie 9?
    breq ueberlauf  ;Ueberlauf auf naechste Stelle
    inc tmp         ;sonst erhoehe um 1
    st y,tmp
    ret
ueberlauf:          ;erhoehe naechste Stelle
    st y,null
    adiw y,1
    rjmp nextstelle

wait:
    ldi tmp,50      ;aeusseren Zaehler laden 50 ms
wl1:
    ldi tmp2,250    ;inneren Zaehler 1ms bei 1Mhz
wl2:
    nop
    dec tmp2        ;tmp2--
    brne wl2        ;Sprung wenn nicht null
    dec tmp         ;tmp--
    brne wl1        ;Sprung wenn nicht null
    ret

bcd_7:  ;Feld fuer BCD nach 7-Segment Wandlung
    .db 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f

    .dseg; Datenbereich
anzeige: ;Feld fuer die 4 Stellen 
    .byte 4; Feld mit 4 Byte
