Forum: Mikrocontroller und Digitale Elektronik Fehlersuche AVR-assembler


von Stephan E. (loetzinn02)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

in meiner Freizeit programmiere ich gerne mit kleinen Mikrocontrollern 
(bislang ausschließlich 8-Bit-AVR), dabei verwende ich C und Assembler.
Für letzteres verwende ich als Umgebung bzw. Simulator "Gerds 
AVR-Simulator" 
(http://www.avr-asm-tutorial.net/avr_sim/avr_sim-download.html). Leider 
hat dieser ein paar Probleme, insbesondere der Watchdog sowie der 
Schlafmodus scheinen im Simulator nicht zu funktionieren und es gibt 
noch ein paar andere Unzulänglichkeiten. Dennoch ist dieser Simulator 
zumindest für mich überaus hilf- und lehrreich. Mangels Windows kann ich 
das AVR-Studio nicht nutzen.

Allerdings bin ich jetzt an einem Punkt, an dem ich nicht weiterkomme.
Anbei ein kleines Assembler-Programm welches zwar erst mal funktioniert 
wie es soll, aber nach einer unvorhersehbaren Zeit den Betrieb 
einstellt. D.h. die LEDs bleiben in einem Status stecken und auf den 
Taster erfolgt ebenfalls keine Reaktion mehr. Manchmal nach wenigen 
Sekunden, manchmal erst nach einer halben Stunde oder länger.
Nach einem Reset ist alles wieder normal.
Laut Tutorial kommen solche Fehler dann vor, wenn ein Register oder ein 
Flag im Statusregister von anderer Stelle überschrieben wird.

Ich habe in allen Interrupthandlern das Statusregister sowie andere 
verwendete Register gesichert und wiederhergestellt. Bei den Subroutinen 
werden auch die Register auf den Stack geschoben und wieder von dort 
abgeholt.

Ich habe das Gefühl daß es irgendwo in den Interrupts hakt, denn das 
Problem tritt unabhängig von der Programmnummer (siehe Quelltext) auf. 
Ich habe sie -zig mal durchgeschaut, finde dort aber leider nicht die 
mögliche Ursache (sofern sie überhaupt dort liegt).
Leider habe ich weder Debugger noch sonst irgendwelche bessere 
Möglichkeiten als den eingangs erwähnten Simulator, welcher mir keine 
Probleme gezeigt hat.

Vielleicht kann hier ein Profi mal drüberschauen und mir einen Tipp 
geben?

Anbei der vollständige Quellcode. Er läuft auf einem attiny13a, welcher 
den internen 128KHz-Oszillator verwendet. Angeschlossen sind neben dem 
ISP-Programmer, welcher auch die Stromversorgung übernimmt, 100n 
zwischen VCC und GND sowie zwei LEDs mit passenden Vorwiderständen an 
PB0 bzw. PB1 und ein Taster an PINB4.

von Harald K. (kirnbichler)


Lesenswert?

Der attiny13 lässt sich mit DebugWire ansteuern; d.h. Du könntest mit 
einem Debugger nachsehen, was Dein Programm treibt, wenn es sich so 
verhält.

DebugWire nutzt beim Tiny13a PB5, was normalerweise der Reset-Pin ist.

von Volker B. (Firma: L-E-A) (vobs)


Lesenswert?

Stephan E. schrieb:

> Anbei ein kleines Assembler-Programm welches zwar erst mal funktioniert
> wie es soll, aber nach einer unvorhersehbaren Zeit den Betrieb
> einstellt.

Du solltest nicht für main() bzw. die von dort aufgerufene(n) 
Funktion(en) und für die Interrupthandler das selbe Register zum sichern 
der Statusflags nutzen -- oder zumindest direkt vor dem Sichern der 
Flags aus main() heraus die Interrupts sperren.

Grüßle,
Volker

: Bearbeitet durch User
von S. L. (sldt)


Lesenswert?

> Du solltest nicht ...

Denn am Ende von 'write2eeprom' wird u.U. (per rSreg) der Inhalt von 
SREG aus einer ISR geladen, d.h. mit gesperrtem 'Global Interrupt Flag'.

Nur am Rande: weshalb soll überhaupt in 'write2eeprom' SREG gesichert 
werden?

von Stephan E. (loetzinn02)


Lesenswert?

> Nur am Rande: weshalb soll überhaupt in 'write2eeprom' SREG gesichert
> werden?

Stimmt, Du hast Recht - mein Fehler.

@kirnbichler - von DebugWire habe ich ehrlich gesagt noch nie gehört und 
mir den hiesigen Artikel mal angeschaut. Das klingt sehr interessant, 
werde ich mir genauer anschauen.

@vobs - auch an Dich danke für den Hinweis, das werde ich testen.

von Volker B. (Firma: L-E-A) (vobs)


Lesenswert?

Stephan E. schrieb:

> @vobs - auch an Dich danke für den Hinweis, das werde ich testen.

Die Interrupts dann aber auch erst wieder freigeben, wenn das 
Statusregister zurückgeschrieben wurde.

Grüßle,
Volker

P.S.: Eine Hochsprache, wie C, hat doch auch ihre Vorteile :-)

