Forum: Mikrocontroller und Digitale Elektronik AVR Timing genau einhalten in C


von Stefan S. (st_schulte)


Lesenswert?

Hallo!

Ich versuche den NEC-Code als Fernbedienungs-Ersatz per
Mega48 und C (ICC AVR 6.31 Demo) zu senden.
Irgendwie passen die Timings noch nicht.
Kann man im AVR Studio (4.12.460) die Stopuhr (Stop Watch) des 
Prozessor-Fensters zum messen des Zeitverbrauchs einsetzen (dann habe 
ich ein heftiges Problem :-) oder wie kann man in C eine High-Low 
Sequenz am besten Zeitgenau hinkriegen?

cu

Stef@n

von Stefan (Gast)


Lesenswert?


von Stefan S. (st_schulte)


Lesenswert?

Hi!

War zwar superschnell, aber "Die Timer/Counter des AVR" bringts nicht.
Ich muß 560us pulsen (das natürlich mit Timer), 1690us pausen für eine 0
und 560us pulsen und 565us pause für eine 1, das ganze 32bit in Reihe
ohne Pause. Da dauert ja das Timer setzen schon zu lange :-)

von antworter (Gast)


Lesenswert?

>560us pulsen

@16MHz:

1 clockcycle = 1 / 16MHz = 62.5 ns

> Da dauert ja das Timer setzen schon zu lange

Benötigt das Timer setzen bei Dir 9000 Takte ?

von johnny.m (Gast)


Lesenswert?

Betreibe den Timer im CTC-Modus mit Portpin toggeln bei Compare. Dann 
hast Du nach einem Umschalten des Portpins jeweils mindestens 560 µs 
Zeit, den Compare-Wert zu ändern. Das sollte überhaupt kein Problem 
sein. Wie man das jetzt genau macht, steht allerdings im Tutorial bzw. 
Datenblatt...

von Rahul, der Trollige (Gast)


Lesenswert?

>Da dauert ja das Timer setzen schon zu lange :-)

Selbst bei 1MHz würde der Controller erst sehr langsam ins Schwitzen 
kommen.

von Karl H. (kbuchegg)


Lesenswert?

Stefan Schulte wrote:
> Hi!
>
> War zwar superschnell, aber "Die Timer/Counter des AVR" bringts nicht.
> Ich muß 560us pulsen (das natürlich mit Timer), 1690us pausen für eine 0
> und 560us pulsen und 565us pause für eine 1, das ganze 32bit in Reihe
> ohne Pause. Da dauert ja das Timer setzen schon zu lange :-)

Allerdings.

Bei diesen Zeiten musst du schon kräftig aufpassen, dass dir
der AVR vor Langeweile nicht einschläft. Wenn der erst mal
vor sich hinschnarcht, kriegt man den so schnell nicht
mehr wach :-)

von Stefan S. (st_schulte)


Lesenswert?

johnny.m wrote:
> Betreibe den Timer im CTC-Modus mit Portpin toggeln bei Compare. Dann
> hast Du nach einem Umschalten des Portpins jeweils mindestens 560 µs
> Zeit, den Compare-Wert zu ändern. Das sollte überhaupt kein Problem
> sein. Wie man das jetzt genau macht, steht allerdings im Tutorial bzw.
> Datenblatt...

Nun, das geht eben nicht, da der Portpin ja bereits mit 38kHz toggelt.
Ich schalte nur das DDRx im Taktverhältnis um (pulsen, nicht pulsen).

Mein Problem ist irgenwie das Umsetzen der Timer in C (denn das war auch 
meine erste Idee), da ich nicht genau weiß wie lange der C-Code für das
umschalten, starten, DDRx ändern, ... benötigt.
Das Oszi sieht jedenfalls nicht gut aus.
Die Idee das mit AVR Studio zu testen war dann durch das Port-Toggeln 
auch dahin.
Deshalb die Frage nach der Stop Watch. (verläßliche Simulationszeit?)

von Rahul, der Trollige (Gast)


Lesenswert?

Wie wäre es, wenn du deinen Code mal posten würdest?

von johnny.m (Gast)


Lesenswert?

Wenn Du ne PWM drauf hast, dann geht das doch im Prinzip genau so! Du 
musst nur den Compare-Wert ändern. Und dafür hast Du reichlich Zeit...

von Stefan S. (st_schulte)


Lesenswert?

Rahul, der Trollige wrote:
> Wie wäre es, wenn du deinen Code mal posten würdest?

Ein paar Spaghetti:

#pragma global_register DelayL:21
unsigned char DelayL;

#pragma global_register DelayH:22
unsigned char DelayH;

void NECLogic0(void)
/* Sendet eine "0" */
{
  DDRB |= (1<<PB2);    // OC1B = PB2 auf Ausgang
  DelayH = 4;        // 500us
  loop125u();
  DelayL = 60;      // +60 = 560us
  loop();
  DDRB &= ~(1<<PB2);    // OC1B = PB2 Ausschalten
  DelayH = 4;        // 500us
  loop125u();
  DelayL = 65;      // +65 = 565us
  loop();
}

