Forum: Compiler & IDEs Software-UART: Problem beim Empfangen


von BruceCompanys (Gast)


Lesenswert?

Hallo,

ich habe die Suche benutzt und leider konnte mir kein gefundener Beitrag 
hier im Forum (oder bei Google) helfen.

Folgendes Problem:
Ich habe für eine Facharbeit einen Software-UART für den ATtiny13 
geschrieben.
Dafür wird der Timer TCNT0 über die beiden Register OCR0A zum Senden und 
OCR0B zum Empfangen benutzt.
Zum Empfangen des Startbits bei dem Standard RS232 wird der Interrupt 
INT0 zum Erkennen von steigenden Flanken benutzt.

Aber das Empfangen klappt vorne und hinten nicht, dafür aber das Senden.
Meine Frage ist jetzt: Warum läuft das nicht? Ich hab jetzt so oft über 
den Quellcode geblinzelt, dass ich den nicht mehr sehen kann incl. 
Fehler.

Könnte jmd.eventuell mal ein Auge draufwerfen?

MfG
BruceCompanys
1
#include "uart.h"
2
3
4
5
static volatile uint16_t outframe;
6
7
8
9
static volatile uint16_t inframe;
10
11
static volatile uint8_t indata, inbits, received;
12
13
14
15
void uart_init()
16
17
{
18
19
  /* Sichern des Status Registers incl. I-Flag */
20
21
  uint8_t sreg = SREG;
22
23
    cli();
24
25
26
27
    SUART_TXD_DDR   |= (1 << SUART_TXD_BIT);  /* Pin als Ausgang */
28
29
  SUART_TXD_PORT   &= ~(1 << SUART_TXD_BIT);  /* Low-Pegel */
30
31
32
33
  SUART_RXD_DDR   &= ~(1 << SUART_RXD_BIT);  /* Pin als Eingang */
34
35
  SUART_RXD_PORT   |= (1 << SUART_RXD_BIT);   /* Pull-up Widerstand */
36
37
38
39
  /* Output Compare Register A 
40
41
    Anzahl der Taktzyklen für eine Bitlänge 
42
43
    
44
45
    F_CPU = 1,2 MHz; Baudrate = 9600 => 125 Taktzyklen pro Bitlänge */
46
47
  OCR0A = 125; /* OCR0A wird zum Senden verwendet */
48
49
  OCR0B = 183; /* OCR0B wird zum Empfangen verwendet. Korrigierter Stand, eig. 188! */
50
51
52
53
  /* Timer/Control Register A: setzte Clear Timer/Counter on Compare Match  */
54
55
  TCCR0A |= (1 << WGM01);
56
57
58
59
  /* Timer/Control Register B: stoppe Timer*/
60
61
  TCCR0B = 0; /* REF NO.: F02/01 */
62
63
64
65
    /* Setze Interrupt und Flags für Timer zurück */
66
67
  TIFR0  = (1 << OCF0B) | (1 << OCF0A);
68
69
  TIMSK0 = 0;
70
71
72
73
    /* Interrupt wird ausgelöst bei steigender Flanke an PB1 */
74
75
    MCUCR   |= (1 << ISC01) | (1 << ISC00);
76
77
    GIFR    = (1 << INTF0);
78
79
    GIMSK   &= ~(1 << INT0);
80
81
  
82
83
  SREG = sreg;
84
85
}
86
87
88
89
void uart_putc(const char c)
90
91
{
92
93
  /* Warte bis alles gesendet ist */
94
95
  do {
96
97
    sei(); nop(); cli();
98
99
    } while (outframe);
100
101
102
103
  /* frame = *.P.7.6.5.4.3.2.1.0.S   S=Start(0), P=Stop(1), *=Endmarke(1) */
104
105
    outframe = (3 << 9) | (((uint8_t) c) << 1);
106
107
108
109
    /* Setze Interrupt für Timer zurück */
110
111
    TIFR0   = (1 << OCF0A);
112
113
114
115
  /* Timer/Counter Interrupt Mask Register 
116
117
    OCIE0A = 1
118
119
    => Timer/Counter0 Output Compare Match A Interrupt Enable 
120
121
  
122
123
    Aktiviere Interrupt für Timer */
124
125
  TIMSK0 |= (1 << OCIE0A);
126
127
    TCCR0B |= (1 << CS00); /* starte Timer mit 1,2 MHz Takt*/
128
129
  
130
131
  sei();
132
133
}
134
135
136
137
char uart_getc()
138
139
{
140
141
    cli();
142
143
    GIFR    = (1 << INTF0);
144
145
    GIMSK   |= (1 << INT0);
146
147
  
148
149
  received = 0;
150
151
    sei();
152
153
154
155
  /* Warte bis alles empfangen ist */
156
157
  while(received == 0) {}
158
159
160
161
    TCCR0B   = 0; /* Stoppe Timer wieder REF NO.: F02/01 */
162
163
    received = 0;
164
165
166
167
  return (char) indata;
168
169
}
170
171
172
173
/* Senden - Interrupt Service Routine */
174
175
ISR(TIM0_COMPA_vect)
176
177
{
178
179
    uint16_t data = outframe;
180
181
   
182
183
    if(data & 1)   SUART_TXD_PORT &= ~(1 << SUART_TXD_BIT);
184
185
  else       SUART_TXD_PORT |=  (1 << SUART_TXD_BIT);
186
187
188
189
     /* Endmarke erreicht, kille Interrupt */
190
191
    if (1 == data) {
192
193
        TIMSK0 &= ~(1 << OCIE0A);
194
195
        TCCR0B  = 0; /* stoppe Timer REF NO.: F02/01 */
196
197
    }   
198
199
   
200
201
    outframe = data >> 1;
202
203
}
204
205
206
207
/* Empfangen - Interrupt Service Routine */
208
209
ISR(INT0_vect)
210
211
{
212
213
    OCR0B    = 181; /* REF NO.: F01/01 */
214
215
  TIFR0    = (1 << OCF0B);
216
217
  TIMSK0   |= (1 << OCIE0B);
218
219
    TCCR0B  |= (1 << CS00); /* Starte Timer mit 1,2 Mhz Takt */
220
221
222
223
    GIMSK   &= ~(1 << INT0);
224
225
226
227
  inframe  = 0;
228
229
  inbits    = 0;
230
231
}
232
233
234
235
ISR(TIM0_COMPB_vect)
236
237
{
238
239
  uint16_t data = inframe >> 1;
240
241
    OCR0B = 123;
242
243
244
245
  if(!(SUART_RXD_PIN & (1 << SUART_RXD_BIT))) {
246
247
        data |= (1 << 9);
248
249
  }
250
251
252
253
  uint8_t bits = inbits+1;
254
255
256
257
  if(bits >= 10) {
258
259
    /* Kontrolle von Start- und Stoppbit */
260
261
        if( !(data & 1) && (data >= (1 << 9)) ) {
262
263
          indata = data >> 1;
264
265
    }
266
267
268
269
        received = 1;
270
271
        TIMSK0 &= ~(1 << OCIE0B);
272
273
    } else {
274
275
        inbits = bits;
276
277
        inframe = data;
278
279
    }
280
281
}

von Peter D. (peda)


Lesenswert?

BruceCompanys schrieb:
> F_CPU = 1,2 MHz; Baudrate = 9600 => 125 Taktzyklen pro Bitlänge */

Wenn man nicht super optimal programmiert, sind 125 Zyklen schneller 
vorbei, als Du denkst.
Z.B. eine 16Bit Variable im Interrupt incrementieren, dauert schon 18 
Zyklen.
Overhead für einen leeren Interrupt: 25 Zyklen usw.

Setz in mal besser auf 9,6MHz.


Peter

von BruceCompanys (Gast)


Lesenswert?

Vielen Dank erst einmal für deine schnelle Hilfe!

Habe die Fusebits für 9,6 MHz gesetzt, leider funktioniert das Empfangen 
immer noch nicht richtig.

Werde mal versuchen die Werte von OCR0B weiter herunter zu drehen.

Wobei ich den Takt für den Timer bei 1,2 Mhz gelassen habe, da das 
Timer-Register sonst überläuft (und mir nichts bringt.)

MfG
Bruce

von Tropenhitze (Gast)


Lesenswert?

Wenn Du weder Quarz noch keram. Resonator als Frequenzgeber hast, 
kannste einen UART - ob soft oder hard - vergessen.

von BruceCompanys (Gast)


Lesenswert?

Benutze den internen RC-Oszillator, der reicht mir bisher von der 
Genauigkeit.

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.