von Martin J. (martiko)


Lesenswert?

Deine EEPROM-Write-Verwaltung mit PROGRAMHASCHANGED und SAVENEEDED halte 
ich für unnötig kompliziert.

Warum nicht so: Bei jedem Main-Durchlauf wird geprüft, ob gerade ein 
EEPROM-Schreibvorgang läuft. Wenn ja: Nichts machen; wenn nein, 
folgendes machen: Das EEPROM-Datenbyte auslesen (das kostet keine Zeit 
und nutzt die EEPROM-Zelle nicht ab) und mit programmnummer vergleichen. 
Wenn gleich: Nichts machen; wenn ungleich: EEPROM-Schreibvorgang mit der 
aktuellen programmnummer triggern.

Irgendwelche Wartereien auf die Beendigung eines EEPROM-Schreibvorgangs 
sind dann überflüssig, also verzichte darauf und nutze den sich daraus 
ergebenden Vorteil aus: Du kannst alles prima in einem Timer-Interrupt 
mit konstanter Periode unterbringen, wo die Interrupts sowieso gesperrt 
sind.

von Stephan E. (loetzinn02)


Lesenswert?

Martin J. schrieb:

> Warum nicht so: Bei jedem Main-Durchlauf wird geprüft, ob gerade ein
> EEPROM-Schreibvorgang läuft. Wenn ja: Nichts machen; wenn nein,
> folgendes machen: Das EEPROM-Datenbyte auslesen (das kostet keine Zeit
> und nutzt die EEPROM-Zelle nicht ab) und mit programmnummer vergleichen.

"Main" läuft ohnehin nur ein mal, danach kommt der eigentliche Loop.
Dort muß ich nur ein Bit prüfen und anhand dem wird entschieden, ob die 
programmnummer geschrieben werden muß.

Das mit dem Vergleich der programmnummer wollte ich tatsächlich noch 
einbauen, aber erst nachdem alles andere erst mal ordentlich läuft.
Und das scheint es jetzt zu tun. Zumindest ist der Controller bis jetzt 
nicht mehr hängen geblieben (Sicherung des Statusregister aus 
write2eeprom entfernt sowie alle Interruptfreigaben/-sperren nochmal 
überprüft).


> Irgendwelche Wartereien auf die Beendigung eines EEPROM-Schreibvorgangs
> sind dann überflüssig, also verzichte darauf und nutze den sich daraus
> ergebenden Vorteil aus: Du kannst alles prima in einem Timer-Interrupt
> mit konstanter Periode unterbringen, wo die Interrupts sowieso gesperrt
> sind.

Das hingegen ist ein gutes Argument für Deine Vorgehensweise. Danke für 
den Denkanstoß.


@vobs:
Ja, C wird vermutlich spätestens dann praktisch alternativlos, wenn es 
mal um komplexere Dinge geht. Aber ich muß sagen, daß ich Assembler ein 
ganzes Stück weit interessanter und faszinierender finde.
Man schiebt halt wirklich extrem hardwarenah die einzelnen Bits umher - 
Schritt für Schritt, genau so wie es der Mikrocontroller dann 
abarbeitet. Bei C laufen die Sachen mehr oder weniger unsichtbar im 
Hintergrund.

