mikrocontroller.net

Forum: Compiler & IDEs Suche Software UART mit Timer0 oder 2 und INT0


Autor: Upi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich suche einen SW-UART mit Timer0 oder Timer2 (Timer1 ist durch einen 
CTC-Mode besetzt).

Ich habe hier einiges gefunden. Vor allem P. Danneggers Lösung kommt 
meinen Anforderungen am nächsten. Hier: 
Beitrag "Software UART"

Das Problem aber ist, das ich an den Pins 11 und 12 von einem ATMega644 
sitze, wo es kein Input-Capture gibt. Die SW von P.D. aber will input 
capture. Theoretisch könnte man ja in der ISR von INT0 den Timerwert 
speichern. Reicht das?
Weiter will die SW mittels der COMxyA Ausgänge die Bits ausgeben. Da 
müsste ich dann die Port Bits selbst setzen.
Ausserdem weiss ich gerade nicht ob man die Compare-Interrupts nutzten 
kann ohne die Ausgangsbits zu toggeln. (wenn man die Port-Bits 
anderweitig braucht).

Hat das schonmal jemand so gemacht? EInen SW-Uart mit Timer0 oder Timer2 
nur mit INT0-Eingangs-Interrupt und COMxyz Ausgang um die Bits 
rauszugeben.
Ich brauche nur 9600 8N1.

Wäre für jeden Hinweis dankbar.

Autor: Upi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also, ich habe hier mal versucht meine Lösung zu skizzieren. Sieht 
jemand eine Fehler oder hat Anmerkungen dazu?

#include "main.h"
#include "suart.h"


#define BIT_TIME  (u16)((XTAL + BAUD/2) / BAUD)


volatile u8 stx_count;
u8 stx_data;

volatile u8 srx_done;
u8 srx_data;
u8 srx_mask;
u8 srx_tmp;


void suart_init( void )
{
  OCR0A = TCNT0 + 1;      // force first compare
  // TCCR0A = 0;            // Default: Normal port operation
  TCCR0B = 1<<CS00;            // clk/1
  TIFR0 = 1<<ICF1;      // clear pending interrupt
  TIMSK0 = 1<<OCIE0A;            // enable tx and wait for start

  stx_count = 0;      // nothing to sent
  srx_done = 0;        // nothing received
  STXDDR |= 1<<STX;      // TX output
}


u8 sgetchar( void )      // get byte
{
  while( !srx_done );      // wait until byte received
  srx_done = 0;
  return srx_data;
}


ISR (INT0_vect)    // rx start
{
  OCR0B = TCNT0 + (u16)(BIT_TIME * 1.5);// scan 1.5 bits after start
  srx_tmp = 0;        // clear bit storage
  srx_mask = 1;        // bit mask
  TIFR0 = 1<<OCF0B;      // clear pending interrupt
  if( !(SRXPIN & 1<<SRX))    // still low
    TIMSK = 1<<OCIE0A^1<<OCIE0B;  // wait for first bit
}


ISR(TIMER0_COMPB_vect)
{
  u8 in = SRXPIN;      // scan rx line

  if( srx_mask ){
    if( in & 1<<SRX )
      srx_tmp |= srx_mask;
    srx_mask <<= 1;
    OCR1B += BIT_TIME;      // next bit slice
  }else{
    srx_done = 1;      // mark rx data valid
    srx_data = srx_tmp;      // store rx data
    TIFR0 = 1<<ICF1;      // clear pending interrupt
    TIMSK0 = 1<<TICIE0^1<<OCIE0A;        // enable tx and wait for start
  }
}


void sputchar( u8 val )      // send byte
{
  while( stx_count );      // until last byte finished
  stx_data = ~val;      // invert data for Stop bit generation
  stx_count = 10;      // 10 bits: Start + data + Stop
}


void sputs( u8 *txt )      // send string
{
  while( *txt )
    sputchar( *txt++ );
}


SIGNAL(TIMER0_COMPA_vect)    // tx bit
{
  u8 dout;
  u8 count;

  OCR0A += BIT_TIME;      // next bit slice
  count = stx_count;

  if( count ){
    stx_count = --count;    // count down
    ??? dout = 1<<COM0A1;      // set low on next compare
    if( count != 9 ){      // no start bit
      if( !(stx_data & 1) )    // test inverted data
  ??? dout = 1<<COM0A1^1<<COM0A0;  // set high on next compare
      stx_data >>= 1;      // shift zero in from left
    }
    TCCR0A = dout;
  }
}

Mir ist nur nicht klar, wo das senden des Startbits anfängt. Ausserdem 
weiss ich nicht genau wie ich in dem Compare A interrupt das senden 
behandle. Dort wird ja der Ausgang für das nächste compare interrupt 
gesetzt. Ich müsste ja schon am Anfang der ISR das gegenwärtige Bit 
ausgeben, oder?

