The micro:bit's LED display may only be 5x5 but it is very versatile. If you want to make use of it directly then you are going to have to master some lower level functions.

Mostly the functions provided by the framework are all you need to work with the LED display. They are also fairly self explanatory and easy to use so there is no point in going over them. You can look them up in the documentation and use them in programs without too much trouble. In this chapter we cover the structure of the LED display and some interesting ways of using it in the hope that you might find creative ways to use the display.

Now On Sale!

You can now buy a print edition of micro:bit IoT in C.

You can buy it from:

USA and World  Amazon.com
Canada              Amazon.ca
UK                      Amazon.co.uk
France                Amazon.fr
Germany            Amazon.de
Spain                  Amazon.es
Brazil                  Amazon.br
Italy                    Amazon.it
Japan                 Amazon.co.jp
Mexico               Amazon.com.mx 

 

 The full contents can be seen below. 

Chapter List

  1. Getting Started With C/C++
    Anyone who wants to use the BBC micro:bit to its full potential as an IoT device needs to look outside the coding environments provided by its own website. As an mbed device, however, the micro:bit  is capable of being programmed in C/C++. Here we look at how to use the mbed online compiler for a simple demo program.

  2. Offline C/C++ Development  
    We have already discovered how to use the online editor to create a C/C++ program. Now we are going to move to the desktop with an offline approach. This has the advantage that we can use any tools we care to select and no Internet connection is needed.
  3. First Steps With The GPIO 
    The most basic task when working with the micro:bit is controlling the I/O lines. This isn't difficult if you use the framework provided but there some subtle points to watch out for. This chapter looks a the basics of using the GPIO.

  4. Working Directly With The Hardware - Memory Mapping. 
    The framework makes working with the GPIO and other devices as easy as it can be but there are many layers of software to go through before you get to the hardware. Writing directly to the hardware can make things up to ten times faster and give you access to things that their framework doesn't. It is also an educational experience to deal with the raw hardware directly.

  5. Pulse Width Modulation, Servos And More
    In this chapter we take a close look at pulse width modulation PWM including, sound, driving LEDs and servos.

  6. I2C
    The I2C bus is one of the most useful ways of connecting moderately sophisticated sensors and peripherals to the any processor. The only problem is that it can seem like a nightmare confusion of hardware, low level interaction and high level software. There are few general introductions to the subject because at first sight every I2C device is different, but here we present one.

  7. I2C Temperature Measurement
    Using I2C devices is fairly easy once you have successfully used one - and hence know what information you need and what to look for in a working system. In this chapter we use the HTU21D temperature and humidity sensor as a case study of I2C in action. It also happens to be a useful sensor.

  8. A Custom Protocol - The DHT11/22

  9. The DS18B20 - One Wire Bus

  10. The SPI Bus
    The SPI bus can be something of a problem because it doesn't have a well defined standard that every device conforms to. Even so if you only want to work with one specific device it is usually easy to find a configuration that works - as long as you understand what the possibilities are. 

  11. SPI MCP3008/4 AtoD   
    The SPI bus can be difficult to make work at first but once you know what to look for about how the slave claims to work it gets easier. To demonstrate how its done let's add eight channels of 12 bit AtoD using the MCP3008.

  12. Serial Connections
    The serial port is one of the oldest of ways of connecting devices together but it is still very, very useful. The micro:bit has a single serial interface but it can be directed to use any of the GPIO piins as Rx and Tx. 

  13. WiFi 
    The micro:bit has a radio that works in Bluetooth LE and point-to-point ad-hoc mode, but at the moment it lacks WiFi connectivity. The solution is to use the low cost ESP8266 to make the connection via the micro:bit's serial port. 

  14. LED Display 
    The micro:bit's LED display may only be 5x5 but it is very versatile. If you want to make use of it directly then you are going to have to master some lower level functions.

 

Driving The LEDs

The basic structure of the LED array is fairly simple and obvious if you have ever done anything similar. The LEDs are arranged in a grid of three rows and five columns. Each row has a GPIO line to drive it and each row has a GPIO line that acts as ground or not depending on whether the LED should be on or off. 

The arrangement of LEDs and GPIO lines is:

As you can see there are two LEDs missing from the grid. If we had the full set of 3x9 LEDs we would have 27 and a 5x5 array only needs 25. 

LED MATRIX COLS
COL1 = p4
COL2 = p5
COL3 = p6
COL4 = p7
COL5 = p8
COL6 = p9
COL7 = p10
COL8 = p11
COL9 = p12
LED MATRIX ROWS
ROW1 = p13
ROW2 = p14
ROW3 = p15

