Sign in Join
Wiki copytemporary

AVR Embedded Tutorial - UART/de: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: AVR Embedded Tutorial - UART/de

OldNewLine
1+# UART Polling
2+
3+## ATmega328p (Arduino uno/nano)
4+
5+Für die serielle Ausgabe wird ein Terminal mit folgenden Einstellungen gebraucht:
6+
7+| | |
8+|-----------|------|
9+| Baud Rate | 9600 |
10+| Bits | 8 |
11+| Stopbits | 1 |
12+| Parity | none |
13+
14+Beim Betätigen von <span class="kbd">Space</span> im Terminal-Programm, sollte ein "Hello World !" zurück kommen.
15+
16+### Konstanten für Inizialisierung
17+
18+``` pascal
19+const
20+ CPU_Clock = 16000000; // Taktfrequenz Arduino, default 16MHz.
21+ Baud = 9600; // Baudrate
22+ Teiler = CPU_Clock div (16 * Baud) - 1;
23+```
24+
25+**Hinweis:** Wen den der CPU-Clock zu klein ist in die Baudrate zu hoch, gibt es Übertragungsfehler. Beispiel: Clock **1MHz**, Baud **9600** geht nicht. Aber **1MHz** und Baud **1200** gehen.
26+
27+### Inizialisieren
28+
29+Die UART-Schnittstelle konfigurierieren.
30+
31+``` pascal
32+ procedure UARTInit;
33+ begin
34+ UBRR0 := Teiler; // Baud
35+
36+ UCSR0A := 0 // Normale Geschwindigkeit
37+ UCSR0B := (1 shl TXEN0) or (1 shl RXEN0); // Empfangen und Senden
38+ UCSR0C := (%011 shl UCSZ0); // 8-Bit Übertragung, 1 Stop-Bits.
39+ end;
40+```
41+
42+### Zeichen empfangen
43+
44+``` pascal
45+ function UARTReadChar: char;
46+ begin
47+ while UCSR0A and (1 shl RXC0) = 0 do; // Warten, bis Zeichen ankommt.
48+ Result := char(UDR0); // Zeichen einlesen.
49+ end;
50+```
51+
52+### Zeichen senden
53+
54+``` pascal
55+ procedure UARTSendChar(c: char);
56+ begin
57+ while UCSR0A and (1 shl UDRE0) = 0 do; // Warten, bis letztes Zeichen gesendet.
58+ UDR0 := byte(c); // Zeichen senden.
59+ end;
60+```
61+
62+### String senden
63+
64+``` pascal
65+ procedure UARTSendString(s: ShortString);
66+ var
67+ i: integer;
68+ begin
69+ for i := 1 to length(s) do begin
70+ UARTSendChar(s[i]); // Zeichen einzeln senden.
71+ end;
72+ end;
73+```
74+
75+### Hauptschleife
76+
77+Hier wird gewartet, bis <span class="kbd">Space</span> ankommt, anschliessend wir "Hello World !" gesendet.
78+
79+``` pascal
80+program Project1;
81+begin
82+ UARTInit;
83+ repeat
84+ if UARTReadChar = #32 then begin // #32 = space
85+ UARTSendString('Hello World !'#13#10);
86+ end;
87+ until 1 = 2;
88+end.
89+```
90+
91+## ATtiny2313
92+
93+Beim ATtiny2313 haben die Register eine andere Bezeichnung als beim ATmega328p.
94+
95+- Die **0** entfällt in der Bezeichnung.
96+- **UBRR0** muss man in **UBRRH** und **UBRRL** zerlegen.
97+
98+Das sieht dann so aus:
99+
100+``` pascal
101+UBRRH := Teiler shr 8;
102+UBRRL := Byte(Teiler);
103+
104+UCSRB := (1 shl TXEN) or (1 shl RXEN);
105+UCSRC := (%011 shl UCSZ);
106+```
107+
108+(Der Code wurde nicht getestet)
109+
110+# Interrupt
111+
112+Beim Polling kann es passieren, das Zeichen verschluckt werden, dies passiert, wen ein Zeichen ankommt und man dieses nicht abholt. Aus diesem Grund empfiehlt es sich, den Zeichenempfang Interrupt gesteuert zum machen.
113+
114+Bei höheren Baudraten, ist dies zwingend.
115+
116+Es wir ein einfacher Ringpuffer mit 16 Zeichen verwendet.
117+
118+Die Beispiele zeigen dies anhand eines ATmega328p (Arduino uno/nano).
119+
120+## Empfangspuffer
121+
122+### Deklaration
123+
124+Zeichenbuffer deklarieren, hier wir ein Puffer von 16 Zeichen angelegt.
125+
126+``` pascal
127+const
128+ Crxlen = 16; // Puffergrösse
129+var
130+ RXBuf: array[0..Crxlen - 1] of byte;
131+ RXread: byte = 0;
132+ RXwrite: byte = 0;
133+```
134+
135+### Inzialisieren
136+
137+Dies ist ähnlich des Polling verfahren, der Unterschied, man teilt mit **RXCIE0** mit, das beim Zeichenempfang ein Interrupt ausgelöst wird.
138+
139+``` pascal
140+ procedure UARTInit;
141+ begin
142+ UBRR0 := teiler;
143+
144+ UCSR0A := (0 shl U2X0);
145+ UCSR0B := (1 shl TXEN0) or (1 shl RXEN0) or (1 shl RXCIE0);
146+ UCSR0C := %011 shl UCSZ0;
147+ end;
148+```
149+
150+### Interrupt
151+
152+Bei auslösen des Interrupts, wird das Zeichen vom UART in Zeichenbuffer kopiert.
153+
154+``` pascal
155+ procedure UART_RX_Empfang; public Name 'USART__RX_ISR'; interrupt;
156+ var
157+ b: byte;
158+ begin
159+
160+ // Zeichen einlesen
161+ b := UDR0;
162+
163+ // Zeichen in Puffer schreiben.
164+ if b <> 0 then begin
165+ RxBuf[rxwrite] := b;
166+ Inc(RXwrite);
167+ if RXwrite >= Crxlen then begin
168+ RXwrite := 0;
169+ end;
170+ end;
171+ end;
172+```
173+
174+### Hauptschleife
175+
176+In der Hauptschleife wird geprüft, ob sich etwas im Empfangspuffer, wen ja, wird ein Zeichen davon ausgelesen. In diesem Beispiel werden, die empfangenen Zeichen in Grossbuchstaben umgewandelt und anschiessend wieder ausgegeben.
177+
178+``` pascal
179+begin
180+ // UART inizialisieren, die Interrupt gesteuert ist.
181+ UARTInit;
182+
183+ // Interrupt aktivieren.
184+ asm sei end;
185+
186+ // Hauptschleife.
187+ repeat
188+
189+ // Ist ein Zeichen im Puffer ?
190+ while RXwrite <> rxread do begin
191+
192+ // Interrupt sperren.
193+ asm cli end;
194+
195+ // Zeichen aus Puffer lesen
196+ ch := RXBuf[rxread];
197+ Inc(RXread);
198+ if RXread >= Crxlen then begin
199+ RXread := 0;
200+ end;
201+
202+ // Interrupt aktivieren.
203+ asm sei end;
204+
205+ // Wen kleiner Buchstaben, diesen in einen grossen umwandeln.
206+ if ch in [$61..$7A] then begin
207+ Dec(ch, 32);
208+ end;
209+
210+ // Zeichen ausgeben.
211+ UARTSendChar(char(ch));
212+ end;
213+ until 1 = 2;
214+end.
215+```
216+
217+## Sendepuffer
218+
219+### Deklaration
220+
221+Sendepuffer deklarieren, hier wir ein Puffer von 16 Zeichen angelegt.
222+
223+``` pascal
224+const
225+ CTXlen = 16; // Puffergrösse
226+var
227+ TXBuf: array[0..CTXlen - 1] of byte;
228+ TXread: byte = 0;
229+ TXwrite: byte = 0;
230+```
231+
232+### Inzialisieren
233+
234+Dies ist ähnlich des Empfangsbuffer verfahren, wichtig dabei, die Interrupt-Funktion darf noch **nicht** aktiviert werden, da es am Anfang noch nichts zu senden gibt.
235+
236+``` pascal
237+ procedure UARTInit;
238+ begin
239+ UBRR0 := teiler;
240+
241+ UCSR0A := (0 shl U2X0);
242+ UCSR0B := (1 shl TXEN0) or (1 shl RXEN0);
243+ UCSR0C := %011 shl UCSZ0;
244+ end;
245+```
246+
247+### Interrupt
248+
249+Der Interrupts, wird ausgelöst, sobald das Zeichen im UART gesendet wurde.
250+
251+Sobald der Ringpuffer leer ist, wird die Interrupt-Funktion wider deaktiviert.
252+
253+Die Abfrage von **UDRE0** wird hier nicht gebraucht, da der Interrupt nur ausgelöst wird, wen der Puffer leer ist.
254+
255+``` pascal
256+ procedure UART_UDRE_Senden; public Name 'USART__UDRE_ISR'; interrupt;
257+ begin
258+ // Zeichen Senden.
259+ UDR0 := TXBuf[TXread];
260+
261+ // Ringpuffer verschieben.
262+ Inc(TXread);
263+
264+ // Wen Zeiger am Ende, wieder von vorn beginnen.
265+ if TXread >= CTXlen then begin
266+ TXread := 0;
267+ end;
268+
269+ // Ist Ringpuffer leer, kein Interrupt mehr nötig-
270+ if TXread = TXwrite then begin
271+ UCSR0B := UCSR0B and not (1 shl UDRIE0);
272+ end;
273+ end;
274+```
275+
276+### String senden
277+
278+Sobald sich etwas im Rindpuffer befindet, wird der Interrupt weder aktiviert.
279+
280+``` pascal
281+ procedure UARTSendString(s: ShortString);
282+ var
283+ i: byte;
284+ begin
285+
286+ // Ringpuffer mit Daten befüllen,
287+ for i := 1 to Length(s) do begin
288+ TXBuf[TXwrite] := byte(s[i]);
289+ Inc(TXwrite);
290+ if TXwrite >= CTXlen then begin
291+ TXwrite := 0;
292+ end;
293+ end;
294+
295+ // Sende Interrupt aktivieren.
296+ UCSR0B := UCSR0B or (1 shl UDRIE0);
297+ end;
298+```
299+
300+### Hauptschleife
301+
302+Der String wird extra ein wenig koatisch ausgegeben, dies sollte zeigen, das die Ausgabe im Hintergrund abläuft. Im Beispiel, blinkt sogar eine LED während der Ausgabe.
303+
304+``` pascal
305+var
306+ z: byte;
307+ s: ShortString;
308+begin
309+ DDRB := DDRB or (1 shl 5);
310+ // UART inizialisieren, die Interrupt gesteuert.
311+ UARTInit;
312+
313+ // Interrupt aktivieren.
314+ asm Sei end;
315+
316+ // Hauptschleife.
317+ repeat
318+ UARTSendString('Hello World');
319+ UARTSendString(' !'#13#10);
320+
321+ PORTB := PORTB or (1 shl 5);
322+ mysleep(sl);
323+
324+ Str(z: 3, s);
325+ UARTSendString(s + '. ');
326+ Inc(z);
327+
328+ PORTB := PORTB and not (1 shl 5);
329+ mysleep(sl);
330+
331+ until 1 = 2;
332+end.
333+```
334+
335+# Software - UART / UART - Emulation
336+
337+Es ist auch möglich eine UART-Schnittstelle per Software zu emulieren.
338+
339+Dies ist praktisch, wen man einen AVR hat, welcher keinen/zuwenig Hardware-UART hat.
340+
341+**Hinweis:** Dieser Code funktioniert nur mit **16MHz** CPU-Takt und **9600Baud**. Einfach **Baud** und **CPU_Clock** anzupassen reicht nicht, weil die Formel noch einen Fehler hat.
342+
343+**Wichtig:** Während der Übertragung, müssen Interrupts gesperrt werden.
344+
345+Gleichzeigtes lesen und schreiben funktioniert nicht, da kein Puffer vorhanden ist.
346+
347+Im Idealfall ist dies gut verwendbar, um Fehler auf ein UART-Terminal auszugeben.
348+
349+## Konstanten
350+
351+In diesem Beispiele werden die Pin 2 & 3 von PortD verwendet.
352+
353+``` pascal
354+const
355+ CPU_Clock = 16000000; // Taktfrequenz Arduino, default 16MHz.
356+ Baud = 9600; // Baudrate
357+
358+ TXpin = (1 shl 3); // Pin 3 ( Empfangen )
359+ RXpin = (1 shl 2); // Pin 2 ( Senden )
360+
361+ SoftTeiler = CPU_Clock div (Baud * 22); // Bitzeitzähler
362+```
363+
364+Den Empfangs-Pin auf Input stellen.
365+
366+``` pascal
367+begin
368+ DDRD := DDRD or TXpin;
369+ ...
370+ // Loop
371+ ...
372+end.
373+```
374+
375+**Hinweis:** Will man die gleichen Pins wie bei der Hardware-UART verwenden, muss beim Arduino der Pin **0**& **1** frei gegeben werden.
376+
377+``` pascal
378+begin
379+ UCSR0B := 0; // Blockierter Pin 0 & 1 frei geben
380+ ...
381+ // Loop
382+ ...
383+end.
384+```
385+
386+## Zeichen senden
387+
388+``` pascal
389+ procedure UARTSoftSendByte(c: byte);
390+ var
391+ i, Data: int16;
392+ j: Int8;
393+ begin
394+ asm Cli end; // Interrupt sperren
395+ Data := (c shl 1) or $FE00;
396+ for j := 0 to 9 do begin // Bit ausgeben, inklusiv Stop-Bits.
397+ if (Data and 1) = 1 then
398+ PORTD := PORTD or TXpin // HIGH
399+ else
400+ PORTD := PORTD and not TXpin; // LOW
401+ Data := Data shr 1;
402+ for i := 0 to SoftTeiler do; // Ein Takt Pause
403+ end;
404+ asm Sei end; // Interrupt erlauben
405+ end;
406+```
407+
408+## Zeichen empfangen
409+
410+**Hinweis:** Wen kein Zeichen im Empfang kommt, hängt die Schleife im Endlosen.
411+
412+``` pascal
413+ function UARTSoftReadByte: byte;
414+ var
415+ i: Int16;
416+ j: Int8;
417+ begin
418+ asm Cli end; // Interrupt sperren
419+ Result := 0;
420+ while PIND and RXpin > 0 do; // Warten bis erste fallende Flanke
421+ for i := 0 to SoftTeiler div 2 do; // Ein halber Takt Pause
422+ for j := 0 to 7 do begin
423+ for i := 0 to SoftTeiler do; // Ein Takt Pause
424+ Result := Result shr 1;
425+ if PIND and RXpin <> 0 then
426+ Inc(Result, $80);
427+ end;
428+ for i := 0 to SoftTeiler; // Ein Takt Pause
429+ asm Sei end; // Interrupt erlauben
430+ end;
431+```
432+
433+# Siehe auch
434+
435+- Übersichtseite [[AVR Embedded Tutorial/de|AVR Embedded Tutorial]]
436+
437+Autor: Mathias
438+
439+---
440+*From the Free Pascal wiki, page "AVR Embedded Tutorial - UART/de", as it stood on 2022-01-06 after a change by VamLykTan, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
441+Category: AVR/de
442+Category: Arduino/de
443+Category: Deutsch
444+Category: Embedded/de
445+Category: Tutorials/de

Back to history