Lesson 2 - Basic Input and Output (GPIO)
Lesson 2 - Basic Input and Output (GPIO)
IODIR controls the 'direction' of the GPIO pin. You use this register to set a GPIO pin to either Input (0) or Output (1). For
example, if we want to use our GPIO pin to send signals 'out' from the microcontroller to some external device, we need to
set a pin (for example GPIO 0.10) to 'Output' (1). We could do that with the following code:
If we wanted to use our GPIO pin to receive information from the outside world (reading it inside the microcontroller), we
would need to set GPIO 0.10 to 'Input' (0). That could be accomplished the following code:
To set several pins to output at once we could do either of the following (which will produce identical code when compiled):
GPIO0_IODIR |= 00008C00;
If you don't understand why these lines are equivalent, feel free to take a look at Understanding Hexadecimal Numbers.
While ''(1 << 10) | (1 << 11) | (1 << 15)" is probably clearer to read and easier to understand, hexadecimal numbers are
(for better or for worse) the norm in embedded development and you'll need to come to terms with them sooner or later.
// Turn the LEDs off using IOSET (which supplies 3.3V and breaks our circuit)
You might have noticed when running this code that as soon as you set the direction of the pins to 'Output' with IODIR (the
first line of code), the LEDs turned on! This is because the pins will default to being 'low' (meaning a connection to GND)
when you set them to output, which completes our circuit and turns the LEDs on before we even have time to try to clear
the pins ourselves in the second line of code. In this particular case, the second line of code (where we use IOCLR to 'clear'
the pins explicitly) won't actually do anything, since they are already 'low'. However, you should never depend on the
'default' value of a pin when writing your code since the pin's value may have been changed elsewhere (such as in an
interrupt).
IOPIN
Regardless of whether your GPIO pin's direction is set to Input or Output, you can use the IOPIN register to read the
current 'state' of every GPIO pin in the pin block (the collection of all 32 pins in GPIO0). A 1 value means that the pin is
currently 'high', and a 0 value means the pin is currently 'low'. (Please note that since IOPIN returns the current state of
ALL 32 pins in the pin block, you have to do a little bit of extra work to determine the value of one specific pin, but we'll
give you an example below.)
You could read the IOPIN register, for example, to see if your LED was currently turned on or off, where IOPIN would
return 0 for pin 10 (LED1) if it was currently turned on (since setting the GPIO pin to low turns the LED on) and 1 if the LED
was off (since setting the GPIO pin high turns our LED off). Here's a simple method showing how to do this, including one
way to read the value of a single pin from the 32-bit value returned by IOPIN. This method will return '1' if the supplied pin
is currently 'high', and '0' if it is currently low:
return pinState;
Looking at this sample code, if the LED was currently turned on, "int state = getPinState(10);" should set 'state' to 0 since
pin 10 is set to low; if the LED was currently turned off, the same 'state' variable would equal 1 since pin 10 is set to high.
Sidenote: Sometimes smaller really is better!
In general, most experienced software developers try to keep their code as 'concise' as possible (without sacrificing
readability). The entire method above, for example, could be rewritten using one line that accomplishes exactly the same
thing:
Your compiler would almost certainly compile the code identically in these two cases, but in the latter case you end up with
source code that isn't as sprawling and may be a bit quicker to analyse. Another good example in the above code is the "?",
called a conditional operator (sometimes called a 'ternary operator' as well). The conditional operator will return one of
two values, depending on whether the code it is 'evaluating' -- (GPIO0_IOPIN & (1 << pinNumber)) in this case -- is true
(1) or false (0). If it is true, the first value will be returned. If it is false, the second value (following the ":") will be returned.
You could just as easily write this code as:
if (GPIO0_IOPIN & (1 << pinNumber)) == "1"
return "1";
else
return 0;
but that's a complete waste of space, and takes more effort to write as well as to read it. Again, the compiled code would
almost certainly be identical in either case, but the aesthetic appeal and readability of your code can often be improved with
a bit of experience and conscious effort on your part.
Everything you need to know to do this exercise has already been explained in this article, and you'll probably get the most
benefit out of it by trying to find a solution yourself first. If you've already made an effort, though, or are having a hard
time, here are some basics steps that you could follow to finish this exercise:
1. Set the direction of your buttons to input with IODIR
2. Set the direction of your LEDs to output with IODIR
3. Turn your LEDs off by setting them 'High'
4. In an infinite loop ("while (1) { ... }"), do the following:
5. Read the current state of Button1 and Button2 with IOPIN
6. If either button is pressed (remember pressed = 'Low' or '0'), ...
7. ... turn the appropriate LED on by clearing it to 'Low'.
8. If a button is not pressed (released = 'High' or '1') ...
9. ... turn the appropriate LED off by setting it to 'High'
I'm HOPELESSLY lost .... can you just give me the answer?
For the sake of completeness, and just to have something to compare your own efforts against, here is one possible
solution for this exercise, building on some of the code we have already introduced. In this particular example, when
Button1 is pressed, LED1 will be turned on, when Button1 is not pressed, LED1 will be turned off:
#include "lpc214x.h"
// Method prototypes
int main(void)
while (1)
if (getPinState(15))
GPIO0_IOSET |= LED1;
else
// Turn LED1 on
GPIO0_IOCLR |= LED1;
}
}
return pinState;