mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik RC-Empfänger an Mikrocontroler v2


Autor: Chrisopher Rohe (newguy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nabend.

Ich hab vor zwei Tagen ein STK500 mit einem Atmega8515L-Muster bekommen. 
Und genau so alt sind auch meine Programmier-Kentnisse. Habe mir als 
erstes Projekt vorgenommen das Signal eines RC-Empfängers auswerten zu 
lassen. Ich weiß dass es hier schon Code für sowas gibt. Allerdings 
möchte ich das Programm in Assembler schreiben und auch selbst 
erarbeiten. Ich habe jetzt ein erstes Programm zusammen geschrieben:
.include "m8515def.inc"

.def impulslaenge_ch1 = r16
.def temp = r17

.org 0x000
  
  rjmp main                    ;Reset Handler

.org INT0addr

  rjmp impuls_ch1                  ;IRQ0 Handler


main:

  ldi temp, 0xFF
  out DDRB, temp                  ;PortB auf Ausgang

  ldi temp, 0x00
  out DDRD, temp                  ;PortD auf Eingang

  ldi temp, (1<<ISC00) | (1<<ISC01)
  out MCUCR, temp                  ;INT0 auf steigende Flanke

  ldi temp, (1<<INT0)
  out GICR, temp                  ;INT0 aktivieren

  sei

  cpi impulslaenge_ch1, 50            ;Timerwert vergleichen
  brne unter200




unter200:

  ldi temp, 0x00
  out PORTB, temp                  ;alle LEDs aus

  rjmp main

ueber200:

  ldi temp, 0xFF
  out PORTB, temp                  ;alle LEDs an

  rjmp main

loop:

  rjmp loop                    ;leere Warteschleife


impuls_ch1:
  
  ldi temp, 0
  out TCNT0, temp                  ;Timer Reset

  ldi temp, (1<<CS00) | (0<<CS01) |(0<<CS02)    ;Vorteiler auf 8

  out TCCR0, temp                  ;Timer starten
  
  ldi temp, (0<<ISC00) | (1<<ISC01)        ;INT0 auf fallende Flanke

  ldi temp, (0<<CS00)
  out TCCR0, temp                  ;Timer stoppen

  ldi impulslaenge_ch1, TCCR0            ;Wert von Timer0 speichern

  reti


Dieses Programm soll folgendermaßen arbeiten:

Bei steigender Flanke des RC-Signals soll der Timer0 gestartet werden. 
Sobald die Flanke abfällt wird dieser gestoppt. Anschließend wird der 
Wert des Timers in impulslaenge_ch1 gespeichert welcher als 
Vergleichswert dienen soll.

Ich kann mir gut vorstellen das es nicht schön geschrieben ist und 
wahrscheinlich auch ziemlich durcheinander.

Leider funktioniert das Programm nicht. Die LEDs sind dauerhaft 
eingeschaltet und ändern diesen Zustand auch nicht. Bevor ich jetzt 
stundenlang nach einem vll simplen Fehler suche welchen ich als Anfänger 
nicht finde dachte ich: Fragste hier mal um Hilfe.

Danke schonmal für eure Mühen

Gruß
Christopher

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja, Servoimpulse auswerten ist für den Anfänger schon ein hartes Brot.
Schau doch einfach mal, wie es Andere gelöst haben. Einfach mal 
versuchen, Codebeispiele Anderer zu analysieren und zu verstehen. Ein 
paar uralte Beispiele von mir findest Du hier:
http://www.hanneslux.de/avr/mobau/index.html

...

Autor: Chrisopher Rohe (newguy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das es kein leichtes Projekt ist habe ich schon bemerkt. Aber an 
einfachen Projekten lernt man weniger. Ist meine Meinung.

Ich hab mir die Programme auf deiner Seite schon zuvor ein mal 
angesehen. Wirklich viel mit anfangen konnte ich damit nicht:

- Das Sendepult arbeitet anders herum. Außerdem will ich im Moment nur 
ein einzelnes Signal auslesen und (noch) nicht sieben

- der Impulsdekoder generiert aus dem Summensignal die einzelnen Signale 
der Kanäle. Damit kann ich auch (noch) nicht viel anfangen. Zumindest 
finde ich keinen passenden Ansatz

- Die Regler-Programme sind ewig lang. Bis ich da nen Durchblick habe 
kann ich mein Programm noch 3mal schreiben ;) Ob ich den Fehler dann 
aber gefunden hab steht auf einem anderen Blatt.


Im Moment möchte ich nur einmal das Signal auslesen. Was damit im 
Anschluss gemacht wird ist noch offen und wird den Schwierigkeitsgrad 
erneut nach oben treiben. Das An- und Abschalten der LEDs sollte nur 
eine erste Erfolgskontrolle sein und zum ermitteln der erreichten 
Timer-Werte dienen. Für die Projekte in meinem Kopf ist das bei weitem 
noch nicht ausreichend ;)


