AVR Timing genau einhalten in C

OP #502308
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
Gast #502346
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...
#502357
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 :-)
OP #502361
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?)
OP #502385
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 :-)
Gast #502391
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.
#502405
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren