Article Index

GPIO Capabilities

Another complication is that GPIO lines can be assigned to other internal devices within the processor. For example, the serial hardware (UART) can make use of any two GPIO lines for its RX and TX pins.

There is also a single AtoD converter device and this can be connected to any of a subset of GPIO lines that then act as analog inputs. To create analog outputs the Pulse Width Modulation (PWM) capabilities of some of the GPIO lines is used - see the chapter on PWM. By pairing the AtoD with the DtoA provided by the PWM some of the pins are designated as analog capable. 

Analog GPOIO Pins 
MICROBIT_PIN_P0
MICROBIT_PIN_P1 
MICROBIT_PIN_P2
MICROBIT_PIN_P3
MICROBIT_PIN_P4
MICROBIT_PIN_P10

Notice that pins 3, 4 and 10 are involved in driving the LED display and this allows for some interesting possibilities such as using the array as a light sensor. 

In the case of the micro:bit there is also a GPIO mode that allows you to use the pin as a debounced high impedance input on pins P0, P1 and P2. This makes it easy to use things like fruit and other objects as creative input devices.

Digital GPIO

With all of these possiblities using the GPIO lines can seem quite complicated. For simplicity, and because it is the most common way they are used, we will concentrating on their use as simple digital I/O lines and forget, for the moment analog, PWM and touch outputs and their use as I2C and SPI buses all of which are covered in later chapters.  

The basic interface to the GPIO lines is the MicroBitPin object.  You can create and use MicroBitPin objects as needed or you can use the MicroBitPin objects created on the uBit object. If you create your own MicroBitPin objects create them as global objects and do not create a uBit object. 

For example you can use to access Pin 0 you can either use

#include "MicroBit.h"  
MicroBit uBit;

main(){ 
    uBit.init();
    uBit.io.P0.setDigitalValue(1);
}

or

#include "MicroBit.h"
main(){
 MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL);
 P0.setDigitalValue(1);
}

Don't worry about the MicroBitPin constructor or the setDigitalValue function. All that matters at the moment is that you see the two ways of getting at a GPIO line.

If you are not troubled by resource shortages then use the uBit object approach because it frees you from having to instantiate a MicroBitPin object for each pin. If you are having resource problems then only instantiating the objects you are going to use is a better strategy. 

Notice that when you work with the uBit object the GPIO lines are just referred to as P0, P1 and so on. When you work with the MicroBitPin object directly the lines are referred to as MICROBIT_PIN_P0 etc. Be careful not to confuse the two naming systems.

If you want to create MicroBitPin objects then the constructor allows you to specify an event id, pin name and pin capability - i.e. what you are going to use the pin for. 

MicroBitPin(int id,PinName name, PinCapability capability)

In most cases you use the message bus id provided for you. PinCapability is one of:

(PIN_CAPABILITY_DIGITAL, PIN_CAPABILITY_ANALOG, PIN_CAPABILITY_AD, PIN_CAPABILITY_ALL)

Compared to other GPIO libraries the strangest thing about the micro:bit's framework is that it doesn't have functions that set the direction of a GPIO line. Instead the direction is set the first time you try to read or write a line. To write a 0 or a 1 to the line you would use:

int setDigitalValue(int value)

and to read a line:

int getDigitalValue()

This approach has the advantage of not needing much in the way of initialization but if the line isn't already set up correctly for reading or writing then it takes extra time to configure it. What this means is that the first time you read or write a line it can take longer than the second and subsequent use. As a result it is sometimes necessary to do an extra read or write to the line before you start using it as a way of setting things up.

Apart from these two main function there are a few useful utilities. You can test to see if a GPIO line is currently a digital line and if it is  an input or an output:

int isDigital()
int isInput()
int isOutput()

 

Drive Type

The GPIO output can be configured into one of a number of modes but the most important is pull-up/down. 

Before we get to the code to do the job it is worth spending a moment explaining the three basic output modes.

In pushpull mode two transistors of opposite polarity are used, for example:

The circuit behaves like the two-switch equivalent shown on the right. Only one of the transistors, or switches is "closed" at any time. If the input is high then Q1 is saturated and the output is connected to ground - exactly as if S1 was closed. If the input is low then Q2 is saturated and it is as if S2 was closed and the output is connected to +V.