EDIT: Ich habe grad gesehen das ich "ueber200" gar nicht aufrufe. Der 
Fehler könnte also dort liegen. Werde ein wenig experimentieren. Freue 
mich aber trotzdem über helfende Vorschläge

Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gibst du denn auch nur "ein" Signal raus? Bei einer normalen 
Fernsteuerung werden doch alle Kanäle nacheinander ausgegeben; d.h. wenn 
nur ein Signal länger ist, sieht es so aus als würden die LEDs dauerhaft 
leuchten (was sie aber nicht tun)

Autor: Chrisopher Rohe (newguy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab nochmal ein wenig am Queltext gedreht. Aber auch die aktuelle 
Version funktioniert nicht:
.include "m8515def.inc"

.def impulslaenge_ch1 = r16
.def temp = r17

.org 0x000
  
  rjmp main                    ;Reset Handler

.org INT0addr

  rjmp impuls_ch1                  ;IRQ0 Handler

main:

  ldi temp, 0xFF
  out DDRB, temp                  ;PortB auf Ausgang

  ldi temp, 0x00
  out DDRD, temp                  ;PortD auf Eingang

  ldi temp, (1<<ISC00) | (1<<ISC01)
  out MCUCR, temp                  ;INT0 auf steigende Flanke

  ldi temp, (1<<INT0)
  out GICR, temp                  ;INT0 aktivieren

  sei                        ;Interrupts aktivieren


  cpi impulslaenge_ch1, 125            ;Timerwert vergleichen
  brne unter125
  rjmp ueber125


loop:

  rjmp loop                    ;leere Warteschleife


impuls_ch1:
  
  ldi temp, 0
  out TCNT0, temp                  ;Timer Reset

  ldi temp, (1<<CS00) | (0<<CS01) |(0<<CS02)    ;Vorteiler auf 8

  out TCCR0, temp                  ;Timer starten
  
  ldi temp, (0<<ISC00) | (1<<ISC01)        ;INT0 auf fallende Flanke

  ldi temp, (0<<CS00)
  out TCCR0, temp                  ;Timer stoppen
  
  cli                        ;Interrupts deaktivieren

  ldi impulslaenge_ch1, TCCR0            ;Wert von Timer0 speichern



  cpi impulslaenge_ch1, 125            ;Timerwert vergleichen
  brne unter125                  ;springe zu unter125
  rjmp ueber125                  ;springe zu ueber125

  reti

unter125:

  ldi temp, 0xFF
  out PORTB, temp                  ;alle LEDs aus

  rjmp main

ueber125:

  ldi temp, 0x00
  out PORTB, temp                  ;alle LEDs an

  rjmp main

Hier noch eine Erläuterung wie ich Empfänger und SKT500 verbunden habe. 
Nicht dass der Fehler hier liegt und ich mich am Programm die Finger 
wund tipe:

Der Empfänger wird über ein externes Akkupack mit 6V versorgt. Das 
SKT500 bekommt seinen Saft über ein externes Netzteil.

Der Masse-Pin des Servo-Anschlusses ist mit dem Masse-Pin (8) der 
Steckleiste PORTD des SKT500 verbunden.

Der Signal-Pin des Servo-Anschlusses ist mit dem PD2-Pind der 
Steckleiste PORTD des SKT500 verbunden.

Gruß
Christopher

Autor: Andreas W. (geier99)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
< cpi impulslaenge_ch1, 125            ;Timerwert vergleichen
 < brne unter125                  ;springe zu unter125
 < rjmp ueber125                  ;springe zu ueber125

nur wenn der Timmerwert genau 125 ist wird die über125 angesprungen 
sonst wird immer zu unter125 gesprungen (zb auch  bei 130). Ist das so 
gewollt?

Hmm, ausserdem wird die ISR nie mit reti verlassen. Du springst direkt 
aus der ISR  mit rjmp wieder zurück in die main

Gruss Andi

Autor: Chrisopher Rohe (newguy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das "brne" war tatsächlich falsch und wurde durch ein "brlt" ersetzt. 
Auch hatte ich einen Denkfehler drin. Die Zeilen lauten jeztzt
  cpi impulslaenge_ch1, 125            ;Timerwert vergleichen
  brlt unter125                  ;springe zu unter125
  rjmp ueber125                  ;springe zu ueber125

Dass ich die ISR nicht reti verlasse stimmt. Muss mir da noch was 
einfallen lassen wie ich das ander lösen kann. Aber nicht mehr heute 
Nacht.

Danke schonmal für die Hinweise. Morgen gehts weiter ;)

Autor: Andreas W. (geier99)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
.... da es schon etwas spät ist, sehe ich gerade nicht alle Fehler :-)

