Forum: Mikrocontroller und Digitale Elektronik Interrupts verhalten sich seltsam bei Code in der Main-Lopp - Atmega88


von Max (Gast)


Lesenswert?

Hallo,

ich habe ein absolut seltsames Verhalten mit einem Atmega88 (AVRStudio 
4.18).

Der Atmega liest die Temperatur von einem TSIC506-Temperatursensor. 
Dieser sendet die Daten über das Zacwire-Protokoll (1-wire). Er sendet 
ca. alle 97.3 ms 2 Datenbytes (+Parity), das dauert dann ca. 2.7 ms. Vor 
jedem Datenbyte sendet er eine "Strobe Time" im Startbit. Dh. die Zeit 
zwischen fallender Flanke und steigender Flanke ist die Zeit nach der 
die folgenden Bits gesampelt werden sollen.

Hierzu benutze ich jetzt 2 Interrupts (INT0 und TIMER1_COMPA_vect). Nach 
der Synchronisation (sicherstellen daß wir uns gerade in den 97ms 
befinden in denen Funkstille herrscht) beginnt daß samplen der Daten.

Nun wird INT0 auf fallende Flanke gesetzt, COMPA ist aus.

Bei der ersten fallenden Flanke (Startbit) wird der Timer zurückgesetzt, 
INT0 auf steigende Flanke.

Bei der steigenden Flanke wird der Timer ausgelesen(=Strobetime) und der 
Wert gespeichert. INT0 wird wieder auf fallende Flanke gesetzt.

Bei der nächsten fallenden Flanke (=1. Datenbit) wird INT0 abgeschaltet 
und COMPA mit der gespeicherten Strobetime initialisiert.

Der Timer feuert nun nach Strobetime, das Bit wird gesampelt, COMPA 
abgeschaltet und INT0 wieder auf fallende Flanke.

Das ganze Spiel wiederholt sich jetzt bis alles eingelesen ist.


Das ganze funktioniert auch wunderbar, auch das auslesen der Werte über 
I2C funktioniert.

Es gibt nur ein Problem: Das Programm soll in der main-loop noch was 
anderes machen. Solange die mainloop leer ist funktioniert alles 
wunderbar.
Sobald ich aber nur einen simplen Befehl wie temp=1; ausführe
 treten sehr oft Parityfehler auf, dh. es werden nicht alle Bits richtig 
gesampelt. Das kann wohl nur damit zusammenhängen daß die Interrupts 
verzögert ausgeführt werden. Bei der Zuweisung temp=0 tritt das Problem 
seltsamerweise nicht auf, wenn ich aber irgendetwas anderes mache als 
einer Variable den Wert 0 zuzuweisen fangen die Interrupts an zu spinnen 
?

Den Code kann ich auch gern posten, ich denke aber die Beschreibung ist 
schneller zu verstehen als der unaufgeräumte Code :) Ich hoffe mal drauf 
daß ich hier eher eine allgemeine Problematik übersehen habe.

von Edi R. (edi_r)


Lesenswert?

Ich tippe mal blind auf ein Timingproblem. Aber:

> Den Code kann ich auch gern posten, ich denke aber die Beschreibung ist
> schneller zu verstehen als der unaufgeräumte Code :)

So sehen wir nur, wie es sein soll, und nicht wie es wirklich ist.

von (prx) A. K. (prx)


Lesenswert?

Max schrieb:

> Bei der steigenden Flanke wird der Timer ausgelesen(=Strobetime) und der
> Wert gespeichert. INT0 wird wieder auf fallende Flanke gesetzt.

Für präzise Zeiterfassung ist es besser, den Timer die Zeit selbst 
auslesen zu lassen, d.h. die Capture-Funktion des Timers sollte 
verwendet werden. Damit spielen Reaktionszeiten auf Interrupts keine 
Rolle mehr.

von (prx) A. K. (prx)


Lesenswert?

Wie schnell laufen Timer und Controller, 8- oder 16-Bit Timer, wann wird 
gesampelt? Alles Fragen, die (hoffentlich) am einfachsten der Code 
liefert.

von Reinhard Kern (Gast)


Lesenswert?

Max schrieb:
> .... Das kann wohl nur damit zusammenhängen daß die Interrupts
> verzögert ausgeführt werden. Bei der Zuweisung temp=0 tritt das Problem
> seltsamerweise nicht auf, wenn ich aber irgendetwas anderes mache als
> einer Variable den Wert 0 zuzuweisen fangen die Interrupts an zu spinnen

Hallo,

vielleicht startet dein C-Compiler selbst einen konkurrierenden 
Time-Interrupt? Oder benutzt den gleichen Timer? Für solche 
Entwicklungen muss man schon genau wissen, was hinter den Kulissen der 
Hochsprache passiert.

Es könnte ja sein, dass temp=0 wegoptimiert wird, kein main() existiert 
und daher auch keine Laufzeitumgebung gestartet wird. Oder 1000 andere 
Sachen. ICE oder Logic Ananlyzer würden sowas aufdecken.

