Sign in Join
Wiki copytemporary

AVR Embedded Tutorial - UART: what changed

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

OldNewLine
11# UART polling
22
33## ATmega328p (Arduino Uno/Nano)
44
55A terminal with the following settings is required for serial output:
66
77| Setting | Value |
88|-----------|-------|
99| Baud Rate | 9600 |
1010| Bits | 8 |
1111| Stopbits | 1 |
1212| Parity | none |
1313
14-When pressing the <span class="kbd">Space Bar</span> in the terminal program, a "Hello World!" message is returned.
14+When pressing the `Space Bar` in the terminal program, a "Hello World!" message is returned.
1515
1616### Constants
1717
1818``` pascal
1919 const
2020 CPU_Clock = 16000000; // Arduino clock frequency, default 16MHz.
2121 Baud = 9600; // baud rate
2222 Divider = CPU_Clock div(16 * baud) - 1;
2323```
2424
2525Note: If the CPU clock is too slow for the baud rate, there are transmission errors. Example: With a clock of 1MHz, a baud rate of 9600 does not work. However, a 1MHz clock and 1200 baud will work.
2626
2727### Initialization
2828
2929Configure the UART interface.
3030
3131``` pascal
3232 procedure UARTInit;
3333 begin
3434 UBRR0 := divider; // Baud
3535 UCSR0A := 0; // Normal speed
3636 UCSR0B := (1 shl TXEN0) or (1 shl RXEN0); // Receive and send
3737 UCSR0C := (%011 shl UCSZ0); // 8-bit word, 1 stop bit
3838 end;
3939```
4040
4141### Receive characters
4242
4343``` pascal
4444 function UARTReadChar : char;
4545 begin
4646 while UCSR0A and (1 shl RXC0) = 0 do
4747 ; // Wait for a character to arrive
4848 Result := char(UDR0); // Read character
4949 end;
5050```
5151
5252### Send characters
5353
5454``` pascal
5555 procedure UARTSendChar(c : char);
5656 begin
5757 while UCSR0A and (1 shl UDRE0) = 0 do
5858 ; // Wait for the last character to be sent
5959 UDR0 := byte(c); // Send character
6060 end;
6161```
6262
6363### Send string
6464
6565``` pascal
6666 procedure UARTSendString(s : ShortString);
6767 var
6868 i : integer;
6969 begin
7070 for i := 1 to Length(s) do
7171 begin
7272 UARTSendChar(s[i]); // send characters one by one
7373 end;
7474 end;
7575```
7676
7777### Main loop
7878
7979Here you wait until the space character arrives, then transmit "Hello World!".
8080
8181``` pascal
8282program Project1;
8383 begin
8484 UARTInit;
8585
8686 repeat
8787 if UARTReadChar = #32 then // #32 = space character
8888 begin
8989 UARTSendString('Hello World!'#13#10);
9090 end;
9191 until 1 = 2;
9292 end.
9393```
9494
9595## ATtiny2313
9696
9797The registers on the ATtiny2313 have different names than on the ATmega328p.
9898
9999- The 0 is omitted in the name.
100100- UBRR0 must be broken down into UBRRH and UBRRL .
101101
102102It will look like this:
103103
104104``` pascal
105105 UBRRH := divider shr 8;
106106 UBRRL := byte(divider);
107107
108108 UCSRB := (1 shl TXEN ) or (1 shl RXEN);
109109 UCSRC := (%011 shl UCSZ);
110110```
111111
112112(The code has not been tested)
113113
114114# UART interrupt
115115
116116With UART polling, a character may be lost. This can happen when a character arrives and you cannot pick it up in time. For this reason it is advisable to have the reception of characters controlled by an interrupt. At higher baud rates, this is mandatory.
117117
118118A simple 16-character ring buffer is used.
119119
120120The examples show this using an ATmega328p (Arduino Uno/Nano).
121121
122122## Receive buffer
123123
124124### Declaration
125125
126126Declare a character buffer: here a 16 character buffer is created.
127127
128128``` pascal
129129 const
130130 Crxlen = 16; // Buffer size
131131 var
132132 RXBuf : array[0..Crxlen - 1] of byte;
133133 RXread : byte = 0;
134134 RXwrite : byte = 0;
135135```
136136
137137### Initialization
138138
139139This is similar to polling, the difference is that **RXCIE0** is used to indicate that an interrupt is triggered when a character is received.
140140
141141``` pascal
142142 procedure UARTInit;
143143 begin
144144 UBRR0 := divider;
145145 UCSR0A := (0 shl U2X0);
146146 UCSR0B := (1 shl TXEN0) or (1 shl RXEN0) or (1 shl RXCIE0);
147147 UCSR0C := %011 shl UCSZ0;
148148 end;
149149```
150150
151151### Interrupt
152152
153153When the interrupt is triggered, the character is copied from the UART into the character buffer.
154154
155155``` pascal
156156 procedure UART_RX_Receiving; public name 'USART__RX_ISR'; interrupt;
157157 var
158158 b : byte;
159159 begin
160160 // Read characters
161161 b := UDR0;
162162
163163 // Write characters to buffer
164164 if b <> 0 then
165165 begin
166166 RxBuf[Rxwrite] := b;
167167 Inc(RXwrite);
168168
169169 if RXwrite >= Crxlen then
170170 begin
171171 RXwrite := 0;
172172 end;
173173 end;
174174 end;
175175```
176176
177177### Main loop
178178
179179The main loop checks whether something is in the receive buffer, and if so, a character is read from it. In this example, the characters received are converted to uppercase letters and then output.
180180
181181``` pascal
182182 begin
183183 // Initialize UART which is interrupt controlled
184184 UARTInit;
185185
186186 // Enable interrupts
187187 asm
188188 sei
189189 end;
190190
191191 // Main loop
192192 repeat
193193
194194 // Is there a character in the buffer?
195195 while RXwrite <> Rxread do
196196 begin
197197 // Disable interrupts
198198 asm
199199 cli
200200 end;
201201
202202 // Read character from buffer
203203 ch := RXBuf[rxread];
204204 Inc(RXread);
205205
206206 if RXread >= Crxlen then
207207 begin
208208 RXread := 0;
209209 end;
210210
211211 // Enable interrupts
212212 asm
213213 sei
214214 end;
215215
216216 // Convert lowercase letters, into uppercase ones
217217 if ch in[$61..$7A] then
218218 begin
219219 Dec(ch, 32);
220220 end;
221221
222222 // Output character
223223 UARTSendChar(char(ch));
224224 end;
225225 until 1 = 2;
226226 end.
227227```
228228
229229## Send buffer
230230
231231### Declaration
232232
233233Declare a send buffer: here a 16 character buffer is created.
234234
235235``` pascal
236236 const
237237 CTXlen = 16; // Buffer size
238238 var
239239 TXBuf : array[0..CTXlen - 1] of byte;
240240 TXread : byte = 0;
241241 TXwrite : byte = 0;
242242```
243243
244244### Initializarion
245245
246246This is similar to the receive buffer. Important: the interrupt function must not be activated yet, because there is nothing to send yet.
247247
248248``` pascal
249249 procedure UARTInit;
250250 begin
251251 UBRR0 := divider;
252252 UCSR0A := (0 shl U2X0);
253253 UCSR0B := (1 shl TXEN0) or (1 shl RXEN0);
254254 UCSR0C := %011 shl UCSZ0;
255255 end;
256256```
257257
258258### Interrupt
259259
260260The interrupt is triggered as soon as a character has been sent to the UART.
261261
262262As soon as the ring buffer is empty, the interrupt function is deactivated again.
263263
264264The query of **UDRE0** is not needed here, since the interrupt is only triggered when the buffer is empty.
265265
266266``` pascal
267267 procedure UART_UDRE_Send; public name 'USART__UDRE_ISR'; interrupt;
268268 begin
269269 // Send character
270270 UDR0 := TXBuf[TXread];
271271
272272 // Move ring buffer
273273 Inc(TXread);
274274
275275 // When pointer is at the end, start again
276276 if TXread >= CTXlen then
277277 begin
278278 TXread :=0;
279279 end;
280280
281281 // If the ring buffer is empty, an interrupt is no longer necessary
282282 if TXread = TXwrite then
283283 begin
284284 UCSR0B := UCSR0B and not (1 shl UDRIE0);
285285 end;
286286 end;
287287```
288288
289289### Send string
290290
291291As soon as something is in the send buffer, the interrupt is not activated.
292292
293293``` pascal
294294 procedure UARTSendString(s : ShortString);
295295 var
296296 i : byte;
297297 begin
298298 // Fill the ring buffer with data
299299 for i := 1 to Length(s) do
300300 begin
301301 TXBuf[TXwrite] := byte(s[i]);
302302 Inc(TXwrite);
303303
304304 if TXwrite >= CTXlen then
305305 begin
306306 TXwrite := 0;
307307 end;
308308 end;
309309
310310 // Enable send interrupt
311311 UCSR0B := UCSR0B or (1 shl UDRIE0);
312312 end;
313313```
314314
315315### Main loop
316316
317317The string is output in the background. In the example, an LED flashes during output.
318318
319319``` pascal
320320 var
321321 z : byte;
322322 s : ShortString;
323323 begin
324324 DDRB := DDRB or (1 shl 5);
325325
326326 // Initialize UART that is interrupt controlled
327327 UARTInit;
328328
329329 // Enable interrupts
330330 asm
331331 sei
332332 end;
333333
334334 // Main loop
335335 repeat
336336 UARTSendString('Hello World');
337337 UARTSendString('!'#13#10);
338338
339339 PORTB := PORTB or (1 shl 5);
340340 mysleep(sl);
341341
342342 Str(z : 3, s);
343343 UARTSendString(s + '.');
344344 Inc(z);
345345
346346 PORTB := PORTB and not (1 shl 5);
347347 mysleep(sl);
348348
349349 until 1 = 2;
350350 end.
351351```
352352
353353# UART emulation in software
354354
355355It is also possible to emulate a UART interface using software.
356356
357357This is practical if you have an AVR which has no/too few hardware UARTs.
358358
359359Note: This code only works with a 16MHz CPU clock and 9600 baud. Simply adjusting baud and CPU_Clock is not enough because the formula still has an error.
360360
361361Important: Interrupts must be blocked during the transmission.
362362
363363Reading and writing at the same time does not work because there is no buffer.
364364
365365Ideally, this can be used to output errors to a UART terminal.
366366
367367## Constants
368368
369369In this example, Pins 2 and 3 from Port D are used.
370370
371371``` pascal
372372 const
373373 CPU_Clock = 16000000; // Arduino clock frequency, default 16MHz.
374374 Baud = 9600; // Baud rate
375375
376376 TXpin = (1 shl 3); // Pin 3 (receive)
377377 RXpin = (1 shl 2); // Pin 2 (send)
378378
379379 softDivider = CPU_Clock div(Baud * 22); // bit counter
380380```
381381
382382## Set the receive pin to input
383383
384384``` pascal
385385 begin
386386 DDRD := DDRD or TXpin;
387387 ...
388388 // loop
389389 ...
390390 end.
391391```
392392
393393Note: If you want to use the same pins as for the hardware UART, pins 0 and 1 must be enabled on the Arduino.
394394
395395``` pascal
396396 begin
397397 UCSR0B := 0; // Release blocked pins 0 and 1
398398 ...
399399 // loop
400400 ...
401401 end.
402402```
403403
404404## Send characters
405405
406406``` pascal
407407 procedure UARTSoftSendByte(c : byte);
408408 var
409409 i, data : int16;
410410 j : Int8;
411411 begin
412412 asm
413413 cli // Disable interrupts
414414 end;
415415
416416 Data := (c shl 1) or $FE00;
417417
418418 for j := 0 to 9 do // Output bits, including stop bit
419419 begin
420420 if(Data and 1) = 1 then
421421 PORTD := PORTD or TXpin // HIGH
422422 else
423423 PORTD := PORTD and not TXpin; // LOW
424424
425425 Data := Data shr 1;
426426
427427 for i := 0 to softDivider do // One bit break
428428 ;
429429 end;
430430
431431 asm
432432 sei
433433 end; // Enable interrupts
434434 end;
435435```
436436
437437## Receive characters
438438
439439Note: If no character is received, the loop hangs forever.
440440
441441``` pascal
442442 function UARTSoftReadByte : byte;
443443 var
444444 i : Int16;
445445 j : Int8;
446446 begin
447447 asm
448448 cli // Disable interrupts
449449 end;
450450
451451 Result := 0;
452452
453453 while PIND and RXpin > 0 do // Wait until the first falling edge
454454 ;
455455
456456 for i := 0 to softDivider div 2 do // Half a bit break
457457 ;
458458
459459 for j : = 0 to 7 do
460460 begin
461461 for i := 0 to softDivider do // One bit break
462462 ;
463463
464464 Result := Result shr 1;
465465
466466 if PIND and RXpin <> 0 then
467467 Inc(Result, $80);
468468 end;
469469
470470 for i := 0 to softDivider // One bit break
471471 ;
472472
473473 asm
474474 sei // Enable interrupts
475475 end;
476476 end;
477477```
478478
479479# See also
480480
481481- [[AVR Embedded Tutorial|AVR Embedded Tutorials]] - Overview
482482
483483---
484484*From the Free Pascal wiki, page "AVR Embedded Tutorial - UART", as it stood on 2022-01-07 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).*
485485Category: AVR
486486Category: Arduino
487487Category: Embedded
488488Category: Tutorials

Back to history