• No results found

Calculate the Boolean expression for each set of inputs A, B, C

In document Embedded Systems Shape the World (Page 55-63)

Checkpoint 4.11. What does negative logic mean?

4.6 Calculate the Boolean expression for each set of inputs A, B, C

01012

= 5

01102 = 6

01112

= 7

10102

= 10

00012 = 1

11102

= 14

10012

= 9

01112 = 7

4.6 Calculate the Boolean expression for each set of inputs A, B, C

A B C A&& (B ||

C)

True False False

True False True

False True False

True True False

Chapter 5: Introduction to C Programming Embedded Systems - Shape the World Jonathan Valvano and Ramesh Yerraballi

This chapter covers the C Programming language starting with the structure, constants and variable declarations, the main subroutine, simple input/output, arithmetic expressions, Boolean expressions, the assignment statement, the while loop and lastly simple functions with at most one input and one output.

Learning Objectives:

Know the elements of a C program: What goes where, what are the syntax rules

Know declarations: simple data types, char, short, long (unsigned and signed)

Know the form of the mandatory main subroutine for a program to be executable

Know the basic assignment statement: variable = expression;

Know how to use printf and scanf for I/O.

Know the basic form of the if-statement and the while(1) statement and how they use the conditional Boolean expression.

Know the importance of functions and understand the use with inputs and outputs.

5.0 Introduction

This course presents the art and science of designing embedded systems. In this module we will introduce C programming. If you need to write a paper, you decide on a theme, and then begin with an outline. In the same manner, if you design an embedded system, you define its specification (what it does) and begin with an organizational plan. In this chapter, we will present three graphical tools to describe the organization of an embedded system: flowcharts, data flow graphs, and call graphs. You should draw all three for every system you design. In this section, we introduce the flowchart syntax that will be used throughout the class. Programs themselves are written in a linear or one-dimensional fashion. In other words, we type one line of software after another in a sequential fashion. Writing programs this way is a natural process, because the computer itself usually executes the program in a top-to-bottom sequential fashion. This one-dimensional format is fine for simple programs, but conditional branching and function calls may create complex behaviors that are not easily observed in a linear fashion. Flowcharts are one way to describe software in a two-dimensional format, specifically providing convenient mechanisms to visualize conditional branching and function calls. Flowcharts are very useful in the initial design stage of a software system to define complex algorithms. Furthermore, flowcharts can be used in the final documentation stage of a project, once the system is operational, in order to assist in its use or modification.

Where does one begin when learning a new skill? To me, I begin software development with the question, “What is it the program is supposed to do?” Next, I think of how I will test it. Testing is essentially a mechanism to see if the program does exactly what it is supposed to do, no more no less.

Next, I list what are the inputs, and what are the outputs. Inside the system we have data. The data defines what do I know, so I define the data and give some examples. The software algorithm connects the inputs to the data, and software must connect the data to the outputs. Lastly, I test it. So you see I begin with testing and end with testing.

We will use flowcharts to illustrate what the software does (Figure 5.1). The oval shapes define entry and exit points. The main entry point is the starting point of the software. Each function, or subroutine, also has an entry point. The exit point returns the flow of control back to the place from which the function was called. When the software runs continuously, as is typically the case in an embedded system, there will be no main exit point. We use rectangles to specify process blocks. In a high-level flowchart, a process block might involve many operations, but in a low-level flowchart, the exact operation is defined in the rectangle. The parallelogram will be used to define an input/output operation. Some flowchart artists use rectangles for both processes and input/output. Since input/output

operations are an important part of embedded systems, we will use the parallelogram format, which will make it easier to identify input/output in our flowcharts. The diamond-shaped objects define a branch point or conditional block. Inside the diamond we can define what is being tested. Each arrow out of a condition block must be labeled with the condition causing flow to go in that direction. There must be at least two arrows out of a condition block, but there could be more than two. However, the condition for each arrow must be mutually exclusive (you can’t say “if I’m happy go left and if I’m tall go right”

because it is unclear what you want the software to do if I’m happy and tall). Furthermore, the complete set of conditions must define all possibilities (you can’t say “if temperature is less than 20 go right and if the temperature is above 40 go left” because you have not defined what to do if the temperature is between 20 and 40). The rectangle with double lines on the side specifies a call to a predefined function. In this book, functions, subroutines, and procedures are terms that all refer to a well-defined section of code that performs a specific operation. Functions usually return a result parameter, while procedures usually do not. Functions and procedures are terms used when describing a high-level language, while subroutines are often used when describing assembly language. When a function (or subroutine or procedure) is called, the software execution path jumps to the function, the specific operation is performed, and the execution path returns to the point immediately after the function call.

