Introduction Embedded electronics is all about interlinking circuits (processors or other integrated circuits) to create a symbiotic system. In order for those individual circuits to swap their information, they must share a common communication protocol. Hundreds of communication protocols have been defined to achieve this data exchange, and, in general, each can be separated into one of two categories: parallel or serial. Serial Parallel interfaces transfer multiple bits at the same time. They usually require buses of data - transmitting across eight, sixteen, or more wires. Data is transferred in huge, crashing waves of 1’s and 0’s. An 8-bit data bus, controlled by a clock, transmitting a byte every clock pulse.
9 wires are used. Serial interfaces stream their data, one single bit at a time. These interfaces can operate on as little as one wire, usually never more than four.
Example of a serial interface, transmitting one bit every clock pulse. Just 2 wires required! Think of the two interfaces as a stream of cars: a parallel interface would be the 8+ lane mega-highway, while a serial interface is more like a two-lane rural country road.
Over a set amount of time, the mega-highway potentially gets more people to their destinations, but that rural two-laner serves its purpose and costs a fraction of the funds to build. Parallel communication certainly has its benefits. It’s fast, straightforward, and relatively easy to implement. But it requires many more input/output (I/O) lines. If you’ve ever had to move a project from a basic to a, you know that the I/O lines on a microprocessor can be precious and few.
So, we often opt for serial communication, sacrificing potential speed for pin real estate. Asynchronous Serial Over the years, dozens of serial protocols have been crafted to meet particular needs of embedded systems. USB (universal serial bus), and Ethernet, are a couple of the more well-known computing serial interfaces. Other very common serial interfaces include SPI, I 2C, and the serial standard we’re here to talk about today. Each of these serial interfaces can be sorted into one of two groups: synchronous or asynchronous. A synchronous serial interface always pairs its data line(s) with a clock signal, so all devices on a synchronous serial bus share a common clock.
This makes for a more straightforward, often faster serial transfer, but it also requires at least one extra wire between communicating devices. Examples of synchronous interfaces include SPI, and I 2C. Asynchronous means that data is transferred without support from an external clock signal.
This transmission method is perfect for minimizing the required wires and I/O pins, but it does mean we need to put some extra effort into reliably transferring and receiving data. The serial protocol we’ll be discussing in this tutorial is the most common form of asynchronous transfers. It is so common, in fact, that when most folks say “serial” they’re talking about this protocol (something you’ll probably notice throughout this tutorial).
The clock-less serial protocol we’ll be discussing in this tutorial is widely used in embedded electronics. If you’re looking to add a GPS module, Bluetooth, XBee’s, serial LCDs, or many other external devices to your project, you’ll probably need to whip out some serial-fu. Suggested Reading This tutorial builds on a few lower-level electronics concepts, including:. If you’re not super familiar with any of those concepts, consider checking those links out. Now then, let’s go on a serial journey.
Rules of Serial The asynchronous serial protocol has a number of built-in rules - mechanisms that help ensure robust and error-free data transfers. These mechanisms, which we get for eschewing the external clock signal, are:.
Data bits,. Synchronization bits,. Parity bits,. and Baud rate.
Through the variety of these signaling mechanisms, you’ll find that there’s no one way to send data serially. The protocol is highly configurable.
The critical part is making sure that both devices on a serial bus are configured to use the exact same protocols. Baud Rate The baud rate specifies how fast data is sent over a serial line. It’s usually expressed in units of bits-per-second (bps). If you invert the baud rate, you can find out just how long it takes to transmit a single bit. This value determines how long the transmitter holds a serial line high/low or at what period the receiving device samples its line.
Baud rates can be just about any value within reason. The only requirement is that both devices operate at the same rate. One of the more common baud rates, especially for simple stuff where speed isn’t critical, is 9600 bps. Other “standard” baud are 1200, 2400, 4800, 19200, 38400, 57600, and 115200. The higher a baud rate goes, the faster data is sent/received, but there are limits to how fast data can be transferred.
You usually won’t see speeds exceeding 115200 - that’s fast for most microcontrollers. Get too high, and you’ll begin to see errors on the receiving end, as clocks and sampling periods just can’t keep up. Framing the data Each block (usually a byte) of data transmitted is actually sent in a packet or frame of bits.
Frames are created by appending synchronization and parity bits to our data. A serial frame. Some symbols in the frame have configurable bit sizes. Let’s get into the details of each of these frame pieces. Data chunk The real meat of every serial packet is the data it carries. We ambiguously call this block of data a chunk, because its size isn’t specifically stated. The amount of data in each packet can be set to anything from 5 to 9 bits.
Certainly, the standard data size is your basic 8-bit byte, but other sizes have their uses. A 7-bit data chunk can be more efficient than 8, especially if you’re just transferring 7-bit ASCII characters.
After agreeing on a character-length, both serial devices also have to agree on the endianness of their data. Is data sent most-significant bit (msb) to least, or vice-versa? If it’s not otherwise stated, you can usually assume that data is transferred least-significant bit (lsb) first. Synchronization bits The synchronization bits are two or three special bits transferred with each chunk of data. They are the start bit and the stop bit(s). True to their name, these bits mark the beginning and end of a packet. There’s always only one start bit, but the number of stop bits is configurable to either one or two (though it’s commonly left at one).
The start bit is always indicated by an idle data line going from 1 to 0, while the stop bit(s) will transition back to the idle state by holding the line at 1. Parity bits Parity is a form of very simple, low-level error checking. It comes in two flavors: odd or even.
To produce the parity bit, all 5-9 bits of the data byte are added up, and the evenness of the sum decides whether the bit is set or not. For example, assuming parity is set to even and was being added to a data byte like 0b01011101, which has an odd number of 1’s (5), the parity bit would be set to 1. Conversely, if the parity mode was set to odd, the parity bit would be 0. Parity is optional, and not very widely used. It can be helpful for transmitting across noisy mediums, but it’ll also slow down your data transfer a bit and requires both sender and receiver to implement error-handling (usually, received data that fails must be re-sent). 9600 8N1 (an example) 9600 8N1 - 9600 baud, 8 data bits, no parity, and 1 stop bit - is one of the more commonly used serial protocols. So, what would a packet or two of 9600 8N1 data look like?
Let’s have an example! A device transmitting the characters ‘O’ and ‘K’ would have to create two packets of data. The ASCII value of O (that’s uppercase) is 79, which breaks down into an 8-bit binary value of 01001111, while K’s binary value is 01001011. All that’s left is appending sync bits. It isn’t specifically stated, but it’s assumed that data is transferred least-significant bit first. Notice how each of the two bytes is sent as it reads from right-to-left. Since we’re transferring at 9600 bps, the time spent holding each of those bits high or low is 1/(9600 bps) or 104 µs per bit.
For every byte of data transmitted, there are actually 10 bits being sent: a start bit, 8 data bits, and a stop bit. So, at 9600 bps, we’re actually sending 9600 bits per second or 960 (9600/10) bytes per second. Now that you know how to construct serial packets, we can move on to the hardware section. There we’ll see how those 1’s and 0’s and the baud rate are implemented at a signal level!
Wiring and Hardware A serial bus consists of just two wires - one for sending data and another for receiving. As such, serial devices should have two serial pins: the receiver, RX, and the transmitter, TX. It’s important to note that those RX and TX labels are with respect to the device itself. So the RX from one device should go to the TX of the other, and vice-versa. It’s weird if you’re used to hooking up VCC to VCC, GND to GND, MOSI to MOSI, etc., but it makes sense if you think about it. The transmitter should be talking to the receiver, not to another transmitter.
A serial interface where both devices may send and receive data is either full-duplex or half-duplex. Full-duplex means both devices can send and receive simultaneously. Half-duplex communication means serial devices must take turns sending and receiving.
Some serial busses might get away with just a single connection between a sending and receiving device. For example, our are all ears and don’t really have any data to relay back to the controlling device. This is what’s known as simplex serial communication. All you need is a single wire from the master device’s TX to the listener’s RX line.
Hardware Implementation We’ve covered asynchronous serial from a conceptual side. We know which wires we need.
But how is serial communication actually implemented at a signal level? In a variety ways, actually. There are all sorts of standards for serial signaling.
Let’s look at a couple of the more popular hardware implementations of serial: logic-level (TTL) and RS-232. When microcontrollers and other low-level ICs communicate serially they usually do so at a TTL (transistor-transistor logic) level. TTL serial signals exist between a microcontroller’s voltage supply range - usually 0V to 3.3V or 5V. A signal at the VCC level (3.3V, 5V, etc.) indicates either an idle line, a bit of value 1, or a stop bit. A 0V (GND) signal represents either a start bit or a data bit of value 0. RS-232, which can be found on some of the more ancient computers and peripherals, is like TTL serial flipped on its head.
RS-232 signals usually range between -13V and 13V, though the spec allows for anything from +/- 3V to +/- 25V. On these signals a low voltage (-5V, -13V, etc.) indicates either the idle line, a stop bit, or a data bit of value 1. A high RS-232 signal means either a start bit, or a 0-value data bit. That’s kind of the opposite of TTL serial.
Between the two serial signal standards, TTL is much easier to implement into embedded circuits. However the low voltage levels are more susceptible to losses across long transmission lines. RS-232, or more complex standards like RS-485, are better suited to long range serial transmissions. When you’re connecting two serial devices together, it’s important to make sure their signal voltages match up.
You can’t directly interface a TTL serial device with an RS-232 bus. You’ll have to!
Continuing on, we’ll explore the tool microcontrollers use to convert their data on a parallel bus to and from a serial interface. UARTs The final piece to this serial puzzle is finding something to both create the serial packets and control those physical hardware lines. Enter the UART. A universal asynchronous receiver/transmitter (UART) is a block of circuitry responsible for implementing serial communication. Essentially, the UART acts as an intermediary between parallel and serial interfaces.
On one end of the UART is a bus of eight-or-so data lines (plus some control pins), on the other is the two serial wires - RX and TX. Super-simplified UART interface. Parallel on one end, serial on the other. UARTs do exist as stand-alone ICs, but they’re more commonly found inside microcontrollers. You’ll have to check your microcontroller’s datasheet to see if it has any UARTs. Some have none, some have one, some have many. For example, the Arduino Uno - based on the “old faithful” ATmega328 - has just a single UART, while the Arduino Mega - built on an ATmega2560 - has a whopping four UARTs.
As the R and T in the acronym dictate, UARTs are responsible for both sending and receiving serial data. On the transmit side, a UART must create the data packet - appending sync and parity bits - and send that packet out the TX line with precise timing (according to the set baud rate). On the receive end, the UART has to sample the RX line at rates according to the expected baud rate, pick out the sync bits, and spit out the data.
Internal UART block diagram (courtesy of the Exar ST16C550 datasheet) More advanced UARTs may throw their received data into a buffer, where it can stay until the microcontroller comes to get it. UARTs will usually release their buffered data on a first-in-first-out (FIFO) basis. Buffers can be as small as a few bits, or as large as thousands of bytes. Software UARTs If a microcontroller doesn’t have a UART (or doesn’t have enough), the serial interface can be bit-banged - directly controlled by the processor. This is the approach Arduino libraries like take. Bit-banging is processor-intensive, and not usually as precise as a UART, but it works in a pinch!
Common Pitfalls That’s about all there is to serial communication. I’d like to leave you with a few common mistakes that are easy for an engineer of any experience level to make: RX-to-TX, TX-to-RX Seems simple enough, but it’s a mistake I know I’ve made more than a few times. As much as you want their labels to match up, always make sure to cross the RX and TX lines between serial devices. Programming a.
Note RX and TX’s crossed! Contrary to what the esteemed Dr. Egon Spengler would, cross the streams. Baud Rate Mismatch Baud rates are like the languages of serial communication.
If two devices aren’t speaking at the same speed, data can be either misinterpreted, or completely missed. If all the receiving device sees on its receive line is garbage, check to make sure the baud rates match up. Data transmitted at 9600 bps, but received at 19200 bps. Baud mismatch = garbage. Bus Contention Serial communication is designed to allow just two devices to communicate across one serial bus. If more than one device is trying to transmit on the same serial line you could run into bus-contention. For example, if you’re connecting a GPS module up to your Arduino, you may just wire that module’s TX line up the Arduino’s RX line.
But that Arduino RX pin is already wired up to the TX pin of the USB-to-serial converter, which is used whenever you program the Arduino or use the Serial Monitor. This sets up the potential situation where both the GPS module and FTDI chip are trying to transmit on the same line at the same time. Two transmitters sending to a single receiver sets up the possibility for bus contention.
Two devices trying to transmit data at the same time, on the same line, is bad! At “best” neither of the devices will get to send their data. At worst, both device’s transmit lines go poof (though that’s rare, and usually protected against). It can be safe to connect multiple receiving devices to a single transmitting device. Not really up to spec and probably frowned upon by a hardened engineer, but it’ll work. For example, if you’re connecting a serial LCD up to an Arduino, the easiest approach may be to connect the LCD module’s RX line to the Arduino’s TX line. The Arduino’s TX is already connected to the USB programmer’s RX line, but that still leaves just one device in control of the transmission line.
Distributing a TX line like this can still be dangerous from a firmware perspective, because you can’t pick and choose which device hears what transmission. The LCD will end up receiving data not meant for it, which could command it to go into an unknown state. In general - one serial bus, two serial devices! In 2003, CU student Nate Seidle blew a power supply in his dorm room and, in lieu of a way to order easy replacements, decided to start his own company. Since then, SparkFun has been committed to sustainably helping our world achieve electronics literacy from our headquarters in Boulder, Colorado. No matter your vision, SparkFun's products and resources are designed to make the world of electronics more accessible. In addition to over 2,000 open source components and widgets, SparkFun offers curriculum, training and online tutorials designed to help demystify the wonderful world of embedded electronics.
We're here to help you start something.
What's Wrong with Serial Ports? A common serial port, the kind with TX and RX lines, is called “asynchronous” (not synchronous) because there is no control over when data is sent or any guarantee that both sides are running at precisely the same rate. Since computers normally rely on everything being synchronized to a single “clock” (the main crystal attached to a computer that drives everything), this can be a problem when two systems with slightly different clocks try to communicate with each other. To work around this problem, asynchronous serial connections add extra start and stop bits to each byte help the receiver sync up to data as it arrives.
Both sides must also agree on the transmission speed (such as 9600 bits per second) in advance. Slight differences in the transmission rate aren’t a problem because the receiver re-syncs at the start of each byte. (By the way, if you noticed that “11001010” does not equal 0x53 in the above diagram, kudos to your attention to detail. Serial protocols will often send the least significant bits first, so the smallest bit is on the far left. The lower nybble is actually 0011 = 0x3, and the upper nybble is 0101 = 0x5.) Asynchronous serial works just fine, but has a lot of overhead in both the extra start and stop bits sent with every byte, and the complex hardware required to send and receive data. And as you’ve probably noticed in your own projects, if both sides aren’t set to the same speed, the received data will be garbage.
This is because the receiver is sampling the bits at very specific times (the arrows in the above diagram). If the receiver is looking at the wrong times, it will see the wrong bits. A Synchronous Solution SPI works in a slightly different manner. It’s a “synchronous” data bus, which means that it uses separate lines for data and a “clock” that keeps both sides in perfect sync. The clock is an oscillating signal that tells the receiver exactly when to sample the bits on the data line. This could be the rising (low to high) or falling (high to low) edge of the clock signal; the datasheet will specify which one to use.
When the receiver detects that edge, it will immediately look at the data line to read the next bit (see the arrows in the below diagram). Because the clock is sent along with the data, specifying the speed isn’t important, although devices will have a top speed at which they can operate (We’ll discuss choosing the proper clock edge and speed in a bit). One reason that SPI is so popular is that the receiving hardware can be a simple. This is a much simpler (and cheaper!) piece of hardware than the full-up UART (Universal Asynchronous Receiver / Transmitter) that asynchronous serial requires. Receiving Data You might be thinking to yourself, self, that sounds great for one-way communications, but how do you send data back in the opposite direction? Here’s where things get slightly more complicated. In SPI, only one side generates the clock signal (usually called CLK or SCK for Serial ClocK).
The side that generates the clock is called the “master”, and the other side is called the “slave”. There is always only one master (which is almost always your microcontroller), but there can be multiple slaves (more on this in a bit). When data is sent from the master to a slave, it’s sent on a data line called MOSI, for “Master Out / Slave In”. If the slave needs to send a response back to the master, the master will continue to generate a prearranged number of clock cycles, and the slave will put the data onto a third data line called MISO, for “Master In / Slave Out”. Notice we said “prearranged” in the above description. Because the master always generates the clock signal, it must know in advance when a slave needs to return data and how much data will be returned. This is very different than asynchronous serial, where random amounts of data can be sent in either direction at any time.
In practice this isn’t a problem, as SPI is generally used to talk to sensors that have a very specific command structure. For example, if you send the command for “read data” to a device, you know that the device will always send you, for example, two bytes in return.
(In cases where you might want to return a variable amount of data, you could always return one or two bytes specifying the length of the data and then have the master retrieve the full amount.) Note that SPI is “full duplex” (has separate send and receive lines), and, thus, in certain situations, you can transmit and receive data at the same time (for example, requesting a new sensor reading while retrieving the data from the previous one). Your device’s datasheet will tell you if this is possible. Slave Select (SS) There’s one last line you should be aware of, called SS for Slave Select. This tells the slave that it should wake up and receive / send data and is also used when multiple slaves are present to select the one you’d like to talk to.
The SS line is normally held high, which disconnects the slave from the SPI bus. (This type of logic is known as “active low,” and you’ll often see used it for enable and reset lines.) Just before data is sent to the slave, the line is brought low, which activates the slave. When you’re done using the slave, the line is made high again. In a, this corresponds to the “latch” input, which transfers the received data to the output lines. Multiple slaves There are two ways of connecting multiple slaves to an SPI bus:. In general, each slave will need a separate SS line.
To talk to a particular slave, you’ll make that slave’s SS line low and keep the rest of them high (you don’t want two slaves activated at the same time, or they may both try to talk on the same MISO line resulting in garbled data). Lots of slaves will require lots of SS lines; if you’re running low on outputs, there are that can multiply your SS outputs. On the other hand, some parts prefer to be daisy-chained together, with the MISO (output) of one going to the MOSI (input) of the next. In this case, a single SS line goes to all the slaves. Once all the data is sent, the SS line is raised, which causes all the chips to be activated simultaneously.
This is often used for daisy-chained shift registers and. Note that, for this layout, data overflows from one slave to the next, so to send data to any one slave, you’ll need to transmit enough data to reach all of them. Also, keep in mind that the first piece of data you transmit will end up in the last slave. This type of layout is typically used in output-only situations, such as driving LEDs where you don’t need to receive any data back. In these cases you can leave the master’s MISO line disconnected.
However, if data does need to be returned to the master, you can do this by closing the daisy-chain loop (blue wire in the above diagram). Note that if you do this, the return data from slave 1 will need to pass through all the slaves before getting back to the master, so be sure to send enough receive commands to get the data you need. Programming for SPI Many microcontrollers have built-in SPI peripherals that handle all the details of sending and receiving data, and can do so at very high speeds. The SPI protocol is also simple enough that you (yes, you!) can write your own routines to manipulate the I/O lines in the proper sequence to transfer data. (A good example is on the.) If you’re using an Arduino, there are two ways you can communicate with SPI devices:. You can use the and commands.
These are software-based commands that will work on any group of pins, but will be somewhat slow. Or you can use the, which takes advantage of the SPI hardware built into the microcontroller. This is vastly faster than the above commands, but it will only work on certain pins. You will need to select some options when setting up your interface. These options must match those of the device you’re talking to; check the device’s datasheet to see what it requires. The interface can send data with the most-significant bit (MSB) first, or least-significant bit (LSB) first. In the Arduino SPI library, this is controlled by the function.
The slave will read the data on either the rising edge or the falling edge of the clock pulse. Additionally, the clock can be considered “idle” when it is high or low. In the Arduino SPI library, both of these options are controlled by the function.
SPI can operate at extremely high speeds (millions of bytes per second), which may be too fast for some devices. To accommodate such devices, you can adjust the data rate. In the Arduino SPI library, the speed is set by the function, which divides the master clock (16MHz on most Arduinos) down to a frequency between 8MHz (/2) and 125kHz (/128).
If you’re using the SPI Library, you must use the provided SCK, MOSI and MISO pins, as the hardware is hardwired to those pins. There is also a dedicated SS pin that you can use (which must, at least, be set to an output in order for the SPI hardware to function), but note that you can use any other available output pin(s) for SS to your slave device(s) as well. On older Arduinos, you’ll need to control the SS pin(s) yourself, making one of them low before your data transfer and high afterward. Newer Arduinos such as the Due can control each SS pin automatically as part of the data transfer; see the for more information. Resources and Going Further Tips and Tricks. Because of the high speed signals, SPI should only be used to send data over short distances (up to a few feet). If you need to send data further than that, and consider using.
If things aren’t working the way you think they should, a logic analyzer is a very helpful tool. Smart analyzers like the can even decode the data bytes for a display or logging. Advantages of SPI:. It’s faster than asynchronous serial. The receive hardware can be a simple shift register. It supports multiple slaves Disadvantages of SPI:. It requires more signal lines (wires) than other communications methods.
The communications must be well-defined in advance (you can’t send random amounts of data whenever you want). The master must control all communications (slaves can’t talk directly to each other). It usually requires separate SS lines to each slave, which can be problematic if numerous slaves are needed. Further Reading Check out the, which includes lots of good information on SPI and other synchronous interfaces.
Presents a more correct way to set up an SPI network amongst your embedded devices, particularly for use with an microcontroller. A number of SparkFun products have SPI interfaces. For example, the has an easy-to-use SPI interface that you can use to turn any of 30 LEDs on or off. Other communication options:.
Now that you’re a pro on SPI, here are some other tutorials to practice your new skills:. In 2003, CU student Nate Seidle blew a power supply in his dorm room and, in lieu of a way to order easy replacements, decided to start his own company. Since then, SparkFun has been committed to sustainably helping our world achieve electronics literacy from our headquarters in Boulder, Colorado. No matter your vision, SparkFun's products and resources are designed to make the world of electronics more accessible.
In addition to over 2,000 open source components and widgets, SparkFun offers curriculum, training and online tutorials designed to help demystify the wonderful world of embedded electronics. We're here to help you start something.
Chapter 11: Serial Interfacing Embedded Systems - Shape The World Jonathan Valvano and Ramesh Yerraballi This chapter provides an introduction to serial interfacing, which means we send one bit at time. Serial communication is prevalent in both the computer industry in general and the embedded industry in specific. There are many serial protocols, but in this course we will show you one of the first and simplest protocols that transmit one bit at a time. We will show the theory and details of the universal asynchronous receiver/transmitter (UART) and then use it as an example for developing an I/O driver. We will use busy-wait to synchronize the software with the hardware. Learning Objectives:. I/O synchronization.
Models of I/O devices (busy, done, off). Learn how to program the UART. Build a distributed system by connecting two systems together. Learn how to convert between numbers and ASCII strings Video 11.0. Introduction to Serial Communication 11.1. I/O Synchronization Before we begin define serial communication, let's begin by introducing some performance measures.
As engineers and scientists we are constantly making choices as we design new product or upgrade existing systems. A performance measure is a quantitative metric that the goodness of the system.
The metrics and synchronization algorithms presented in this section will apply to all I/O communication. Latency is the time between when the I/O device indicated service is required and the time when service is initiated. Latency includes hardware delays in the digital hardware plus computer software delays.
For an input device, software latency (or software response time) is the time between new input data ready and the software reading the data. For an output device, latency is the delay from output device idle and the software giving the device new data to output. In this book, we will also have periodic events. For example, in our data acquisition systems, we wish to invoke the analog to digital converter (ADC) at a fixed time interval. In this way we can collect a sequence of digital values that approximate the continuous analog signal. Software latency in this case is the time between when the ADC conversion is supposed to be started, and when it is actually started.
The microcomputer-based control system also employs periodic software processing. Similar to the data acquisition system, the latency in a control system is the time between when the control software is supposed to be run, and when it is actually run. A real-time system is one that can guarantee a worst case latency. In other words, the software response time is small and bounded. Furthermore, this bound is small enough to satisfy overall specification of the system, such as no lost data. Throughput or bandwidth is the maximum data flow in bytes/second that can be processed by the system. Sometimes the bandwidth is limited by the I/O device, while other times it is limited by computer software.
Bandwidth can be reported as an overall average or a short-term maximum. Priority determines the order of service when two or more requests are made simultaneously. Priority also determines if a high-priority request should be allowed to suspend a low priority request that is currently being processed. We may also wish to implement equal priority, so that no one device can monopolize the computer. In some computer literature, the term 'soft-real-time' is used to describe a system that supports priority.
The purpose of our interface is to allow the microcontroller to interact with its external I/O device. One of the choices the designer must make is the algorithm for how the software synchronizes with the hardware. There are five mechanisms to synchronize the microcontroller with the I/O device.
Each mechanism synchronizes the I/O data transfer to the busy to done transition. The methods are discussed in the following paragraphs. Synchronization Mechanisms Video 11.1. Device Communication requires Synchronization Blind cycle is a method where the software simply waits a fixed amount of time and assumes the I/O will complete before that fixed delay has elapsed. For an input device, the software triggers (starts) the external input hardware, waits a specified time, then reads data from device.
Blind cycle synchronization for an input device is shown on the left part of Figure 11.1. For an output device, shown on the left part of Figure 11.2, the software writes data to the output device, triggers (starts) the device, then waits a specified time. We call this method blind, because there is no status information about the I/O device reported to the software. It is appropriate to use this method in situations where the I/O speed is short and predictable.
We can ask the LCD to display an ASCII character, wait 37 µs, and then we are sure the operation is complete. This method works because the LCD speed is short and predictable.
Another good example of blind-cycle synchronization is spinning a stepper motor. If we repeat this 8-step sequence over and over 1) output a 0x05, 2) wait 1ms, 3) output a 0x06, 4) wait 1ms, 5) output a 0x0A, 6) wait 1ms, 7) output a 0x09, 8) wait 1ms, the motor will spin at a constant speed. The output device sets a flag when it has finished outputting the last data. Interactive Tool 11.1 Use the following tool to see how blind-cycle synchronization works. You will need to enter a number between 1-10 to simulate the timing behavior of the device.
Enter an amount of time to wait (1-10): Busy Wait is a software loop that checks the I/O status waiting for the done state. For an input device, the software waits until the input device has new data, and then reads it from the input device, see the middle parts of Figures 11.1 and 11.2.
For an output device, the software writes data, triggers the output device then waits until the device is finished. Another approach to output device interfacing is for the software to wait until the output device has finished the previous output, write data, and then trigger the device. Busy-wait synchronization will be used in situations where the software system is relatively simple and real-time response is not important.
The UART software in this chapter will use busy-wait synchronization. Interactive Tool 11.2 Use the following tool to see how busy-wait synchronization works. You will press the 'Ready' button to simulate the device being ready. Click to simulate I/O device becoming ready.
An interrupt uses hardware to cause special software execution. With an input device, the hardware will request an interrupt when input device has new data. The software interrupt service will read from the input device and save in global RAM, see the right parts of Figures 11.1 and 11.2. With an output device, the hardware will request an interrupt when the output device is idle. The software interrupt service will get data from a global structure, and then write to the device.
Sometimes we configure the hardware timer to request interrupts on a periodic basis. The software interrupt service will perform a special function. A data acquisition system needs to read the ADC at a regular rate.
Interrupt synchronization will be used in situations where the system is fairly complex (e.g., a lot of I/O devices) or when real-time response is important. Interrupts will be presented in Chapter 12. Interactive Tool 11.3 Use the following tool to see how interrupt-based synchronization works. The foreground thread and background thread (the Interrupt Service Routine or ISR) communicate using a buffer called a first in first out queue (FIFO).
Periodic Polling uses a clock interrupt to periodically check the I/O status. At the time of the interrupt the software will check the I/O status, performing actions as needed. With an input device, a ready flag is set when the input device has new data. At the next periodic interrupt after an input flag is set, the software will read the data and save them in global RAM. With an output device, a ready flag is set when the output device is idle.
At the next periodic interrupt after an output flag is set, the software will get data from a global structure, and write it. Periodic polling will be used in situations that require interrupts, but the I/O device does not support interrupt requests directly. DMA, or,direct memory access, is an interfacing approach that transfers data directly to/from memory. With an input device, the hardware will request a DMA transfer when the input device has new data. Without the software’s knowledge or permission the DMA controller will read data from the input device and save it in memory. With an output device, the hardware will request a DMA transfer when the output device is idle. The DMA controller will get data from memory, and then write it to the device.
Sometimes we configure the hardware timer to request DMA transfers on a periodic basis. DMA can be used to implement a high-speed data acquisition system. DMA synchronization will be used in situations where high bandwidth and low latency are important. DMA will not be covered in this introductory class. For details on how to implement DMA on the LM4F120/TM4C123, see.
One can think of the hardware being in one of three states. The idle state is when the device is disabled or inactive. No I/O occurs in the idle state. When active (not idle) the hardware toggles between the busy and ready states. The interface includes a flag specifying either busy (0) or ready (1) status.
Hardware-software synchronization revolves around this flag: The hardware will set the flag when the hardware component is complete. The software can read the flag to determine if the device is busy or ready.
The software can clear the flag, signifying the software component is complete. This flag serves as the hardware triggering event for an interrupt. For an input device, a status flag is set when new input data is available.
The “busy to ready” state transition will cause a busy-wait loop to complete, see middle of Figure 11.1. Once the software recognizes the input device has new data, it will read the data and ask the input device to create more data. It is the busy to ready state transition that signals to the software that the hardware task is complete, and now software service is required. When the hardware is in the ready state the I/O transaction is complete.
Often the simple process of reading the data will clear the flag and request another input. The problem with I/O devices is that they are usually much slower than software execution. Therefore, we need synchronization, which is the process of the hardware and software waiting for each other in a manner such that data is properly transmitted. A way to visualize this synchronization is to draw a state versus time plot of the activities of the hardware and software.
For an input device, the software begins by waiting for new input. When the input device is busy it is in the process of creating new input.
When the input device is ready, new data is available. When the input device makes the transition from busy to ready, it releases the software to go forward. In a similar way, when the software accepts the input, it can release the input device hardware. The arrows in Figure 11.3 represent the synchronizing events.
In this example, the time for the software to read and process the data is less than the time for the input device to create new input. This situation is called I/O bound, meaning the bandwidth is limited by the speed of the I/O hardware. The software must wait for the input device to be ready (I/O bound input interface). If the input device were faster than the software, then the software waiting time would be zero. This situation is called CPU bound (meaning the bandwidth is limited by the speed of the executing software). In real systems the bandwidth depends on both the hardware and the software. Another characteristic of real systems is the data can vary over time, like car traffic arriving and leaving a road intersection.
In other words, the same I/O channel can sometimes be I/O bound, but at other times the channel could be CPU bound. We can store or buffer data in a first in first out (FIFO) queue, see Figure 11.4, while passing the data from one module to another. These modules may be input devices, output devices or software. Because the buffer separates the generation of data from the consumption of data, it is very efficient, and hence it is prevalent in I/O communication. In particular, it can handle situations where there is an increase or decrease in the rates at which data is produced or consumed. Other names for this important interfacing mechanism include bounded buffer, producer-consumer, and buffered I/O. Data are entered into the FIFO as they arrive; we call Put to store data in the FIFO.
Data are removed as they leave; we call Get to remove data from the FIFO. The FIFO maintains the order of the data, as it passes through the buffer. We can think of a FIFO like a line at the post office. There is space in the lobby for a finite number of people to wait.
As customers enter the post office they get in line at the end (put onto FIFO). As the postal worker services the customers, people at the front leave the line (get from the FIFO). It is bad situation (a serious error) if the waiting room becomes full and there is no room for people to wait (full FIFO). However, if there are no customers waiting (empty FIFO) the postal worker sits idle.
An empty FIFO may be inefficient, but it is not considered an error. A FIFO queue can be used to pass data from a producer to a consumer. At any given time there can be a variable number of elements stored in the FIFO. The order in which data are removed is the same as the order the data are entered. The busy-wait method is classified as unbuffered because the hardware and software must wait for each other during the transmission of each piece of data.
The interrupt solution (shown in the right part of Figure 11.1) is classified as buffered, because the system allows the input device to run continuously, filling a FIFO with data as fast as it can. In the same way, the software can empty the buffer whenever it is ready and whenever there is data in the buffer.
The buffering used in an interrupt interface may be a hardware FIFO, a software FIFO, or both hardware and software FIFOs. We will see the FIFO queues will allow the I/O interface to operate during both situations: I/O bound and CPU bound. For an output device, a status flag is set when the output is idle and ready to accept more data. The “busy to ready” state transition causes a busy-wait loop to complete, see the middle part of Figure 11.2.
Once the software recognizes the output is idle, it gives the output device another piece of data to output. It will be important to make sure the software clears the flag each time new output is started. Figure 11.5 contains a state versus time plot of the activities of the output device hardware and software. For an output device, the software begins by generating data then sending it to the output device. When the output device is busy it is processing the data. Normally when the software writes data to an output port, that only starts the output process. The time it takes an output device to process data is usually longer than the software execution time. When the output device is done, it is ready for new data.
When the output device makes the transition from busy to ready, it releases the software to go forward. In a similar way, when the software writes data to the output, it releases the output device hardware. The output interface illustrated in Figure 11.5 is also I/O bound because the time for the output device to process data is longer than the time for the software to generate and write it. Again, I/O bound means the bandwidth is limited by the speed of the I/O hardware. The software must wait for the output device to finish the previous operation (I/O bound).
The busy-wait solution for this output interface is also unbuffered, because when the hardware is done, it will wait for the software and after the software generates data, it waits for the hardware. On the other hand, the interrupt solution (shown as the right part of Figure 11.2) is buffered, because the system allows the software to run continuously, filling a FIFO as fast as it wishes. In the same way, the hardware can empty the buffer whenever it is ready and whenever there is data in the FIFO. Again, FIFO queues allow the I/O interface to operate during both situations: I/O bound and CPU bound. On some systems an interrupt will be generated on a hardware failure.
Examples include power failure, temperature too high, memory failure, and mechanical tampering of secure systems. Usually, these events are extremely important and require immediate attention. The Cortex™-M processor will execute special software ( fault) when it tries to execute an illegal instruction, access an illegal memory location, or attempt an illegal I/O operation.
Universal Asynchronous Receiver Transmitter (UART) Video 12.2a. UART Background and Launchpad Support Video 12.2b. UART Operation In this section we will develop a simple device driver using the Universal Asynchronous Receiver/Transmitter (UART).
This serial port allows the microcontroller to communicate with devices such as other computers, printers, input sensors, and LCDs. Serial transmission involves sending one bit at a time, such that the data is spread out over time. The total number of bits transmitted per second is called the baud rate. The reciprocal of the baud rate is the bit time, which is the time to send one bit. Most microcontrollers have at least one UART. The LM4F120/TM4C123 has 8 UARTs. Before discussing the detailed operation on the TM4C, we will begin with general features common to all devices.
Each UART will have a baud rate control register, which we use to select the transmission rate. Each device is capable of creating its own serial clock with a transmission frequency approximately equal to the serial clock in the computer with which it is communicating. A frame is the smallest complete unit of serial transmission. Figure 11.6 plots the signal versus time on a serial port, showing a single frame, which includes a start bit (which is 0), 8 bits of data (least significant bit first), and a stop bit (which is 1).
There is always only one start bit, but the Stellaris ® UARTs allow us to select the 5 to 8 data bits and 1 or 2 stop bits. The UART can add even, odd, or no parity bit. However, we will employ the typical protocol of 1 start bit, 8 data bits, no parity, and 1 stop bit. This protocol is used for both transmitting and receiving. The information rate, or bandwidth, is defined as the amount of data or useful information transmitted per second. From Figure 11.6, we see that 10 bits are sent for every byte of usual data. Therefore, the bandwidth of the serial channel (in bytes/second) is the baud rate (in bits/sec) divided by 10.
A serial data frame with 8-bit data, 1 start bit, 1 stop bit, and no parity bit. Common Error: If you change the bus clock frequency without changing the baud rate register, the UART will operate at an incorrect baud rate.
Checkpoint 11.1: Assuming the protocol drawn in Figure 11.6 and a baud rate of 1000 bits/sec, what is the bandwidth in bytes/sec? Table 11.1 shows the three most commonly used RS232 signals. The EIA-574 standard uses RS232 voltage levels and a DB9 connector that has only 9 pins. The most commonly used signals of the full RS232 standard are available with the EIA-574 protocols. Only TxD, RxD, and SG are required to implement a simple bidirectional serial channel, thus the other signals are not shown (Figure 11.7).
We define the data terminal equipment (DTE) as the computer or a terminal and the data communication equipment (DCE) as the modem or printer. DB9 Pin EIA-574 Name Signal Description True DTE DCE 3 103 TxD Transmit Data -5.5V out in 2 104 RxD Receive Data -5.5V in out 5 102 SG Signal Ground Table 11.1. The commonly-used signals on the EIA-574 protocol.
Hardware interface implementing an asynchronous RS232 channel. The TM4C123 has eight UART ports. Observation: The LaunchPad sends UART0 channel through the USB cable, so the circuit shown in Figure 11.7 will not be needed.
On the PC side of the cable, the serial channel becomes a virtual COM port. RS232 is a non-return-to-zero (NRZ) protocol with true signified as a voltage between -5 and ‑15 V. False is signified by a voltage between +5 and +15 V. A MAX3232 converter chip is used to translate between the +5.5/-5.5 V RS232 levels and the 0/+3.3 V digital levels. The capacitors in this circuit are important, because they form a charge pump used to create the ±5.5 voltages from the +3.3 V supply. The RS232 timing is generated automatically by the UART. During transmission, the MAX3232 translates a digital high on microcontroller side to -5.5V on the RS232/EIA‑574 cable, and a digital low is translated to +5.5V.
During receiving, the MAX3232 translates negative voltages on RS232/EIA‑574 cable to a digital high on the microcontroller side, and a positive voltage is translated to a digital low. The computer is classified as DTE, so its serial output is pin 3 in the EIA‑574 cable, and its serial input is pin 2 in the EIA‑574 cable. When connecting a DTE to another DTE, we use a cable with pins 2 and 3 crossed. I.e., pin 2 on one DTE is connected to pin 3 on the other DTE and pin 3 on one DTE is connected to pin 2 on the other DTE.
When connecting a DTE to a DCE, then the cable passes the signals straight across. In all situations, the grounds are connected together using the SG wire in the cable. This channel is classified as full-duplex, because transmission can occur in both directions simultaneously.
Asynchronous Communication We will begin with transmission, because it is simple. The transmitter portion of the UART includes a data output pin, with digital logic levels as drawn in the following interactive tool.
The transmitter has a 16-element FIFO and a 10-bit shift register, which cannot be directly accessed by the programmer. The FIFO and shift register in the transmitter are separate from the FIFO and shift register associated with the receiver.
In other words each UART has a receiver and a transmitter, but the interactive tool just shows the transmitter on one microcontroller and the receiver on the other. To output data using the UART, the transmitter software will first check to make sure the transmit FIFO is not full (it will wait if TXFF is 1) and then write to the transmit data register (e.g., UART0DRR). The bits are shifted out in this order: start, b 0, b 1, b 2, b 3, b 4, b 5, b 6, b 7, and then stop, where b 0 is the LSB and b 7 is the MSB. The transmit data register is write only, which means the software can write to it (to start a new transmission) but cannot read from it. Even though the transmit data register is at the same address as the receive data register, the transmit and receive data registers are two separate registers. The transmission software can write to its data register if its TXFF (transmit FIFO full) flag is zero.
TXFF equal to zero means the FIFO is not full and has room. The receiving software can read from its data register if its RXFE (receive FIFO empty) flag is zero. RXFE equal to zero means the FIFO is not empty and has some data. While playing the following interactive tool, watch the behavior of the TXFF and RXFE flags. Interactive Tool 11.4 Use the following tool to watch the steps involved in Serial Communication of a simple two-byte message. Click Start/next over and over to single step the process, and click Run to run the entire sequence. Click Start to Send 'H' to the direction register. When a new byte is written to UART0DRR, it is put into the transmit FIFO.
Byte by byte, the UART gets data from the FIFO and loads them into the 10-bit transmit shift register. The 10-bit shift register includes a start bit, 8 data bits, and 1 stop bit. Then, the frame is shifted out one bit at a time at a rate specified by the baud rate register. If there are already data in the FIFO or in the shift register when the UART0DRR is written, the new frame will wait until the previous frames have been transmitted, before it too is transmitted. The FIFO guarantees the data are transmitted in the order they were written.
The serial port hardware is actually controlled by a clock that is 16 times faster than the baud rate, referred to in the datasheet as Baud16. When the data are being shifted out, the digital hardware in the UART counts 16 times in between changes to the U0Tx output line. The software can actually write 16 bytes to the UART0DRR, and the hardware will send them all one at a time in the proper order. This FIFO reduces the software response time requirements of the operating system to service the serial port hardware.
Unfortunately, it does complicate the hardware/software timing. At 9600 bits/sec, it takes 1.04 ms to send a frame. Therefore, there will be a delay ranging from 1.04 and 16.7 ms between writing to the data register and the completion of the data transmission. This delay depends on how much data are already in the FIFO at the time the software writes to UART0DRR. Receiving data frames is a little trickier than transmission because we have to synchronize the receive shift register with the incoming data. The receiver portion of the UART includes a U0Rx data input pin with digital logic levels.
At the input of the microcontroller, true is 3.3V and false is 0V. There is also a 16-element FIFO and a 10-bit shift register, which cannot be directly accessed by the programmer (shown on the right side of the interactive tool). The receive shift register is 10 bits wide, but the FIFO is 12 bits, 8 bits of data and 4 error flags. Again the receive shift register and receive FIFO are separate from those in the transmitter. The receive data register, UART0DRR, is read only, which means write operations to this address have no effect on this register (recall write operations activate the transmitter). The receiver obviously cannot start a transmission, but it recognizes a new frame by its start bit. The bits are shifted in using the same order as the transmitter shifted them out: start, b 0, b 1, b 2, b 3, b 4, b 5, b 6, b 7, and then stop.
There are six status bits generated by receiver activity. The Receive FIFO empty flag, RXFE, is clear when new input data are in the receive FIFO. When the software reads from UART0DRR, data are removed from the FIFO. When the FIFO becomes empty, the RXFE flag will be set, meaning there are no more input data. There are other flags associated with the receiver. There is a Receive FIFO full flag RXFF, which is set when the FIFO is full. There are four status bits associated with each byte of data.
For this reason, the receive FIFO is 12 bits wide. The overrun error, OE, is set when input data are lost because the FIFO is full and more input frames are arriving at the receiver. An overrun error is caused when the receiver interface latency is too large. The break error, BE, is set when the input is held low for more than a frame.
Parity is a mechanism to send one extra bit so the receiver can detect if there were any errors in transmission. With even parity the number of 1's in the data plus parity will be an even number. The PE bit is set on a parity error. Because the error rate is so low, most systems do not implement parity. We will not use parity in this class. The framing error, FE, is set when the stop bit is incorrect.
Framing errors are probably caused by a mismatch in baud rate. The receiver waits for the 1 to 0 edge signifying a start bit, then shifts in 10 bits of data one at a time from the U0Rx line.
The internal clock is 16 times faster than the baud rate. After the 1 to 0 edge, the receiver waits 8 internal clocks and samples the start bit. 16 internal clocks later it samples b 0. Every 16 internal clocks it samples another bit until it reaches the stop bit.
The UART needs an internal clock faster than the baud rate so it can wait the half a bit time between the 1 to 0 edge beginning the start bit and the middle of the bit window needed for sampling. The start and stop bits are removed (checked for framing errors), the 8 bits of data and 4 bits of status are put into the receive FIFO. The hardware FIFO implements buffering so data is safely stored in the receiver hardware if the software is performing other tasks while data is arriving. Observation: If the receiving UART device has a baud rate mismatch of more than 5%, then a framing error can occur when the stop bit is incorrectly captured. An overrun occurs when there are 16 elements in the receive FIFO, and a 17 th frame comes into the receiver. In order to avoid overrun, we can design a real-time system, i.e., one with a maximum latency.
The latency of a UART receiver is the delay between the time when new data arrives in the receiver ( RXFE=0) and the time the software reads the data register. If the latency is always less than 160 bit times, then overrun will never occur.
Observation: With a serial port that has a shift register and one data register (no FIFO buffering), the latency requirement of the input interface is the time it takes to transmit one data frame. TM4C UART Details Next we will overview the specific UART functions on the TM4C microcontroller. This section is intended to supplement rather than replace the Texas Instruments manuals. When designing systems with any I/O module, you must also refer to the reference manual of your specific microcontroller. It is also good design practice to review the errata for your microcontroller to see if any quirks (mistakes) exist in your microcontroller that might apply to the system you are designing.
Stellaris TM4C microcontrollers have eight UARTs. The specific port pins used to implement the UARTs vary from one chip to the next.
To find which pins your microcontroller uses, you will need to consult its datasheet. Table 11.2 shows some of the registers for the UART0 and UART1.
For the other UARTs, the register names will replace the 0 with a 1 – 7. For the exact register addresses, you should include the appropriate header file (e.g., tm4c123gh6pm.h). To activate a UART you will need to turn on the UART clock in the RCGC1 register. You should also turn on the clock for the digital port in the RCGC2 register. You need to enable the transmit and receive pins as digital signals. The alternative function for these pins must also be selected.
In particular we set bits in both the AFSEL and PCTL registers. The OE, BE, PE, and FE are error flags associated with the receiver. You can see these flags in two places: associated with each data byte in UART0DRR or as a separate error register in UART0RSRR. The overrun error ( OE) is set if data has been lost because the input driver latency is too long. BE is a break error, meaning the other device has sent a break.
PE is a parity error (however, we will not be using parity). The framing error ( FE) will get set if the baud rates do not match.
The software can clear these four error flags by writing any value to UART0RSRR. The status of the two FIFOs can be seen in the UART0FRR register. The BUSY flag is set while the transmitter still has unsent bits, even if the transmitter is disabled. It will become zero when the transmit FIFO is empty and the last stop bit has been sent. If you implement busy-wait output by first outputting then waiting for BUSY to become 0 (right flowchart of Figure 11.10), then the routine will write new data and return after that particular data has been completely transmitted. The UART0CTLR control register contains the bits that turn on the UART.
TXE is the Transmitter Enable bit, and RXE is the Receiver Enable bit. We set TXE, RXE, and UARTEN equal to 1 in order to activate the UART device. However, we should clear UARTEN during the initialization sequence.
31–12 11 10 9 8 7–0 Name $4000.C000 OE BE PE FE DATA UART0DRR 31–3 3 2 1 0 $4000.C004 OE BE PE FE UART0RSRR 31–8 7 6 5 4 3 2–0 $4000.C018 TXFE RXFF TXFF RXFE BUSY UART0FRR 31–16 15–0 $4000.C024 DIVINT UART0IBRDR 31–6 5–0 $4000.C028 DIVFRAC UART0FBRDR 7 6 – 5 4 3 2 1 0 $4000.C02C SPS WPEN FEN STP2 EPS PEN BRK UART0LCRHR 31–10 9 8 7 6–3 2 1 0 $4000.C030 RXE TXE LBE SIRLP SIREN UARTEN UART0CTLR 31–12 11 10 9 8 7–0 $4000.D000 OE.
Introduction In the last post about communication we discussed what it took to build up the ESP tool chain as well as the STM32 Toolchain. We loaded the esp-link interface on to the esp and a simple serial communication program onto the STM32 and had the two micro-controllers communicate. It was amazing but for the Sunleaf team sending data between micro-controllers just wasn't enough.
Serial Communication Interface Standards
We are building a next generation internet of things device. This means it has to work with a cloud system. This also means we need to be able to do basic. If you need to know more about REST commands I added a link here. The esp-link supports micro-controller REST Commands but only though a SLIP interface. This was a surprise.
Initially we were planning on using simple AT commands to 'GET' and 'POST' data. But there are a few issues with this.
The basic serial communication protocol is error prone. Even with some nice libraries that do error checking we still experienced a few errors in the communication. It is also slower. We really though the right way to build this up is to add a SLIP interface to the STM32 so we can communicate with the esp-link and the rest of the internet reliably and quickly. In this log we first discuss what SLIP is.
Then we'll cover the port we are using for the STM and give a working example. SLIP: What is it, really? When I first realized that I needed a SLIP interface I was hopeful.
The STM32F446RE is a fairly common chip. There had to be someone working on an open source version that I could use. I couldn't find one.
There are a lot of closed source versions available, if you have between $10K-$30K to drop on a license. But if that's you then you probably aren't reading this now.
This definitely isn't us. So we decided we needed to build our own. To do this we needed to learn a little more about SLIP, why it exists and why is it used? The SLIP protocol consists of commands with binary arguments sent from the attached the STM32 to the esp8266. The ESP8266 then performs the command and responds back. The responses back use a callback address in the attached STM-Client code. For example the command sent by the STM32 contains a callback address and the response from the esp8266 starts with that callback address.
This enables asynchronous communication where esp-link can notify the STM32 when requests complete or when other actions happen, such as wifi connectivity status changes or RSSI Values. However, there is no true internet standard for Serial Line IP. There is a good deal of work done on this still. It is intended to work as a point-to-point serial connection for TCP/IP connections. SLIP is commonly used on dedicated serial links but has been replaced on many CPUs with PPP. For embedded systems like the STM32F+ESP8266 system it works well.
It is also nice because I don't have to post my SSID and wifi password wit this configuration. This is because SLIP allows mixes of hosts and routes to communicate with one another (host-host, host-router and router- router are all common SLIP network configurations). Basically it is a simple two layer protocol providing basic IP framing. Here is a diagram of how the protocol is supposed to work: Here is a summary of SLIP:. Breaks an IP datagram into bytes. Send the END character (value “192”) after the first and last byte of the datagram;. If any byte to be sent in the datagram is “192”, replace it with “219 220”.
If any byte to be sent is “219”, replace it with “219 221” Kinda nifty? Well, not really. It is very old but for an embedded system to post reliable REST requests it is top-notch. Next we talk about how it is implemented in the STM32F446RE Board.
SLIP for the STM32: STM-Client Overview As we discussed we've loaded a sun leaf version of the esp-link software onto the ESP. Here is where the source is kept:. Now, versions of esp-link use the SLIP protocol over the serial link to support simple outbound HTTP REST requests and an MQTT client. There are examples of these libraries for REST and MQTT libraries as well as demo sketches in the repository. These only work on Arduino boards and I think a few NXP boards. If you want to use a real micro-controller you'll have to port some of this to C.
Which is what I did.so I guess you don't have to any more. The homepage for the mbed repository: Again keeping in mind that is is largely ported from the EL-Client repository here are some highlights about how this port works. First there are two serial objects that the client requires: Serial and DebugSerial. This is extreamly helpful because you are essentially blind to what is going on inside the STM32 once the program kicks off unless you use the debugSerial object. In the El-Client repo there is no true main.cpp for the rest example. This is because the program is intended for the Arduino.
So the first thing we did was make our own. It was a very similiar example. The main difference is that the STM32 (or maybe the mbed API.) dosen't support asynchronous communication. There are a lot of stream objects in the Arduino code that don't port correctly to C. This is fairly easy to overcome by Arduino the serial objects in main and changing the stream.
datatype classes to serial. datatypes.
This was really the biggest catch. Once I figured this out the only other trick was to reset the ESP8266. A lot of research went into figuring out how to do this correctly.
I almost ported a LWIP library but found the STM-Client to work better. Here is a screen shot of the system sending a 'get' request to. I used putty.but you can use any serial output. This text is all through the serial debug port. There is also a 'post' example that is commented out in the code for. This also works but I'm working on this to get it talking to vivaplanet right now so it has limited functionality.
There are a few more firmware goals we are going to address in the next two months. The first will be posting to a cloud based server: vivaplanet. Posting to something like an Azure cloud service is non-trival for a platform but I'm confident it can be done. The second is OTA programming the STM32. This is also non-trival and may require a hardware change to the rev 2 board. Lastly, I need to get the Kalman filter engine working.
If we're lucky we'll get to all of these topics before October. We also need a rev 2 board and a rugged mechanical housing but that will also be coming soon to a log entry near you.