Forum: Mikrocontroller und Digitale Elektronik Programmierbarer Timer


von Leo F. (Gast)



Lesenswert?

Hallo, Ich heiße Leo, habe mich im Offtopic-Forum auch schon 
vorgestellt.
Ich bin seit einer Woche dabei, das Programmieren in Assembler zu üben 
und habe jetzt mein erstes "größeres" Projekt geschrieben:

An PB2 liegt normalerweise ein Abreisskontakt nach Masse, der beim 
Abziehen einen Interrupt auslösen soll, um nach einer gewissen Zeit PB3 
auf LOW zu setzen.

Zum Einstellen der Zeit kann man anstatt des Abreisskontakts einen 
Taster (schließend nach Masse) anschließen, der die Variable "Zeit" mit 
jedem Low-Pegel inkrementiert. Zwischen den einzelnen Abfragen wird 
100mS lang gewartet. Der Pull-up ist durch das Programm eingeschaltet.
Beim Abziehen der Stromversorgung, die an PB1 einen Interrupt auslöst, 
soll die Variable im EEprom abgelegt werden, der im Schaltplan 
eingezeichnete Kondensator besteht aus 1x100n und 1x10µ, was beim 
Abziehen der 5 Volt für 10mS bei 2mA reichen sollte.
Nun gibt der Assembler des Avr- Studios verschiedene Fehler aus, von 
denen ich einige nicht nachvollziehen kann. Könnt ihr mir da helfen ?
Anbei der Text und ein minimalistischer Schaltplan.
Das Programm ist hier leider ein wenig verschoben, daher auch noch mal 
als .asm Datei angehängt.

; setzt nach "Zeit*10" Sekunden PB3 auf 0
; "Zeit" wird bei Abziehen der Stromversorgung in EEprom gespeichert
; alle 100mS inkrementiert ein LOW- Pegel an PB1 "Zeit"

; PROGRAMMIERBARER TIMER: 
;5
   .include "tn13def.inc"
   .def     Zeit          = r20   ; Register für Variablen festlegen
   .def     Adresse       = r19   ; Register für Variablen festlegen
   .def     EEmode        = r18   ; Register für Variablen festlegen
   rjmp     Anfang 
;10
   .org     0x0001                ; Sprungbefehl setzen
   rjmp     INT0
   .org     0x0002                ; Sprungbefehl setzen
   rjmp     PCINT2
Anfang: 
;15
   ldi      r21,0b00              ; Durch Löschen des 7.Bits (ADEN)
   out      ADCSRA,r21            ; wird der ADC ausgeschaltet
   ;cbi      ADCSRA,ADEN           ; Durch Löschen des 7.Bits (ADEN) den 
ADC ausschalten
   sbi      ddrb,0                ; PortB0 als Ausgang definieren
   sbi      portb,0               ; PortB0 "High" setzen 
;20
   sbi      portb,2               ; Pull-Up an PortB2 setzen
   sbic     pinb,2                ; PB2 in r16 einlesen
   ldi      r16,0
   cpi      r16,0                 ; r16-0
   breq     Programmieren         ; Sprung zu Unterprogramm, wenn 
Ergebnis (PortB2)=0        ;25
   ldi      r17,0x00000011        ; über den Umweg r17
   out      PCINT2,r17            ; den Pin Change Interrupt auf 
steigende Flanke konfigurieren
   ldi      r17,0x00000000
   out      INT0,r17              ; INT0 auf LOW-Level konfigurieren
   SEI                            ; Global Interrupts freigeben, 
unbedingt vor SLEEP- Befehl ;30
   SLEEP                          ; Sleepmodus wenn PortB2>0
Programmieren:
   CLI                            ; Global Interrupts verhindern(PCINT0 
löst auf steigende Flanke an PB2 aus)
   clr      Zeit                  ; ÜBERARBEITUNG ERFORDERLICH
   sbic     pinb,2 
;35
   ldi      r16,0
   cpi      r16,0
   breq     Zeit_erhöhen          ; Wenn PortB2=0, Zeit inkrementieren
   rcall    100mS Warten
   SEI                            ; Interrupts wieder freigeben 
;40
   ret                            ; Rücksprung

Zeit_erhöhen:
   inc      Zeit
   ret                            ; MÖGLICHE FEHLERQUELLE
                                                                                             ;45
100mS Warten:
 Beginn:
   PUSH    r16                    ; Register sichern
   PUSH    r17                    ; Register sichern
   rcall   Warten 
;50
 Warten:
   Ldi     r16,166
 Warten1:                         ; äußere Schleife
   Ldi     r17,166
 Warten2:                         ; innere Schleife 
;55
   dec     r17
   brne    Warten2
   dec     r16
   brne    Warten1                ; Summe:83337 Takte = 100,004ms
   POP     r17                    ; Register wiederherstellen 
;60
   POP     r16                    ; Register wiederherstellen
   ret                            ; Rücksprung
INT0:                             ; Interrupt auf Wegfall der 
Spannungsversorgung
   rcall   EEPROM_write           ; "Zeit" in EEprom schreiben
   reti                           ; Rücksprung 
;65
EEPROM_write:                     ; Schreibt "Zeit" in EEprom
   CLI                            ; Global Interrupts verhindern, da 
Schreibvorgang zeitkritisch
   sbic    EECR, EEPE             ; andere Schreibzugriffe abwarten
   rjmp    EEPROM_write
   ldi     EEmode, 0              ; Adresse über das Register r16 
