www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Software UART von Peter Dannegger


Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich habe gerade den Software UART von Peter Dannegger auf nem ATMega48 
getestet. Läuft super und schön schlank gehalten.

Jetzt würde ich den Code gerne folgendermaßen abändern:

Peter benutzt zur Erkennung des Datenempfangs den Input Capture 
Interrupt. Ich würd das gerne auf nen Pin Change Interrupt ändern, um 
beim Pin für den Empfang variabel zu sein.

Was ich nicht ganz verstehe ist, wie er in seinem Code den Wert aus dem 
ICR1 Register verwendet. Hier die beiden Interrupts:
/******************************  Interrupts *******************************/

ISR(TIMER1_CAPT_vect)        // start detection
{
  s16 i = ICR1 - BIT_TIME / 2;      // scan at 0.5 bit time

  OPTR18          // avoid disoptimization
  if( i < 0 )
    i += BIT_TIME;        // wrap around
  OCR1B = i;
  srx_state = 10;
  TIFR1 = 1<<OCF1B;        // clear pending interrupt
  if( SRXD_PIN == 0 )        // still low
    TIMSK1 = 1<<OCIE1A^1<<OCIE1B;    // wait for first bit
}


ISR(TIMER1_COMPB_vect)        // receive data bits
{
  u8 i;

  switch( --srx_state ){

    case 9:  if( SRXD_PIN == 0 )    // start bit valid
         return;
       break;

    default: i = srx_data >> 1;            // LSB first
       if( SRXD_PIN == 1 )
         i |= 0x80;      // data bit = 1
       srx_data = i;
       return;

    case 0:  if( SRXD_PIN == 1 ){    // stop bit valid
         i = srx_in;
         ROLLOVER( i, SRX_SIZE );
         if( i != srx_out ){    // no buffer overflow
     srx_buff[srx_in] = srx_data;
     srx_in = i;      // advance in pointer
         }
       }
       TIFR1 = 1<<ICF1;      // clear pending interrupt
  }
  TIMSK1 = 1<<ICIE1^1<<OCIE1A;      // enable next start
}

Kann mir das jemand erklären? Vielen Dank!

Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was ich nicht verstehe ist, dass der Input Capture doch die Zeit 
zwischen zwei Events misst. Doch hier wird der Input Capture doch schon 
nach dem ersten Durchlauf verwendet und hat immer einen anderen Wert.

Also wozu genau ist dieser Wert gut?

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bjoern schrieb:
> Jetzt würde ich den Code gerne folgendermaßen abändern:
Das solltest du nur tun, wenn du genau verstanden hast, was da 
passiert.

> dass der Input Capture doch die Zeit zwischen zwei Events misst.
> Also wozu genau ist dieser Wert gut?
Er zeigt an, wieviele Bits (vielfache einer Bitdauer) zwischen zwei 
Capture Ereignissen high- oder low (= 0 oder 1) waren...

Autor: Bjoern (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
>> dass der Input Capture doch die Zeit zwischen zwei Events misst.
>> Also wozu genau ist dieser Wert gut?
>Er zeigt an, wieviele Bits (vielfache einer Bitdauer) zwischen zwei
>Capture Ereignissen high- oder low (= 0 oder 1) waren...

Ok, das wäre also die Dauer des ersten Low Pulses meines zu empfangenden 
Zeichen. Siehe Bild im Anhang. Richtig?

Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ne, meine Vermutung ist nicht richtig! Hab mir gerade mal nen anderen 
Pin zu vergleichen getoggelt.

Doch welche Bits werden dann gemessen? Und zwischen welchen Ereignissen?

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bjoern schrieb:
> Doch welche Bits werden dann gemessen? Und zwischen welchen Ereignissen?
Ich war auf dem Irrweg... :-(
Da wird gar nichts gemessen, es wird einfach die erste fallende Flanke 
genommen, und dann der Compare-Timer so gesetzt, dass ab da jedes Bit in 
der Mitte abgetastet wird...

Der Pin und der zugehörige Timer wird in 2 Betriebsarten betrieben:
1) Bis zum Erkennen des Startbits ist das ein Input-Capture.
1a) Dann wird umgeschaltet auf den Timer-Compare:
   TIMSK1 = 1<<OCIE1A^1<<OCIE1B;    // wait for first bit
