Up to this point this book hasn’t talked much about how to write code for the LaunchPad. You have seen many examples, and learning by example is often the fastest way to get something done. But at some point a more organized approach is needed. You have reached that point.
The programming language you have been using on the LaunchPad is C++. There are many good books on C++ programming and hundreds of free online tutorials for the language. This chapter will concentrate on the simple parts of the language that are the most used, but you will skip over whole sections of the language that are more advanced and complicated and that are rarely (if ever) used on small computers like the LaunchPad.
A C++ program usually starts with a function called main(). When using Energia, the main() function is actually hidden in the startup code. The hidden main() function calls your by-now-familiar setup() routine once and then calls the loop() function over and over again forever. So if you are reading a tutorial about C++ and are wondering where the main() function is, now you know.
A function is a bit of code that does something when it is called. Every bit of code that does something is inside some function or another. The familiar setup() and loop() functions are good examples.
The simplest functions don’t return values to the code that calls them. The setup() and loop() functions are two examples of functions that don’t return values. You tell the C++ compiler that the function has no return value by putting the keyword “void” in front of the function name. If the calling code does not pass any information to the function, you also use the keyword “void” to indicate that, by putting it inside the parentheses after the function name.
This is why the setup() and loop() functions look like this:
If you want a function to add two numbers together, you would need to tell the compiler about the types of numbers you want to add. They can be integers or they can be floating-point numbers (so you can express fractions). Either of those types can come in different sizes, with the larger sizes able to hold bigger numbers.
On the LaunchPad, the integer types are
The floating-point types are
The char type is 8 bits (it can hold numbers from -127 to 127). The short type and the int type are actually the same on the LaunchPad, each holding 16 bits (-32,767 to 32,767). The long type is 32 bits (-2,147,483,647 to 2,147,483,647).
The float type is 32 bits, and the double type is 64 bits. They can handle both huge numbers and very tiny numbers, but they are slow on the LaunchPad because the LaunchPad has no dedicated hardware for calculating with floating-point numbers.
So here is what the function to add two 16-bit integers would look like:
The first int keyword tells the C++ compiler that the add() function will return a 16-bit integer.
The declarations inside the parentheses tell the compiler that the add() function takes two 16-bit integer arguments, which will be referred to in this function as a and b.
The return statement returns the 16-bit value that results from adding a to b.
The function that calls the add() function might have a statement that looks like this:
This declares a new variable called “answer” that holds a 16-bit integer. The new variable will now hold the value returned by calling the add() function with the numbers 14 and 92. The answer variable can then be used later as input to other functions or calculations. For example, you might want to delay by that number of milliseconds:
Sometimes you want to give a name to a number. This makes it easier to understand what the number is used for, and it makes it easy to change all of the places where the number is used, all at once. You have seen many examples of this earlier—you use the enum statement to do this:
If you omit the equal signs, the enum statement will assign 0 to the first name, 1 to the second, 2 to the third, and so on.
Constants are things that don’t change. Numbers are constants, like 3 or 98.
You can also have character constants that store letters of the alphabet or punctuation. You enclose these in single quotes, as in ‘A’ or ‘+’.
(If you want to indicate the single-quote character, put a backslash in front of it, as in \’. To indicate a backslash, put two backslashes: \\. This is called “escaping” the single quote, or escaping the backslash.)
You can store strings of characters using double quotes, as in “Please push the button”.
Numbers can be integers like 123, or floating-point numbers, like 13.88 and 0.003.
Sometimes it is helpful to use number bases other than 10 to describe constants.
The constant 0b111 means to set all three of the lowest bits to 1. This is the same as the decimal value 7. The 0b means the number is in binary (base 2).
You can use base 8 (called octal) by putting a zero in front of the other digits (since the 0 looks like the o in octal). Thus 017 is the same as the decimal number 15.
You can use base 16 (called hexadecimal) by putting a 0x in front of the other digits and using the letters A through F to indicate the numbers you can’t express in decimal. Thus 0xA is the same as decimal 10, and 0xF is the same as decimal 15, and 0xFFFF is 16 bits that have all of the bits turned on (the same as 0b1111111111111111).
You can copy information into variables:
You can copy the information in one variable into another:
You use a single equal sign to indicate that the variable on the left gets changed to the value stored in the variable on the right. Nothing happens to the information in the variable on the right (it does not change or get lost anywhere).
If you put a value from an 8-bit integer variable into a 16-bit integer variable, it fits just fine, and all is well.
If you put a value from a 16-bit variable into an 8-bit variable, only the low eight bits get stored. The high eight bits are ignored. Thus the expression:
would put the value 0b01001001 into the variable called eight_bits and ignore the two high bits.
You can do calculations on your variables and constants by using operators such as plus and minus:
The operators * and / are used for multiplication and division:
The last two variables are grouped in parentheses to indicate that the multiplication should be done first, then the division. Otherwise the result would have been the same as (psi / width) * height.
Multiplication and division are done before addition and subtraction:
The other arithmetic operators are the unary minus sign, used to negate a number or variable (such as -1, -price, or -(tax+license_fee)); the modulo operator % (which is the remainder after division, so 5 % 3 is 2); and the increment and decrement operators ++ and --, used to add or subtract 1 from a variable. These last two operators change the variable they are used on. If they are used before a variable, they change the variable before its value is used in the rest of the expression, so in the bit of code:
the value of time would be 14. If the code looked like this:
then the value would be 13. In both cases, the value of seconds after the last statement would be 1.
The C++ language has a large number of operators for constructing expressions, and you have seen examples earlier of the “shift” operators >> and <<, the “and” operator &, the “or” operator | and the exclusive-or operator ^. All of these operators have their “precedence” much as multiplication happens before addition. But if you string together a lot of operators, my suggestion is to use parentheses to group them rather than trying to remember all of the precedence rules. Your code will then be readable by people who have forgotten whether shifts happen before exclusive-or.
To compare values, there are relation operators. These are for equal to, not equal to, less than, greater than, greater than or equal to, and less than or equal to. These are most often used in “if” statements and loop controls, but they can also be used as arithmetic operators, evaluating to 1 for true and 0 for false.
Likewise mostly used in control flow statements are the logical operators !, &&, and ║, for NOT, AND, and OR. An interesting feature of the ║ operator is that if the first operand is true, the expression after the ║ is ignored, since it will not change the value. This is true even if it has some effect, such as incrementing, assigning, or calling a function. The same is true of the && operator if the first operand is false.
The bitwise operators are and
for complement, AND, OR, XOR, left-shift, and right shift.
The complement of a number is where each bit is the opposite of what it used to be:
The value of not_pins is 0b101.
When you use the & operator, the result has a 1 bit only if the corresponding bits in each operand are 1. Thus in:
the variable not_pins would have the value 0b10.
When you use the | operator, the result is a 1 bit if either of the corresponding bits is 1:
and not_pins ends up with the value 0b11111.
When you use the ^ operator (exclusive or), the result is a 1 bit if one or the other corresponding bits is 1, but not both (the “exclusive” adjective):
and not_pins ends up with the value 0b11101.
The shift operators move all the bits left or right by the amount given in the second operand:
gives the value 0b110 to the variable not_pins.
For the operators that take two operands, you can simplify assignments by combining the operator with an equal sign. Thus these two statements result in the same action:
You have seen several examples of control flow statements. The simplest is the if statement:
You can also add an else statement after it:
Another simple control flow statement is the while:
A little more complicated is the for statement, which has three parts—the initialization, the condition, and the loop expression (where the control variable is usually modified):
All three parts are optional—the most reduced form is for(;;), which means to loop forever.
If you want to group several statements together so they can be controlled by an if, a while, or a for, simply put curly braces around the group of statements:
Comments are any text after two forward slashes. Everything from the slashes to the end of the line is ignored by the compiler.
Statements end in a semicolon, as you have seen in all of the examples in this book.
Energia provides a convenient library of built-in functions you can use in your programs. If you click on the Help button in Energia, it will bring up the Arduino Language Reference page, since Energia is based on the older Arduino platform. That is where you will find the documentation for many of the functions you have used in the examples in this book, such as pinMode(), digitalWrite(), analogRead(), etc.
Most of those functions are known to the compiler already. Others, such as the Servo class, are in extra libraries, and you need to include a header file before you use them. The Arduino Language Reference page you get when you click on the Help button in Energia has a link to the library page, where several libraries are described. In addition, you can create your own libraries.
For most simple programs, nothing more than the elements discussed so far are needed. But some additional information can make hard problems simple.
The first thing to discuss is called scope. This is the concept that variables have a lifetime, and which corresponds to where they are in the program.
If a variable is declared outside of a function, it is said to have global scope. That means that all functions after the variable is declared can see and use that variable.
If a variable is declared inside a function (or as a parameter to the function), it has function scope. It is known only inside that one function. If another function declares a variable with the same name, it is a new and entirely separate variable.
The space that variables in function scope occupy is released when the function returns, and that space can then be used by other functions for their own variables. This is important when you have a lot of data in your program and your microprocessor chip only has 512 bytes of data to use for everything.
You can also declare variables inside any block of statements enclosed in curly braces. When the program leaves that block, the space is reclaimed, just as if a function had returned.
Inside a for statement, you can declare the variable used to do the counting. The scope of that variable is the for statement and the block or statement the for controls. This is handy for simple variables you just use for counting, such as i or x.
If you want to prevent a variable from being reclaimed when it goes out of scope, you can declare it static. A static variable keeps its value across function calls or when exiting blocks.
Complex Data Types
Sometimes you want to group data together. The simplest example of this is the array.
An array is just a bunch of simple data types strung together one after the other so they can be indexed by a counting variable. You have used arrays several times in the examples in this book. Arrays are declared by putting their size in square brackets after the name of the variable:
If the array is initialized by putting values in curly braces after an equal sign, then the compiler can count the number of items itself, and you can omit the number in the square brackets:
You can retrieve the values in the array by using an index inside square brackets:
Sometimes you want to group different types of data into one bundle, to keep them all in one place. You use a structure for that:
You can then declare an instance of that structure by using the structure name:
To use the variables inside the structure, join them to the variable with a period:
Structures can be initialized by a function called a constructor. It is a function that has no return value (not even void) and has the same name as the structure:
Arguments to the constructor are passed in when the variable is declared:
Structures can have functions inside them besides the constructor. Those functions can see the variables inside the structure and can change them.
These functions inside structures are called “methods.” The change() method in the Throb structure changes the brightness of an LED every time it is called. It remembers the previous brightness so it can slowly ramp the brightness up and down if it is called many times.
All of the internal variables and methods in a structure are public and can be used, changed, and called by any function. If you have data you want to protect from other functions, you can declare it private:
A type of structure where everything is private unless you declare it otherwise is called a class. You can see the Throb class in use in this example: http://artists.sci-toys.com/Throb.txt.
That example uses the operator called new. The new operator creates space for the class and calls the constructor. This space is not freed up to be used by other functions until it is destroyed, using the delete operator. In a tiny computer like the LaunchPad, this kind of memory management is seldom used.
The Throb class described in the previous section used pointers to the three instances of the class Throb.
A pointer is declared by putting an asterisk in front of the variable name:
Throb *a, *b, *c;
You can have pointers to integers, chars, floats, doubles, structs, or anything that has a memory address. A pointer is simply a number that says where in memory some object is.
To use a pointer to a simple type, just put an asterisk in front of the variable name:
The ampersand gets the address of the object so you can store it in the pointer.
If the object is a struct or a class, you use the arrow operator to reference things inside the structure:
This says to call the change() method of the Throb object that the pointer a points to.
Pointers can be very useful in large, complicated programs but are not often seen in programs that run on tiny microcontrollers.
You can get the address of a function and use a pointer to call that function. This can be handy when you want to have an array of things that the program can do and select a function from the array. You could make your Rover (see page 142) dance randomly:
This code says to define a type (that’s the typedef operator) called function that is a pointer to a function that takes no arguments and returns no value. Then it uses that type to declare a list of addresses of functions (the motion functions you already have in the Rover program). Then the dance() function picks a random number from 0 to 9 and uses that as the index to pick a function to call.
Again, this kind of thing is rarely done on tiny computers.