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


von Upi (Gast)


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.

von Upi (Gast)


Lesenswert?

Also, ich habe hier mal versucht meine Lösung zu skizzieren. Sieht 
jemand eine Fehler oder hat Anmerkungen dazu?
1
#include "main.h"
2
#include "suart.h"
3
4
5
#define BIT_TIME  (u16)((XTAL + BAUD/2) / BAUD)
6
7
8
volatile u8 stx_count;
9
u8 stx_data;
10
11
volatile u8 srx_done;
12
u8 srx_data;
13
u8 srx_mask;
14
u8 srx_tmp;
15
16
17
void suart_init( void )
18
{
19
  OCR0A = TCNT0 + 1;      // force first compare
20
  // TCCR0A = 0;            // Default: Normal port operation
21
  TCCR0B = 1<<CS00;            // clk/1
22
  TIFR0 = 1<<ICF1;      // clear pending interrupt
23
  TIMSK0 = 1<<OCIE0A;            // enable tx and wait for start
24
25
  stx_count = 0;      // nothing to sent
26
  srx_done = 0;        // nothing received
27
  STXDDR |= 1<<STX;      // TX output
28
}
29
30
31
u8 sgetchar( void )      // get byte
32
{
33
  while( !srx_done );      // wait until byte received
34
  srx_done = 0;
35
  return srx_data;
36
}
37
38
39
ISR (INT0_vect)    // rx start
40
{
41
  OCR0B = TCNT0 + (u16)(BIT_TIME * 1.5);// scan 1.5 bits after start
42
  srx_tmp = 0;        // clear bit storage
43
  srx_mask = 1;        // bit mask
44
  TIFR0 = 1<<OCF0B;      // clear pending interrupt
45
  if( !(SRXPIN & 1<<SRX))    // still low
46
    TIMSK = 1<<OCIE0A^1<<OCIE0B;  // wait for first bit
47
}
48
49
50
ISR(TIMER0_COMPB_vect)
51
{
52
  u8 in = SRXPIN;      // scan rx line
53
54
  if( srx_mask ){
55
    if( in & 1<<SRX )
56
      srx_tmp |= srx_mask;
57
    srx_mask <<= 1;
58
    OCR1B += BIT_TIME;      // next bit slice
59
  }else{
60
    srx_done = 1;      // mark rx data valid
61
    srx_data = srx_tmp;      // store rx data
62
    TIFR0 = 1<<ICF1;      // clear pending interrupt
63
    TIMSK0 = 1<<TICIE0^1<<OCIE0A;        // enable tx and wait for start
64
  }
65
}
66
67
68
void sputchar( u8 val )      // send byte
69
{
70
  while( stx_count );      // until last byte finished
71
  stx_data = ~val;      // invert data for Stop bit generation
72
  stx_count = 10;      // 10 bits: Start + data + Stop
73
}
74
75
76
void sputs( u8 *txt )      // send string
77
{
78
  while( *txt )
79
    sputchar( *txt++ );
80
}
81
82
83
SIGNAL(TIMER0_COMPA_vect)    // tx bit
84
{
85
  u8 dout;
86
  u8 count;
87
88
  OCR0A += BIT_TIME;      // next bit slice
89
  count = stx_count;
90
91
  if( count ){
92
    stx_count = --count;    // count down
93
    ??? dout = 1<<COM0A1;      // set low on next compare
94
    if( count != 9 ){      // no start bit
95
      if( !(stx_data & 1) )    // test inverted data
96
  ??? dout = 1<<COM0A1^1<<COM0A0;  // set high on next compare
97
      stx_data >>= 1;      // shift zero in from left
98
    }
99
    TCCR0A = dout;
100
  }
101
}

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?

von Daniel Held (Gast)


Lesenswert?

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