The LEDs are also not arranged in a neat row column numbering. Instead they are arranged in an order that makes the PCB layout easier:

 

 

So to illuminate a given LED i.e. one at position row,column all we have to do is set the row GPIO high and the column GPIO low - this picks out just one LED to be on.

For example to select the center LED 2,3 you would need to set GPIO line p14 high and line p6 low. The problem is that the framework only provides objects for GPIO lines that are brought out to the the edge connector and none of the row lines are and only some of the column lines are.. 

The solution is to make us of the mbed functions that the framework is built in. The disadvantage of this is that you don't get any of the event handling facilities. There are mbed functions for most of the hardware facilities that we have used and you can use them if you want to. They are slightly faster than the framework functions because in general the framework functions call the mbed functions to get the work done.

For example to flash the middle LED we need a DigitalOut object:

 
DigitalOut (PinName pin)
Create a DigitalOut connected to the specified pin. 
  DigitalOut (PinName pin, int value)
Create a DigitalOut connected to the specified pin with given initial value
void  write (int value)
Set the output, specified as 0 or 1 (int) 
int  read ()
Return the output setting, represented as 0 or 1 (int) 
   
int  is_connected ()
Return the output setting, represented as 0 or 1 (int) 
DigitalOut &  operator= (int value)
A shorthand for write() 
  operator int ()
A shorthand for read() 

 

All you need to know to make use of this is that there is also a complete set of pin names like P0_n for GPIO Pin n. Using this you can access any GPIO line.

In addition there are also predefined pin names for COL1 through COL9 and ROW1, ROW2 and ROW3.

So to flash the center LED you would use:

#include "MicroBit.h"
MicroBit uBit;
int main() {
   uBit.init();
   uBit.display.disable();
   
   DigitalOut col3(P0_6,0);
   DigitalOut row2(P0_14);
    while(1) {
        row2 = 1;
        wait(0.2);
        row2 = 0;
        wait(0.2);
    }

   release_fiber();
    return 0;
}

Notice that to take control of the GPIO lines that work with the display you either have to not create the uBit object or you have to call the disable method of the display. After this the program simply sets col3 to zero and toggles row2 to flash the LED. 

We could equally well have used:

 DigitalOut col3(COL3,0);
 DigitalOut row2(ROW2);

If you think about the hardware for a moment it should be obvious that we can turn on any LED in a single row by setting its row to on and multiple columns to off. 

How then can we create a display where LEDs in different rows are on at the same time?

The answer is that we turn then on an off so fast that the human eye can't see the flashing. 

For example to turn the LEDs on in column 3 and rows 1, 2 and 3 you could use:

   DigitalOut col3(COL3,0);
   DigitalOut row1(ROW1);
   DigitalOut row2(ROW2);
   DigitalOut row3(ROW3);
   while(1) {
        row1 = 1;
        wait(0.2);
        row1 = 0;
        row2 = 1;
        wait(0.2);
        row2=0;
        row3 = 1;
        wait(0.2);
        row3=0;
    }

If you run this program you will see the LEDs on the diagonal flash one after another. Now take out the wait function calls - you will see all three LEDs on the diagonal on apparently at the same time.

This is what the framework display object does for you automatically. It scans the three rows turning on and off LEDs in that row to create a complete display. It is another job performed by the framework's event handling system. 

If you want to do this sort of job at a reasonable speed you need to set and unset all of the LEDs in a single row in one operation as explained in the chapter on fast memory mapped GPIO. The mbed library has functions that you can use to do this or you can write your own.

Grey Scale

One of the features of the framework's support for the LED display that isn't so obvious is that you can use it as a grey scale display.  The reason it isn't so obvious is that there isn't a setValue type function for a single LED. It doesn't have any functions that let you work with a single LED at all. 

However the display object uses a buffer which is a MicroBitImage object. The basic idea is that the render function displays what ever is in the buffer. You can create MicroBitImage objects and print them to the display or you can work directly with the buffer MicroBitImage object to display "live" graphics. 

The key method is:

int setPixelValue( int16_t  x,int16_t  y,uint8_t value)

and the value can be anything from 0 to 255. The brightness is created by simply turning the LED on and off in the correct ration as in PWM. However PWM isn't used in the display it is a simple consequence of the repeated rendering of the display and the percentage of time a pixel is set to on.

For example you can slowly increase the brightness of the center LED:

 

uBit.init();
uBit.display.setDisplayMode(DISPLAY_MODE_GREYSCALE);
int i;
for(;;){
 for(i=0;i<256;i++){
    uBit.display.image.setPixelValue(2,2,i);
    uBit.sleep(50);
 }    
}

