CAB202 Tutorial 7 - v2 PDF
CAB202 Tutorial 7 - v2 PDF
Overview
The first half of this unit has involved using the C programming language to perform complex
software-based operations on a computer. The next step is using the capabilities of the C language
to perform low level operations (turning on LEDs, responding to button presses, controlling an LCD
screen, etc.) on a real microcontroller (the Teensy). This tutorial will work through the setup of the
environment required for the microcontroller and will be needed for the remainder of CAB202. The
tutorial will also introduce the basics behind configuring and performing digital input and output (IO)
on a real microcontroller with C code. This tutorial is worth 3% of your final mark for this subject.
The installation files and setup instructions can be found here for:
To test that avr-gcc is installed and setup correctly on your computer, or is already installed on a
university computer, type in the following command into your chosen terminal window:
avr-gcc --version
If everything was installed and setup properly you should see an output similar to this:
Once that command has finished, use the following command to complete compilation and produce
a *.hex file:
So for a helloworld program you would use the following two lines in a terminal to create a
*.hex file ready for upload to the Teensy with the Teensy uploader application:
Please take note that the DF_CPU flag is compiling the program with a CPU speed of 8MHz. In your
code you must also request a CPU speed of 8MHz. This is done by setting the CLKPCE bit of the
Clock Prescaler Register (CLKPR), and then providing a prescaler selection in bits 3-0. See the
cpu_speeds.h header for a series of macros that perform this (and see the datasheet on
Blackboard for the port and pin configurations). In short, always start your main() function with:
set_clock_speed(CPU_8MHz);
DDRX
The data direction register is used to configure a pin in an AVR microcontroller as an output
or an input. Setting a bit to a 1 will make the respective pin an output, while setting it to a 0 will
make it an input. The following example would set the 0th pin in PORTB (also referred to with PB0) as
an output, while the rest of the pins on the port are set as inputs:
DDRB = 0b00000001;
This line of code can also be performed using bit shifting. Bit shifting is a lot easier to debug and
understand as it explicitly states what bit is being set:
DDRB = (1<<0);
To set a pin as an input we need to set the respective bit to a 0. This can be achieved through
complementing (NOT operator) the bit shifting operation. The following line would bit shift 1 three
places, then take the bitwise NOT of the value. The result is pin 3 of PORTB set as an input:
DDRB = ~(1<<3);
However, it should be noted that the if the following lines of code were ran sequentially, the second
line would override what was done in the first line, and in fact pin 2 of PORTB (PB2) would be
configured as an input. The reason for this is simple, we first write the binary value of 0b11111011
to the DDRB register, but then we write the value of 0b11110111, and so we are overriding that
previously written zero in the second bit.
DDRB = ~(1<<2);
DDRB = ~(1<<3);
There is a common solution to this however; we can use the OR and AND bitwise operators. If we
perform a bitwise AND or a bitwise OR on the register with whatever we are trying to setup as an
input or output, we will only change those bits we indeed wanted as an input or output (while the
rest of the register stays the same). For example to configure pin 3 of PORTB as an input, and not
affect the rest of the bits, we would have the following code:
The DDRB register is in some unknown state, and all we know is that we dont want to change it. So
we shall write it as 0bxxxxxxxx, where x is an unknown bit state. Also performing the bit shifting
and complement (NOT) of the second part of the line we would get 0b11110111. Thus we would
have:
Now let us perform the bitwise AND operation. We have 2 cases, we either have the bitwise AND of x
and 1, or the case of x and 0. In each of these cases there are, again, two cases for the value of x.
However, for example in case 1, AND of x and 1, we could have x = 1 or x = 0. Performing the
operation for both of these cases would get a result of 1 for the case x = 1, and a result of 0 for the
case x = 0. Thus the result is simply the value of x. The same can be done for the second case when
the AND of x and 0 is found. These are the results of the 4 possible cases:
So this bitwise AND technique can be used when we wish to set a register to a value of 0, and do not
want to change the any of the other bits. The same sort of process can be applied to bitwise OR,
which is used when we want to set a bit in a register to a value of 1 and do not want to change any
of the other values. The following table summarise the bitwise OR cases:
OR of X and 1 OR of X and 0
X=1 1 (Bit equals 1 as desired, does not depend 1 (Value of X, so stays in same state as
on original value of X) previously)
X=0 1 (Bit equals 1 as desired, does not depend 0 (Value of X, so stays in same state as
on original value of X) previously)
It should also be mentioned that these lines of code can be written in a compound form by using the
compound assignment operators (http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B):
PORTX
The PORT register is used to set pins that are configured as outputs to either high / on / 1 or
low / off / 0. This is done by configuring the bit you wish to be high or low as 1 or 0 respectively,
assuming that the bit you wish to set high or low was already configured by the corresponding DDR
register. For example, the following line of code would configure pin 7 on PORTA (PA7) to output 1,
assuming previously set up as an output using the DDRA register.
PINX
The PIN register is used to read the states of a pin, again assuming that the pins you wish to
read was setup previously as an input. For example, the following code would read all the values on
PINC:
However this would read all 8 bits from PINC into the variable readValue. In most cases, you are
only interested in reading a single bit of this register (i.e. finding whether it is logical low or high).
This becomes a lot clearer with a solid example. For example, consider a scenario where pin 3 on
port C is connected to a button and when the button is pressed the pin value goes high (i.e. to 1).
Obviously, we are only interested in the value of pin 3 (there could be other buttons on other pins
that obviously shouldnt effect checking the state of the button on pin 3). To only check the value of
a single bit in PINC, bit operators are used. The following code checks if bit 3 of PINC is logical high
and stores the result in switchValue:
Assume that the pin on bit 3 is some unknown value (denoted by S) and the values of all the other
bits on the PINC register are unknown (denoted by x). So that means PINC defined as:
PINC = 0bxxxxSxxx
Now, the bit shifting operation is performed (PINC>>3). This means that the value of PINC is
shifted 3 places to the right. The result of this operation is:
(PINC>>3) = 0b000xxxxS
Note that the bit of interest (S) is now in the least significant bit (LSB) position. The AND bitwise
operation is then executed with this bit shifted value and the value 1. Therefore, the result is as
follows:
Consequently, the original line of code will store the above value is in the switchValue variable.
Therefore:
switchValue = 0b0000000S
So if S was equal to 1, then the result would be 1, and if S was 0 the result would be 0. Importantly,
as was desired at the start of this explanation, switchValue would be equal to 1 if the switch was
logical high, or 0 if it was logical low.
Non-assessable Exercises
NOTE: These exercises are not assessable. Assessable exercises are in the following section.
2. Bitwise operations
What would the following bitwise operations equal in binary and in hex?
(1<<5)
(1<<3) | (1<<7)
~(1<<7)
What would equivalent bitwise operations be for the following hex values?
0xFE
0x07
0x14
DDRB = (1<<3);
a) Sets the value of the DDRB register to 3, which sets pin 3 high.
b) Sets the value of the DDRB register to 8, which sets pin 3 of PORTB as an output.
c) Sets the third bit of PORTB to an output.
d) Pin 3 of PORTB is set low.
Complete the assessed exercises in the week 8 section on the AMS. The AMS is available at the URL
http://bio.mquter.qut.edu.au/CAB202/.
Challenge Exercises
Flash both LEDs at approximately 1 Hz.
Flash the LEDs alternatively at 1 Hz (one on, one off like a railroad crossing).
Flash LED0 at a rate of 3Hz and flash LED1 at a rate of 0.4Hz.
Write a program to toggle the LEDs when a button is pressed. (Google switch debouncing, or
talk to your tutor).