von Upi (Gast)


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.
1
#include "main.h"
2
#include "suart.h"
3
4
5
6
#define BIT_TIME  (u8)((XTAL / BAUD) / PRESCALE)
7
8
9
volatile u8 stx_count;
10
u8 stx_data;
11
12
volatile u8 srx_done;
13
u8 srx_data;
14
u8 srx_mask;
15
u8 srx_tmp;
16
17
18
void suart_init( void )
19
{
20
  SBIT(PORTD,STX) = 1;
21
  // OCR0A = TCNT0 + 1;    // force first compare
22
  // TCCR0A = 0;        // Default: Normal port operation
23
  TCCR0B = 1 << CS01 | 1<<CS00;  // clk/64
24
  // TIFR0 = 1<<ICF1;      // clear pending interrupt
25
  TIMSK0 = 1 << OCIE0A;    // enable output compare interrupt
26
27
  EICRA = 1 << ISC01;      // falling edge
28
  EIMSK = 1 << INT0;      // enable edge interrupt
29
30
  stx_count = 0;        // nothing to sent
31
  srx_done = 0;            // nothing received
32
  STXDDR |= 1 << STX;      // TX output
33
}
34
35
36
u8 sgetchar (void)      // get byte
37
{
38
  while (!srx_done);      // wait until byte received
39
  srx_done = 0;
40
  return srx_data;
41
}
42
43
44
ISR (INT0_vect)    // rx start
45
{
46
  OCR0B = TCNT0 + (u8)((BIT_TIME * 3) / 2);// scan 1.5 bits after start
47
48
  srx_tmp = 0;        // clear bit storage
49
  srx_mask = 1;        // bit mask
50
  EIMSK &= ~(1 << INT0);      // disable edge interrupt
51
  //if( !(SRXPIN & 1<<SRX))    // still low
52
    TIMSK0 = 1<<OCIE0A^1<<OCIE0B;  // wait for first bit
53
  TIFR0 = 1<<OCF0B;      // clear pending interrupt ? why does that output compare int occur?
54
// EIFR &= ~(1 << INTF0);    // clear any pending edge interrupt
55
}
56
57
58
ISR (TIMER0_COMPB_vect)
59
{
60
  u8 in = SRXPIN;      // scan rx line
61
  PORTB = 0x00;
62
  if (srx_mask) {
63
      if (in & 1 << SRX)
64
      srx_tmp |= srx_mask;
65
    srx_mask <<= 1;
66
    OCR0B += BIT_TIME;      // next bit slice
67
  } else {
68
    srx_done = 1;      // mark rx data valid
69
    srx_data = srx_tmp;      // store rx data
70
    // TIFR0 = 1<<ICF1;      // clear pending interrupt
71
    TIMSK0 = 1<<OCIE0A;        // enable tx and wait for start
72
    EIMSK = 1 << INT0;  // reenable edge interrupt
73
74
  }
75
    PORTB = 0x02;
76
}
77
78
79
void sputchar (u8 val)      // send byte
80
{
81
  while (stx_count);      // until last byte finished
82
  stx_data = ~val;      // invert data for Stop bit generation
83
  stx_count = 10;      // 10 bits: Start + data + Stop
84
}
85
86
87
void sputs (u8 *txt)      // send string
88
{
89
  while (*txt)
90
    sputchar (*txt++);
91
}
92
93
94
ISR (TIMER0_COMPA_vect)    // tx bit
95
{
96
  u8 dout;
97
  u8 count;
98
99
  OCR0A += BIT_TIME;      // next bit slice
100
  count = stx_count;
101
102
  if (count) {
103
    stx_count = --count;    // count down
104
    // ??? dout = 1<<COM0A1;      // set low on next compare
105
    dout = 0;
106
    if (count != 9) {      // no start bit
107
      if (!(stx_data & 1))    // test inverted data
108
        //??? dout = 1<<COM0A1^1<<COM0A0;  // set high on next compare
109
        dout = 1;
110
          stx_data >>= 1;      // shift zero in from left
111
    }
112
    // TCCR0A = dout;
113
    // STX = (dout == 0) ? 1 : 0;
114
    SBIT(PORTD,STX) = dout;
115
  }
116
}

von STK500-Besitzer (Gast)


Lesenswert?

Atmel hat zu dem Thema sogar eine Application Note...

von Upi (Gast)


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.

von Alex N. (alex009)


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

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.