Chapter 1: Beginnings
Chapter 2: Further Beginnings
Chapter 3: Hello World
Chapter 4: More On GPIO
Chapter 5: Interrupts
Chapter 6: More On Interrupts
Chapter 7: Timers
Chapter 8: Adding Some Real-World Hardware
Chapter 9: More Timers and Displays
Chapter 10: Buttons and Bouncing
Chapter 11: Button Matrix & Auto Repeating
Chapter 12: Driving WS2812 RGB LEDs
Would you like to be notified by email when Stephen Friederichs publishes a new blog?
If you take a high-level view of software systems you might say that the overall goal of software is to generate outputs from inputs. It’s a gross simplification of a nuanced and complex field but the truth of the statement is unarguable: data goes in, is manipulated and then is spat out again.That’s what software does. The simplicity of the statement contributes to the joy of Computer Science majors who take an abstract view of everything from software to love but infuriates engineers. Engineers of all stripes are tasked with dragging down the ivory-tower proclamations of so-called ‘scientists’ into the dirty, complicated real world. What, they ask, if your data is wrong? What if it describes something that just plain can’t happen? What do you do then? While Computer Scientists may be hesitant to discriminate between data sets based on vague ideas of ‘impossibility’ (‘After all’, they might argue, ‘How are we to know whether reality isn’t in the wrong?’’) engineers are tasked with keeping bridges erect and planes aloft. When developing embedded systems for the real world an understanding of how to account for data validity will help you develop more reliable software.
In real-world embedded systems data can be invalid for a variety of reasons. A 32-bit unsigned integer can represent an absurd range of numbers - depending on the real-world data you’re trying to represent you may only use a small fraction of that entire range. For example, an angle sensor should return an angle measurement between 0 and 360 degrees. There are any number of errors that can cause the data received from the sensor to fall outside of that range - communication errors, parsing errors, malfunction of the magnetometer, etc. Alternately, if the data is received periodically over a serial link you need to ensure that data has actually been received before attempting to generate any outputs. As a programmer you can’t blindly utilize data without knowing whether it is valid and have a strategy to handle invalid data.
There are many schemes that can be used to keep track of the validity of software. In the above example you know that angle data can only range from 0 to 360. The first and quickest validity check you could implement is to ensure that the data is, in fact, between 0 and 360 degrees. That takes the form of a single ‘if’ statement whenever the data is used. If the data is utilized in several different places and it would be tedious to validate repeatedly, the check could be performed once and you can utilize a boolean variable to store the result. Alternately, if the data is invalid you could set it to an agreed-upon value that signifies an error (such as -1). Every time new data arrives, its validity would be checked and stored for subsequent use.
It isn’t always possible to perform a simple range check on data to determine its validity. UART data, for example, is typically stored in an unsigned char variable. In this situation the entire range of the unsigned char represents valid data, so there’s no indication in the data itself whether or not it is invalid. In this case, the validity typically depends on several external factors. Generally, microcontrollers have hardware status flags that will tell you whether a reception error has occurred (such as a parity failure, overflow, framing error, etc.). This information contributes to the validity of the received data and can be stored in a boolean which will be checked along-side the data.
Another factor to consider in the validity of data is its timeliness. Real-time systems typically operate on a frame rate: all inputs and outputs are refreshed every 10ms for example. In some real-time systems, any data the arrives late cannot be relied on and must be discarded. In this situation it would be typical to have a flag that represents whether new data has been received. When new data is received (generally in an ISR or retrieved from a buffer at the beginning of a frame) the flag is set to true. Any processes that utilize the data check the flag - if the data is stale, it’s unused. Once the frame is finished executing the data is marked as stale so that it won’t be used until it’s refreshed.
All of this still leaves you with the question of what to do with invalid data. This is generally dependent on the type of system you’re designing. For a hard real-time safety-critical system you may not have any recourse except to cease operation and enter a fail-safe state. That’s the worst possible case. In less critical systems it may be possible to continue operation with reduced performance: you might be able to get away with holding the last valid value in memory, extrapolating new data based on past inputs or even estimating the invalid data from other system parameters. If there’s a user interface in your application you can raise a notification to the user to address the situation or at least make them aware of it. In some systems it may be permissible to indicate there is no valid data - perhaps display a ‘?’ to indicate no valid data.
By adopting or adapting some of the techniques suggested in this article you can write more reliable software that respects the realities of the world it operates in.
There are no comments yet!
Add a Comment