Forum: Mikrocontroller und Digitale Elektronik Freiwillige vor: Funktioniert das so? (Rücksprünge/Stack)


von Michael Dierken (Gast)


Angehängte Dateien:

Lesenswert?

Guten Tag.

Habe ein "kleines" Progrämmchen geschrieben b.z.w. bin noch dabei.

Leider ist das Programm jetzt bereits so groß das ich obwohl ich es 
möglichst übersichtlich gehalten habe nicht mit 100%iger Sicherheit 
überprüfen kann ob ich auch alles richtig gemacht habe (Kein überfluten 
des Stack, Sicherung aller benötigten Register und richtige 
Rücksicherung ...)

Wie schon gesagt, Überprüfung ist freiwillig.

von Der E. (rogie)


Lesenswert?

Was die Kommentare angeht: sehr gut, aber data_kill solltest du auch 
besser kommentieren.
Bei Assemblerprogrammen sollte man fast jeden Befehl kommentieren, damit 
man auch später (und ev. andere) nachvollziehen kann, warum man das so 
gemacht hat.

Nur meine bescheidene Meinung.

von Peter D. (peda)


Lesenswert?

Gewöhn Dir sowas garnicht erst an. Aus nem Interrupt raus gibt es nur 
das RETI und nichts weiter!
Ins Main zurück springen ist ne Zeitbombe, die Dir irgendwann 
explodiert, wenn das Progrämmchen auch nur ein Pfitzelchen größer wird.
Und warten im Interrupt ist auch Pfui-Bäh.

Sei nicht so sparsam mit Kommentaren.


Peter

von Michael Dierken (Gast)


Lesenswert?

Data_kill bin ich gerade bei ... (wird gerade kommentiert) ...

und hier ist es auch schon:
1
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2
;
3
;  Daten ignorieren
4
5
Data_kill:
6
  push    temp
7
  in      temp,    sreg
8
  push    temp                        ; Register sichern
9
  push    temp4
10
  ldi     temp4,   10                 ; 11 bits müssen ignoriert werden
11
  ldi     ZL,      LOW(kill)          ; beim nächsten Interrupt auf Kill springen
12
  ldi     ZH,      HIGH(kill)
13
  ldi     Wait1,   $FF                ; Wartezeit um im Wait den Interrupt zu empfangen
14
  sei                                 ; - und temp4 nicht sichern zu müssen
15
  rcall   Wait
16
  ldi     ZL,      LOW(Data_Recieve)  ; sollt kein Interrupt aufgetreten sein (Daten abgerissen)
17
  ldi     ZH,      HIGH(Data_Recieve) ; - geht der nächste Interupt wieder in die Empfangsroutine
18
  pop     temp4          
19
  pop     temp                        ; Register zurücksichern
20
  out     sreg,    temp
21
  pop     temp
22
  reti                                ; Zurück ins Programm
23
killing:
24
  pop     temp
25
  pop     temp
26
  pop     temp                        ; rcall und Interuptadressen aus Stack löschen
27
  pop     temp
28
  dec     temp4                       ; zu ignorierende Bit verringern
29
  breq    killed                      ; wenn alle gekilled bei killed weiter machen
30
  ldi     Wait1,   $FF
31
  sei                                 ; wenn nicht in Warteschleife auf weitere Warten
32
  rcall   Wait
33
killed:
34
  cli
35
  pop     temp4                      ; Interupts deaktiveren um Fehler zu verhindern
36
  pop     temp
37
  ou      sreg,    temp              ; Register wieder herstellen
38
  pop     temp
39
  ldi     ZL,      LOW(Data_Recieve) ; Beim nächsten Interrupt wieder normal Daten empfangen
40
  ldi     ZH,      HIGH(Data_Recieve)
41
  reti                               ; Zurück ins Programm

von spess53 (Gast)


Lesenswert?

Hi

>Und warten im Interrupt ist auch Pfui-Bäh.

LCD auch.

Aus langjähriger Assemblererfahrung: Mit dem Ansatz wirst du nicht froh.

Was soll das genau machen? PC-Tastetur einlesen?

MfG Spess