Circles are used as connectors. A connector with an arrow pointing out of the circle defines a label or a spot in the algorithm. There should be one label connector for each number. Connectors with an arrow pointing into the circle are jumps or goto commands. When the flow reaches a goto connector, the execution path jumps to the position specified by the corresponding label connector. It is bad style to use a lot of connectors.

Figure 5.1. Flowchart symbols.

There are a seemingly unlimited number of tasks one can perform on a computer, and the key to developing great products is to select the correct ones. Just like hiking through the woods, we need to develop guidelines (like maps and trails) to keep us from getting lost. One of the fundamentals when developing software, regardless whether it is a microcontroller with 1000 lines of assembly code or a large computer system with billions of lines of code, is to maintain a consistent structure. One such framework is called structured programming. C is a structured language, which means we begin with a small number of simple templates, as shown in Figure 5.2. A good high-level language will force the programmer to write structured programs. Structured programs in C are built from three basic templates:

the sequence, the conditional, and the while-loop. At the lowest level, the “block” contains simple and well-defined commands, like Area = Height*Width; I/O functions are also low-level building blocks. To program in C, we combine existing structures into more complex structures. Each of the

“blocks” in Figure 5.2 is either a simple well-defined command or another structure.

Figure 5.2. Flowchart showing the basic building blocks of structured programming.

Example 5.1: Using a flowchart describe the control algorithm that a toaster might use to cook toast.

There will be a start button the user pushes to activate the machine. There is other input that measures toast temperature. The desired temperature is preprogrammed into the machine. The output is a heater, which can be on or off. The toast is automatically lowered into the oven when heat is applied and is ejected when the heat is turned off.

Solution: This example illustrates a common trait of an embedded system, that is, they perform the same set of tasks over and over forever. The program starts at main when power is applied, and the system behaves like a toaster until it is unplugged. Figure 5.3 shows a flowchart for one possible toaster algorithm. The system initially waits for the operator to push the start button. If the switch is not pressed, the system loops back reading and checking the switch over and over. After the start button is pressed, heat is turned on. When the toast temperature reaches the desired value, heat is turned off, and the process is repeated.

Figure 5.3. Flowchart illustrating the process of making toast.

Safety tip: When dealing with the potential for fire, you may want to add some safety features such as a time out or an independent check for temperature overflow.

Observation: The predefined functions in this chapter do not communicate any data between the calling routine and function. Data passed into a function are called input parameters, and data passed from the function back to the calling routine are called output parameters.

Observation: Notice in Figure 5.3 we defined a function Cook even though it was called from only one place. You might be tempted to think it would have been better to paste the code for the function into the one place it was called. There are many reasons it would be better to define the function as a separate software object: it will be easier to debug because there is a clear beginning and end of the function, it will make the overall system simpler to understand, and in the future we may wish to reuse this function for another purpose.

Example 5.2. The system has one input and one output. An event should be recognized when the input goes from 0 to 1 and back to 0 again. The output is initially 0, but should go 1 after four events are detected. After this point, the output should remain 1. Design a flowchart to solve this problem.

Solution: This example also illustrates the concept of a subroutine. We break a complex system into smaller components so that the system is easier to understand and easier to test. In particular, once we know how to detect an event, we will encapsulate that process into a subroutine, called Event. In this example, the main program first sets the output to zero, calls the function Event four times, then it sets the output to one. To detect the 0 to 1 to 0 edges in the input, it first waits for 1, and then it waits for 0 again. The letters A through H in Figure 5.4 specify the software activities in this simple example. In this example, execution is sequential and predictable.

Figure 5.4. Flowchart illustrating the process waiting for four events.

Before we write software, we need to develop a plan. Software development is an iterative process.

Even though we list steps the development process in a 1,2,3,4 order, in reality we cycle through these steps over and over. I like to begin with step 4), deciding how I will test it even before I decide what it does.

1) We begin with a list of the inputs and outputs. This usually defines what the overall system will do.

We specify the range of values and their significance.

2) Next, we make a list of the required data. We must decide how the data is structured, what does it mean, how it is collected, and how it can be changed.

