Editor’s Note: In Part 2, excerpted from their book Fast and effective embedded systems design: Applying the ARM mbed , authors Tim Wilmshurstand Rob Toulson introduce a number of C/C++ programming methods for ARM designs using the open source mbed integrated development environment.
There are many different approaches to development in embedded systems. As noted in Part 1, with the mbed there is no software to install, and no extra development hardware needed for program download. All software tools are placed online so that you can compile and download wherever you have access to the Internet.
Notably, there is a C++ compiler and an extensive set of software libraries used to drive the peripherals. Thus, also as noted in Part 1, there is no need to write code to conﬁgure peripherals, which in some systems can be very time consuming.
BUT, although this book does not assume that you have any knowledge of C or C++, you have an advantage if you do.
The mbed Compiler and API
The mbed development environment uses the ARM RVDS (RealView Development Suite) compiler, currently Version 4.1. All features of this compiler relevant to the mbed are available through the mbed portal .
One thing that makes the mbed special is that it comes with an application programming interface (API). In brief, this is the set of programming building blocks, appearing as C++ utilities, which allow programs to be devised quickly and reliably. Therefore, we will be writing code in C or C++, but drawing on the features of the API.
As just mentioned, the mbed development environment uses a C++ compiler.That means that all ﬁles will carry the .cpp (Cplusplus) extension. C, however, is a subset of C++, and is simpler to learn and apply. This is because it does not use the more advanced ‘object-oriented’ aspects of C++.
In general, C code will C compile on a C++ compiler, but not the other way round. C is usually the language of choice for any embedded program of low or medium complexity, so will suit us well here. For simplicity, therefore, we aim to use only C in the programs we develop. It should be recognized, however, that the mbed API is written in C++ and uses the features of that language to the full. We will aim to outline any essential features when we come to them.
Program Design and Structure
There are numerous challenges when tackling an embedded system design project. It is usually wise first to consider the software design structure, particularly with large and multi-functional projects. It is not possible to program all functionality into a single control loop, so the approach for breaking up code into understandable features should be well thought out. In particular, it helps to ensure that the following can be achieved:
- that code is readable, structured and documented
- that code can be tested for performance in a modular form
- that development reuses existing code utilities to keep development time short
- that code design supports multiple engineers working on a single project
- that future upgrades to code can be implemented efficiently.
There are various C/C++ programming techniques that enable these design requirements to be considered, as discussed here, including: functions, flow charts, pseudocode and code reuse.
The role of Functions
A function is a portion of code within a larger program. The function performs a specific task and is relatively independent of the main code. Functions can be used to manipulate data; this is particularly useful if several similar data manipulations are required in the program. Data values can be input to the function and the function can return the result to the main program. Functions, therefore, are particularly useful for coding mathematical algorithms, look-up tables and data conversions, as well as control features that may operate on a number of different parallel data streams. It is also possible to use functions with no input or output data, simply to reduce code size and to improve readability of code. Figure 6.1 illustrates a function call.
There are several advantages when using functions. First, a function is written once and compiled into one area of memory, irrespective of the number of times that it is called from the main program, so program memory is reduced. Functions also allow clean and manageable code to be designed, allowing software to be well structured and readable at a number of levels of abstraction. The use of functions also enables the practice of modular coding, where teams of software engineers are often required to develop large and advanced applications. Writing code with functions therefore allows one engineer to develop a particular software feature, while another engineer may take responsibility for something else.
Using functions is not always completely beneficial, however. There is a small execution time overhead in storing program position data and jumping and returning from the function, but this should only be an issue for consideration in the most time-critical systems. Furthermore, it is possible to ‘nest’ functions within functions, which can sometimes make software challenging to follow. A limitation of C functions is that only a single value can be returned from the function, and arrays of data cannot be passed to or from a function (only single-value variables can be used). Working with functions and modular techniques therefore requires a considered software structure to be designed and evaluated before programming is started.
Using Flowcharts to Define Code Structure
It is often useful to use a flowchart to indicate the operation of program flow and the use of functions. Code flow can be designed using a flowchart prior to coding. Figure 6.2 shows some of the flowchart symbols that are used.
For example, take the following software design specification:
Design a program to increment continuously the output of a seven-segment numerical light-emitting diode (LED) display (as shown in Figure 6.3 , and similar to the one used in Part 1) through the numbers 0 to 9, then reset back to 0 to continue counting. This includes:
- Use a function to convert a hexadecimal counter byte A to the relevant seven- segment LED output byte B.
- Output the LED output byte to light the correct segment LEDs.
- If the count value is greater than 9, then reset to zero.
- Delay for 500 ms to ensure that the LED output counts up at a rate that is easily visible.
The output of the seven-segment display has been discussed previously in Part 1 and in particular in Table 3.4. A feasible software design is shown in Figure 6.4 .
Flowcharts allow us to visualize the order of operations of code and to make judgments on which sections of a program may require the most attention or take the most effort to develop. They also help with communicating a potential design with non-engineers, which may hold the key to designing a system that meets a very detailed specification.Pseudocode
Pseudocode consists of short, English phrases usedto explain specific tasks within a program. Ideally, pseudocode shouldnot include keywords in any specific computer language. Pseudocodeshould be written as a list of consecutive phrases; we can even drawarrows to show looping processes. Indentation can be used to show thelogical program flow in pseudocode.
Writing pseudocode saves timelater during the coding and testing stage of a program’s developmentand also helps communication among designers, coders and projectmanagers. Some projects may use pseudocode for design, others may useflowcharts, and some a combination of both.
The software design shown by the flowchart in Figure 6.4 could also be described in pseudocode as shown in Figure 6.5 below
Notethat the functions SegConvert( ) and Delay( ) are defined elsewhere,for example in a separate ‘utilities’ file, authored by a differentengineer. Function SegConvert( ) could implement a simple look-up tableor number of if statements that assigns the suitable value to B.
Working with Functions on the mbed
Implementing a Seven-Segment Display Counter. Program Example 6.1 below shows a program which implements the designs described by theflowchart in Figure 6.4 and the pseudocode shown in Figure 6.5.
Itapplies some of the techniques first used in Program Example 3.5 inPart 1, but goes beyond these. The main design requirement is that aseven-segment display is used to count continuously from 0 to 9 and loopback to 0. Declarations for the BusOut object and the A and Bvariables, as well as the SegConvert( ) function prototype, appear earlyin the program. It can be seen that the main( ) program function isfollowed by the SegConvert( ) function, which is called regularly fromwithin the main code. Notice in the line
B=SegConvert(A); // Call function to return B
that B can immediately take on the return value of the SegConvert( ) function.
Notice the SegConvert( ) function the final line immediately below, which applies the return keyword:
Thisline causes program execution to return to the point from which thefunction was called, carrying the value SegByte as its return value. Itis an important technique to use once you start writing functions thatprovide return values. Notice that SegByte has been declared as part ofthe function prototype early in the program listing.
Connect aseven-segment display to the mbed and implement Program Example 6.1. Thewiring diagram for a seven-segment LED display was shown previously inFigure 3.10 in Part 1. Verify that the display output continuouslycounts from 0 to 9 and then resets back to 0. Ensure that you understandhow the program works by cross-referencing with the flowchart andpseudocode designs shown previously.
Nowthat we have a function to convert a decimal value to a seven-segmentdisplay byte, we can build projects using multiple seven-segmentdisplays with little extra effort. For example, we can implement asecond seven-segment display (Figure 6.6 ) by simply defining its mbed BusOut declaration and calling the same SegConvert( ) function as before.
Itis possible to implement a counter program that counts from 00 to 99 bysimply modifying the main program code to that shown in Program Example 6.2 .Note that the SegConvert( ) function previously defined in ProgramExample 6.1 is also required to be copied (reused) in this example. Notealso that a slightly different programming approach is used; here weuse two for loops to count each of the tens and units values.
Usingtwo seven-segment displays, with pin connections shown in Figure 6.6above, implement Program Example 6.2 and verify that the display outputcounts continuously from 00 to 99 and then resets back to 0. Review theprogram design and familiarize yourself with the method used to countthe tens and units digits each from 0 to 9.
A more advancedprogram could also read two numerical values from a host terminalapplication and display these on two seven-segment displays connected tothe mbed. The program can therefore display any integer number between00 and 99, as required by user key presses.
An example programdesign uses four functions to implement the host terminal output onseven-segment displays. The four functions are as follows:
- SegInit( ) e to set up and initialize the seven-segment displays
- HostInit( ) e to set up and initialize the host terminal communication
- GetKeyInput( ) e to get keyboard data from the terminal application
- SegConvert( ) e function to convert a decimal integer to a seven-segment display data byte.
Wewill use the mbed universal serial bus (USB) interface to communicatewith the host PC, and two seven-segment displays, as in the previousexercise.
For the first time now we come across a method forcommunicating keyboard data and display characters, using ASCII codes.The term ASCII refers to the American Standard Code for InformationInterchange method for defining alphanumeric characters as 8-bit values.Each alphabet character (lower and upper case), number (0e9) and aselection of punctuation characters are all described by a uniqueidentification byte, i.e. the ‘ASCII value’. Therefore, for example,when a key is pressed on a computer keyboard, its ASCII byte iscommunicated to the PC. The same applies when communicating withdisplays.
The ASCII byte for numerical characters has the higherfour bits set to value 0x3 and the lower four bits represent the valueof the numerical key which is pressed (0x0 to 0x9). Numbers 0e9 aretherefore represented in ASCII as 0x30 to 0x39.
To convert theASCII byte returned by the keyboard to a regular decimal digit, thehigher four bits need to be removed. We do this by logically ANDing theASCII code with a bitmask, a number with bits set to 1 where we want tokeep a bit in the ASCII, and set to 0 where we want to force the bit to0. In this case, we apply a bitmask of 0x0F. The logical AND applies theoperator ‘&’ and appears in the line:
return (c&0x0F); // apply bit mask to convert to decimal, and return
Example functions and program code are shown in Program Example 6.3 .
Once again, the function SegConvert( ), as shown in Program Example 6.1, should be added to compile the program.
Tim Wilmshurst ,head of Electronics at the University of Derby, led the ElectronicsDevelopment Group in the Engineering Department of Cambridge Universityfor a number of years, before moving to Derby. His design career hasspanned much of the history of microcontrollers and embedded systems.
Rob Toulson is Research Fellow at Anglia Ruskin University in Cambridge. Aftercompleting his PhD, Rob spent a number of years in industry, where heworked on digital signal processing and control systems engineeringprojects, predominantly in audio and automotive ﬁelds. He then moved toan academic career, where his main focus is now in developingcollaborative research between the technical and creative industries.
This article is excerpted from Fast and effective embedded systems design: Applying the ARM mbed byRob Toulsonand Tim Wilmshurst, used with permission from Newnes, adivision of Elsevier. Copyright 2012. All rights reserved. For moreinformation on this title and other similar books, visit www.newnespress.com .