void NECLogic1(void)
/* Sendet eine "1" */
{
  DDRB |= (1<<PB2);    // OC1B = PB2 auf Ausgang
  DelayH = 4;        // 500us
  loop125u();
  DelayL = 60;      // +60 = 560us
  loop();
  DDRB &= ~(1<<PB2);    // OC1B = PB2 Ausschalten
  DelayH = 13;      // 500us
  loop125u();
  DelayL = 65;      // +65 = 1690us
  loop();
}

void loop(void)
/* Schleife mit DelayL Verzoegerung */
{
  asm ("_loop1:: dec %DelayL\n"
          "nop\n"
          "nop\n"
          "nop\n"
          "nop\n"
          "nop\n"
              "brne _loop1");
}

void loop125u(void)
/* Schleife mit 125us*DelayH Verzoegerung */
{
  asm ("_looph:: ldi R30,250\n"
     "_loopl:: dec R30\n"
           "nop\n"
          "brne _loopl\n"
          "dec %DelayH\n"
          "brne _looph");
}

in main habe ich dann mal ein paar "0"en und "1"en fürs Oszi ausgegeben.
Nur das Ergebnis sieht nicht sehr "richtig" aus :-)

von Rahul, der Trollige (Gast)


Lesenswert?

Wenn man nicht mehr als diese Funktion haben will, kann man auch 
problemlos 2 Timer benutzen: Der eine erzeugt 38kHz und der andere die 
Pulse und Pausen. Das kann man dann entweder einer ISR verunden oder 
extern.

von Karl H. (kbuchegg)


Lesenswert?

Ich würd alles mit einem Timer machen.
Der löst regelmässig einen Interrupt aus.
Im Interrupt wird dann mitgezählt:

  einmal die notwendigen Interruptaufrufe um auf
  die 32 kHz zu kommen und bei richtigem Zählerstand
  den Output Pin zu toggeln

  und zum zweiten zählt er noch das Puls/Pause
  Verhältnis mit. Ist er im Puls wird der vorher
  ermittelte Trägerwellenzustand an den Portpin
  gegeben. Ist er in der Pause, wird der Portpin
  auf 0 (oder 1, je nachdem wie die LED angeschlossen ist)
  eingefroren.

Die einzige Herausforderung ist es einen Vorteiler zu wählen,
so dass sich das komplette Timing (32 kHz + die Puls/Pausen
Zeiten für 0 und 1) sauber ausgeht.

Ohne jetzt das Timing durchgerechnet zu haben, so
ungefähr:

ISR (was_weis_ich_wahrscheinlich_ein_CTC_am_Timer_1)
{
  Takt++;

  if( Takt == was_auch_immer_notwendig_für_32_khz_ist ) {
    Takt = 0;
    Neuer_Port_Zustand = 1 - Alter_Port_Zustand;
  }

  if( SchickePuls ) {
    Pulsdauer--;
    if( Pulsdauer == 0 ) {     // der Puls ist vorbei
      SchickePuls = 0;
      Pausendauer = was_auch_immer_notwendig_ist_bei_dem Vorteiler;
    }
  }
  else {
    Neuer_Port_Zustand = 0;
    Pausendauer--;
    if( Pausendauer == 0 ) {
      if( nächstes_Bit == 0 )
        Pulsdauer = was_auch_immer_notwendig_ist_um die_Zeit
                    für_einen_1_Puls_zu kriegen
      else
        Pulsdauer = was_auch_immer_notwendig_ist_um die_Zeit
                    für_einen_0_Puls_zu kriegen
      SchickePuls = 1;
    }
  }

  PORTx = Neuer_Port_Zustand;
  Aler_Port_Zustand = Neuer_Port_Zustand;
}

Das ist jetzt mal so schnell als Skizze hingeschrieben.
Aber so ungefähr würde ich das versuchen.

Jetzt heist es natürlich mal ein bischen rechnen, so dass
sich beim gewählten Vorteiler und dem CTC-Wert (*) schöne
Zahlen für die "was_weis_ich...." Zählerstände ergeben.

(*) Ev. gehts auch mit Overflows aber ich denke mal
mit dem CTC gehts einfacher die Zeiten Taktgenau
einzustellen.

von Rahul, der Trollige (Gast)


Lesenswert?

>die 32 kHz zu kommen

Es sind 38kHz, wenn ich mich nicht verlesen habe...

von Karl H. (kbuchegg)


Lesenswert?

Rahul, der Trollige wrote:
>>die 32 kHz zu kommen
>
> Es sind 38kHz, wenn ich mich nicht verlesen habe...

Auch gut.
Wo hab ich jetzt die 32 kHz her?


Was hältst du von der Skizze. Würde das so in etwa
gehen?

von Rahul, der Trollige (Gast)


Lesenswert?

>Was hältst du von der Skizze. Würde das so in etwa gehen?
Ja, warum auch nicht?

>Wo hab ich jetzt die 32 kHz her?
"Karl Heinz, der magische"...

von Karl H. (kbuchegg)


Lesenswert?

Rahul, der Trollige wrote:
>>Was hältst du von der Skizze. Würde das so in etwa gehen?
> Ja, warum auch nicht?
>
>>Wo hab ich jetzt die 32 kHz her?
> "Karl Heinz, der magische"...

Ommmmmmmmmmmmmm

von Winne (Gast)


Lesenswert?

 so wie k.h.
würde ich das auch machen.

und wenns wirklich mal schneller gehen muss lassen sich asm-routinen 
einbinden was hier nicht nötig werden dürfte.

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.