Listen to your HMI

Serial communication basics part 2

In part 1 of this article series, published last Sunday, we learned about the difference between CPU and MCU, about integrated peripherals which act independently and communicate only on purpose with the CPU, and about (because the UART is the most solicited peripheral in most MCU based projects) the way, an Arduino (or any other MCU) handles incoming serial communication. But, as I learned from your feedback, many readers didn’t yet see the link with the underlying problem which led to the creation to these tutorials. Thus, I’ll try to move on, this week, and make things still clearer!

A look into the Arduino’s screenplay

The faulty version

User code: “Serial.begin(9600);”
Arduino framework: 
tells the CPU to write different bytes into the UART’s different configuration registers…
Arduino framework: 
tells the CPU to set up a 64 byte ring buffer with read and write pointers…
CPU: 
does so
UART:“Now I’m ready to receive data at 9600 baud, 8 bits inside a frame of one start, one stop and no parity bit.”
User code:
“int c = Serial.read();”
Arduino framework: tells the CPU to copy the byte from the receive buffer at the position pointed by…
Arduino framework: the read pointer onto the address of the integer variable c…
User code: 
“Serial.print(“c: “);”
User code: “Serial.println(c);”
RX line: “0110100101”
UART: “Seems like I received a 01001011 byte, embedded in a 0 as start bit and a 1 as stop bit – Hello, CPU!!!”
CPU: “Oh no, an interrupt from the UART!”
CPU: puts everything aside and looks up the interrupt vector table what to do, and jumps to the address…
CPU: executes the interrupt handler…
CPU: copying the byte from UART’s input register into the receive buffer at the position indicated by the write pointer...
CPU: increments the write pointer by 1 to be ready for the next incoming byte
CPU: 
“Phew…” – returns to what it was doing before the interrupt was called
User code (again): “int c = Serial.read();”
Arduino framework: tells the CPU to copy the byte from the receive buffer at the position pointed by…
Arduino framework: the read pointer onto the address of the integer variable c…
User code: “Serial.print(“c: “);”
User code: “Serial.println(c);”

– we do not lose time to look at the internals – just look at the result :

 

Huh? Where do these -1 come from? We received 11010010 on the RX line, which, after taking into account that the LSB is transmitted first, gives a byte which is reversed, thus, 01001011 in binary, or 4B in hex, or “K” in ASCII !

It looks like we did Serial.read something which hadn’t been received by Serial and on the other side, we didn’t read what was received 🙁

Why?

Remember, our first Serial.read() happened before anything was received. Thus, the position 0 of the Serial buffer contained the default (reset) value of 11111111. Since we asked the compiler to interpret our reading as a signed integer, it did so by printing out -1. After that read(), the read pointer was incremented by 1, but the write pointer stands still at 0 since no UART interrupt has occurred.

Now, when our 01001011 byte arrived, all the interrupt magic happened, and it was copied – you guess it – into the receive buffer at position 0 (the write pointer).

Then, the second Serial.read() was asked for and executed on the position 1 of the receive buffer, because the read pointer had been incremented before, after our first Serial.read(). It returns now again the default value of 11111111 or -1 because the received byte was stored at position 0.

How to prevent this mess?

The answer is simple: Before executing Serial.read(), make sure that there is something to read! The Arduino framework gives us a helper function for this: Serial.available(). It returns a number which corresponds to the difference between the write pointer and the read pointer of the receive buffer. If both have equal values, that means that either nothing has yet been received or everything received has already been read, the return value is 0. If there are unread bytes in the buffer, the write pointer will be greater as the read pointer, and the return value will be a positive number.

With our first Serial.read() above, we did something meaningless: We moved the read pointer in front of the write pointer, so that Serial.available() should basically return -1. But because the 64 byte receive buffer is circular, it would return 63 and we’d have to execute 63 more Serial.read() returning rubbish, before we’d access our incoming byte.

Now, using C logic which interprets 0 as false and any positive number as true, we can “secure” our Serial.read() operations simply by packing it inside an if() condition:

if (Serial.available()) {
  int c = Serial.read();
  Serial.print("c: ");
  Serial.println(c);
}

With this, we can now be sure that even when checking repeatedly inside loop() if something has arrived on the serial port, we’ll never mess up with the read and write pointers of the buffer. And the Serial.read() and the following printout of what had been read will only show what was really received.

And that will also solve R.’s problem, since he will never again be confronted to almost endless series of printed out -1 🙂

Thank you for reading and happy Nextioning!

Questions, comments, critics, suggestions? Just send me an email to thierry (at) itead (dot) cc! 🙂

And, by the way, if you like what I write, and you are about to order Nextion stuff with Itead, please use my referral link! To you, it won’t make a change for your order. But it will pay me the one or the other beer or coffee. And that will motivate me to write more interesting blogs 😉