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.
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.
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.
Wie schnell laufen Timer und Controller, 8- oder 16-Bit Timer, wann wird gesampelt? Alles Fragen, die (hoffentlich) am einfachsten der Code liefert.
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
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.
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.
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.
PS: Ein 8-Bit Timer tut es dann auch.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.