2)Danach werden die Bits empfangen.
2a)Und anschliessend wird wieder auf Capture-Interrupt umgeschaltet:
   TIMSK1 = 1<<ICIE1^1<<OCIE1A;      // enable next start
Und damit geht es wieder bei 1) los.

Weil das alles so hübsch auf den Timer zugeschnitten ist, wirst du damit 
keine chance haben:
>>> Ich würd das gerne auf nen Pin Change Interrupt ändern

Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Da wird gar nichts gemessen, es wird einfach die erste fallende Flanke
>genommen, und dann der Compare-Timer so gesetzt, dass ab da jedes Bit in
>der Mitte abgetastet wird...

Doch wie wird das genau gemacht? Der ICR1 Wert sieht bei jedem Sprung in 
den Interrupt anders aus. Warum? Wie krieg ich es dann hin die Bits in 
der Mitte abzutasten?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann es sein, dass der Timer selber dann auch noch im CTC Modus 
betrieben wird?

Wo ist die generelle Timer Konfiguration?

Wenn da noch ein CTC Modus am Timer läuft, würde das so manches 
erklären.
Kann natürlich auch sein, dass der CTC indirekt über einen

ISR(TIMER1_COMPA_vect)
{
  TCNT1 = 0;
}

realisiert ist.

Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Yep, ein CTC ist drin. Hier die Timer Konfiguration:
void suart_init( void )
{
  OCR1A = BIT_TIME - 1;
  TCCR1A = TX_HIGH;      // set OC1A high, T1 mode 4
  TCCR1B = 1<<ICNC1^1<<WGM12^1<<CS10;  // noise canceler, 1-0 transition,
          // CLK/1, T1 mode 4 (CTC)
  TCCR1C = 1<<FOC1A;
  stx_state = 0;
  stx_in = 0;
  stx_out = 0;
  srx_in = 0;
  srx_out = 0;
  STXD_DDR = 1;        // output enable
  TIFR1 = 1<<ICF1;      // clear pending interrupt
  TIMSK1 = 1<<ICIE1^1<<OCIE1A;    // enable tx and wait for start
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So wie das für mich aussieht, läuft der Timer insofern durch als einmal 
'Rundum' des Timers genau der Bitzeit eines UART Bits entspricht.

Das bedeutet: Der Input Capture Wert ist im Grunde einfach nur der 
Offset, wann das erste Bit in Bezug zum 0-Durchgang des Timers begonnen 
hat.
Von diesem Wert ausgehend wird dann ausgerechnet, bei welchem Timerwert 
jeweils das Bit zur Hälfte durch ist und der Compare am B Kanal 
entsprechend eingerichtet.

Clever

Autor: R. Max (rmax)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, sehr clever. Das erspart einem nämlich die Taktzählerei, die man 
betreiben müßte, wollte man den Timer bei jedem Interrupt zurücksetzen, 
ohne sich einen Jitter oder systematischen Fehler einzuhandeln.

Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und welche Möglichkeit habe ich jetzt, dass ganze mit nem Pin Change 
Interrupt zu machen? Oder besser gefragt, wie kann ich diesen Offset 
sonst noch messen?

Autor: R. Max (rmax)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du müßtest in der ISR statt des Capture-Registers den aktuellen 
Zählerstand auslesen.

Weil aber zwischen der Flanke des Startbits und dem Auslesen des Zählers 
noch etwas Zeit vergeht (in der der Zähler weiterzählt), bekommst Du auf 
die Weise entweder einen systematischen Fehler, d.H. Du samplest immer 
ein Stück hinter der Mitte der Bits, oder Du mußt die oben erwähnte 
Taktzählerei betreiben, um zu wissen, wieviel Du vom Zählerstand 
abziehen mußt, um diesen Fehler auszugleichen.

Wie groß der Fehler ist und ob man ihn herausrechnen muß oder ignorieren 
kann, hängt vom Verhältnis zwischen Taktfrequenz und Baudrate und von 
Deinem Baudratenfehler ab.

Autor: Huch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier gibt es eine Implementierung mit dem Flankeninterrupt. 
Beitrag "Re: Software UART" Vielleicht hilft Dir 
das weiter.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.