3) Next we develop the software algorithm, which is a sequence of operations we wish to execute. There are many approaches to describing the plan. Experienced programmers can develop the algorithm directly in C language. On the other hand, most of us need an abstractive method to document the desired sequence of actions. Flowcharts and pseudo code are two common descriptive formats. There are no formal rules regarding pseudo code, rather it is a shorthand for describing what to do and when to

do it. We can place our pseudo code as documentation into the comment fields of our program. Next we write software to implement the algorithm as define in the flowchart and pseudo code.

4) The last stage is debugging. Learning debugging skills will greatly improve the quality of your software and the efficiency at which you can develop code.

5.1 Background

We will use C in this class for two reasons. First, over the last ten years, it has ranked one or two out of all high-level languages. Second, C is by far the most common language for writing software for embedded systems.

Figure 5.5. Graph of popular programming languages over time.

Position Jun 2013

Position

Jun 2012 Programming Language Ratings Jun 2013

Delta Jun 2012

1 1 C 17.809% +0.08%

2 2 Java 16.656% +0.39%

3 4 Objective-C 10.356% +1.26%

4 3 C++ 8.819% -0.54%

5 7 PHP 5.987% +0.70%

6 5 C# 5.783% -1.24%

7 6 (Visual) Basic 4.348% -1.70%

8 8 Python 4.183% +0.33%

9 9 Perl 2.273% +0.05%

10 11 JavaScript 1.654% +0.18%

11 10 Ruby 1.479% -0.20%

12 12 Visual Basic .NET 1.067% -0.15%

13 17 Transact-SQL 0.913% +0.21%

14 14 Lisp 0.879% -0.11%

15 16 Pascal 0.779% -0.07%

16 21 Bash 0.711% +0.09%

17 19 PL/SQL 0.657% +0.02%

18 13 Delphi/Object Pascal 0.602% -0.55%

19 18 Ada 0.575% -0.11%

20 22 MATLAB 0.563% 0.00%

Table 5.1. Top 20 popular programming languages.

C is a general-purpose programming language initially developed by Dennis Ritchie between 1969 and 1973 while at AT&T Bell Labs, see Figure 5.6. At the time, there were programming languages called A and another named B, so Ritchie decided to name his language C. Dennis Ritchie and Brian Kernighan wrote the first book on C, The C Programming Language. Ritchie was also one of the developers of the Unix operating system.

Figure 5.6. Dennis MacAlistair Ritchie

As C became more popular, many derivative languages were introduced. C++ was developed by Bjarne Stroustrup 1979-1983 also at Bell Labs. C++ is a language originally called “C plus classes”. In 1999, a professional standard version of C, called C99, was defined. When you download Tivaware (http://www.ti.com/tool/sw-tm4c) from Texas Instruments, you will notice TI’s example code for the TM4C123 has been written in C99. In this class however, we will use the more simple C language.

A compiler is system software that converts a high-level language program (human readable format) into object code (machine readable format). It produces software that is fast but to change the software we need to edit the source code and recompile.

C code (z = x+y;) → Assembly code (ADD R2,R1,R0) → Machine code (0xEB010200)

An assembler is system software that converts an assembly language program (human readable format) into object code (machine readable format).

Assembly code (ADD R2,R1,R0) → Machine code (0xEB010200)

An interpreter executes directly the high level language. It is interactive but runs slower than compiled code. Many languages can be compiled or interpreted. The original BASIC (Beginner's All-purpose Symbolic Instruction Code) was interpreted. This means the user typed software to the computer, and the interpreter executed the commands as they were typed. In this class, an example of the interpreter will be the command window while running the debugger. For more information on this interpreter, run Keil uVision and execute Help->uVisionHelp. Next, you need to click the Contents tab, open the uVisionIDEusersGuide, and then click DebugCommands. It will show you a list of debugger commands you can type into the command window.

A linker builds software system by connecting (linking) software components. In Keil uVision, the build command (Project->BuildTarget) performs both a compilation and a linking. The example code in this module has three software components that are linked together. These components are

startup.s uart.c main.c

A loader will place the object code in memory. In an embedded system, the loader will program object code into flash ROM. In Keil uVision, the download command (Flash->Download) performs a load operation.

A debugger is a set of hardware and software tools we use to verify system is operating correctly. The two important aspects of a good debugger are control and observability.

In document Embedded Systems Shape the World (Page 55-63)