Gruss Reinhard

von (prx) A. K. (prx)


Lesenswert?

Reinhard Kern schrieb:

> vielleicht startet dein C-Compiler selbst einen konkurrierenden
> Time-Interrupt? Oder benutzt den gleichen Timer?

Ein Compiler?? Für AVR??

Compiler starten keine Timer. Betriebssysteme oder Realtime-Kernels tun 
das.

von Max (Gast)


Angehängte Dateien:

Lesenswert?

Im Anhang der relevante Teil des momentanen Quelltexts.

Das ursprüngliche Problem scheint gelöst zu sein...

Auch bei ausgeschaltetem Comparematch wird das Flag OCF1A gesetzt, bei
erneutem einschalten des Comparematch kann der Interrupt dann evtl.
direkt ausgelöst werden.

Nun funktioniert soweit alles, allerdings nur mit einer fest
eingestellen Strobetime (490 = ~ 61.25 µS bei 8Mhz und Prescaler 1).
Schreibe ich die zuvor ermittelte Strobetime in OCR1A wird der Interrupt
viel zu früh ausgelöst (ermittelt mit Logic Analyzer), wobei es bei den
ersten 2-3 Datenpaketen noch richtig funktioniert, danach aber nicht
mehr.

Der ermittelte Wert liegt aber eben in diesem Bereich (490), auch dann
wenn es nicht mehr funktioniert.

Da die strobeTime sich aber mit der Temperatur ändern kann funktioniert
das nat. nur für einen bestimmten Temperaturbereich, deswegen wärs gut
wenn ich das auch noch hinbekomme.

Die Zeit mit Inputcapture zu ermitteln wäre sicherlich auch nicht
schlecht, allerdings bin ich da ja an einen festen Pin (zb ICP1)
gebunden. Dort habe ich dann wiederum keine Edge-Interrupts für das
samplen der Bits. Die Interruptsreaktionszeit wird sich allerdings in
einem ziemlich engen Rahmen bewegen, daher kann man sie auch ermitteln
und dann von der Strobetime abziehen.

von (prx) A. K. (prx)


Lesenswert?

Max schrieb:
> Die Zeit mit Inputcapture zu ermitteln wäre sicherlich auch nicht
> schlecht, allerdings bin ich da ja an einen festen Pin (zb ICP1)
> gebunden.

Bei INTx nicht?

> Dort habe ich dann wiederum keine Edge-Interrupts für das
> samplen der Bits.

Doch. Die Captures reagieren auf Flanke und liefern Interrupts.

Ausserdem musst du das Signal nicht zeitkritisch samplen sondern kannst 
die Dauer des low-Pulses bestimmen. Und mit der Dauer des ersten Pulses 
vergleichen, wodurch sich das Verfahren selbst kalibiriert.

So ungefähr würde ich es machen, basierend auf der ZACWire Info:

- Timer auf Zyklus deutlich über der maximal möglichen Bitdauer 
konfigurieren. Falling edge capture int und overflow int einschalten. 
Timer nullen.

- Overflow interrupt: Idle Phase. Status/Zähler/... initialisieren. Ovf 
int abschalten. Wenn Leitung low dann Sensor im Ar...

- Falling edge capture int: capture Wert merken und auf rising edge 
umstellen.

- Rising edge capture int: Vorigen capture Wert vom aktuellen abziehen. 
Wenn erstes Bit, dann als Referenz speichern, sonst mit Referenz 
vergleichen gibt 0/1. Auf falling edge umstellen. Ovf int einschalten. 
Timer nullen.

Bei diesem Schema taucht nirgends die Reaktionszeit des Interrupts auf 
und es gibt auch keine kumulierenden Zeitfehler.

von (prx) A. K. (prx)


Lesenswert?

PS: Ein 8-Bit Timer tut es dann auch.

von Max (Gast)


Lesenswert?

Die Lösung mit Inputcapture mag noch etwas eleganter sein, diese hier 
hab ich jetzt aber soweit daß sie funktioniert, ich werd den Teufel tun 
und nochmal 3 Tage damit zu verbringen einen Temperatursensor 
einzulesen.

Die Lösung mit dem OutputCompare Interrupt liegt darin den CTC-Modus 
abzuschalten bevor man in OCR1A schreibt, danach dann wieder an. 
Ansonsten wird selbst der Inhalt der Variable die man dort reinschreibt 
gelöscht.

Selbst der Inhalt einer 2. Variable der man den Wert der 1. vorher 
zugewiesen hat wird gelöscht. Das kann ich mich nur mit einem 
fehlerhaften Optimierungsversuch vom Compiler erklären der irgendwie 
versucht mit Referenzen statt mit Kopien zu arbeiten. (Optimierungsstufe 
Os).

Nach der Anpassung funktioniert alles wunderbar.

Vielen Dank für die Antworten

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.