;70
   out     EECR, EEmode           ; in das EEprom Control Register
   out     EEARL,Adresse          ; Adresse in EEARL laden
   out     EEDR, r16              ; Programming Mode in das Register 
EEDR
   sbi     EECR, EEMPE            ; Bit EEMPE setzen
   sbi     EECR, EEPE             ; Schreibvorgang starten 
;75
   SEI                            ; Global Interrupts freigeben
   ret

PCINT2:                           ; Interrupt auf steigende Flanke an 
PortB2
   rcall   EEPROM_read            ; "Zeit" aus EEprom holen, in 
"Zeit"(r20) ablegen          ;80
   mov     r21, Zeit              ; "Zeit" in Register 21 kopieren
   dec     r21
   brne    1s Warten              ; 1s Warten "Zeit" mal ausführen
   cbi     portb,0                ; PortB0 auf LOW setzen
   CLI                            ; Interrupts verhindern, damit nach 
Abtrennen der          ;85
   reti                           ; Stromversorgung "Zeit" im EEprom 
erhalten bleit(INT0 wird verhindert)


EEPROM_read:
   CLI                            ; Global Interrupts verhindern (lesen 
ist zeitkritisch)    ;90
   sbic    EECR, EEPE             ; warten bis eventuelle 
Schreibzugriffe fertig sind
   rjmp    EEPROM_read
   out     EEARL, Adresse         ; Adresse in das Register EEARL 
kopieren
   sbi     EECR, EERE             ; EERE Bit (EEprom REad) setzen
   in      Zeit, EEDR             ; Daten in "Zeit" kopieren 
;95
   SEI                            ; Global Interrupts freigeben
   ret

1s_Warten:
   ldi     r18,0b0A 
;100
   rcall   100mS Warten
   dec     r18
   brne    1s_Warten
   ret
                                                                                             ;105

Hier einmal die Fehlermeldungen:
64  : error : Duplicate Label
80  : error : Duplicate Label
39  : error : Garbage at end of line
47  : error : Syntax Error
64  : error : Internal-Label changed between passes -conditonal on 
forward reference?
80  : error : Internal-Label changed between passes -conditonal on 
forward reference?
84  : error : Garbage at end of line
100 : error : Syntax error
101 : error : Garbage at end of line
102 : error : Garbage at end of line
104 : error : Relative branch out of line

von spess53 (Gast)


Lesenswert?

Hi

Scmeiss erst mal deine Umlaute aus dem Programm und dann hänge es als 
Anhang an.

MfG Spess

von Herr M. (herrmueller)


Lesenswert?

Die Fehlermeldungen sind erst einmal egal, da das Programm vom Ablauf 
her keine Chance hat, irgendwas Sinnvolles zu machen.
Auf jeden Fall am Anfang den Stackpointer initialisieren, sonst 
funktioniert kein Unterprogramm. Der Sleepmode muss vor dem Sleep Befehl 
eingestellt werden.
------------- nicht nachvollziehbar ---
   sbic     pinb,2                ; PB2 in r16 einlesen
   ldi      r16,0
   cpi      r16,0                 ; r16-0
   breq     Programmieren         ; Sprung zu Unterprogramm, wenn 
Ergebnis (PortB2)=0        ;25
---------------------------------------
Zeit_erhöhen:
   inc      Zeit
   ret             *********** ret wohin ?
--------------------------------------------
100mS Warten:
 Beginn:
   PUSH    r16                    ; Register sichern
   PUSH    r17                    ; Register sichern
   rcall   Warten     ******** ruft Warten auf **                    * 
;50
 Warten:   ******* fuehrt nochmal warten aus ? ***
   Ldi     r16,166
 Warten1:                         ; äußere Schleife
   Ldi     r17,166
 Warten2:                         ; innere Schleife 
;55
   dec     r17
   brne    Warten2
   dec     r16
   brne    Warten1                ; Summe:83337 Takte = 100,004ms
   POP     r17                    ; Register wiederherstellen 
;60
   POP     r16   ***** 1*gepusht und 2* gepopt, da Warten 2*ausgef. 
wird*
   ret                            ; Rücksprung
----------------------------------------------

alle Interruptroutinen ohne Status Register Sicherung.

----------------------------------------------
PCINT2:                           ; Interrupt auf steigende Flanke an 
PortB2
   rcall   EEPROM_read            ; "Zeit" aus EEprom holen, in 
"Zeit"(r20) ablegen          ;80
   mov     r21, Zeit              ; "Zeit" in Register 21 kopieren
   dec     r21
   brne    1s Warten              ; 1s Warten "Zeit" mal ausführen
********* Verlassen der IR. Routine ohne RETI *********
   cbi     portb,0                ; PortB0 auf LOW setzen
   CLI                            ; Interrupts verhindern, damit nach 
Abtrennen der          ;85
   reti
----------------------------------

und zu guter Letzt: Keine Hauptschleife vorhanden und kein sinnvoller 
Programmablauf erkennbar.


Leider sind wohl noch zu viele Schwachstellen vorhanden:
keine Programmstruktur
 Programmablauf, Unterprogramme, Stack
Befehle falsch benutzt
...

Ich fürchte, Du musst erst mit einfacheren Sachen beginnen. Auch nach 
Behebung der Fehlermeldungen wird das Programm nicht funktionieren.


herrmueller

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.