Liebe Forenmitglieder, ich sehe mitlerweile weder Wald noch Bäume, da ich mich hier an einem Timingproblem ziemlich lange Zeit festgebissen habe. Es geht um folgende Anforderung: Alle xx000 us muss an einem Pin ein 128us langer low-puls generiert werden. Ansonsten wird über den selben Pin UART versendet, das muss je nach Kommando ca. 1ms vor oder nach dem Puls geschehen. Der Puls muss vollständig jitterfrei sein, die Frequenz ist in Grenzen durch die Applikation einstellbar. Ich scheitere daran das ganze jitterfrei zu gestalten, auch wenn es seit dem Entdecken des GTCCR-Registers besser geworden ist, so ist hier wohl immer noch nicht alles nach Lehrbuch aufeinander 'abgesynct'. Ich bin über jeden sachdienlichen Hinweis dankbar. Einen guten Start ins Wochenende, Max //------------------------------------------------------------------- //TIMER1 COMPA interrupt: Timer 1 in CTC mode every xx000 us, //Prescaler=8, FCPU=8MHz //------------------------------------------------------------------- ISR(TIMER1_COMPA_vect) { //Sync prescaler of timer0 & timer1 //---------------------------------------------------------------- GTCCR |= (1<<TSM)|(1<<PSRSYNC); GTCCR = 0; //HANDLE SOME STUFF that needs to be 1ms ahead of pin toggeling //Start pin-toggle timer0, will fire in 1ms //---------------------------------------------------------------- TCCR0B |= (1<<CS02); //disable Uart and set Tx-pin to output & high //---------------------------------------------------------------- UCSR0B &= ~(1 << TXEN0); } //------------------------------------------------------------------- //TIMER0 COMPA interrupt: starts pulling Tx-pin low for 128us low //------------------------------------------------------------------- ISR(TIMER0_COMPA_vect) { //Stop timer 0, set value to match distance to COMPB-IRQ and //start with new prescaler //---------------------------------------------------------------- TCCR0B &= ~(1<<CS02); TCNT0 = 72; TCCR0B |= ((1<<CS01)|(0<<CS00)); //pull pin low to generate pulse //---------------------------------------------------------------- UART_PORT &= ~(1 << UART_PIN); } //------------------------------------------------------------------- //TIMER0 COMPB interrupt: sets Tx-pin high again 128us after COMPA-IRQ //------------------------------------------------------------------- ISR(TIMER0_COMPB_vect) { UART_PORT |= (1 << UART_PIN); //Stop timer 0, set value to match distance to OVF-IRQ //and start with new prescaler //---------------------------------------------------------------- TCCR0B &= ~((1<<CS01)|(1<<CS00)); TCNT0 = 220; TCCR0B |= (1<<CS02); } //------------------------------------------------------------------- //TIMER0 OVF interrupt: disables Timer0 and enables uart again, hits 1ms after COMPB //------------------------------------------------------------------- ISR(TIMER0_OVF_vect) { //disable Timer for pulse generation //---------------------------------------------------------------- TCCR0B &= ~(1<<CS02); //safely enable UART again after IO-mode for 128us-low-pulse //---------------------------------------------------------------- UCSR0B |= (1 << TXEN0); }
Interrupts sind nie jitterfrei, da immer mindestens 1 CPU Zyklus gebraucht wird, um ihn zu erkennen. Da manche Befehle mehr als 1 Zyklus brauchen, ist die Unsicherheit der Antwort auf den IRQ immer da, denn es wird ja auf jeden Fall der laufende Befehl noch beendet. Den Overhead des Compilers allerdings kannst du ihn gewissen Grenzen beeinflussen, wenn du die ISR als 'NAKED' deklarierst. Dnn musst du allerdings selber für Sicherung der benötigten Register usw. sorgen.
max123 schrieb: > Der Puls muss > vollständig jitterfrei sein "vollständig jitterfrei" gibt es gar nicht. Du musst schon einen maximal erlaubten Wert angeben. Dann kann man darüber reden, ob man da drunter bleiben kann, und wenn ja, mit welchen Maßnahmen. Mit einem in einer ISR geschalteten Pin hast du auf jeden Fall schon mal mindestens einen Jitter von 3 Takten wegen unterschiedlicher Interrupt-Latenzen. Ein weiterer Punkt ist das > //HANDLE SOME STUFF that needs to be 1ms ahead of pin toggeling Laufzeitunterschiede in dem Teil erzeugen zusätzlichen Jitter.
Matthias Sch. schrieb: > Den Overhead des Compilers allerdings kannst du ihn gewissen Grenzen > beeinflussen, wenn du die ISR als 'NAKED' deklarierst. Dnn musst du > allerdings selber für Sicherung der benötigten Register usw. sorgen. Wozu soll das gut sein? Dieser Overhead hat keinen Einfluss auf den Jitter, weil er eine konstante Laufzeit hat.
Liegt der Jitter denn nur an den noch zu beendenden Instruktionen, aus denen der Vektor dann angesprungen wird? Denn Pro- und Epilog der ISR sollten ja konstant sein und nur fuer ein festes Delay sorgen, oder etwa nicht.. Ist der Umgang mit der Prescaler-Synchronisation denn auf diese Art und Weise so korrekt?
Stefan Ernst schrieb: > Ein weiterer Punkt ist das >> //HANDLE SOME STUFF that needs to be 1ms ahead of pin toggeling > Laufzeitunterschiede in dem Teil erzeugen zusätzlichen Jitter. Eigentlich nicht, da der Kram nur sehr kurz dauert und vor den Events der anderen Routinen längst fertig ist. Oder übersehe ich etwas?
Entschuldigung, natürlich übersehe ich etwas, in meine Codevereinfachung hat sich ein Fehler eingeschlichen, das kommt natürlich UNTER das starten des zweiten Timers!
Verstehe ich es richtig, dass mit meinem Ansatz der zwei Timer - wenn ich ansonsten alles richtig mache - immer eine Unsicherheit von 6 Taktzyklen bleibt? Die erste ISR kann um 3 Taktzyklen zu spät kommen, startet dann den 2. Timer, dieser ist ggf. schon drei Zyklen zu spät und kann nun bei seinem Eintreten nochmals um maximal 3 Zyklen verzögert werden, bis der Pin tatsächlich gezogen wird? Ist es zweckmäßig die Counterregister auszulesen, um festzustellen wie viele Ticks seit dem OVF/Comparematch schon vorbeigelaufen sind? Oder verstrickt man sich hier nur in Probleme?
Max 123 schrieb: > Ist es zweckmäßig die Counterregister auszulesen, um festzustellen wie > viele Ticks seit dem OVF/Comparematch schon vorbeigelaufen sind? Was soll das bringen, wenn 1 Timer-Tick 8 (oder sogar mehr) CPU-Ticks sind? Was ist denn nun die tatsächliche Anforderung an den Jitter?
Tja, darüber hätte ich mir mal Gedanken machen sollen. Diese durchaus berechtigte Frage muss ich mit Schulterzucken beantworten. Ich werde mal zurück auf 'Los' gehen, mir meine Lektion notieren, nämlich dass Timer-Interrupts nicht jitterfrei sind, mir mal Gedanken machen was hier eigentlich tatsächlich benötigt wird und dann nochmal über diese Timerschachteleien nachdenken. Ich habe mich zu sehr darin verbissen, das ganze in der Genauigkeit des Quarztaktes aufzulösen, was ja recht wahrscheinlich garnicht notwendig ist.
max123 schrieb: > immer noch nicht alles nach Lehrbuch aufeinander 'abgesynct'.
1 | //Prescalerzähler auf 0 setzen und halten(!)
|
2 | GTCCR |= (1 << TSM) | (1 << PSRSYNC); |
3 | |
4 | //Timer einstellen
|
5 | TCCRblabla
|
6 | |
7 | //Prescaler (und Timer) starten
|
8 | GTCCR &= ~(1 << TSM); |
max123 schrieb: > FCPU=8MHz Das ist doch wohl hoffentlich ein Quarz und nicht der interne Oszillator. Löt da mal 20MHz an. Dann hast du zwar immer noch n Takte Latenz aber n ist dann 2,5* kürzer. mfg.
Stefan Ernst schrieb: > "vollständig jitterfrei" gibt es gar nicht. Du musst schon einen maximal > erlaubten Wert angeben. So isses. Worauf bezieht sich der Jitter überhaupt? Auf die Wiederholfrequenz (x * 1ms) oder auf die Pulsdauer (128µs) oder auf beides? Ist es nur der Puls, dann ist es ganz einfach. Man stellt eine entsprechende Baudrate ein und sendet ein Byte mit 1..9 0-Bits. Peter
Stefan Ernst schrieb: >> Den Overhead des Compilers allerdings kannst du ihn gewissen Grenzen >> beeinflussen, wenn du die ISR als 'NAKED' deklarierst. Dnn musst du >> allerdings selber für Sicherung der benötigten Register usw. sorgen. > > Wozu soll das gut sein? Dieser Overhead hat keinen Einfluss auf den > Jitter, weil er eine konstante Laufzeit hat. Woher soll ich das wissen? Das ist nur der Hinweis, das es sowas gibt und man damit den Vor- und Auslauf der Interruptroutine beeinflussen kann. Wenn der TE genau 1 mSec braucht, (warum auch immer) und das nicht mit jitterarmer Hardware lösen kann/will, könnte ihm das evtl. helfen. Ich persönlich würde Hardware oder ASM nehmen.
Matthias Sch. schrieb: > Das ist nur der Hinweis, das es sowas gibt > und man damit den Vor- und Auslauf der Interruptroutine beeinflussen > kann. Wenn der TE genau 1 mSec braucht, (warum auch immer) und das nicht > mit jitterarmer Hardware lösen kann/will, könnte ihm das evtl. helfen. Eben nicht. "Vor- und Auslauf" haben eine konstante Länge, und damit keinen Einfluss auf den Jitter.
Habe gerade in einem anderen Beitrag etwas tolles gefunden zum Thema konstante IRQ Response-Time, das sei hier mal der Vollstaendigkeit halber angefügt: Autor: Hannes Lux (hannes) Datum: 04.02.2013 09:28 c-hater schrieb: >> warum sollte man das machen? > > Weil man Strom sparen will? Nicht unbedingt. Die meisten meiner ASM-Programme fallen nach jeder Runde der Mainloop in den Sleep und werden von Ereignissen (Interrupts) wieder geweckt. Meist geht es dabei nicht um Stromsparen, sondern um eine konstante Interrupt-Response-Time (ohne Jitter). Der Sleep-Mode richtet sich danach, welcher Interrupt den AVR wecken soll. Sleep ist also die Normalität und dient nicht nur zum Abschalten des Gerätes.
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.