Autor: Daniel Held (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Google mal danach "softuart_gittins_avr"
Diese Version arbeitet nur mit Timer Interrupts.
Wurde von mir schon erfolgreich auf ATMega8 und ATMega168 getestet

Autor: Upi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also,ich habe da mal eine Weile daran herumgekämpft. Leider bleibt noch 
ein seltsamer Effekt. Im INT0_vect wird mit if( !(SRXPIN & 1<<SRX)) 
geprüft ob das Startbit immer noch anliegt. Wohl um Schmutzeffekte zu 
vermeiden.

Wenn das drinbleibt, dann geht der Empfang allerdings nur mit bestimmten 
Zeichen gut. Z.B. 0x00 geht ziemlich gut und endlos. Hingegen, ein Wert 
wie 0x01 oder sonst führt meist nach den ersten Byte oder nach zweien 
oder dreien dazu, das der Empfang nicht mehr geht. Scheint irgendwie mit 
der Anzahl der Einsen zusammenzuhängen. Also habe ich in INT0_vect und 
dann in TIMER0_COMPB_vect mal einen Portpin getoggelt wenn ich in der 
ISR bin. Bei den problematischen Zeichen, wurde eben einfach INT0_vect 
nicht mehr angesprungen. Da dieser erst wieder in TIMER0_COMPB_vect 
freigegeben wird, habe ich dort den Port toggeln lassen. Da passiert es 
auch. (Wie gesagt, mit 0x00 gab es keine Problem).
Daraus folgere ich, das bei bestimmten Zeichen, in INT0_vect der 
Interrupt TIMER0_COMPB durch setzen von TIMSK0 nicht mehr enabled wird.

Da dies eben von der oben genannten Bedingung abhängt, habe ich die mal 
auskommentiert. Und siehe da, dann geht es mit allen Zeichen. Aber nun 
hängt es in etwa von der Anzahl der Einsen ab ob das Ding ein oder Zwei 
Zeichen sieht.

Vielleicht hat ja jemand ein Idee.
Das Ganze soll wiegesagt ein Software UART mit Timer0 und INT0 sein.
#include "main.h"
#include "suart.h"



#define BIT_TIME  (u8)((XTAL / BAUD) / PRESCALE)


volatile u8 stx_count;
u8 stx_data;

volatile u8 srx_done;
u8 srx_data;
u8 srx_mask;
u8 srx_tmp;


void suart_init( void )
{
  SBIT(PORTD,STX) = 1;
  // OCR0A = TCNT0 + 1;    // force first compare
  // TCCR0A = 0;        // Default: Normal port operation
  TCCR0B = 1 << CS01 | 1<<CS00;  // clk/64
  // TIFR0 = 1<<ICF1;      // clear pending interrupt
  TIMSK0 = 1 << OCIE0A;    // enable output compare interrupt

  EICRA = 1 << ISC01;      // falling edge
  EIMSK = 1 << INT0;      // enable edge interrupt

  stx_count = 0;        // nothing to sent
  srx_done = 0;            // nothing received
  STXDDR |= 1 << STX;      // TX output
}


u8 sgetchar (void)      // get byte
{
  while (!srx_done);      // wait until byte received
  srx_done = 0;
  return srx_data;
}


ISR (INT0_vect)    // rx start
{
  OCR0B = TCNT0 + (u8)((BIT_TIME * 3) / 2);// scan 1.5 bits after start

  srx_tmp = 0;        // clear bit storage
  srx_mask = 1;        // bit mask
  EIMSK &= ~(1 << INT0);      // disable edge interrupt
  //if( !(SRXPIN & 1<<SRX))    // still low
    TIMSK0 = 1<<OCIE0A^1<<OCIE0B;  // wait for first bit
  TIFR0 = 1<<OCF0B;      // clear pending interrupt ? why does that output compare int occur?
// EIFR &= ~(1 << INTF0);    // clear any pending edge interrupt
}


ISR (TIMER0_COMPB_vect)
{
  u8 in = SRXPIN;      // scan rx line
  PORTB = 0x00;
  if (srx_mask) {
      if (in & 1 << SRX)
      srx_tmp |= srx_mask;
    srx_mask <<= 1;
    OCR0B += BIT_TIME;      // next bit slice
  } else {
    srx_done = 1;      // mark rx data valid
    srx_data = srx_tmp;      // store rx data
    // TIFR0 = 1<<ICF1;      // clear pending interrupt
    TIMSK0 = 1<<OCIE0A;        // enable tx and wait for start
    EIMSK = 1 << INT0;  // reenable edge interrupt

  }
    PORTB = 0x02;
}


void sputchar (u8 val)      // send byte
{
  while (stx_count);      // until last byte finished
  stx_data = ~val;      // invert data for Stop bit generation
  stx_count = 10;      // 10 bits: Start + data + Stop
}


void sputs (u8 *txt)      // send string
{
  while (*txt)
    sputchar (*txt++);
}


ISR (TIMER0_COMPA_vect)    // tx bit
{
  u8 dout;
  u8 count;

  OCR0A += BIT_TIME;      // next bit slice
  count = stx_count;

  if (count) {
    stx_count = --count;    // count down
    // ??? dout = 1<<COM0A1;      // set low on next compare
    dout = 0;
    if (count != 9) {      // no start bit
      if (!(stx_data & 1))    // test inverted data
        //??? dout = 1<<COM0A1^1<<COM0A0;  // set high on next compare
        dout = 1;
          stx_data >>= 1;      // shift zero in from left
    }
    // TCCR0A = dout;
    // STX = (dout == 0) ? 1 : 0;
    SBIT(PORTD,STX) = dout;
  }
}


Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Atmel hat zu dem Thema sogar eine Application Note...

Autor: Upi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>zu dem Thema

Die App-Note (Du meinst wahrscheinlich 304) behandelt einen 
Half-Duplex-SW-UART, keinen Full-Duplex, wie der von Peter Danneger, auf 
dem mein Code beruht. Es gibt einige Unterschiede in der 
Implementierung. So wird der CTC-Mode benutzt. Bei mir nicht. D.h. der 
Timer wird nicht zurückgesetzt, wenn eine Compare-Match auftritt. Das 
ist, weil im Full-Duplex-Mode beide Compares benutzt werden.

Das passt also nicht.

Autor: Alex N. (alex009)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Upi,

Dein Beitrag ist ja schon eine Weile her, aber ich habe gerade exakt die 
selbe Problematik, dass Timer 1 bereits belegt ist.
Hast Du Dein Problem inzwischen gelöst ?

Grüße
Alex

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.