;*******************************************************************************
;*
;*	Titel:	Aufnahmegeraet mit EEPROM (24LC256)
;*      (C306_I2C_EEPROM_ADDA.asm)
;*	Datum:	25/01/11	Version:	0.1
;*	Autor:	WEIGU
;*
;*	Informationen zur Beschaltung:
;*      
;*	Prozessor: ATmega32A			Quarzfrequenz:	16MHz
;*	Eingaenge: Potentiometer (0-5V) an PA1, Mikrofonschaltung an PA0 (0-5V)
;*                 Aufnahmetaster an PC2
;*	Ausgaenge: D/A-Wandler an PortB, EEPROM (I2C) an SCL und SDA
;*                 (PC0 und PC1), LED Aufnahme an PC3, LED Abspielen an PC4
;*
;*	Informationen zur Funktionsweise:
;*			
;*******************************************************************************

;-------------------------------------------------------------------------------
;	Einbinden der controllerspezifischen Definitionsdatei
;-------------------------------------------------------------------------------
.NOLIST				;List-Output ausschalten
.INCLUDE "m32def.inc"		;AVR-Definitionsdatei einbinden
.LIST				;List-Output wieder einschalten

;-------------------------------------------------------------------------------
;       Organisation des Datenspeichers (SRAM)
;-------------------------------------------------------------------------------
.DSEG                           ;was ab hier folgt kommt in den SRAM-Speicher
E24ADL:  .BYTE 1                ;16 Bit Adressezeiger EEPROM
E24ADH:  .BYTE 1
E24BYTE: .BYTE 1                ;Datenbyte EEPROM
E24PAGE: .BYTE 64               ;64 Byte Seite (Page) im EEPROM
TCNT:    .BYTE 1                ;Tabellenzeiger
TAB1:    .BYTE 128              ;128 Byte Tabelle

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;       Programmspeicher (FLASH)     Programmstart nach RESET ab Adr. 0x0000
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.CSEG                           ;was ab hier folgt kommt in den FLASH-Speicher
.ORG	0x0000			;Programm beginnt an der FLASH-Adresse 0x0000
RESET1:	rjmp	INIT		;springe nach INIT (ueberspringe ISR Vektoren)

;-------------------------------------------------------------------------------
;       Sprungadressen fuer die Interrupts organisieren (ISR VECTORS)
;-------------------------------------------------------------------------------
        ;Vektortabelle (im Flash-Speicher)
.ORG    OC2addr                 ;interner Vektor fuer Timer2 Compare
        rjmp    ISR_T2          ;Bei TCNT2=OCR2 springe zur ISR Timer2
.ORG    OC0addr                 ;interner Vektor fuer Timer0 Compare
        rjmp    ISR_T0          ;Bei TCNT0=OCR0 springe zur ISR Timer0
.ORG    ADCCaddr                ;interner Vektor fuer ADCC (alt.: .ORG 0x0020)
                                ;AD-Wandlung vollstaendig ADC complete
        rjmp    ISR_AD          ;Springe zur ISR von ADCC

;-------------------------------------------------------------------------------
;	Initialisierungen und eigene Definitionen
;-------------------------------------------------------------------------------
.ORG    INT_VECTORS_SIZE        ;Platz fuer ISR Vektoren lassen
INIT:
.DEF    Zero = r15              ;Register 1 wird zum Rechnen benoetigt
        clr     r15             ;und mit Null belegt