aber hier ist noch einer:

<     ldi impulslaenge_ch1, TCCR0            ;Wert von Timer0 speichern

hier lädst du immer einen Konstantenwert in deine Variable (und zwar die 
Adresse vom TCCR0)

Autor: Harry S. (littlegonzo)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich würde jetzt erstmal aus einen nicht initialisierten Stack tippen...
Oder sehe ich es nur nicht?

Gruß
Littlegonzo

Autor: Tim (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Harry S.:
Der Punkt geht an dich. Stackpointer init fehlt.

@Chrisopher Rohe:
Bau nach "main:" mal folgendes ein:
         ; Stackpointer init:
         ldi temp, LOW(RAMEND)
         out SPL, temp
         ldi temp, HIGH(RAMEND)
         out SPH, temp

Autor: Harry S. (littlegonzo)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hey Supi,
zum Jahresende einen Punkt bekommen^^

Autor: Chrisopher Rohe (newguy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe noch ein bischen am Programm geschraubt aber es funktioniert noch 
immer nicht. Habe grad erst den Post von Andreas W. gelesen



<     ldi impulslaenge_ch1, TCCR0            ;Wert von Timer0 speichern

hier lädst du immer einen Konstantenwert in deine Variable (und zwar die
Adresse vom TCCR0)


Mit welchem Code kann ich denn den Wert eines Timers in eine Variable 
speichern. Das dürfte ja ein grundlegender Fehler sein ;)

Gruß
Christopher

Autor: MWS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Timer0 per IN in ein Register laden und von da per STS in die Variable 
speichern.

Autor: Andreas W. (geier99)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
... wobei das "in" schon ausreichend ist, da ja "impulslaenge_ch1" schon 
ein Register ist.

Autor: MWS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> da ja "impulslaenge_ch1" schon ein Register ist

Yep, übersehen.

Autor: Chrisopher Rohe (newguy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
aus
ldi impulslaenge_ch1, TCCR0

wurde jetzt
in impulslaenger_ch1, TCNT0

denn TCNT ist doch das Register in dem der Timer hochzählt und nicht 
TCCR oder?

Und trotzdem funktioniert das Programm nicht.

Ich habe "unter125" und "ueber125" mal so geändert das in beiden Fällen 
die LEDs anspringen müssten. Doch das passiert nicht. Also scheint das 
Programm erst gar nicht in diese Bereiche des Programms zu springen.

Hier die aktuellste Version:
.include "m8515def.inc"


.def temp = r17
.def impulslaenge_ch1 = r18

.org 0x000
  
  rjmp main                    ;Reset Handler

.org INT0addr

  rjmp impuls_ch1                  ;IRQ0 Handler

main:
    cli
       
  ldi temp, LOW(RAMEND)
  out SPL, temp
  ldi temp, HIGH(RAMEND)
  out SPH, temp                  ; Stackpointer init:

  ldi temp, 0xFF
  out DDRB, temp                  ;PortB auf Ausgang

  ldi temp, 0x00
  out DDRD, temp                  ;PortD auf Eingang


  ldi temp, (1<<INT0)
  out GICR, temp                  ;INT0 aktivieren


  ldi temp, (1<<ISC00) | (1<<ISC01)
  out MCUCR, temp                  ;INT0 auf steigende Flanke

  cpi impulslaenge_ch1, 125            ;Timerwert vergleichen
  brlt ueber125                  ;springe zu ueber125
  rjmp unter125                  ;springe zu unter125


  sei                        ;Interrupts aktivieren


loop:

  rjmp loop                    ;leere Warteschleife


impuls_ch1:
  
  ldi temp, 0
  out TCNT0, temp                  ;Timer Reset

  ldi temp, (1<<CS00) | (0<<CS01) |(0<<CS02)    ;Vorteiler auf 8

  out TCCR0, temp                  ;Timer starten
  
  ldi temp, (0<<ISC00) | (1<<ISC01)        ;INT0 auf fallende Flanke

  ldi temp, (0<<CS00)
  out TCCR0, temp                  ;Timer stoppen
  
  cli                        ;Interrupts deaktivieren

  in impulslaenge_ch1, TCNT0            ;Wert von Timer0 speichern

  reti

unter125:

  ldi temp, 0x00
  out PORTB, temp                  ;alle LEDs aus

  rjmp main

ueber125:

  ldi temp, 0xFF
  out PORTB, temp                  ;alle LEDs an

  rjmp main

Autor: Tim (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
(ASM Kenntnisse hervorkram...)

>denn TCNT ist doch das Register in dem der Timer hochzählt und nicht
>TCCR oder?

Ohne in die .pdf zu gucken (mit / ohne 0,1,2): Ja.

>Und trotzdem funktioniert das Programm nicht.

Glaube ich.
Also wenn ich den Programmablauf richtig verstehe macht das Ding 
folgendes:
1. Stack / Ports Init
2. INT0 auf steigende Flanke einstellen
3. noch nie gesetztes Register impulslaenge_ch1 mit 125 vergleichen
4a. nach unter125 springen, Leds aus und zurück zu Punkt 1
4b. nach ueber125 springen, Leds an und zurück zu Punkt 1

Also je nach (zufälligem) wert im r18 gehen die leds nach dem reset
an oder aus. Mehr passiert nicht.

Für deinen Vergleich:
   cpi impulslaenge_ch1, 125            ;Timerwert vergleichen
   brlo is_lower                  ; Springe wenn < 125
   rcall ueber125                 ; Nicht kleiner, also grösser
   rjmp is_done                   ; Vergleich fertig
is_lower:
   rcall unter125                 ; Kleiner...
is_done:
   ; Vergleich fertig.
So kannst du (r)call verwenden und in ueber125 || unter125 per
ret zurückspringen.

Der AVR sichert bei einem Interrupt bzw (r)call NUR den PC.
Das Status Register sreg musst du selbst sichern und wiederherstellen:
impuls_ch1:
   push temp        ; Ein Register Freimachen
   in temp, sreg    ; Status Register hohlen
   push temp        ; Status Register sichern

   ; bla bal 
   ; hier kannst du mit temp machen was du willst

   pop temp         ; Status Register zurückhohlen
   out sreg, temp   ; Status Register setzen
   pop temp         ; temp widerherstellen
   reti             ; ISR ende

Jetzt darf dein Interrupt auch zwischen einem Vergleich (cp/cpi/...)
und dessen Auswertung (br??/...) kommen. Ohne sichern vom sreg
würde br?? auf grund der änderungen am sreg durch die ISR springen....

Den cli innerhalb von einer ISR ist überflüssig. Die Interrupts
werden beim anspringen automatisch abgeschaltet (deswegen ret*I*).

Da dein Programm etwas wirr ist folgender Vorschlag zum Ablauf:
1. Stack / Ports init, Timer starten, INT0 auf steigende Flanke, sei
2. loop: rjmp loop

In impuls_ch1:
0. Sreg & benötigte Register sichern
1. TCNT0 hohlen und merken, TCNT0 zurücksetzen
2. Festellen ob IRQ wegen Steigender (A) oder Fallende Flanke (B) 
(MCUCR)
3A. (0->1) auf fallende Flanke einstellen und rjmp 5.
3B. (1->0) auf steigende Flanke einstellen
4. gemerkten wert von TCNT0 auswerten, leds an/aus
5. schritt 0 rückgängig, reti
(Nur so als Vorschlag....)

Autor: Chrisopher Rohe (newguy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
puh. Ne Menge Input aber ich denke das krieg ich durchgearbeitet.

Wenn ich in C programmiere war es doch so das der uC nach dem includen 
und definieren mit void (main) beginnt. Auch wenn vorher noch ein paar 
funktionen definiert werden. Ist es in assembler ähnlich oder arbeitet 
der uC hier strikt von oben nach unten sofern keine Intterrupts und 
sprünge dazwischen funken?

Autor: Tom M. (tomm) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Chrisopher Rohe schrieb:
> Wenn ich in C programmiere war es doch so das der uC nach dem includen
> und definieren mit void (main) beginnt. Auch wenn vorher noch ein paar
> funktionen definiert werden.

So sieht es zumindest aus... ;-)

Der Einstiegspunkt wird durch den Reset-Vektor definiert. Irgendwo in 
der Compiler-Linker Kette wird dann der Vektor so eingerichtet, dass er 
auf "main" zeigt. Wird wohl auch beim ASM-Programmieren nicht anders 
sein, du definierst auch eine Marke "main", die dann nach irgendeiner 
Konvention als Einstiegspunkt gilt.

Wie das Einrichten der Reset-Vektors genau funktioniert, hab ich mir 
(noch) nicht angesehen.

Autor: Tom M. (tomm) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Chrisopher Rohe schrieb:
> .org 0x000
>   rjmp main                    ;Reset Handler
...
> main:
>     cli

Ich seh' gerade, da steckt keine Magie drin. Du hast den Reset-Vektor ja 
explizit definiert und beginnst dann gleich mit "cli" und der 
Stack-Initialisierung. Deine Frage hättest du also auch selbst 
beantworten können. 8)

Autor: Chrisopher Rohe (newguy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und schon wieder eine neue Variante meines Programms. Doch noch immer 
funktioniert es nicht. Habe versucht alle Vorschläge von Tim einzubauen. 
Lediglich "5. schritt 0 rückgängig, reti" ist mir nicht ganz klar. Hier 
der aktuellste Code:
.include "m8515def.inc"

.def impulslaenge_ch1 = r16
.def temp = r17
.def leds = r18

.org 0x00
  
  rjmp main                      ;Reset Handler

.org INT0addr

  rjmp impuls_ch1                    ;IRQ0 Handler

main:

    ldi temp, LOW(RAMEND)
   out SPL, temp
    ldi temp, HIGH(RAMEND)
    out SPH, temp                              ;Stackpointer init:

    ldi temp, 0xFF
    out DDRB, temp                            ;PortB auf Ausgang

    ldi temp, 0x00
    out DDRD, temp                            ;PortD auf Eingang
  
  ldi temp, (1<<ISC00) | (1<<ISC01)
  out MCUCR, temp                    ;INT0 auf seigende Flange einstellen
  
  sei                          ;Interrupts aktivieren

loop:

  rjmp loop                      ;leere Warteschleife

impuls_ch1:

  in temp, MCUCR                    ;MCUCR auslesen um Triggerflanke zu bestimmen
  cpi temp, 0x03                    ;Teste MCUCR auf steigende Flange
  breq sflanke                    ;wenn steigende FLanke springe zu sflanke
  rcall fflanke                    ;sonst springe zu fflanke

  pop impulslaenge_ch1                ;impulslaenge_ch1 zurückholen

  cpi impulslaenge_ch1, 125                    ;Timerwert vergleichen
     brlo is_lower                              ;Springe wenn < 125
     rcall ueber125                             ;Nicht kleiner, also grösser
     rjmp is_done                               ;Vergleich fertig

is_lower:
   rcall unter125                             ;Kleiner...
is_done:
                               ;Vergleich fertig.
  reti


sflanke:

  ldi temp, (0<<CS00) | (1<<CS01) | (0<<CS02)      ;Prescaler auf 8
  out TCCR0, temp                    ;Timer starten

  ldi temp, (0<<CS00) | (1<<CS01)            
  out MCUCR, temp                    ;INT0 auf fallende Flanke einstellen

  ret

fflanke:

  ldi temp, (0<<CS00) | (0<<CS01) | (0<<CS02)  
  out TCCR0, temp                    ;Timer stoppen
  
  in impulslaenge_ch1, TCNT0              ;Timer auslesen und Wert in impulslaenge_ch1 schreiben
  push impulslaenge_ch1                ;impulslaenge_ch1 sichern
  
  ldi temp, 0
  out TCNT0, temp                    ;Timer zurücksetzen

  ret

unter125:

  ldi leds, 0xFF
  out PORTB, leds                    ;Alle LEDs aus
  ret

ueber125:

  ldi leds, 0x00
  out PORTB, leds                    ;Alles LEDs an
  ret


Danke mal wieder für alle Tips und Vorschläge

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erkläre mir (und Dir selbst) bitte mal, wie das Programm diese Sequenz 
erreicht:

  pop impulslaenge_ch1                ;impulslaenge_ch1 zurückholen

  cpi impulslaenge_ch1, 125                    ;Timerwert vergleichen
     brlo is_lower                              ;Springe wenn < 125
     rcall ueber125                             ;Nicht kleiner, also grösser
     rjmp is_done                               ;Vergleich fertig

is_lower:
   rcall unter125                             ;Kleiner...
is_done:
                               ;Vergleich fertig.
  reti

Denn darüber steht ein bedingter und unbedingter Sprung:
  cpi temp, 0x03                    ;Teste MCUCR auf steigende Flange
  breq sflanke                    ;wenn steigende FLanke springe zu sflanke
  rcall fflanke                    ;sonst springe zu fflanke

und die folgende Sequenz hat kein Label als Sprungziel, ist also toter 
Code.

...

Autor: Chrisopher Rohe (newguy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erkläre mir (und Dir selbst) bitte mal, wie das Programm diese Sequenz
erreicht:
[code]
  pop impulslaenge_ch1                ;impulslaenge_ch1 zurückholen

  cpi impulslaenge_ch1, 125                    ;Timerwert vergleichen
     brlo is_lower                              ;Springe wenn < 125
     rcall ueber125                             ;Nicht kleiner, also grösser
     rjmp is_done                               ;Vergleich fertig

is_lower:
   rcall unter125                             ;Kleiner...
is_done:
                               ;Vergleich fertig.
  reti

Ich hatte es im Tutorial so verstanden das ein "ret" am Ende einer 
Sequenz zu der gesprungen wurde dafür sorgt, dass der uC wieder zur 
Sprungstelle zurück kehrt und dort mit dem Code weiter macht.


der bestimmte Sprung ist doch nötig damit der Vergleich ausgwertet wird. 
In diesem Falle wenn das Z-Flag gesetzt ist also der aus MCUCR Wert 
gleich 0x03 (INT0 auf steigende Flanke eingestellt)

der unbestimmte Sprung ist quasi der "else"-sprung. Das hab ich so aus 
dem Tutorial abgeleitet (Siehe Vergleiche)

btw: Kann mir jemand den Unterschied zwischen rcall und rjmp erklären?

Autor: MWS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
breq sflanke
...
sflanke:
...
ret

Sicher ? :D

Ret ohne Call ? Ein Breq ist kein Call, beim Ret werden 2 Bytes vom 
Stack in den PC gepoppt (mit ungeklärtem Ziel) und tschüss.

Relative Call und Relative Jump, beim Call wird der PC auf den Stack 
gepusht, beim Jump nicht. Bei Atmel gibt's das komplette Instruction Set 
als Pdf.

Autor: Chrisopher Rohe (newguy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sicher? Auf keinen Fall. Ich schreibe erst seid 4 Tagen Programme für 
meinen 8515.

Und was ist der PC? Wohl nicht mein Personal Computer :D

Das mit dem Stack muss ich mir noch ein paar mal durchlesen. Davon hab 
ich bisher am wenigsten verstanden. Und es scheint ja durchaus ein 
wichtiges Kapitel zu sein XD

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
PC = Program Counter

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Bei Atmel gibt's das komplette Instruction Set als Pdf.

Und auch in der mit F1 erreichbaren Online-Hilfe des AVR-Studios...

Ein Branch (BRxx) ist ein bedingter Sprung über kurzr Distanz mit 
relativer Angabe der Distanz.

Ein RJMP ist ein unbedingter Sprung über mittlere Distanz mit relativer 
Angabe der Distanz.

Ein JMP ist ein unbedingter Sprung großer Distanz mit absoluter Angabe 
der Zieladresse. Den gibt es beim AVR erst ab 16KB Flash.

Ein IJMP ist ein indizierter Sprung, dessen Zieladresse um Z-Pointer 
steht. Man kann also die Zieladresse vorher anhand einiger Bedingungen 
(z.B. Menüpunktnummer) berechnen.

Ein Skip (SBRS, SBRC, SBIS, SBIC) ist ein Sprung über den nächsten 
Befehl drüberweg, der von einer Bitprüfung in einem Register oder 
I/O-Register abhängig ist und keine Flags im SREG beeinflusst.

Alle diese Sprungbefehle (und auch CPSE) sichern sich nicht die 
Rücksprungadresse auf dem Stack und ermöglichen somit keinen Rücksprung 
per RET / RETI.

Und dann gibt es noch die Sprungbefehle, die den aktuellen 
Programmcounter auf Stack legen, damit per RET (RETI bei INT-Aufrufen) 
an die alte Stelle (also hinter den Sprungbefehl) zurückgesprungen 
werden kann. Dies wären:

RCALL

CALL

ICALL

Interrupt-Aufruf per Hardware

Einzelheiten gibt's in der Onlinehilfe zum AVR-Studio sowie im 
AVR-Instruction-set, eine gut formatierte Zusammenfassung gibt es am 
Ende des Datenblatts des jeweiligen AVRs.

...

Autor: MWS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein Breq ist ein bedingter Rjmp. Beim Call dagegen sichert der µC die 
Rücksprungadresse auf dem Stack, er schiebt dort den PC (program 
counter) als 2 Bytes rauf, beim Ret holt er sich die 2 Bytes wieder und 
lädt sie in den PC zurück. Der Stack arbeitet nach dem LIFO Prinzip, 
last in first out.

Da aber ein Breq nix auf den Stack sichert, ist auch keine gültige 
Rücksprungadresse auf dem Stack, sondern Irgendetwas.

Speziell in Deinem Code, da diese Routine in einer ISR steht, besteht 
dieses Irgendetwas zufälligerweise aus der Rücksprungadresse der ISR. 
Die würdest Du eigentlich erst beim Reti wieder anspringen wollen.

Nun hat das Ret gegenüber dem Reti einen entscheidenden Unterschied: Das 
Reti setzt wieder das global interrupt flag im Statusregister des µC, 
welches beim Start der ISR automatisch gelöscht wurde. Das Ret dagegen 
setzt es nicht mehr.

Mal abgesehen davon, daß Du an der falschen Stelle die ISR verlässt, 
sind zusätzlich ab diesem Zeitpunkt keine weiteren Interrupts mehr 
möglich, eben wegen gesperrten Interrupt Flag.

Autor: Marvin M. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin,

und bedenke, dass Du mit push und pop den gleichen Stack benutzt, in den 
auch die Rücksprungadressen für Unterprogramme und Interrupts 
geschrieben werden.
Wenn Du µC schonmal in C programmiert hast, wieso machst Du den Schritt 
zu Assembler zurück?
Tipp: Back erstmal kleinere Brötchen und lass mal eine LED blinken (ohne 
Interrupts), dann langsam steigern. Zum Ausprobieren würde ich alles 
komplett erstmal ohne Interrupts machen, bis das Gefühl für Stack, calls 
und Nutzung von Speicher/Registern da ist.
Wenns ohne Interrupt dann funktioniert, kann man es immer noch 
verschlimmbessern ;)

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Chrisopher Rohe schrieb u.A.:
> Sicher? Auf keinen Fall. Ich schreibe erst seid 4 Tagen Programme für
> meinen 8515.

Dann wäre es vermutlich doch hilfriech, Dir die Befehlsliste (Datenblatt 
des AVRs) auszudrucken und damit ASM-Quelltexte anderer Leute zu 
analysieren.

...

Autor: Chrisopher Rohe (newguy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marvin M. schrieb:
> Wenn Du µC schonmal in C programmiert hast, wieso machst Du den Schritt
> zu Assembler zurück?

Das letzte Mal das ich einen µC programmiert habe war im Rahmen eines 
Roboterwettbewerbes. Damals hatte ich lediglich 3 Wochen Zeit und der µC 
war das Hirn eines Asuros. Also schon gut vorbereitet. Und die 
programmierten Funktionen waren wesentlich einfacher.

Jetzt möchte ich aber richtig in das Thema einsteigen. Daher wollte ich 
bei Null anfangen. Und in dem Tutorial hier steht das man mit Assembler 
anfangen sollte um die Arbeitsweise besser verstehen zu können.



Hannes Lux schrieb:
> Dann wäre es vermutlich doch hilfriech, Dir die Befehlsliste (Datenblatt
> des AVRs) auszudrucken und damit ASM-Quelltexte anderer Leute zu
> analysieren.


Das Problem, das ich dabei sehe, ist, dass jeder seinen eigenen "Stil" 
hat. Und es ist auch was anderes ein funktionierendes Programm zu 
"lesen". Dass man es dann auch "verstanden" hat sehe ich nicht immer als 
gegeben. Zumindest bei mir ist es so. Ich bin eher der Praktiker der was 
macht und dann fragt warum es nicht funktioniert. Wie man m Verlaufe 
dieses Threads vielleicht schon erkennen kann

Marvin M. schrieb:
> Tipp: Back erstmal kleinere Brötchen und lass mal eine LED blinken (ohne
> Interrupts), dann langsam steigern.

Das wird wohl der Tipp dieses Threads. Ich hatte es mir ehrlich 
einfacher vorgestellt. Die Geschichte mit dem Stack ist echt ne Nummer 
für sich. Auch wenn meine Kenntnisse im C-Programmieren auch eher gegen 
Null laufen kommt es mir so vor als wäre Assembler schwieriger.

Was würdet ihr einem Anfänger raten? C oder Assembler?

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>> Dann wäre es vermutlich doch hilfriech, Dir die Befehlsliste (Datenblatt
>> des AVRs) auszudrucken und damit ASM-Quelltexte anderer Leute zu
>> analysieren.


>Das Problem, das ich dabei sehe, ist, dass jeder seinen eigenen "Stil"
>hat. Und es ist auch was anderes ein funktionierendes Programm zu
>"lesen".

Das ist wirklich manchmal ein Problem. Fehler in eigenen finde ich meist 
auch wesentlich schneller, als in fremden Programmen.

>Was würdet ihr einem Anfänger raten? C oder Assembler?

Kommt darauf, wer antwortet. Aber auch für C-Programmierer ist es 
manchmal hilfreich den vom Compiler erzeugten Assemblercode zu 
verstehen.

MfG Spess  (notorischer Assemblerprgrammierer)

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Die Geschichte mit dem Stack ist echt ne Nummer für sich.

Überhaupt nicht...

Der Stack ist ein Stück RAM (SRAM), das einen eigenen Zeiger (den 
Stackpointer) hat, der beim AVR nach unten (zur kleineren Adresse hin) 
wächst, wenn man etwas auf den Stack legt. Daher initialisiert man den 
Stackpointer (SPL, SPH) zweckmäßigerweise auf die letzte verfügbare 
SRAM-Adresse.

Es gibt nun Befehle, die etwas auf den Stack legen ("parken") und 
welche, die etwas vom Stack herunternehmen. Sie aktualisieren dabei den 
Stackpointer per Hardware, da musst Du Dich also nur bei der 
Initialisierung drum kümmern. Wichtig ist allerdings, dass sich das 
Drauflegen und Herunternehmen die Waage halten, da es sonst zu einem 
Stacküberlauf kommt, der zum Absturz des AVRs führt.

Folgende Befehle nutzen den Stack:

PUSH (legt ein Byte auf den Stack)

POP (holt ein Byte vom Stack)

CALL, RCALL, ICALL (legt die Rücksprung-Adresse auf den Stack und 
springt)

RET (nimmt die Rücksprungadresse vom Stack in den ProgrammCounter)

Int-Aufruf (legt die Rücksprungadresse auf den Stack und springt zur 
entsprechenden Adresse in der Interrupt-Sprungtabelle, in der dann Dein 
(R)JMP zur ISR (Interrupt-Service-Routine) steht, löscht dabei das 
I-Flag im SREG)

RETI (holt die Rücksprungadresse vom Stack in den ProgrammCounter und 
setzt das I-Flag im SREG)

Damit der als Stack benutzte Teil des SRAMs nicht mit den anderen im 
SRAM gehaltenen Variablen kollidiert, legt man die normalen Variablen an 
den Anfang des SRAMs, während der Stack am Ende beginnt.

Hochsprachen brauchen noch einen separaten Stack zur Übergabe und 
Rückgabe von Parametern zu Funktionen. Dieser wird dann meist mit dem 
X-Pointer realisiert.

Charakteristisch für einen Stack (Stapelspeicher, Kellerspeicher, Lifo) 
ist die Tatsache, dass man immer nur das zuletzt hineingelegte Byte 
herausholen kann. Bei einem Fifo (Ringpuffer) ist das umgedreht.

...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.