Hallo miteinander,
Ich bin da ein bisschen am verzweifeln. Und zwar wollte ich mir eine
1Wire Lib schreiben, die auf Timer Interrupts setzt, da mir die
Übertragungsdauern von bis zu 14ms (mit LogicAnalyzer gemessen) für den
blockierenden Betrieb zu lange dauern.
Mein Problem ist nun, dass ich bereits beim Start des Timers einen
Interrupt erhalte, der da nach meinem Verständnis nicht kommen dürfte.
Mit CTC hatte ich scheinbar das Timing nicht geschafft, deshalb bin ich
auf PWM Mode 15 gewechselt.
Wo ist also mein Denkfehler?
A. Z. schrieb:> Mein Problem ist nun, dass ich bereits beim Start des Timers einen> Interrupt erhalte, der da nach meinem Verständnis nicht kommen dürfte.
Du hast das:
> TIFR1 |= (1 << TOV1); //clear TOV Interrupt> TIMSK1 |= (1 << TOIE1); //enable TOV Interrupt
an der falschen Stelle erledigt. Das tut man unmittelbar bevor man das
tut:
> TCCR1B |= (1 << CS10); //start the counter
insbesondere, wenn man dazwischen mit solchem unnützen Vollschrott wie
dem Arduino-Gedöhns hantiert, wo
> pinMode(pin, OUTPUT);
Schonmal ein "paar" Takte länger dauern kann als das Äquivalent in einer
sinnvollen Umgebung...
c-hater schrieb:> an der falschen Stelle erledigt. Das tut man unmittelbar bevor man das> tut:
Aber wodurch wird denn der Interrupt ausgelöst, OCR1A ist zu dem
Zeitpunkt, wo das TOIE1 Bit gesetzt wird, ungleich TCNT1 und TCNT1 läuft
noch nicht, da CS10 noch nicht gesetzt ist.
c-hater schrieb:> TIFR1 enthält nur strobe-Bits. Korrekt wäre:>> TIFR1 = (1 << TOV1);
Das nehme ich mal so mit. Muss mir so reingerutscht sein ^^.
A. Z. schrieb:> Aber wodurch wird denn der Interrupt ausgelöst, OCR1A ist zu dem> Zeitpunkt, wo das TOIE1 Bit gesetzt wird, ungleich TCNT1 und TCNT1 läuft> noch nicht, da CS10 noch nicht gesetzt ist.
Wenn er aber bereits lief (nur so ist die Sache erklärbar)? Direkt nach
einem Reset würde es funktionieren.
A. Z. schrieb:> Mit CTC hatte ich scheinbar das Timing nicht geschafft, deshalb bin ich> auf PWM Mode 15 gewechselt.
Ganz schlechte Idee, in den PWM-Modi werden einige Register verzögert
geladen.
Nimm CTC oder Normal-Mode.
Peter D. schrieb:> Ganz schlechte Idee, in den PWM-Modi werden einige Register verzögert> geladen.
Das ist so gewollt und i.d.R. auch überaus nützlich. Man muß bloß
verstehen, was die Konsequenz ist und den Timer entsprechend benutzen...
Scheint aber hier nicht das Problem zu sein...
Also ich habe erstmal wieder auf CTC umgebaut und es sieht soweit auch
ganz gut aus.
So sieht der Code für die erste Übertragung aus, also das initiale
Reset:
1
// CTC (Mode 4)
2
TCCR1A=(1<<COM1A0);
3
TCCR1B=(1<<WGM12);
4
5
6
DDRB|=(1<<DDB1);// pinMode(9, OUTPUT);
7
8
OCR1A=16*480-1;
9
TCNT1=0;//reset timer
10
TIFR1=(1<<OCF1A);//clear OC1A Interrupt
11
TIMSK1|=(1<<OCIE1A);//enable OC1A Interrupt
12
TCCR1B|=(1<<CS10);//start the timer
Jetzt bekomme ich einen Presence Pulse und kann auch ein Byte (0x33)
übertragen, aber nach den ersten 4 Bit verpasst er OCR1A und muss
erstmal bis zum Overflow hochzählen. Dabei unterscheidet sich der Code
für das 5te Bit nich, bzw. ist der gleiche wie für die anderen. Andere
Interrupt etc. sind auch nicht aktiv.
A. Z. schrieb:> Jetzt bekomme ich einen Presence Pulse und kann auch ein Byte (0x33)> übertragen, aber nach den ersten 4 Bit verpasst er OCR1A und muss> erstmal bis zum Overflow hochzählen. Dabei unterscheidet sich der Code> für das 5te Bit nich, bzw. ist der gleiche wie für die anderen. Andere> Interrupt etc. sind auch nicht aktiv.
Zeig' den verdammten Code. Kann nur ein Bug sein, denn prinzipiell ist
es kein nennenswertes Problem, OneWire asynchron zu implementieren. Ich
habe das vor ca. 10 Jahren schon getan...
c-hater schrieb:> Wenn er aber bereits lief (nur so ist die Sache erklärbar)? Direkt nach> einem Reset würde es funktionieren.
Im init, also vor Setup werden alle Timer in Betrieb gesetzt.
Timer0 für PWM und Millis() usw.
Timer 1 und 2 für PWM
Von daher kann es sich schon lohnen, vor einer eigenen initialisierung,
Die die Timer Register explizit zu leeren und so auch den Timer
anzuhalten.
c-hater schrieb:> insbesondere, wenn man dazwischen mit solchem unnützen Vollschrott wie> dem Arduino-Gedöhns hantiert, wo>>> pinMode(pin, OUTPUT);>> Schonmal ein "paar" Takte länger dauern kann als das Äquivalent in einer> sinnvollen Umgebung...
Ach...
Diese einseitige Sicht, was soll das denn...
Hier wird doch nix mit PinMode gemacht, oder?
Die paar Takte, die machen doch in den seltensten Fällen irgendwas aus.
Auch gibts ja immer noch die Möglichkeit pinMode() durch was schnelleres
zu ersetzen, wenn es denn sein muss.
Zudem gibt es ja auch dutzende verschiedene pinMode() Implementierungen.
Für jeden "Arduino fähigen" µC eine eigene.
Aber du begnügst dich ja damit die AVR Implementierung an den Pranger zu
stellen.
Etwas beschränkt, nicht war?
S. Landolt schrieb:>> Also ich habe erstmal wieder auf CTC umgebaut> Wo oder wie passiert das im aktuellen Programm?A. Z. schrieb:> // CTC (Mode 4)> TCCR1A = (1 << COM1A0);> TCCR1B = (1 << WGM12);
S. Landolt schrieb:> aber eigentlich wäre das die Domäne des Fanboys: hat Arduino vielleicht> weitere Timer1-Interrupts freigegeben?
Nö...
Timer1 wird nur für PWM vorbereitet!
A. Z. schrieb:> Das wird einmal vor Aufruf der write Funktion ausgeführt.
Zeige ein vollständiges (aber so gut wie möglich minimiertes) Programm,
welches das Problem aufzeigt, wenn du ernsthaft Hilfe erwartest.
Alles andere ist eine grobe Missachtung der potentiell Hilfewilligen.
Wer potentiell Hilfewillige grob missachtet, ist es nicht Wert, dass ihm
geholfen wird...
So einfach ist das.
A. Z. schrieb:> case irq_flag_e::reset_high:> // uint8_t r;> // r=digitalRead(pin);> TCCR1C=(1<<FOC1A);> PORTB&=~(1<<PORTB1);> DDRB|=(1<<DDB1);> delay = 16*410-1;> if (msg.length == 0){> irq_flag = irq_flag_e::end;
Interessanterweise sieht das Ergebnis besser aus, wenn ich das Timing im
Resetbereich anpasse und 420µs statt 410µs warte
c-hater schrieb:> Zeige ein vollständiges (aber so gut wie möglich minimiertes) Programm
Ich habs nochmal in einer Datei zusammengefasst, falls es hilft.
@c-hater: Vielleicht hatest du lieber woanders, wenn du keine Lust hast
sachlich zu bleiben?
Frage eines Assemblerprogrammierers: ist es zulässig, das Hauptprogramm
in setup zu packen? Oder anders gefragt: was passiert im Arduino
eigentlich zwischen setup und loop?
S. Landolt schrieb:> Frage eines Assemblerprogrammierers: ist es zulässig, das> Hauptprogramm> in setup zu packen? Oder anders gefragt: was passiert im Arduino> eigentlich zwischen setup und loop?
Falls du AVR Arduinos meinst, verlierst du nur serialEvent()
Siehe:
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/main.cpp
Bei ESP und andere Exoten kann das durchaus anders aussehen.
Danke.
Ich bezog mich auf das gezeigte Programm und den ATmega328. Ich verstehe
einfach nicht, weshalb es "stottert" wie zu Beginn, oder jetzt läuft,
weil, von außen betrachtet, irgendwo 6720 statt 6560 Takte eingestellt
werden.
Also ich habe das jetzt nochmal etwas kondensiert, kann mir das
Timingproblem aber immernoch nicht erklären.
Ich bekomme insgesamt 3 verschiedene Ergebnisse, je nach dem ob die
Zeile 126 mit
1
Serial.begin(115200);
auskommentiert ist oder nicht und ob as Delay in case
irq_flag_e::reset_high: auf 420µs oder 410µs gesetzt wird.
Mit
1
//Serial.begin(115200);
und
1
delay=16*420-1;
scheints zu passen.
Die Timingprobleme treten auch nur im ersten Byte auf.
Pin 13 (B5) wird getoggelt um darauf zu triggern und zu sehen, wann der
Interrupt kommt.
Also für mich sieht es so aus, als ob Arduino manchmal die 160 Takte
nicht reichen; wundert mich zwar, denn 160 ist viel (in Assembler).
Vielleicht gerät dieses millis dazwischen; ich würde es probeweise
abwürgen per TIMSK0=0 und schauen, was passiert.
S. Landolt schrieb:> TIMSK0=0
Das scheints geswesen zu sein.
Da das Problem mit einer kurzen Übertragung nur schwer reproduzierbar
ist, habe ich die Übertragung mal auf 80 Bytes aufgeweitet.
Mit TIMSK0=0 kann jedes einzelne Byte wie gewünscht übertagen werden,
ohne kommt es zu 2 Überläufen und 5 zu kurzen Timeslots.
S. Landolt schrieb:> denn 160 ist viel
Das hatte ich eigentlich auch gedacht, zumal die Timer 0 ISR jetzt auch
nicht unbedingt lang ist.
1
ISR(TIMER0_OVF_vect)
2
{
3
// copy these to local variables so they can be stored in registers
4
// (volatile variables must be read from memory on every access)
Ich hab jetzt nicht sonderlich viel Erfahrung mit Assembler, aber wenn
ich mich nicht verrechnet habe, benötigt die Timer0 ISR 98.5 Takte.
Das ist natürlich im Vergleich zu 160 Takten CTC ne ganz schöne Ansage.
Grenzfallbetrachtung: gleich zu Beginn der Timer0-Interruptbearbeitung
(millis) erfolgt der Timer1-CTC-Interrupt; letzterer muss nun warten,
bis erstere abgearbeitet ist (98 T), dann ein Befehl im Hauptprogramm
(typisch rjmp mit 2 T), nun kommt erst die Timer1-ISR bis zum Setzen
von OCRA1=delay (? T). Das alles muss innerhalb der 160 Takte erfolgen,
sonst ist TCNT1 bereits über 160 gelaufen und dreht eine Ehrenrunde mit
65536 Takten.
PS:
Die scheinbare Abhängigkeit von serial.begin oder den 420 us kommt
daher, dass dabei zwischen Timer0 und Timer1 unterschiedliche
Zeitversätze auftreten. Aber der Fehler tritt früher oder später immer
auf, deshalb war es auch sinnvoll, die Übertragung mal deutlich zu
verlängern.
Eine vage Idee: den Write-time-slot mit seinen 70 us als Ganzes
betrachten, Timer1-Modus 12, Interrupt auf Overflow. OCR1A auf den Wert
für 10 bzw. 60 us setzen, das Toggeln erfolgt dann ja automatisch
zeitlich korrekt; ICR1 auf den TOP-Wert für 70 us, hierfür den Interrupt
aktivieren, wenn dieser dann um die 100 Takte der
Arduino-millis-Timer0-ISR überschritten wird, bleibt man trotzdem noch
deutlich unter den (lt. Datenblatt DS18B20) zulässigen 120 us. Müsste
man mal ausprobieren.
Read wurde bislang ja noch nicht diskutiert.
Korrektur: geht doch nicht, wäre dasselbe Problem.
Aber: völlig ohne CTC, Interrupt auf OCR1B mit 70 us, in der COMPB-ISR
das zweite Toggeln des Ausgangs OC1A von Hand, d.h. per FOC1A, und
TCNT1=0 setzen. So irgendwie ...
Anbei mal der komplette Code aus einen 8051-Projekt.
Die Funktion OnwiReadRom wird nur im Init benötigt, d.h. sie wartet, bis
sie fertig ist.
Die anderen Funktionen sind nicht blockierend. Sie liefern ein Bit
zurück, ob sie fertig sind.
Der Zugriff auf den 1-Wire-Bus erfolgt im Hintergrund im T0-Interrupt
mit einer Statemaschine.
Für die Zeiten <15µs wird der Interrupt nicht verlassen. Nur die 60µs
und 480µs werden vom Timer erzeugt.
Benutzt wird T0 im 16Bit-Mode. Der T0-Interrupt bekommt die höchste
Priorität zugewiesen, d.h. kann alle anderen Interrupts unterbrechen.
Der 8051 hat ja den Vorteil, daß man bis zu 4 Prioritäten vergeben kann.
Der AT89C51 läuft mit 20MHz / 6 = 3,3MIPS.