.DEF    Tmp1 = r16              ;Register 16 dient als erster Zwischenspeicher
.DEF    Tmp2 = r17              ;Register 17 dient als zweiter Zwischenspeicher
.DEF    Cnt1 = r18              ;Register 18 dient als Zaehler
.DEF    WL = r24                ;Register 24 und 25 dienen als universelles
.DEF    WH = r25                ;Doppelregister W und zur Parameteruebergabe

	;Stapel initialisieren (fuer Unterprogramme bzw. Interrupts)
	ldi	Tmp1,HIGH(RAMEND)	;RAMEND (SRAM) ist in der Definitions-
	out	SPH,Tmp1		;datei festgelegt	
	ldi	Tmp1,LOW(RAMEND)
	out	SPL,Tmp1

        ;ADC initialisieren
        ldi     Tmp1,0b01100000 ;REFS = 01 AVCC als Referenzspannung
        out     ADMUX,Tmp1      ;ADLAR = 1 linksb. (obere 8 Bit in ADCH)
                                ;MUX = 00000 unsymetrisch PA0 (ADC0)
        ldi     Tmp1,0b10100100 ;ADEN = 1 (ADC einschalten)
        out     ADCSRA,Tmp1     ;ADATE = 1 (Auto Trigger Modus ein)
                                ;ADIE = 0  (noch kein Interrupt)
                                ;ADPS = 100 (16MHz/16 = 1MHz)
                                ;andere Bits per default = 0
        in      Tmp1,SFIOR      ;ADTS = 011 Timer 0 Compare
        andi    Tmp1,0b01111111
        ori     Tmp1,0b01100000
        out     SFIOR,Tmp1

        ;Timer0 einschalten und Vorteiler festlegen (SAVE)
        ldi     Tmp1,0x0B       ;TCCR0 = 0b00001011 (Systemtakt/64)
        out     TCCR0,Tmp1      ;WGM = 01 CTC-Modus
        ;Vergleichswert T0 fuer den Zaehler festlegen
        ldi     Tmp1,36         ;36 Zaehlschritte = 6,8kHz; fs = 3,4kHz
        out     OCR0,Tmp1

        ;Timer2 einschalten und Vorteiler festlegen (PLAY)
        ldi     Tmp1,0x0C       ;TCCR0 = 0b00001100 (Systemtakt/64)
                                ;!! CS2 <> CS0 anders als Timer0!!)
        out     TCCR2,Tmp1      ;WGM = 01 CTC-Modus
        ;Vergleichswert T2 fuer den Zaehler festlegen
        ldi     Tmp1,36        ;50 Zaehlschritte = 5000Hz
        out     OCR2,Tmp1

        ;D/A-Wandler initialisieren
        ser     Tmp1            ;D/A-Wandler an PORTB
        out     DDRB,Tmp1

        ;Aufnahmetaster und LEDs initialisieren
        cbi     DDRC,2          ;Aufnahmetaster an PC2
        sbi     PORTC,2         ;Pull-Up
        sbi     DDRC,3          ;AufnahmeLED (SAVE) an PC3
        sbi     DDRC,4          ;AbspieLED (PLAY) an PC4
        
        ;I2C Bus initialisieren (400 kHz)
        rcall   I2CINI
        
        ;Interrupts global erlauben
        sei                     

;-------------------------------------------------------------------------------
;	Hauptprogramm
;-------------------------------------------------------------------------------
MAIN:   ;T2 Interrupt (PLAY) ausschalten
        in      Tmp1,TIMSK      ;
        andi    Tmp1,0x7F       ;Mit einer UND-Maskierung (0b01111111)
                                ;Bit 7 (OCIE2) im TIMSK-Register loeschen
        out     TIMSK,Tmp1      ;
        cbi     PORTC,4         ;Abspiel LED loeschen

        ;Potentiometer zum Verstellen der Abspielgeschwindigkeit an PA1
        ;Option! dieser Teil kann falls erwuenscht geloescht werden
        ldi     Tmp1,0b01100001 ;REFS = 01 AVCC als Referenzspannung
        out     ADMUX,Tmp1      ;ADLAR = 1 linksb. (obere 8 Bit in ADCH)
        ldi     Tmp1,0b10000100 ;ADEN = 1 (ADC einschalten)
        out     ADCSRA,Tmp1     ;ADPS = 100 (16MHz/16 = 1MHz)
        sbi     ADCSRA,ADSC     ;eine AD-Wandlung ausloesen
LOOPV:  sbic    ADCSRA,ADSC     ;Polling bis Wandlung fertig
        rjmp    LOOPV
        in      Tmp1,ADCH       ;oberste 8 Bit einlesen und ausgeben
        lsr     Tmp1            ;Wert durch 4 teilen
        lsr     Tmp1
        brne    LOOPG           ;auf Endstellung (0) 
        ldi     Tmp1,36         ;normale Abspielgeschwindigkeit
LOOPG:  out     OCR2,Tmp1       
        ldi     Tmp1,0b01100000 ;A/D Wandler zuruecksetzen (PA0 Mikrofoneingang)
        out     ADMUX,Tmp1      
        ldi     Tmp1,0b10100100 
        out     ADCSRA,Tmp1     

        sbic    PINC,2          ;Aufnahmetaster
        rjmp    PLAY