Notice that you have to set the greyscale mode because this is more complex and hence not the default. 

You can explore the possibilities of using MicroBitImage object to perform animation and other graphical techniques.

However the real problem is that no matter how clever your software you only have a 5x5 display.

Using Aliasing To Increase Resolution

As an example of how you can use the grey level resolution of the display to increase its effective spatial resolution let's implement a traditional BBC Micro game - Commando Jump. This is a very simple game where a figure's height above the baseline is controlled by how fast the user clicks a button.  It is a simple game but only showing the commando in five positions makes it a little crude.

As an alternative to simply moving the commando by one LED for every ten button clicks we can move the brightness from one to another gradually. You can think of this as locating the commando in the gaps between the LEDs if you want but it is also the standard anti-aliasing technique from higher resolution graphics. 

A function to move the commando by fractional amounts is:

void flashMan(int x, int y, int p, int vx, int vy) {
    uBit.display.image.setPixelValue(x, y, 0);
    uBit.display.image.setPixelValue(x + vx, y + vy, 0);
    uBit.sleep(100);
    uBit.display.image.setPixelValue(x, y, 25 * (10 - p));
    uBit.display.image.setPixelValue(x + vx, y + vy, 25 * p);
    uBit.sleep(100);

}

The commando is at x,y and p is used to indicate how close to being at x+vx and y+vy he is. For example suppose p is 0 then the first LED is set to 10*25 i.e. 250 and the second to 0. If p is 3 then the first is set to 7*25 and the second to 3*25. Finally is p is 10 the first is set to 0 and the second to 10*25. As p varies from 0 to 10 the position of the brightness moves from the first to the second LED.

We also need something to record how many times the button has been clicked. This can be done using an event handler that counts clicks using a global variable. There is no doubt that this would be better implemented as an object using C++ with methods such as getValue and reset and so on but for simplicity we will use C. 

void setUpButton() {
    uBit.buttonB.setEventConfiguration
               (MICROBIT_BUTTON_ALL_EVENTS);
    uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, 
      MICROBIT_BUTTON_EVT_CLICK, buttonClickCount);
}

Now buttonClickCount is called each time the user clicks buttonB:

void buttonClickCount(MicroBitEvent e) {
    clickCount++;
‚Äč}

Now we have all of the elements needed to implement the game. 

The complete listing is:

#include "MicroBit.h"
MicroBit uBit;

int clickCount = 0;

void startGame();
int playGame();
void endGame(int position);void countDown();
void drawPlayArea();
void flashMan(int x, int y, int p, int vx, int vy);
void setUpButton();
void moveHorizontal();
void moveVertical(int x, int h);void buttonClickCount(MicroBitEvent e);

int main() {
 uBit.init();
 startGame();
 int position = playGame();
 endGame(position);
 release_fiber();
 return 0;
}

void startGame() {
 countDown();
 uBit.display.setDisplayMode(DISPLAY_MODE_GREYSCALE);
 drawPlayArea();
 setUpButton();
}

void countDown() {int t;
 for (t = 5; t >= 0; t--) {
  uBit.display.scroll(t);
 }
 uBit.display.scroll("!");
}

void drawPlayArea() {
 uBit.display.clear();
 int y;
 for (y = 1; y <= 5; y++) {
  uBit.display.image.setPixelValue(2, y, 255);
 }
}

int playGame() {
 int position = 4;
 clickCount = 0;
 int t1 = uBit.systemTime();

 while (uBit.systemTime() - t1 < 20000) {
  flashMan(4,  position, clickCount, 0, -1);
  if (clickCount > 10) {
   position = position - 1;
   clickCount = 0;
   if (position == 0)break;
  }
 }
 return position;
}

void endGame(int position) {
 if (position == 0) {
  moveHorizontal();
  moveVertical(0, 0);
 } else {
  moveVertical(4, position);
 }
}

void setUpButton() {
 uBit.buttonB.setEventConfiguration(
                 MICROBIT_BUTTON_ALL_EVENTS);
 uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, 
      MICROBIT_BUTTON_EVT_CLICK, buttonClickCount);
}

void buttonClickCount(MicroBitEvent e) {
 clickCount++;
}

void flashMan(int x, int y, int p, int vx, int vy) {
 uBit.display.image.setPixelValue(x, y, 0);
 uBit.display.image.setPixelValue(x + vx, y + vy, 0);
 uBit.sleep(100);
 uBit.display.image.setPixelValue(x, y, 25 * (10 - p));
 uBit.display.image.setPixelValue(x+vx, y+vy, 25*p);
 uBit.sleep(100);
}

