Forum: Mikrocontroller und Digitale Elektronik Problem mit Timersynchronisation mega168


von max123 (Gast)


Lesenswert?

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);


}

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von max123 (Gast)


Lesenswert?

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?

von max123 (Gast)


Lesenswert?

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?

von max123 (Gast)


Lesenswert?

Entschuldigung, natürlich übersehe ich etwas, in meine Codevereinfachung 
hat sich ein Fehler eingeschlichen, das kommt natürlich UNTER das 
starten des zweiten Timers!

von Max 1. (max123)


Lesenswert?

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?

von Stefan E. (sternst)


Lesenswert?

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?

von Max 1. (max123)


Lesenswert?

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.

von Thomas E. (thomase)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von max123 (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.