SAVE:   ;Timer0 (SAVE) Interrupt einschalten
        in      Tmp1,TIMSK      ;
        ori     Tmp1,0x02       ;Mit einer ODER-Maskierung (0b00000010)
                                ;Bit 2 (OCIE0) im TIMSK-Register auf eins setzen
        out     TIMSK,Tmp1      ;
        sbi     ADCSRA,ADIE

        ;Anfangsadresse und Tabellenzaehler initialisieren, AufnahmeLED ein
        clr     Tmp1
        sts     E24ADL,Tmp1
        sts     E24ADH,Tmp1
        sts     TCNT,Tmp1
        sbi     PORTC,3

SAVE0:  lds     Tmp1,TCNT       ;zweite Haelfte der Tabelle?
        cpi     Tmp1,64
        brge    SAVE1
        rjmp    SAVE0

SAVE1:  clr     Cnt1            ;PAGE (64 Byte) schreiben
        ldi     XL,LOW(E24PAGE)
        ldi     XH,HIGH(E24PAGE)
        ldi     YL,LOW(TAB1)
        ldi     YH,HIGH(TAB1)
LOOP2:  ld      Tmp1,Y+
        st      X+,Tmp1
        inc     Cnt1
        cpi     Cnt1,64
        brne    LOOP2
        rcall   E24SP

        ;Pageadresse erhoehen
        lds     XL,E24ADL
        lds     XH,E24ADH
        adiw    XL,32           ;64 addieren (naechste Seite)
        adiw    XL,32
        sts     E24ADL,XL
        sts     E24ADH,XH

LOOP3:  lds     Tmp1,TCNT       ;erste Haelfte der Tabelle?
        cpi     Tmp1,64
        brlo    SAVE2
        rjmp    LOOP3

SAVE2:  clr     Cnt1            ;PAGE (64 Byte) schreiben
        ldi     XL,LOW(E24PAGE)
        ldi     XH,HIGH(E24PAGE)
        ldi     YL,LOW(TAB1+64)
        ldi     YH,HIGH(TAB1+64)
LOOP4: ld      Tmp1,Y+
        st      X+,Tmp1
        inc     Cnt1
        cpi     Cnt1,64
        brne    LOOP4
        rcall   E24SP

        ;Pageadresse erhoehen
        lds     XL,E24ADL
        lds     XH,E24ADH
        adiw    XL,32           ;64 addieren (naechste Seite)
        adiw    XL,32
        brmi    PLAY            ;Falls EEPROM voll (Bit15 = 1) dann Raus
        sts     E24ADL,XL
        sts     E24ADH,XH
        rjmp    SAVE0

PLAY:   ;T0 Interrupt (SAVE) ausschalten
        in      Tmp1,TIMSK      ;
        andi    Tmp1,0xFD       ;Mit einer UND-Maskierung (0b11111101)
                                ;Bit 1 (OCIE0) im TIMSK-Register loeschen
        out     TIMSK,Tmp1      ;
        cbi     ADCSRA,ADIE

        ;Anfangsadresse und Tabellenzaehler initialisieren, Abspiel LED ein
        clr     Tmp1
        sts     E24ADL,Tmp1
        sts     E24ADH,Tmp1
        sts     TCNT,Tmp1
        sbi     PORTC,4         

PLAY2:  rcall   E24RS           ;Werte an der gleichen Adresse abholen (1,53ms)
        clr     Cnt1            ;und ausgeben
        ldi     XL,LOW(E24PAGE)
        ldi     XH,HIGH(E24PAGE)
        ldi     YL,LOW(TAB1)
        ldi     YH,HIGH(TAB1)
LOOP5:  ld      Tmp1,X+
        st      Y+,Tmp1
        inc     Cnt1
        cpi     Cnt1,64
        brne    LOOP5

        ;Pageadresse erhoehen
        lds     XL,E24ADL
        lds     XH,E24ADH
        adiw    XL,32           ;64 addieren (naechste Seite)
        adiw    XL,32
        ;brmi    MAINR          ;Falls EEPROM voll (Bit15 = 1) dann Raus
        sts     E24ADL,XL
        sts     E24ADH,XH

LOOP6:  lds     Tmp1,TCNT       ;erste Haelfte der Tabelle?
        cpi     Tmp1,64
        brlo    PLAY3
        rjmp    LOOP6