void moveHorizontal() {
 int x, fraction;
 for (x = 4; x >= 0; x--) {
  for (fraction = 0; fraction <= 10; fraction++) {
   flashMan(x, 0, fraction, -1, 0);
  }
 }
}

void moveVertical(int x, int h) {
 int y, fraction;
 if (h != 0) 
  uBit.display.image.setPixelValue(x, h - 1, 0);
 for (y = h; y <= 4; y++) {
  for (fraction = 0; fraction <= 10; fraction++) {
   flashMan(x, y, fraction, 0, 1);
  }
 }
}

If you try it out you will discover that the use of grey scale to increase the resolution works well but it works even better if you scale the LED brightness using a power law as given in the chapter on PWM. 

I don't think that the 5x5 display is going to be hosting an implementation of Doom any time soon but ... who knows.

 

Now On Sale!

You can now buy a print edition of micro:bit IoT in C.

You can buy it from:

USA and World  Amazon.com
Canada              Amazon.ca
UK                      Amazon.co.uk
France                Amazon.fr
Germany            Amazon.de
Spain                  Amazon.es
Brazil                  Amazon.br
Italy                    Amazon.it
Japan                 Amazon.co.jp
Mexico               Amazon.com.mx 

 

 The full contents can be seen below. 

Chapter List

  1. Getting Started With C/C++
    Anyone who wants to use the BBC micro:bit to its full potential as an IoT device needs to look outside the coding environments provided by its own website. As an mbed device, however, the micro:bit  is capable of being programmed in C/C++. Here we look at how to use the mbed online compiler for a simple demo program.

  2. Offline C/C++ Development  
    We have already discovered how to use the online editor to create a C/C++ program. Now we are going to move to the desktop with an offline approach. This has the advantage that we can use any tools we care to select and no Internet connection is needed.
  3. First Steps With The GPIO 
    The most basic task when working with the micro:bit is controlling the I/O lines. This isn't difficult if you use the framework provided but there some subtle points to watch out for. This chapter looks a the basics of using the GPIO.

  4. Working Directly With The Hardware - Memory Mapping. 
    The framework makes working with the GPIO and other devices as easy as it can be but there are many layers of software to go through before you get to the hardware. Writing directly to the hardware can make things up to ten times faster and give you access to things that their framework doesn't. It is also an educational experience to deal with the raw hardware directly.

  5. Pulse Width Modulation, Servos And More
    In this chapter we take a close look at pulse width modulation PWM including, sound, driving LEDs and servos.

  6. I2C
    The I2C bus is one of the most useful ways of connecting moderately sophisticated sensors and peripherals to the any processor. The only problem is that it can seem like a nightmare confusion of hardware, low level interaction and high level software. There are few general introductions to the subject because at first sight every I2C device is different, but here we present one.

  7. I2C Temperature Measurement
    Using I2C devices is fairly easy once you have successfully used one - and hence know what information you need and what to look for in a working system. In this chapter we use the HTU21D temperature and humidity sensor as a case study of I2C in action. It also happens to be a useful sensor.

  8. A Custom Protocol - The DHT11/22

  9. The DS18B20 - One Wire Bus

  10. The SPI Bus
    The SPI bus can be something of a problem because it doesn't have a well defined standard that every device conforms to. Even so if you only want to work with one specific device it is usually easy to find a configuration that works - as long as you understand what the possibilities are. 

  11. SPI MCP3008/4 AtoD   
    The SPI bus can be difficult to make work at first but once you know what to look for about how the slave claims to work it gets easier. To demonstrate how its done let's add eight channels of 12 bit AtoD using the MCP3008.

  12. Serial Connections
    The serial port is one of the oldest of ways of connecting devices together but it is still very, very useful. The micro:bit has a single serial interface but it can be directed to use any of the GPIO piins as Rx and Tx. 

  13. WiFi 
    The micro:bit has a radio that works in Bluetooth LE and point-to-point ad-hoc mode, but at the moment it lacks WiFi connectivity. The solution is to use the low cost ESP8266 to make the connection via the micro:bit's serial port. 

  14. LED Display 
    The micro:bit's LED display may only be 5x5 but it is very versatile. If you want to make use of it directly then you are going to have to master some lower level functions.

 

More Information

developer.mbed.org

http://lancaster-university.github.io/microbit-docs/

Related Articles

The BBC Micro:bit Is An Mbed Device In C/C++ 

Commando Jump Game For The Micro:bit In Python 

BBC Micro To micro:bit 

 

 

comments powered by Disqus