von Michael Dierken (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Gewöhn Dir sowas garnicht erst an. Aus nem Interrupt raus gibt es nur
>
> das RETI und nichts weiter!

Wie meinen? (Weil ich teilweise mit: sei -> reti rausgegangen war?)
Sonst bin ich doch immer mit Reti rausgegangen ...



> Ins Main zurück springen ist ne Zeitbombe, die Dir irgendwann
>
> explodiert, wenn das Progrämmchen auch nur ein Pfitzelchen größer wird.

Springe doch nur einmal ins Main (Loop) nach der Initialisierung und der 
Sprung nach 0x00 ist zur Fehlerbehebung (eingrenzung das ich n Reset 
habe weil ich an den Pin nicht rankomme).



> Und warten im Interrupt ist auch Pfui-Bäh.

Im Interrupt nutze ich massig Register, welche ich sonst immer sichern 
müsste und wieder neu auslesen. Oder kennen sie da eine bessere 
Möglichkeit?


> Sei nicht so sparsam mit Kommentaren.

Dachte das Reicht ... weiß nicht was ich da sonst noch hinschreiben 
sollte ...


Trotzdem vielen Dank für die Anmerkungen

von Michael Dierken (Gast)


Lesenswert?

spess53 schrieb:
> Hi
>
>
>
>>Und warten im Interrupt ist auch Pfui-Bäh.
>
>
>
> LCD auch.
>
>
>
> Aus langjähriger Assemblererfahrung: Mit dem Ansatz wirst du nicht froh.
>
>
>
> Was soll das genau machen? PC-Tastetur einlesen?
>
>
>
> MfG Spess


Beim Warten im Interrupt geht es mir darum das diese Arbeit nicht 
unterbrochen werden darf/soll.


Was ist an meinem Ansatz falsch? Was würden sie empfehlen?


Am Mikrocontroller sind eine Tastatur und ein LCD angeschlossen.
Die Tasten der Tastatur werden auf dem Display dargestellt.
Es soll am Ende möglich sein auf dem Display nicht anders als auf dem PC 
damit zu schrieben.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Michael Dierken schrieb:
> Sonst bin ich doch immer mit Reti rausgegangen ...
Aus jeder Unterroutine darf es nur 1 Ausgang geben. Mehrfache Ausgänge 
(und zudem, wenn sie so verteilt sind) brechen dir garantiert das 
Genick...
Klar kostet das einen Sprung mehr, aber dafür ist das Leben wesentlich 
einfacher und der Stack besser unter Kontrolle!

Michael Dierken schrieb:
> Was ist an meinem Ansatz falsch? Was würden sie empfehlen?
Dort hinein gehört die ganze Arbeit:
1
;  Hauptprogramm
2
3
Loop:
4
  sbis  PINB, 2    ; Noch bei Low PINB2 Reset
5
  rjmp  0x00
6
  rjmp  Loop       ; Rücksprung Loop da noch leer
In Interrupts wird nur gemacht, was das Hauptprogramm unterbricht (so 
sind die Interrupts auch zu ihrem Namen gekommen).

>   rjmp  0x00
Achtung: ein Sprung nach Adresse 0 ist KEIN RESET!

von spess53 (Gast)


Lesenswert?

Hi

>Was ist an meinem Ansatz falsch? Was würden sie empfehlen?

Ich gehe jetzt mal von einer PC-Tastatur aus. Dort gibt es einen Takt, 
bei dem eine Flanke anzeigt wenn die Daten gültig sind. Bei ATmel gibt 
es einen AppNote dazu:

http://www.atmel.com/dyn/resources/prod_documents/doc1235.pdf
http://www.atmel.com/dyn/resources/prod_documents/AVR313.zip

Siehe S.3 The Algorithm

MfG Spess

von Peter D. (peda)


Lesenswert?

Michael Dierken schrieb:
>> Sei nicht so sparsam mit Kommentaren.
>
> Dachte das Reicht ... weiß nicht was ich da sonst noch hinschreiben
> sollte ...

Sorry.
Ich nehm alles zurück.
Ich war in Gedanken bei nem anderen Thread.
Vergiß also, was ich gesagt habe.


Peter

von oldmax (Gast)


Lesenswert?

Hi
Ok, Interrupt heißt, auf kleinste Ereignisse zu reagieren. Z.B. es kommt 
ein Nadelimpuls, dann eine kleine Weile nix. Den Nadelimpuls kannst du 
nur sicher mit einem Interrupt erfassen, also kommt in die ISR ein flag, 
was dieses Ereignis der Programmschleife übergibt. Das Programm prüft 
irgendwann das Flag, bearbeitet das Ereignis und setzt das Flag zurück. 
Unterschätze nicht die Geschwindigkeit deines Controllers. Bedenke, bei 
1 MHz und 2 Taktzyklen pro Befehl dauert die Befehlsbearbeitung mal 
grade zwei µSek. Wenn deine Programmschleife 1000 befehle ababeitet,ist 
die Zykluszeit zwei mSek. Ich glaube nicht, das du so schnell schreiben 
kannst, das selbst bei 1/10 Tastendruck (Puls-Pause) der Controller die 
Zeichen nicht erfassen kann.
Gruß oldmax

von oldmax (Gast)


Lesenswert?

Hi
Hab noch was vergessen...
Du setzt einen Interrupt ein, um schnell reagieren zu können und dann 
programmierst du in die ISR eine Wartezeit. Meinst du nicht auch bei ein 
wenig Überlegung, das diese Vorgehensweise nicht wirklich richtig sein 
kann. ISR - schnell. Also, Ereignis erfassen, zuordnen und wieder raus. 
Und nicht stundenlang überlegen, mach ich was oder nicht....
so, jetzt sollte es gut sein.
Gruß oldmax

von Michael Dierken (Gast)


Lesenswert?

Das Programm arbeitet so das es bei Beginn eines Zeichens (Fallende 
Flank Clock) einen Interrupt auslöst welcher das Startbit abfragt. Ist 
dies da so fährt er vor und wartet auf 8 Datenbits ...

Sollte das Startbit fehlerhaft sein (z.B. High) oder er die 8Bit + 
Parität + Stop empfangen habe sowie Parität geprüft und Zeichen ans 
Display ausgegeben so geht er wieder zurück ins Hauptprogramm.

Die Erkennung des nächsten Zeichens läuft immer über den interrupt 
sodass ich nicht dauernt den Pin abfragen muss.

von Michael Dierken (Gast)


Lesenswert?

Ja aber ... wenn ich im Programm auf den nächsten Interrupt warte muss 
ich die Register immer im SRAM sichern und nachher wieder zurücksichern 
und ich müsste einen Timer setzen um ein abbrechen der Daten zu 
erkennen.

von oldmax (Gast)


Lesenswert?

Hi
Versuchst du eine Soft - UART zu programmieren ? Es ist ja ok, wenn man 
schnelle Signale per ISR erfasst, aber innerhalb einer ISR warten macht 
keinen Sinn. Stell dir vor, du erkennst in der ISR einen Flankenwechsel. 
Wie lange dauert es, bis der nächst eintrifft ? Hast du genug zeit, so 
setze dit in der ISR ein Flag und verlasse diese. Es braucht nur die 
Push und Pop's für die verwendeten Register sowie für's Statusregister. 
Das Haupprogramm hat nun die Aufgabe, die Signalauswertung zu 
übernehmen. Da braucht aber nix gepusht und gepoppt zu werden.
Bspl. einer beliebigen ISR
1
X_ISR:
2
  Push Temp
3
  In Temp, SReg
4
  Push Temp
5
  LDS Temp, Flags
6
  Or  Temp, 0b00000001
7
  STS Flags, Temp
8
  POP Temp
9
  Out SREG, Temp
10
  POP Temp
11
RETI
Wie du siehst, wird nur ein Bit un eine Variable "Flags" geschrieben....
Im Hauptprogramm kannst du nun dieses Bit abfragen und in ein 
Unterprogramm verzweigen:
1
Main:
2
  ....
3
  LDS Temp, Flags
4
  Andi Temp, 0b00000001
5
  BREQ Next
6
  RCALL Meine_Reaktion_Auf_ISR
7
Next:
8
  ....
9
RJmp Main
10
11
Meine_Reaktion_auf_ISR:
12
  ....
13
  ....
14
RET
Vielleicht hilft dir dieses kleine Beispiel.
Gruß oldmax

von Michael Dierken (Gast)


Lesenswert?

ich verstehe leider nicht wie du das meinst.

Die Zeit zwischen den Flankenwechsel ist unterschiedlich lang und ich 
muss zeitweise mit 3 Werten "gleichzeitig" arbeiten.


Mit UART hat das erstmal noch garnichts zu tun (habe den nur zur 
Fehlersuche mit konfiguriert).


Ansonsten läuft das Programm ja so das es in den Interrupt geht und dann 
das Unterprogramm startet wo es Programm dann weiter läuft.

von Michael Dierken (Gast)


Lesenswert?

Ich kann gerne wie ihr meint so machen das ich nach jedem Empfangenen 
Bit wieder in das Hauptprogramm springe, müsste dann aber wissen wie ich 
einen Abbruch des Datenstranges (z.B. bei Abziehen der Tastatur) 
erkennen kann.

von Sascha W. (sascha-w)


Lesenswert?

Michael Dierken schrieb:
> ich verstehe leider nicht wie du das meinst.
wenn wir das richtig sehen liefert deine Tastatur (PS2??) ein serielles 
Datenpaket. Du kannst dazu den UART deines AVR nutzen (in der syncronen 
Betriebsart).

Sascha

von Michael Dierken (Gast)


Lesenswert?

Den UART brauche ich noch um Daten an den PC zu senden, werde ich mir 
aber mal anschauen.

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.