You can see that this pushes the output line high with the same "force" as it pulls it low. 

This is the standard configuration for a GPIO output.

The pullup mode replaces one of the transistors by a resistor:

 

In this case the circuit is equivalent to having a single switch. When the switch is closed the output line is connected to ground and hence driven low. When the switch is open the output line is pulled high by the resistor. 

You can see that in this case the degree of pulldown is greater than the pullup, where the current is limited by the resistor. The advantage of this mode is that it can be used in an AND configuration. If multiple gpio or other lines are connected to the output, then any one of them being low will pull the output line low. Only when all of them are off does the resistor succeed in pulling the line high.

This is used, for example, in a serial bus configuration like the I2C bus. 

Finally the pulldown mode is exactly the same as the pullup only now the resistor is used to pull the output line low:

In the case of the pulldown mode the line is held high by the transistor but pulled low by the resistor only when all the switches are open. Putting this the other way round - the line is high if any one switch is closed. 

Generally speaking the pushpull mode is best for driving general loads, motors, LEDs, etc.

The pullup/down modes are used where you need to create a serial bus of some sort or when the load needs this sort of drive. 

To set the mode for a GPIO line the function to use is:

int setPull ( PinMode pull)

where pull is any of:

PullUp
PullDown
PullNone

PullNone is the default and the value of the pull up/down resistor is in the range 10 to 16K. What this means is that in most cases you will probably have to add an external pull up/down resistor to bring the resistance down/ 

GPIO Drive Characteristics

There is an important question to be answered - what can be safely connected to a GPIO line?

Of course there is no problem connecting things to GPIO lines set to input, which is the default. The only thing you have to check is that the input doesn't go above 3.3V - if it does you could damage the GPIO line and the micro:bit.

When it comes to output you have to be much more cautious to avoid damage to the micro:bit.

It is all a matter of how much current the line can source or sink. Obviously the output varies between 0V and 3.3V which is the working voltage of the micro:bit. So you can only connect devices which can operate at 3.3V but there is also the issue of how much current the device needs to operate.

One of the most difficult specifications to find is how much current the GPIO lines will provide. The reason is partly that the situation is complicated. There are a number of ways that the GPIO line can be set up and there is a limit on the total current in all of the lines as well as a per-line limit. 

What is very clear however is that the current handling ability of the micro:bit is very low. 

This is a problem because of the way that the GPIO lines are made very easy to access using clips and plugs makes it all too easy to connect a device directly to the line without thinking about the possibilities of damaging it. For example you cannot safely conned an LED to the micro:bit - the current draw is simply too much. 

In normal operation the GPIO lines are all configured into standard drive mode which limits the current to 0.5mA. 

This is a very small current - by comparison the Raspberry Pi can work with 3mA on each GPIO line and a single line can work up to 16mA.

There are high current drive modes which can be selected, see the next chapter. With high current drive on a GPIO line can work with a maximum of 5mA however the total current on all the GPIO pins cannot exceed 15mA. So you could have three GPIO lines drawing 5mA each or two drawing 5mA and five more drawing 1mA each.  Notice also that you can set high current drive on a per line basis. That is one line can be high current drive and the next standard. Each line is subject to a 5mA or a 0.5mA maximum current according to its mode. 

This is complicated so a summary will help:

  • In input mode you only have to keep the voltage on a GPIO line between 0 and 3.3V. The GPIO line in input mode is high impedance and draws little current from whatever is connected to it.
  • In output mode with the default standard drive the voltage is between 0 and 3.3V and the current has to be less than 0.5mA
  • In output mode a GPIO line with high drive turned on the current has to be less than 5mA.
  • The total current in all of the GPIO lines has to be less than  15mA.

Notice that you cannot set high drive mode on using the framework. To do this you have to access the hardware directly - see the next chapter. In addition to setting the drive strength you can also opt for open collector or open drain mode.

The bottom line is that in nearly all cases if you want to drive something from a GPIO line you will need to use a transistor or an FET to increase the current and perhaps change the voltage.  If you have seen or tried an LED or other device from a GPIO line without a transistor or FET as a buffer and it worked then either you have been lucky or the drive specifications are pessimistic - however users have destroyed their micro:bit with the same set up.