Es geht um einen attiny13a, welcher ein Lauflicht aus sechs LEDs (Charlieplexing) abhängig von der Umgebungshelligkeit ein- oder ausschaltet.
Anbei ein handgezeichnetes Schaltbild.
Die Schaltung existiert zwei mal. Einmal auf einem Steckbrett und einmal fest verlötet auf einer Lochrasterplatine. Erstere wird direkt aus dem AVR-Programmer mit Strom versorgt (3.3V), letztere aus einem kleinen einzelligen Li-Ion-Akku, welcher wiederum an einem TP4056-Modul hängt.
Für Tests ist zusätzlich ein rs232-Pegelwandler-Modul (MAX3232) angeschlossen: Gnd und Vss direkt vom Programmer bzw. Akku, PB3 als serieller Ausgang des Mikrocontrollers (bitbanging). Zwischen Gnd und Vcc ist ein 100n-Keramikkondensator, zwischen Gnd und PB4 eine Fotodiode mit der Kathode an Gnd und der Anode am PB4.
Das Programm ist auf beiden Umgebungen dasselbe (siehe Anhang, Kommentare stehen direkt im Quellcode. Entwickelt wurde das ganze im avr-Simulator von Gerhard Schmidt).
Wenn ich das Programm starte (egal ob Steckbrett oder Lochraster), funktioniert es so wie erwartet. Das Lauflicht wird abhängig von der Helligkeit ein- bzw. ausgeschaltet. Den ADC-Wert bekomme ich alle zehn Sekunden am seriellen Ausgang ausgegeben, alles gut so weit.
Allerdings hängt sich der Mikrocontroller nach einiger Zeit auf. Manchmal erst nach mehreren Stunden, manchmal nach schon vielleicht 30 Minuten. Konkret reagiert er nicht mehr, wenn es dunkel oder hell wird, und es kommen auch keine seriellen Daten mehr raus. Ich weiß leider nicht, warum. Ich vermute einen Programmfehler, denn das Problem tritt sowohl auf dem Steckbrett als auch der Lochrasterplatine im fest verlöteten Zustand auf, allerdings nach nicht reproduzierbaren Zeitabständen.
Einen internen Pullup haben die AVRs. Ist nicht gerade extrem störsicher, aber sollte funktionieren – insbesondere kann das eher nicht zum beobachteten Verhalten (Firmware fährt sich fest) führen, sondern würde ja eher zu einem Reboot führen.
Watchdog ist standardmäßig aus.
Ich habe allerdings keine Lust, mich durch 500 Zeilen Assemblercode zu wuseln.
Der Watchdog ist als "interrupt-only" konfiguriert, er macht keinen reset. Er wird alle 0.125 Sekunden aufgerufen. Danach läuft die Hauptschleife ein mal durch und geht am Ende dieser Schleife in den Schlafmodus. Bis zum nächsten aufwecken durch den Watchdog.
Nicht jeder µC mag es, wenn sein Reset-Pin frei in der Luft hängt.
Ziehst du den durch einen internen Pull-Up definiert auf High-Pegel?
Der Reset-Pin hängt auf der verlöteten Lochraster-Platine tatsächlich in der Luft. Am Programmer ist der Reset-Pin aber angeschlossen und wird mutmaßlich durch den Programmer oben gehalten.
Einen internen Pullup haben die AVRs. Ist nicht gerade extrem
störsicher, aber sollte funktionieren – insbesondere kann das eher nicht
zum beobachteten Verhalten (Firmware fährt sich fest) führen, sondern
würde ja eher zu einem Reboot führen.
Watchdog ist standardmäßig aus.
Ich habe allerdings keine Lust, mich durch 500 Zeilen Assemblercode zu
wuseln.
Das würde ich natürlich auch nicht verlangen. Allerdings sieht das Forum vor, daß Quellcodes mitgeliefert werden - falls doch mal jemand einen Blick riskieren möchte.
Vielleicht hat jemand mit Erfahrung einen Tipp für mich, der beim beschriebenen Problem der übliche Verdächtige sein könnte? Ggf. gibt's auch eine KI, die den Code mal durchschauen könnte.
µC zusätzlich zum 100n KeKo noch mit einem kleinen Elko (Richtwert 47µ) puffern
Reset mit RC Glied 10n gegen Masse und 10K gegen Ub beschalten
serielle Schnittstelle mal probeweise im Code auskommentieren. Der Tinny hat die serielle Schnittstelle nicht in Hardware, sondern sie muß in SW emuliert werden. Da gibt es Implementierungen, die hängen sich gern auf.
die Fotodiode könnte man auch noch durch ein kleines RC-Glied beruhigen
Immerhin hat er nicht nur Prosa, sondern überhaupt einen Schaltplan, der kein Wimmelbild ist und keine 6 MB groß ist. Also fast alles richtig gemacht ;-)
Am Programmer ist der Reset-Pin aber angeschlossen und wird mutmaßlich
durch den Programmer oben gehalten.
Das wäre aber sehr ungewöhnlich. Wenn der Programmieradapter inaktiv ist, dann ist der Pin am Programmieradapter normalerweise hochohmig. Und dann reichen ein paar cm Leitung schon zum Empfang von Radiowellen.
Der interne Pullup des Mikrocontrollers ist schwach, zudem reagiert er wesentlich empfindlicher als normale I/O Pins, da er anders definierte High/Low Pegel hat.
serielle Schnittstelle mal probeweise im Code auskommentieren. Der Tinny
hat die serielle Schnittstelle nicht in Hardware, sondern sie muß in SW
emuliert werden. Da gibt es Implementierungen, die hängen sich gern auf.
Wäre natürlich ein Punkt.
Wenn Stephan einen Logikanalysator hat, könnte man damit ein paar Testmuster stattdessen ausgeben und so ungefähr nachvollziehen, an welchen Stellen die Software noch lang kommt, bevor sie klemmen bleibt.
µC zusätzlich zum 100n KeKo noch mit einem kleinen Elko (Richtwert
47µ) puffern
Reset mit RC Glied 10n gegen Masse und 10K gegen Ub beschalten
serielle Schnittstelle mal probeweise im Code auskommentieren. Der
Tinny hat die serielle Schnittstelle nicht in Hardware, sondern sie muß
in SW emuliert werden. Da gibt es Implementierungen, die hängen sich
gern auf.
die Fotodiode könnte man auch noch durch ein kleines RC-Glied
beruhigen
Vielen Dank für die Tipps! Ich werde sie allesamt ausprobieren, testen und mich dann nochmal melden.
@Jörg W.: Einen Logikanalysator besitze ich leider nicht. Ich bin nur ein kleiner Hobbybastler ohne nennenswerte Ausstattung (hat man ja schon am Schaltbild gesehen....).
Einen Watchdog in der ISR zu bedienen, ist zu 95% kontraproduktiv. Es kann nämlich durchaus sein, das das Hauptprogramm hängt, die WD ISR aber munter weiterläuft.
Wenn ich mir das Programm so anschaue - warum einfach, wenns auch kompliziert geht?
Einen Watchdog in der ISR zu bedienen, ist zu 95% kontraproduktiv.
Ist halt ein Timer mehr – nicht als Watchdog im eigentlichen Sinne verwendet. Der ATtiny13 hat ja nun nicht so eine üppige Hardware-Ausstattung. (Allerdings würde ich ihn schon deshalb nicht unbedingt hobbymäßig nehmen. Es gibt bessere Alternativen, die deutlich mehr Hardware-Komfort bieten, auch bei AVR.)
Habe mir die Frechheit herausgenommen, das Gerhard Schmidt'sche Programm einmal ins Studio 4 zu laden. (Ja, der mit dem Tutorial. Die Webseite ist aber jetzt anderweitig vergeben, Links stimmen nicht mehr.)
Also Studio 4 Simulator bleibt schon direkt in der Loop hängen.
Rein syntaxmäßig sieht es ja recht gut aus:
ATtiny13A memory use summary [bytes]:
Segment Begin End Code Data Used Size Use%
[.cseg] 0x000000 0x00022c 556 0 556 1024 54.3%
[.dseg] 0x000060 0x00006f 0 15 15 64 23.4%
[.eseg] 0x000000 0x000000 0 0 0 64 0.0%
0 Errors 0 Warnings.
Tipp:
Wirf den Watchdog raus, und programmiere "vernünftige" Zeitschleifen.
(Wurde oben schon angedeutet.)
Einfach mal die Scheuklappen abnehmen und über den Teller-Rand
hinausschauen ....
Muss man nichtmal, schrieb ich aber schon. Bereits ein ATtiny25 (fast genauso uralt wie der originale ATtiny13) würde schon deutlich mehr bieten. Gut, wenn es unbedingt ein DIP-8 als Gehäuse sein muss, dann hört es bei diesen (und den größeren Geschwistern des 25) auf. Wenn es auch ein SOIC-8 sein darf (um bei 8 Pins zu bleiben), dann hätte man mit ATtiny202 oder ATtiny212 dann immerhin schon MCUs, die man mit UPDI viel einfacher programmieren kann als die alten AVRs mit ISP – und selbst bei Reichelt sind die nur halb so teuer wie ein ATtiny13A.
Vielleicht hat jemand mit Erfahrung einen Tipp für mich, der beim
beschriebenen Problem der übliche Verdächtige sein könnte?
Ziemlich wahrscheinlich Konflikte bei der Registernutzung. Warum? Wenn schon der Kommentar zur Registernutzung offensichtlich widersprüchlich ist, dann wird's im Programm selber sicher nicht ordentlicher zugehen...
Er ist schrecklich (vor allem extrem pixelig klein skaliert und mit
schlechtem Kontrast), aber die wesentlichen Details enthält er schon.
Sind Versorgungsspannung kein "wesentliches Detail"?
Von einem Schaltplan erwarte man z.B. auch, dass man die Funktion von Signalen anhand einer sinnvoll gewählten Bezeichnung erkennen kann und nicht so einen locker, mit einer Leitung an irgendeinen Pin angeklöppelten Max32?2, der ansonsten bezugslos in der Luft hängt :-(
Sprichst mir aus dem Herzen:
Was ich bei Gerhard S. Programmen so "schrecklich" finde, sind diese Abkürzungen in "Denglisch".
Beim seinem DCF77 Decoder-Programm sind sogar ein paar Registerdefinitionen im Deklarationsteil angegeben, die im Programm selbst garnicht mehr auftauchen, also hätte man sie ganz weglassen können.(Das Programm, das mit steigenden und fallenden Flankeninterrupts arbeitet, und wahrscheinlich deswegen nicht mehr startet, wenn nach einer Empfangspause das Signal wieder decodierfähig wäre.)
Mir drängt sich der Verdacht auf, dass jeder ASM-Programmierer eben so seinen ganz persönlichen "Stil" pflegt. Nicht unbedingt für "Bastler" schnell zu durchschauen.
Vielleicht hat jemand mit Erfahrung einen Tipp für mich, der beim
beschriebenen Problem der übliche Verdächtige sein könnte?
Ziemlich wahrscheinlich Konflikte bei der Registernutzung. Warum? Wenn
schon der Kommentar zur Registernutzung offensichtlich widersprüchlich
ist, dann wird's im Programm selber sicher nicht ordentlicher zugehen...
Das habe ich auch vermutet. Ich bin das gesamte Programm schon mehrmals Schritt für Schritt durchgegangen, finde das Problem aber leider nicht.
Der etwas weiter oben erwähnte Pullup am Reset-Pin hat das Problem leider nicht behoben. Der Test ohne die serielle Ausgabe läuft noch.
Sind Versorgungsspannung kein "wesentliches Detail"?
Wenn völlig offensichtlich ist, wo das rankommt, ist es nicht mehr wirklich wesentlich. Wenn nur ein Signal zu einem MAX23x geht, kann es auch nur TxD sein.
Wenn es
auch ein SOIC-8 sein darf (um bei 8 Pins zu bleiben), dann hätte man mit
ATtiny202 oder ATtiny212 dann immerhin schon MCUs, die man mit UPDI viel
einfacher programmieren kann als die alten AVRs mit ISP – und selbst bei
Reichelt sind die nur halb so teuer wie ein ATtiny13A.
Ich bleibe lieber bei DIP. SMD ist für meine zwei linken Hände nur sehr bedingt geeignet. Trotzdem wollte ich mir die Series 0 mal irgendwann genauer anschauen - aber nicht heute. Die Kosten für einen Controller stufe ich für meine reinen Hobby- und Freizeitzwecke als eher vernachlässigbar ein. Da geht's in erster Linie um Lernerfolge und den Spaß am reinen "doing".
Ich bleibe lieber bei DIP. SMD ist für meine zwei linken Hände nur sehr
bedingt geeignet.
SOIC mit 1,27 mm Pinraster ist ja nun für SMD-Verhältnisse noch richtig grobschlächtig. Auch Adapter von da auf DIP-8 gibt es ja zur Genüge.
Die einfachere Programmiermöglichkeit via UPDI ist auf jeden Fall angenehm, im Prinzip brauchst du nicht mehr als einen USB-Seriell-Wandler und zwischen TxD und RxD noch einen Widerstand von 1 kΩ.
Die einfachere Programmiermöglichkeit via UPDI ist auf jeden Fall
angenehm, im Prinzip brauchst du nicht mehr als einen
USB-Seriell-Wandler und zwischen TxD und RxD noch einen Widerstand von 1
kΩ.
Wenn die Versorgungsspannung des µC passt. Ansonsten halt noch einen Pegelwandler, bidirektional.
Ansonsten muss man sie passend machen :-) oder einen USB-Seriell-Wandler mit passender Spannung suchen. Zumindest 3,3 V und 5 V sind ja beide gängig, teilweise kann man sie auch umjumpern oder umlöten.
Ich habe aber auch schon einen FTDI mit 5 V genommen und einfach eine rote LED vor die Spannungszuführung geschaltet, weil ein auf der Schaltung befindlicher Sensor nicht mehr als 3,6 V haben möchte. Theoretisch ist dann zwar der Signalpegel von der MCU zum Wandler etwas niedrig, funktioniert aber trotzdem.
Ich habe aber auch schon einen FTDI mit 5 V genommen und einfach eine
rote LED vor die Spannungszuführung geschaltet, weil ein auf der
Schaltung befindlicher Sensor nicht mehr als 3,6 V haben möchte.
Theoretisch ist dann zwar der Signalpegel von der MCU zum Wandler etwas
niedrig, funktioniert aber trotzdem.
Schweizer Taschenmesser und Kaugummi geht bestimmt auch. ;-)
Ansonsten muss man sie passend machen :-) oder einen USB-Seriell-Wandler
mit passender Spannung suchen. Zumindest 3,3 V und 5 V sind ja beide
gängig, teilweise kann man sie auch umjumpern oder umlöten.
Oder einfach den Wandler vom Target aus speisen. Der olle FT232 kann 1,8-5V IO-Spannung. Problem gelöst.
serielle Schnittstelle mal probeweise im Code auskommentieren.
Wollte nochmal Rückmeldung geben - seit die serielle Ausgabe deaktiviert ist, keine Probleme mehr. Ich muß leider zugeben, daß ich nicht ganz nachvollziehen kann, warum das so ist, aber in der Praxis funktioniert jetzt alles so wie geplant.
Mein üblicher Rat wäre: Modularisieren!
Module einzeln ausgiebig testen.
Die serielle Ausgabe habe ich unverändert schon in vielen anderen Programmen verwendet - sie hat nie Probleme gemacht. Ich vermute, daß sich vielleicht die Interrupts in die Quere kommen. Ich werde dort weiter nachforschen.
Die serielle Ausgabe habe ich unverändert schon in vielen anderen
Programmen verwendet - sie hat nie Probleme gemacht. Ich vermute, daß
sich vielleicht die Interrupts in die Quere kommen. Ich werde dort
weiter nachforschen.
Möglicherweise hängt dein nicht funktionierender Watchdog, wenn die Übertragung im ungünstigen Moment startet oder zu lange dauert.
Schon witzig, ein Programm mit Watchdog, das festhängt. Finde den Fehler.
Schon witzig, ein Programm mit Watchdog, das festhängt.
Falk, lass dich doch nicht von dem Wort "Watchdog" ins Bockshorn jagen. Das ist einfach nur ein Timer, der 8mal in der Sekunde einen Interrupt triggert. Mit einem klassischen Watchdog, der den Programmfluss überwacht und ggf. einen Reset triggert, hat das nichts zu tun.
Ich rätsele gerade noch, wo 9k6 8N1 vorkommen als
Übertragungsparameter.
Das label serialsend ist wohl exakt auf die Taktfrequenz genagelt.
ciao
gustav
Bei einer reinen Software-UART gehts nicht halt nicht anders.
Aus power down bräucht es einen Watchdog-Reset, einen brown-out-Reset,
oder INT0 bzw pin change Interrupts zum wieder aufwecken.
Du solltest das Datenblatt richtig lesen lernen. ;-)
Ja, mit "watchdog" war was (und natürlich befreit jeder Reset ihn aus dem Schlaf) – aber neben INT0 / PCINT kann eben auch ein Watchdog-Interrupt wieder aufwecken, und einen solchen hat er 8mal pro Sekunde.
Ansonsten wäre es ja auch völlig unlogisch, dass die Firmware überhaupt erstmal läuft und erst nach längerer Zeit einfriert.
Ich sag mal, daß das Programm an der Stelle hier endgültig zu Ende sein
wird:
sbr rmp, (1<<SM1)|(1<<SE) ; sleep-mode: powerdown
out MCUCR, rmp
loop_ende:
sleep
Aus power down bräucht es einen Watchdog-Reset, einen brown-out-Reset,
oder INT0 bzw pin change Interrupts zum wieder aufwecken.
Der Watchdog-Interrupt (kein reset) reicht. Und der macht das ja auch.
Ich verwende den Watchdog nicht als Watchdog, sondern nur als zweiten timer, welcher einen Interrupt auslöst. Die loop-Schleife wird somit jede 0.125 Sekunden ein mal durchlaufen.
Ich rätsele gerade noch, wo 9k6 8N1 vorkommen als
Übertragungsparameter.
Das label serialsend ist wohl exakt auf die Taktfrequenz genagelt.
ciao
gustav
Bei einer reinen Software-UART gehts nicht halt nicht anders.
Genau so ist es. Ist ja auch nur bitbanging, einen "echten" seriellen Port hat der Controller nicht.
Als ob andere Hersteller keine Ungenauigkeiten in Datenblättern hätten … aber warum reichst du dann nicht einen Bugreport bei Microchip ein? Schließlich haben sie ein Errata-Dokument, das auch Datenblatt-Korrekturen beinhalten kann.
Real programmers can write FORTRAN in any language.
;-)
Danke Jörg,
ja das fällt mir gerade mein abgeschlossenens Studium ein,
da hatten wir gleich im 1 oder 2 Semester eine FORTRAN Klausur.
Man musst sich noch um solche Vorlesungen bewerben und konnte sie nicht hören, wenn man wollte..
Der Watchdog-Interrupt (kein reset) reicht. Und der macht das ja auch.
Ich verwende den Watchdog nicht als Watchdog, sondern nur als zweiten
timer, welcher einen Interrupt auslöst. Die loop-Schleife wird somit
jede 0.125 Sekunden ein mal durchlaufen.
Und du hoffst, dass es niemals dadurch zu einem Problem kommt, dass du in beiden Interrupt-Routinen dasselbe Register zum Retten des Status benutzt?
Allerdings: Wenn ich nichts übersehen habe, sind für rSreg/R15 alle
diese Bedingungen erfüllt.
Siehst du, du gehst davon aus, dass es womöglich doch nicht ok sein könnte.
Eine Prüfung auf mögliche versteckte Fehler kostet unnötig Zeit, und solange man den Fehler nicht gefunden hat, muss man womöglich immer wieder mal drüber nachgrübeln. Zeit, die verschwendet ist.
Ich programmiere aus Erfahrung*) grundsätzlich "defensiv". Solange der Speicher nicht bis zum allerletzten Byte ausgeknautscht ist, nehme ich in diesem konkreten Fall kein Register, sondern eine RAM-Zelle pro ISR.
*) Die letzten Programme in Assembler habe ich vor Jahrzehnten geschrieben. Die liefen auch in Fabrikhallen in anderen Erdteilen. Jeder Stillstand beim Kunden war eine Katastrophe. "Defensiv codieren" war also Pflicht.
Allerdings: Wenn ich nichts übersehen habe, sind für rSreg/R15 alle
diese Bedingungen erfüllt.
Siehst du, du gehst davon aus, dass es womöglich doch nicht ok sein
könnte.
Du übersiehst die Einschränkung meiner Einschätzung auf rSreg/R15. Dafür bin ich sicher, dass dessen Verwendung OK ist.
Nur werden aber blöderweise auch noch etliche andere Register in dem Code benutzt.
Ich programmiere aus Erfahrung*) grundsätzlich "defensiv". Solange der
Speicher nicht bis zum allerletzten Byte ausgeknautscht ist, nehme ich
in diesem konkreten Fall kein Register, sondern eine RAM-Zelle pro ISR.
Das ändert doch garnix, außer das jede ISR-Instanz mindestens vier Takte länger braucht. Erstens muß man für diese RAM-Zelle dann ganz genauso auf korrekte und exklusive Benutzung prüfen und zweitens braucht man immer noch ein MCU-Register zum Sichern, weil es keine Instruktionen gibt, mit der man SREG direkt in den RAM sichern bzw. von dort laden könnte.
"Defensiv codieren" war
also Pflicht.
Richtig programmieren ist noch viel besser als defensiv programmieren. Bezüglich der MCU-Registernutzung bietet der Assembler mit der Möglichkeit zur Vergabe von Aliasnamen sehr gute Voraussetzungen dafür. Man muss sie nur konsequent auch in diesem Sinne nutzen. Das ist im vorliegenden Code für rSreg/R15 geschehen (weswegen sich die Sache hier auch leicht nachprüfen ließ), für andere Register hingegen leider nicht.
Moin,
um den Fehler einzugrenzen, würde ich die LEDs erstmal über größere Widerstände anschließen.
Dann in der Hauptschleife alles rausnehmen und mit CBI und SBI einen Pin toggeln und mit einem Oszilloskope den Pin beobachten. Wenn das längere Zeit stabil läuft nacheinander init, ADC, ISR, watchdog usw. dazu nehmen.
Viel Erfolg
Carsten
Erstens muß man für diese RAM-Zelle dann ganz genauso auf korrekte
und exklusive Benutzung prüfen und zweitens braucht man immer noch
ein MCU-Register zum Sichern, weil es keine Instruktionen gibt, mit
der man SREG direkt in den RAM sichern bzw. von dort laden könnte.
Kann man doch einfach PUSH / POP nehmen, so wie es zum Beispiel avr-gcc macht.
Kann man doch einfach PUSH / POP nehmen, so wie es zum Beispiel avr-gcc
macht.
Das Problem mit Buffer Underrun.
Der Stapel wandert rückwärts in den Programmbereich rein, wenn zu viel push und pop. Oder pop vergessen wurde.
Einrichtung von Stapelzeigerbereich lautet ja bekanntlich RAMEND.
Zählt also runter.
Wann der Crash erfolgen könnte, müsste man im Simulator (built and run) nachvollziehen können.
Wollte nochmal Rückmeldung geben - seit die serielle Ausgabe deaktiviert
ist, keine Probleme mehr.
Ich muß mich korrigieren. Die serielle Ausgabe ist nicht das Problem.
Nach etlichen Tests bin ich mir jetzt zu 99% sicher, daß das Problem mit dem deep-sleep zusammenhängt. Wenn ich statt dem deep-sleep nur den sleep mode "idle" nehme, läuft alles (läuft immer noch, inzwischen schon vier Tage, serielle Ausgabe ist aktiv).
Deine serielle Ausgabe ist ja auch vergleichsweise überschaubar gestrickt. Da hätte ich mir höchstens noch vorstellen können, dass sie zu lange die Interrupts blockiert hält.
Die einzige Programmänderung ... Alles andere ist unverändert.
Pardon, aber ich sehe zusätzlich ein gutes Dutzend auskommentierte Zeilen.
Eigentlich hatte ich vor, das Ganze mit/ohne Absturz nachzuvollziehen (das aktuelle Programm (mit etwas Mühe) auf den nur verfügbaren ATtiny85 umgesetzt), dazu eben diese eine Zeile 'sbr rmp ...' umschaltend; aber jetzt kommen mir doch Zweifel.
Ja, der deep-sleep ist auskommentiert.
Es wird nicht mehr zwischen den Fällen "hell" und "dunkel" unterschieden, deshalb die Auskommentierungen. Stattdessen wird generell nach "loop_ende:" der idle-Modus aktiviert. Im Endeffekt läuft es darauf hinaus, daß nur noch der sleep-idle-Modus aktiviert wird.
Ich habe die Gegenprobe bereits gemacht - mit deep-sleep steigt der Controller nach einer unbestimmten Zeit wieder aus. Ohne deep-sleep nicht mehr. Zumindest läuft er damit jetzt schon eine ganze Weile.
Der Vollständigkeit halber noch diese kleinen Änderungen, die mit dem Absturz aber nichts zu tun haben dürften:
Vorher ab Zeile 172:
1
rcall get_from_sram
2
mov rmp, sramvalue
3
andi rmp,0x07 ; drei lsb aus unterem nibble
4
in temp, DDRB
5
or temp, rmp
6
out DDRB, temp
Nachher ab Zeile 172:
1
rcall get_from_sram
2
mov rmp, sramvalue
3
andi rmp,0x07 ; drei lsb aus unterem nibble
4
in temp, DDRB
5
cbr temp,0x07 ; letzte drei Bit löschen <==== neu
6
add temp, rmp ; und aus dem sram neu setzen <==== statt "or"
7
out DDRB, temp
Analog dazu für PORTB etwas weiter unten.
in "init_watchdog:"
ein cli und sei rein, ein Versuch war's wert. Wie schon von Walter S. geschrieben können die wieder raus, die machen dort wirklich keinen Sinn.
Erstens muß man für diese RAM-Zelle dann ganz genauso
auf korrekte und exklusive Benutzung prüfen
Du hast es nicht verstanden. Ich habe gesagt "eine Zelle pro ISR". Und prüfen muss man da lediglich im Editor, ob der Name dieser Zelle noch anderswo auftaucht (was er natürlich nicht darf). Register dagegen, für die ist es normal, an x Stellen benutzt zu werden.
zweitens braucht man immer noch ein MCU-Register zum Sichern, weil es
keine Instruktionen gibt, mit der man SREG direkt in den RAM sichern bzw.
von dort laden könnte.
Ja und? Dafür kann man ein Register nehmen, welches in der ISR ohnehin benutzt wird.
Richtig programmieren ist noch viel besser als defensiv programmieren.
...
Möglichkeit zur Vergabe von Aliasnamen sehr gute Voraussetzungen dafür.
Man muss sie nur konsequent auch in diesem Sinne nutzen
Du hast es wirklich nicht verstanden. Es geht beim defensiven Programmieren darum, dass man möglichst alles vermeidet, was nur funktioniert, wenn man es richtig (dein "konsequent") macht. Auf Deutsch: Denkbare Schlampereien gar nicht erst ermöglichen.
Aliasnamen sind da eine eher schlechte Lösung. Wenn man jedem Register nur einen Aliasnamen gibt, dann ist der im Kontext A vielleicht passend zur Bedeutung des gespeicherten Werts, im Kontext B aber nicht. Falls es nicht genügend Register für alle Kontexte gibt, muss man also manchen Registern mehr als einen Alias geben. Dann ist das Programm vielleicht korrekt, sicher aber unübersichtlich und fehlerisiko-behaftet. Oder man gibt dann einigen der Register gar keinen Alias - ist das wirklich besser?
Und wo bliebe dabei deine Konsequenz?
Ich habe die Gegenprobe bereits gemacht - mit deep-sleep steigt der
Controller nach einer unbestimmten Zeit wieder aus. Ohne deep-sleep
nicht mehr. Zumindest läuft er damit jetzt schon eine ganze Weile.
Wobei man aber nicht wissen kann, ob das DIREKT an dem Sleep-Modus liegt. Ja nach Sleep-Modus ändern sich ja Timing-Verhältnisse, die auch die Interrupts betreffen.
Kann man doch einfach PUSH / POP nehmen, so wie es zum Beispiel avr-gcc
macht.
Nö. Auch ein C-Compiler kann natürlich nur die Instruktionen nutzen, die das Target zur Verfügung stellt. D.h.: auch der avr-gcc wird nicht sowas wie etwa "push SREG" erzeugen können. Nö, der wird, wie auch der Asm-Programmierer, temporär ein MCU-Register beschäftigen und die Sache so umsetzen müssen:
in reg, SREG
push reg
Auch wenn C-Gläubige immer wieder die fantastischsten Behauptungen über die immensen Fähigkeiten der Compiler verbreiten, müssen diese sich letztlich doch der normativen Kraft des Faktischen beugen...
Wobei man aber nicht wissen kann, ob das DIREKT an dem Sleep-Modus
liegt.
Tut es nicht. Es liegt daran, dass diverse Initialisierungsroutinen innerhalb der Hauptschleife immer mal wieder aufgerufen werden.
Diese Initialisierungsroutinen sind allerdings so geschrieben, wie man sie für die Initialisierungsphase schreiben würde. Sprich: mit der stillschweigenden Annahme, das der Ausgangszustand der Hardware der Reset-Zustand ist und Interrupts noch nicht erlaubt sind. Hier müsste also nachgebessert werden.
Am sinnvollsten wäre allerdings, gleich die gesamte Programmstruktur umzubauen. Alles sollte in den ISRs passieren und die Hauptschleife so aussehen:
mainloop:
sleep
rjmp mainloop
Diese Programmstruktur würde den größten Teil der potentiellen Probleme von ganz alleine lösen. Eins allerdings nicht: Re-Initialisierungen der Hardware müssten natürlich trotzdem noch explizit berücksichtigen, dass sich "ihre" Hardware halt nicht im Reset-Zustand befindet.
LOL. Was glaubst du denn, was eine "Variable" ist? Nix anderes als ein Aliasame für eine Speicherstelle. Naja und ggf. eine Vereinbarung, dass da mehr als eine Speicherstelle dranhängen.
Wenn man jedem Register
nur einen Aliasnamen gibt, dann ist der im Kontext A vielleicht passend
zur Bedeutung des gespeicherten Werts, im Kontext B aber nicht.
Das ist richtig. Genau deswegen gibt es die Möglichkeit, Register lokal mit einem anderen Alias zu belegen und am Ende der lokalen Bedeutung wieder mit dem ursprünglichen zu versehen. Man kann dies über entsprechende Makros sogar mit einer Sicherung der Registerinhalte verbinden (sollte sowas aber möglichst selten, am besten aber nie tun).
sicher aber unübersichtlich
Ja, sowas macht das Programm zunächst mal unübersichtlicher. Es ermöglicht aber eine gezielte (ggf. sogar automatisierte) Suche nach fälschlicher konkurrierender Nutzung.
Was aber vielleicht noch viel wichtiger ist: Bei diesem primitiven Programm wird man bei sinnvoller Programmierung überhaupt noch nicht auf die Situation treffen, dass Register mehrfach benutzt werden müssen. Es gibt halt schlicht und einfach mehr als genug für dieses simple Programm.
LOL. Was glaubst du denn, was eine "Variable" ist? Nix anderes als ein
Aliasame für eine Speicherstelle.
Für wie blöd hältst du mich?
Register haben native Namen, die sind aus Bitmustern gebildet. Weil man sich diese Muster schlecht merken kann, haben die Register vom Assembler vergebene Aliasnamen (z. B. "R16"). Diese Namen nennt man aber in der Fachsprache üblicherweise nicht so: Wenn man da Alias sagt, meint man einen Alias, der eine Stufe höher ist (z. B. kann "Zähler" ein Alias für "R16" sein).
Und nur von den letztgenannten Aliassen war bisher die Rede. :-P
Tut es nicht. Es liegt daran, dass diverse Initialisierungsroutinen
innerhalb der Hauptschleife immer mal wieder aufgerufen werden.
Diese Initialisierungsroutinen sind allerdings so geschrieben, wie man
sie für die Initialisierungsphase schreiben würde. Sprich: mit der
stillschweigenden Annahme, das der Ausgangszustand der Hardware der
Reset-Zustand ist und Interrupts noch nicht erlaubt sind. Hier müsste
also nachgebessert werden.
Dann mal Butter bei die Fisch:
Welche Zeilen genau meinst du damit?
Denn irgendwie scheinst du über ein anderes Programm zu sprechen, als ganz oben angehängt ist.
Das kann jetzt lange dauern, und vielleicht tritt der Fehler auf einem ATtiny85 gar nicht auf. Schau'n wir mal ...
Dies allerdings kann ich fast nicht glauben:
Das Original ist das im ersten Posting
Laden Sie mal exakt diese Programmdatei von hier aus dem Forum herunter, assemblieren und laden Sie in den ATtiny13A, und dann schreiben Sie uns, was die sechs LEDs anzeigen - also nach "Lauflicht" sieht das bei mir nicht aus ...
Ansonsten läuft es bei mir seit 14 Stunden; und solange hier kein Fehler
auftritt, möchte ich mich ungern nur theoretisch in Ihr Programm
einarbeiten.
Danke für die Info, das ist für mich eine sehr hilfreiche Information. Dann handelt es sich eventuell gar nicht um ein Softwareproblem. Vielen Dank für's testen!
Ihr ursprüngliches Programm (mutatis mutandis) lief hier bis jetzt ohne Absturz durch.
Nun weiß ich nicht mehr weiter, bis auf diesen eher hilflosen Versuch: beigefügtes Programm muss bei Ihnen alle sechs LEDs dunkel lassen.
Also ich programmiere seit ca 15 jahren Attiny85 in Standard C und benutze dabei auch den watchdog um im Tiefschlaf-Modus Strom zu sparen. Solche Absturz-Probleme sind mir dabei noch nicht unter gekommen.
Nun habe ich mir doch, etwas widerstrebend, einen ATtiny13A zugelegt - also:
Es scheint, als liege das Hängenbleiben daran, dass dem laufenden Timer0 (mit gesetztem OCIE) per PRR der Takt ab- und zugeschaltet wird (egal was das Datenblatt dazu schreibt). Eigentümlich ist, dass es beim ATtiny13A relativ häufig, beim ATtiny85 hingegen nur äußerst selten auftritt.
Schalte ich den Timer0 ab vor Setzen von PRTIM0 und nach Löschen von PRTIM0 wieder an, tritt das Hängenbleiben auch nach vielen Stunden auf dem ATtiny13A nicht auf.
Könnte natürlich auf einen Bug in der Implementierung hindeuten, aber dafür müsste man das Hängenbleiben mit einem Minimalbeispiel reproduzierbar bekommen.
ATtinyX5 werden einfach mal andere Implementierungen haben, auch wenn der Ursprung vielleicht dem des ATtiny13 ähnelt. Selbst bei gleichem RTL-Code im Digitalbereich kann sich schon aus dem unterschiedlichen Layout ja ein leicht anderes Verhalten ergeben.
Haben wir irgendwo auch hier im Forum beackert.
Sir Landolt, war auch dabei, unter seinem alten Namen, so wie ich auch.
Finde es leider so ad hock nicht wieder.
Aufgefallen ist es in einer Arduino Anwendung.
Es sollte ein Timer Interrupt mit einstellbarer Zeit verwendet werden.
Das misslang mit allen Timern von ATMega328P und auch ATMega2560.
Bin mir nicht mehr sicher das auch ATMega32U2 und ATTiny85 getestet wurden.
Fehlerbeschreibung, aus dem Gedächtnis:
Arduino stellt in seiner Initialisierung alle Timer für PWM ein.
Die AVR verwenden dann für z.B. OCR1A Schattenregister.
Stellt man den Timer nur auf CTC um, versagt die korrekte Verwendung des Schattenregisters.
Der Effekt ist folgender:
Der Interrupt kommt immer zu der zuvor in OCR1A eingestellten Zeit (default ist nach dem Reset 0).
Man kann in OCR1A einstellen was man will. Es ändert sich nichts.
Es wird weiter der Wert aus dem Schatten Register verwendet.
Obwohl dieses laut Doku im CTC Mode gar nicht verwendet wird.
Liest man OCR1A aus, sieht man den gewünschten, selbst gesetzten, Wert, der aber nicht vom Timer genutzt wird.
Die Abhilfe war:
Den Timer erst händisch auf seine Default Reset Werte setzen, dann den CTC Mode setzen.
Dann funktioniert OCR1A wie in der Doku beschrieben.
Nachtrag:
Habe noch ein Testprogramm gefunden, siehe Anhang.
Es betrifft mindestens zwei, nämlich ATtiny13A und ATtiny_5. Beide Typen sehr alt, ich glaube kaum, dass Microchip dafür nochmal aktualisierte Datenblätter herausgibt.
Und, nur nebenbei, ich selbst wäre nie auf die Idee gekommen, einen laufenden Timer per PRR ein- und ausschalten zu wollen.
War jetzt ein (halbwegs) interessanter Exkurs, dabei möchte ich es belassen.
Ich wünsche angenehmen Feiertag oder frohes Schaffen, je nach Bundesland.
Mag jetzt das sprichwörtliche 'Locken drehen auf einer Glatze' sein, aber ich wollte das Thema doch abrunden, und da ich es nun mal hatte, hier auch vorstellen.
Fazit: das Interrupt-Flag des Timers wird irgendwann nicht zurückgesetzt.