Microcontroller Programming
ü Arrays
ü Bit shifting
ü Pointers
Arrays
So far, all variables we used included only one value. However, variables can hold multiple values, if they are
defined as arrays. Array variables can store multiple values of the same data type, where each value is
addressed using an index.
Arrays are created using the [ ] brackets and have to be assigned a size when created:
byte values[6]; //define array „values“ of type byte with 6 fields
To access a certain array field, we use the index:
value[3] = 31; //set the fourth value in values to 31
Alternatively, we can fill the array with values when defining it:
byte values[] = {34,1,7,9,5,2}; //define array „values“ of type byte and
//fill it with values 34, 1, 7, 9, 5, 2
Dr. Jörn Kretschmer Faculty MME
Arrays
Passing arrays to functions
Arrays could be useful to return multiple values from a function,
e.g. sort an array in ascending order. In the example on the right,
we use the Bubble Sort algorithm to sort the array numbers in
ascending order.
In this example, the sorting is done in the same function as where
the array is created. However, it would be beneficial to do the
sorting in a separate function.
Arduino code see
Bubblesort_v1.ino
Dr. Jörn Kretschmer Faculty MME
Arrays
Passing arrays to functions
Now, we have moved all sorting code to a separate function
sort(). However, the screen output shows, that the array size of
the array received in the function is 1, containing only the first
element:
To understand, what happened here, we first need to understand,
how variables are stored and addressed in the ARDUINO memory.
…
Arduino code see
Bubblesort_v2.ino
Dr. Jörn Kretschmer Faculty MME
Pointers
In the previous chapters we have used variables to hold data. A variable is defined by its data type and its
value:
int number = 10;
When this variable is created, the compiler checks, if a variable of the same name already exists and if not,
requests a place to put the variable from the memory manager. The memory manager returns an address
and the compiler uses that address to store the variable. Thus, a variable is not only defined by its name, its
data type and its value, but also by its address in memory. All that information is stored in the symbol table:
Identifier Data type lvalue rvalue
number int 2298 10
The location in memory is called the lvalue (location value) of a variable, the value itself is called the rvalue
(register value). To access the lvalue of a variable, we use a pointer:
Arduino code see
Pointers_v1.ino
int *pointerToNumber; //declare a pointer called pointerToNumber
pointerToNumber = &number; //& returns the address of a variable
Dr. Jörn Kretschmer Faculty MME
Pointers
Referencing
A pointer is variable itself, it has its own lvalue, the rvalue is the lvalue of the variable it points to:
Identifier Data type lvalue rvalue
number int 2298 10
pointerToNumber int 2296 2298
The asterisk placed either besides the data type or the variable name indicates to the compiler, that
pointerToNumber is a pointer. The address-of operator (&) returns the lvalue of a variable:
int *pointerToNumber; //declare a pointer called pointerToNumber
pointerToNumber = &number; //& returns the address of a variable
The second part of the above code is called referencing, thus & is also called the referencing operator.
Arduino code see
Pointers_v2.ino
Dr. Jörn Kretschmer Faculty MME
Pointers
Dereferencing
As a pointer holds the address of a variable in the memory, we can use the pointer to assign a new value to
the variable the pointer points to. This is done with the asterisk, named the dereferencing operator:
*pointerToNumber = 5; //Assign a new value to the variable the pointer is
//referencing
Even though we have never assigned a new value to number directly, the value in number has changed to 5,
because we have changed the value directly in the memory.
Arduino code see
Pointers_v3.ino
Dr. Jörn Kretschmer Faculty MME
Pointers
Passing variables to functions – Pass by Value
When passing variables to a function, there are two different methods:
• Pass by Value
• Pass by Reference
When passing a variable by value, we pass the rvalue of the variable to the function, thus the values of the
variables passed to the function are copied to local instances that only exist while the function is running. If
the value of one the variables in the function is changed, it does not affect the value of the variables outside
the function.
void setup() { void loop() {}
[Link](9600);
int sum(int x, int y){
int a = 10;
x = x+5;
int b = 5;
return x+y;
int res = sum(a,b);
}
[Link]("Result: ");
[Link](res); Arduino code see
} Pointers_v4.ino
Dr. Jörn Kretschmer Faculty MME
Pointers
Passing variables to functions – Pass by Value
Identifier Data type lvalue rvalue Identifier Data type lvalue rvalue
a int 2298 10 b int 2296 5
Identifier Data type lvalue rvalue Identifier Data type lvalue rvalue
x int 2292 10 y int 2294 5
Note, that while the rvalues are the same in a and x and in b and y, their lvalues differ. Thus, the variables
are different elements in the memory.
Dr. Jörn Kretschmer Faculty MME
Pointers
Passing variables to functions – Pass by Reference
Instead of passing the rvalue of a variable to a function, we can also pass the lvalue, using a pointer. In the
example below, b is passed by value, while a is passed by reference. Thus x now is a pointer, its rvalue is the
lvalue of a. By changing the value in memory where x points to, we change the rvalue of a.
void setup() { void loop() {}
[Link](9600);
int sum(int *x, int y){
int a = 10;
//Increase the value of a
int b = 5;
//by dereferencing pointer x
//Pass a by reference, using the
*x = *x+5;
//referencing operator
return *x+y;
int res = sum(&a,b);
}
[Link](“Value of a: ");
[Link](a);
}
Arduino code see
Pointers_v5.ino
Dr. Jörn Kretschmer Faculty MME
Pointers
Passing variables to functions – Pass by Reference
Identifier Data type lvalue rvalue Identifier Data type lvalue rvalue
a int 2298 10 b int 2296 5
Identifier Data type lvalue rvalue Identifier Data type lvalue rvalue
x int 2292 2298 y int 2294 5
Because x now points to the location in memory where a is stored, we can access a in the function by
dereferencing x.
Dr. Jörn Kretschmer Faculty MME
Pointers
Passing arrays to functions
Let us return to the array sorting we discussed before. The array that was passed to the function, seemingly
had a size of 1 and contained only the first element of the array numbers. The reason is, that arrays in
C/C++ are not passed by value, but are passed by reference. Thus, the array nums in the sort() function
void sort(int nums[]){...
is not a copy of the array numbers in the setup() function, but a pointer to that array.
As a pointer has a length of 1, nums (which is not an array but a pointer), seems to only contain one value.
Thus the size of the array:
byte n = sizeof(nums)/sizeof(int);
was calculated incorrectly and the loop was only iterating through the first element of the array numbers.
We can still iterate through arrays even if we only know the address of the first element, but to iterate
through all elements, we need to know the size of the array. As only the pointer to the first element is
passed to the function, we can not derive the size of the array in the function. Thus, we need to add a
second parameter to the function, holding the size of the array:
Arduino code see
void sort(int nums[],byte n){... Bubblesort_v3.ino & Bubblesort_v4.ino
Dr. Jörn Kretschmer Faculty MME
Pointers
Pointers to arrays
Interestingly, we are able to iterate through an array, even if we know only the address of the first element.
Each index of an array points to a new address in memory, but the size in memory depends on the data type
of the array. As the data type of the pointer must match the data type of the variable it points to, the
compiler knows the memory size of the variable. Thus, we can use the pointer to iterate through an array:
When the data type of the array is Byte, rvalue of the pointer will be increased by 1:
byte ByteArray[] = {4, 56, 12, 83};
byte* p = &ByteArray[0]; //pointer to the first element in ByteArray
p++; // Increase p -> pointer will be increased by 1
However, when the data type of the array is Integer, the rvalue of the pointer will increase by 2:
int IntegerArray[] = {5687, 6537, 8986, 2674};
int* p = &IntegerArray[0]; //pointer to the first element in IntegerArray
p++; //Increase p –Y pointer will increase by 2
Check ARDUINO code Array_Pointers_v1.ino and Array_Pointers_v2.ino for examples.
Dr. Jörn Kretschmer Faculty MME
Pointers
Using pointers to reduce code redundancy
Previously, we have written code that modifies variables in the
main loop. Functions were used to source out calculations or
communication through the various interfaces.
Using pointers we can write functions that have direct access to
variables outside their scope. In the following example, we
implement the button state machine that we have written
before, but we use the same code to run two buttons. The code
is running in a function check() that receives all necessary
variables (the pin number, the timing variable, the state, the LED
toggle marker and the LED pin). The variables that need to be
changed (the state, the timing variable and the LED toggle
marker) are sent as pointers, thus allowing the function to modify
their values by dereferencing the pointers. Using the same
function, we can now use multiple buttons by using the function
with the pointers to the respective button variables.
Arduino code see
Pushbutton_pointer.ino
Dr. Jörn Kretschmer Faculty MME
Pointers
Dereferencing pointers to objects
In the previous examples, we have only used pointers to variables and arrays, but not to objects. When
using pointers to objects, we need to be careful when calling object functions with the .-operator.
Example:
We create an object of class Stepper and create a pointer to that object:
Stepper myStepper(200,10,11,12,13); //object of class stepper
Stepper* stepperPointer;
stepperPointer = &myStepper; //reference myStepper with the pointer
When dereferencing the pointer to use the step() function, the following command will lead to an error:
*[Link](1);
Because he compiler tries to call a function from the pointer, not the dereferenced object, the pointer
points to (the .-operator has a higher precedence than the *-operator). Thus, use brackets to override the
precedence:
(*stepperPointer).step(1); //dereference stepperPointer and call step()
Alternatively, we can use -> instead of the *- and .-operators:
stepperPointer->step(1);
Dr. Jörn Kretschmer Faculty MME
Pointers
Using pointers to reduce code redundancy
Homework
Write a function that allows rotating a servo motor
with a defined speed (given in °/sec). In the
function, calculate the necessary delay, rotate the
servo by 1° and use delay() to pause the code
execution until the servo has to rotate by 1° again.
When 180° are reached, return to 0°. Do not use a
loop in the function, but call the function
repeatedly in the loop(). Pass all required
variables by value or reference as necessary.
Dr. Jörn Kretschmer Faculty MME
Bit shifting
The problem of data size in communication
All the variations of communicating between Arduino boards, with sensors or with the PC have the same
problem: we can only pass one byte per transmission. Thus, we either need to convert larger values into a
byte, by e.g. converting the information that is sent (e.g. sending the distance in cm instead of the travel
time in milliseconds that the ultrasound module measured) or we need to split up the value into a series of
bytes. Thus, as a first step, we need to learn how to split up a larger number into multiple bytes and store
them:
int bigNumber = 31671; //Integer number with a value > 1byte
The number above requires 2 bytes to be stored:
0111 1011 1011 0111
Thus, we require a byte array of size 2 to store the number:
byte numberArray[2];
The first byte (1011 0111) of the Integer above should now be stored in numberArray[0], the second
byte (0111 1011) in numberArray[1]. But how do we extract a single byte?
Dr. Jörn Kretschmer Faculty MME
Bit shifting
The problem of data size in communication
The C language allows to shift the position of bits to the left or the right. Using >> shifts a value bitwise to
the right, << shifts it to the left.
Example:
byte a = 21;
The variable a has the value 21, which in binary format is: 0 0 0 1 0 1 0 1
Now, we shift a 3 bits to the left:
a = a<<3;
The variable now has a binary value of: 1 0 1 0 1 0 0 0, which in decimal is a value of: 21*23 = 168
Arduino code see
Bitshifting_v1.ino
Dr. Jörn Kretschmer Faculty MME
Bit shifting
The problem of data size in communication
We can use bit shifting to move the higher byte of the Integer number 31617 to the right by 8 bits, thus
moving the entire byte to the position of the lower byte:
byte highByte = bigNumber>>8; //Move the high byte 8 bits to the right
//and store it in highByte
The variable highByte now has the binary value 0 1 1 1 1 0 1 1, which are the values of the bits 9-16 of
bigNumber.
To get the lower byte isolated, we need to and it with 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1, or 0xFF in
hexadecimal respectively:
byte lowByte = bigNumber&0xFF; //bigNumber is anded with 0xFF to delete the higher
//byte from bigNumber
By anding bigNumber with 0xFF, we basicall delete the higher byte, thus the result is:
0000 0000 1011 0111
highByte now holds only the bits of the high byte of bigNumber, while lowByte holds just the lower
byte. Instead of using highByte and lowByte, we can use numberArray[0] and
numberArray[1].
Dr. Jörn Kretschmer Faculty MME
Bit shifting
The problem of data size in communication
To reassamble, the Integer value from the two separate bytes, we use bit shifting and a bit-operation again
(this time oring). First, we copy the higher byte stored numberArray[0] to our new Integer variable:
newBigNumber = numberArray[0];
Next, we need to shift this byte 8 bits to the left to move it to the correct position:
newBigNumber = newBigNumber<<8;
newBigNumber now has the follwoing value: 0 1 1 1 1 0 1 1 0 0 0 0 0 0 0 0
To add the lower byte, we need to or newBigNumber with numberArray[1], which holds the lower
byte:
newBigNumber = newBigNumber|numberArray[1];
Arduino code see
Bitshifting_v2.ino
Dr. Jörn Kretschmer Faculty MME
I 2C
Sending an Integer value through I2C
With the above code example, we can now transmit value that are larger than 255 via I2C, SPI or the serial
interface. In this example, we send the value 31671 via I2C from the Master to the Slave. Before sending, we
need to convert the Integer value into an array of bytes as done in the example before. We can then send
the byte array by using:
[Link](numberArray,2); //Send two bytes to the slave
On the Slave, we need to receive two bytes and store them in an array:
byte receiveArray[length];
//Receive the message byte by byte and store it in the byte array
for(int i = 0;i<length;i++){
receiveArray[i] = [Link]();
}
Because length is a parameter of the I2C receive ISR, we know how many places the array must have. To
convert the byte array back to the Integer value, we use the procedure from the previous example.
Arduino code see I2C_Integer_Master.ino & I2C_Integer_Slave.ino
Dr. Jörn Kretschmer Faculty MME