PLAY3:  lds     Tmp1,E24ADL     ;erstes Mal?
        cpi     Tmp1,0x40
        brne    PLAY4
        lds     Tmp1,E24ADH
        cpi     Tmp1,0x00
        brne    PLAY4
        
        ;T2 Interrupt einschalten
        in      Tmp1,TIMSK      ;
        ori     Tmp1,0x80       ;Mit einer ODER-Maskierung (0b10000000)
                                ;Bit 7 (OCIE2) im TIMSK-Register auf eins setzen
        out     TIMSK,Tmp1      ;
        cbi     PORTC,3
        
PLAY4:  rcall   E24RS           ;Werte an der gleichen Adresse abholen (1,53ms)
        clr     Cnt1            ;und ausgeben
        ldi     XL,LOW(E24PAGE)
        ldi     XH,HIGH(E24PAGE)
        ldi     YL,LOW(TAB1+64)
        ldi     YH,HIGH(TAB1+64)
LOOP7:  ld      Tmp1,X+
        st      Y+,Tmp1
        inc     Cnt1
        cpi     Cnt1,64
        brne    LOOP7

        ;Pageadresse erhoehen
        lds     XL,E24ADL
        lds     XH,E24ADH
        adiw    XL,32           ;64 addieren (naechste Seite)
        adiw    XL,32
        brmi    MAINR           ;Falls EEPROM voll (Bit15 = 1) dann Raus
        sts     E24ADL,XL
        sts     E24ADH,XH

LOOP8:  lds     Tmp1,TCNT       ;zweite Haelfte der Tabelle?
        cpi     Tmp1,64
        brge    PLAYR
        rjmp    LOOP8

PLAYR:  rjmp	PLAY2           ;Endlosschleife

MAINR:  rjmp    MAIN

;-------------------------------------------------------------------------------
;	Unterprogramme und Interrupt-Behandlungsroutinen
;-------------------------------------------------------------------------------
; Interrupt-Behandlungsroutine Timer2 (PLAY)
ISR_T2: push    r16             ;benutzte Reg. retten (r16 = Zwischenspeicher)
        in      r16,SREG        ;Statusregister einlesen
        push    r16             ;Statusregister retten
        push    r17
        push    YL
        push    YH

        cbi     PORTC,4         ;dient der Visualisierung per Oszilloskop
        nop
        sbi     PORTC,4
        
        lds     r17,TCNT        
        ldi     YL,LOW(TAB1)
        ldi     YH,HIGH(TAB1)
        add     YL,r17
        adc     YH,Zero
        ld      r16,Y           
        out     PORTB,r16       ;Wert aus Tabelle ausgeben
        inc     r17
        cpi     r17,128
        breq    ISR_TF
        sts     TCNT,r17
        rjmp    ISR_TR
ISR_TF: clr     r17
        sts     TCNT,r17

ISR_TR: pop     YH
        pop     YL
        pop     r17
        pop     r16             ;Werte der geretteten Register wieder-
        out     SREG,r16        ;herstellen
        pop     r16
        reti                    ;Ruecksprung aus einer ISR

;Dummy Interrupt-Behandlungsroutine
ISR_T0: reti                    ;Ruecksprung aus einer ISR

;Interrupt-Behandlungsroutine ADC (SAVE)
ISR_AD: push    r16             ;benutzte Reg. retten (r16 = Zwischenspeicher)
        in      r16,SREG        ;Statusregister einlesen
        push    r16             ;Statusregister retten
        push    r17
        push    YL
        push    YH

        cbi     PORTC,3         ;dient der Visualisierung per Oszilloskop
        nop
        sbi     PORTC,3

        lds     r17,TCNT
        ldi     YL,LOW(TAB1)
        ldi     YH,HIGH(TAB1)
        add     YL,r17
        adc     YH,Zero
        in      r16,ADCH        ;oberste 8 Bit einlesen und 
        st      Y,r16           ;abspeichern
        inc     r17
        cpi     r17,128
        breq    ISR_AF
        sts     TCNT,r17
        rjmp    ISR_AR
ISR_AF: clr     r17
        sts     TCNT,r17

ISR_AR: pop     YH
        pop     YL
        pop     r17
        pop     r16             ;Werte der geretteten Register wieder-
        out     SREG,r16        ;herstellen
        pop     r16
        reti                    ;Ruecksprung aus einer ISR

.INCLUDE "SR_I2C.asm"                   ;I2C-Bibliothek einbinden
.INCLUDE "SR_EEPROM_24LC256.asm"        ;EEPROM-Bibliothek einbinden
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.EXIT				;Ende des Quelltextes
