Catto / Topic Map · Computer Science II Level 2
Computer Science II · 120 h · Topic 4 of 8

Peripheral control and communication

A program stops being an exercise when it lights a real LED. At the other end of the cable a microcontroller is waiting for bytes, and you have to agree on what each one means.

Serial port Frames Checksum Buffers Protocol

01From the program to the physical world

A program that only prints to the screen does not need to know anything about the hardware. As soon as you have to light an LED or read a sensor, three questions appear: where does the data go out, how fast, and what does each byte mean.

InterfaceTypical speedWhere it is used
Asynchronous serial (UART/USB-serial)9600 to 921,600 baudThe most common link between a PC and a microcontroller
I²C100 kHz to 1 MHzSensors and peripherals within a board
SPI1 to 50 MHzMemories, displays, fast converters
Native USB12 to 480 Mbit/sInstruments and data acquisition
Ethernet / TCP-IP10 to 1000 Mbit/sNetworked equipment, industry

On the computer, the serial port is opened as if it were a file: you configure it, write to it and read from it. In Python with pyserial it takes three lines; in C, with termios on Linux or CreateFile on Windows, a few more, but the idea is the same.

import serial
p = serial.Serial("COM3", 115200, timeout=0.2)
p.write(b"\xAA\x02\x01\x64\x67")
response = p.read(5)

02The parameters that must be agreed on

Both ends have to be configured the same way or they will not understand each other. With 8N1 —eight data bits, no parity, one stop bit— each byte travels in ten bits:

\[ t_{byte} = \frac{10}{\text{baud}} \quad\Longrightarrow\quad \text{maximum throughput} = \frac{\text{baud}}{10} \ \text{bytes/s} \] At 115,200 baud that is 11,520 bytes per second, and each byte takes 87 µs.
  • Speed in baud: 9600, 19,200, 38,400, 57,600, 115,200…
  • Data bits, almost always 8.
  • Parity: none, even or odd. It detects one flipped bit, not two.
  • Stop bits: 1 or 2.
  • Flow control: hardware (RTS/CTS) or none.
Electrical levels

A microcontroller’s UART works at 3.3 or 5 V; a true RS-232 port uses ±12 V with inverted logic. Connecting them directly burns out the microcontroller. In between goes an adapter —a MAX232 or a USB-serial converter— and, if the voltages differ, a level shifter.

03Inventing a protocol

The port carries loose bytes; the meaning is supplied by the protocol. A minimal scheme that works in practice has four parts:

FieldPurpose
Start byteMarks where the frame begins, so that it can resynchronize
Command and lengthWhat is requested and how many data bytes follow
DataThe content
CheckDetects whether something arrived wrong: sum, XOR or CRC
Lab · building and checking a frame

Change the command and the data, choose the check method and try corrupting a byte to see whether the receiver notices.

04Sum, XOR and CRC

MethodHow it is calculatedWhat it detects
Checksum\( \sum b_i \bmod 256 \)One changed byte; not the order
XOR\( b_1 \oplus b_2 \oplus \cdots \oplus b_n \)One flipped bit per column; fails with two
CRC-8Binary division by a polynomialAll bursts of up to 8 bits
CRC-16 / CRC-32The same, with a longer polynomialWhat is used in Modbus, USB and Ethernet

The sum is the easiest to write and the weakest: if two bytes change in compensating ways, it does not notice. The CRC is a division of polynomials in binary arithmetic and detects practically any real transmission error; that is why every serious protocol uses it. It is implemented with a 256-entry table and is extremely fast, even on a small microcontroller.

05Waiting without getting stuck

The most common mistake when communicating with a device is to wait forever. There are three strategies, and it is worth knowing all three:

  • Read with a timeout. timeout on the port: if the device does not answer within 200 ms, the read returns empty and the program decides what to do. It is the bare minimum.
  • Dedicated thread. One thread reads the port and deposits the frames in a queue; the main program consumes it when it can. This is the producer-consumer scheme.
  • Asynchronous input/output. The system signals when data is available, with nobody waiting.
On the other end of the cable

The microcontroller has the same problem in reverse: if it services the port inside the main loop, it loses bytes while it does something else. That is why reception is done in the UART interrupt, which stores each byte in a circular buffer, and the main program processes the frame when it is complete.

06In the lab

Exercise 1 · Echo and timing measurements

Program the microcontroller to send back every byte it receives. From the computer, measure the round-trip time at 9600 and at 115,200 baud, and compare with 10 bits per byte.

Exercise 2 · A protocol of your own

Define and implement the protocol of this page at both ends: turn an output on and off, set a PWM and read an analog channel. Document the frame in a table.

Exercise 3 · Causing errors

Unplug the cable in the middle of a transmission, or lower the speed on one side. Check that the receiver discards malformed frames and resynchronizes on the start byte, instead of hanging.

07Common mistakes

  • Different speeds at each end. Bytes arrive, but with garbage.
  • Reading without a timeout and leaving the program hung forever.
  • Assuming that one read returns the whole frame. It may come back split: you have to accumulate until you have the declared length.
  • Connecting 5 V to a 3.3 V input.
  • Forgetting the common ground between the two devices.
  • Trusting that the channel is perfect and not checking anything.

08Self-assessment

How many bytes per second come in at 115,200 baud in 8N1?

Each byte takes up 10 bits, so 11,520 bytes/s, about 87 µs per byte.

What is the start byte of a frame for?

To be able to resynchronize: if a byte was lost, the receiver discards data until it finds the next start byte instead of interpreting garbage.

Why is the CRC better than the sum?

Because it detects bursts of errors and combinations that the sum does not see, such as two bytes that change in compensating ways.

Where is it best to receive the bytes on the microcontroller?

In the UART interrupt, storing them in a circular buffer. In the main loop they would be lost while the program does something else.

What happens if a 3.3 V UART and an RS-232 port are connected?

RS-232 uses ±12 V with inverted logic: it burns out the input. You need an adapter such as the MAX232.

A read returned 3 bytes of a 6-byte frame. What should be done?

Accumulate them and keep reading until the declared length is complete, or discard on timeout. A serial read does not guarantee delivering the whole frame at once.

09Further reading

  • Jan Axelson. Serial Port Complete. 2nd ed., Lakeview Research, 2007. Everything about serial ports, USB-serial and programming them on the PC.
  • Michael Barr and Anthony Massa. Programming Embedded Systems in C and C++. 2nd ed., O'Reilly, 2006. The microcontroller side: interrupts, buffers and peripheral drivers.
  • pySerial documentation. pySerial Documentation. Current version. The standard library for talking to a serial port from Python.
Development of the topic “Peripheral control and communication” of Computer Science II (Level 2), based on the curriculum of the UTN Electronic Engineering program, 2023 curriculum — Ordinance No. 1849 of the UTN Higher Council. Back to the Topic Map · catto.ar