Mir geht und ging's bei meinem Hobby in erster Linie darum, den 
Mikrocontroller so gut zu verstehen wie es eben für Laien machbar ist. 
Danach kann ich immer noch mein bißchen C vertiefen. Der Weg ist das 
Ziel.

Wie auch immer - meinen Dank an alle, die geantwortet haben!

von Carsten-Peter C. (carsten-p)


Lesenswert?

Moin,
ich schreibe auch gerne rein aus Hobby in Assembler. Eine Möglichkeit, 
den Fehler zu finden, ist eine kleine Erweiterung in Deiner 
Hauptschleife zu schreiben, um z.B. auf PB2 eine LED zum Blinken zu 
bringen. Als Indikator. Danach nacheinander rcall’s ausschalten bzw. 
zuschalten, bis der Fehler weg ist. Das Programm braucht ja dafür nicht 
mehr zu funktionieren, aber Du siehst, wo es hängen bleibt. Du nutze 
viele Register, wie z.B. r15, um sreg zu sichern. Kann man so machen. 
Ist ja auch schneller, aber wenn die Register mal knapp werden kann man 
das auch auf den Stack packen.
  push r16
  in r16,SREG
  push r16
  push r17
;tuwas
  pop  r17
  pop  r16
  out  SREG,r16
  pop  r16
  reti
Ich versuche  immer die Register ab r16 nicht zu blocken. Jeder, wie er 
möchte.
Gruß
Carsten

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Stephan E. schrieb:

> Ja, C wird vermutlich spätestens dann praktisch alternativlos, wenn es
> mal um komplexere Dinge geht.

Ja, sicher, irgendwann ist die Grenze erreicht, wo es zu komplex wird, 
um eine Sache komplett in Assembler abzuhandeln.

Aber auf einem Tiny13 ist es praktisch unmöglich, dass ein Programm 
diese Komplexitätsgrenze erreicht. Der hat nur 1kB Flash, da passen 
schon rein theoretisch maximal 512 AVR8-Assembler-Instruktionen rein. 
Praktisch werden es wegen konstanter Daten, die ja auch im Flash liegen 
müssen, eher weniger sein. Sachen in diesem Umfang sind noch ziemlich 
problemlos überschaubar.

Bei so knappen Resourcen wie bei einem Tiny13 schafft die Verwendung 
einer Hochsprache eher Probleme, als dass es sie zu lösen vermag.

von Stephan E. (loetzinn02)


Lesenswert?

Ob S. schrieb:
> Aber auf einem Tiny13 ist es praktisch unmöglich, dass ein Programm
> diese Komplexitätsgrenze erreicht. Der hat nur 1kB Flash, da passen
> schon rein theoretisch maximal 512 AVR8-Assembler-Instruktionen rein.

Ich bin immer wieder erstaunt, was sich mit 1k Flash alles erreichen 
läßt, und genau das macht für mich die Faszination aus und freue mich 
wie ein kleines Kind, wenn hinterher alles so funktioniert wie ich mir 
das vorgestellt habe. :)

Als Experiment habe ich mal verglichen - wenn ich über c die 
rand()-Funktion der stdlib verwende, ist die Hälfte des Flashs belegt, 
nur damit ich eine pseudo-Zufallszahl bekomme.
Ich weiß nicht, was die rand-Funktion genau wie macht - aber in 
Assembler bekomme ich meine pseudo-Zufallszahlen über ein 32-bit-lfsr 
mit wesentlich weniger Flash (es waren sechs, sieben Prozent belegt, 
wenn ich mich recht erinnere)

von Oliver S. (oliverso)


Lesenswert?

Stephan E. schrieb:
> Ich weiß nicht, was die rand-Funktion genau wie macht - aber in
> Assembler bekomme ich meine pseudo-Zufallszahlen über ein 32-bit-lfsr
> mit wesentlich weniger Flash

Wenn die stdlib-funktion zufällige Äpfel, deine aber zufällige Birnen 
liefert, hinkt das halt heftig, und das nicht nur zufällig.

Oliver

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.