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.
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?
Google mal danach "softuart_gittins_avr" Diese Version arbeitet nur mit Timer Interrupts. Wurde von mir schon erfolgreich auf ATMega8 und ATMega168 getestet
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 | }
|
Atmel hat zu dem Thema sogar eine Application Note...
>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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.