Article Index

An FET Buffer

Constructing a buffer is very easy and cheap. You can use a Bipolar Junction Transistor BJT if you want to but in this case the simplest solution is to use an FET.

The reason is that an FET hardly draws any current from the GPIO line.  Not only does the FET increase the current in the load it also allows you to change the drive voltage. Although the voltage is shown as 3.3V it can be 5V or anything needed to drive the load. The FET isolates the micro:bit from the higher voltage.

The 100K Ohm resistor is there to ensure that the FET and the load is off when the micro:bit first powers up and the GPIO line defaults to an input. Without it the FET would pick up noise and switch on and off randomly.

The FET shown, 2N7000 is cheap and can work with voltages up to 60V and currents up to 100mA. If you need a higher voltage or a larger current then you need to use a different FET. Power FETs are cheap, a few dollars, and can handle currents of tens of amps. 

GPIO Output

When you are generating GPIO output signals the key questions are often when the signal goes high and when it goes low. In some applications this is a very non-time critical event. As long as the line goes high or low in response to something then all it well. In other situations the response has to be after a given amount of time. If that time interval is longer than 100ms then there is generally no problem. If the interval is less than 100ms then you have to work out the best way to ensure the delay taking account of how accurate it has to be. 

In this part of the chapter we take a look at how fast GPIO lines can be changed and what sort of accuracy is possible. 

How Fast?

A fundamental question that you have to answer for any processor intended for use in embedded or IoT projects is  - how fast can the GPIO lines work? 

Some times the answer isn't of too much concern because what you want to do only works relatively slowly. Any application that is happy with response times in the tens of millisecond region will generally work with almost any processor. However if you want to implement custom protocols or anything that needs microsecond responses the question is much more important. 

It is fairly easy to find out how fast a single GPIO line can be used if you have a logic analyzer or oscilloscope. All you have to do is run the program that we used as a first test in chapter one:

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

The results is a pulse train with pulses ranging from 3.5 to 3.8 microseconds and a 0.1ms pause every 6ms due to the system timer interrupting. There isn't anything that can be done about this 6ms interrupt apart from switching the system timer off. 

Sleep

To generate pulses of a known duration we need to pause the program between state changes. 

The simplest way of sleeping a fiber is to use the sleep command. Unfortunately this uses the system timer and only works in steps of 6ms. 

To try this, include a call to sleep(9) to delay the pulse:

    while(1) {
       uBit.io.P0.setDigitalValue(1);
       uBit.sleep(9);
       uBit.io.P0.setDigitalValue(0);
      uBit.sleep(9);
    }

You will discover that you get 12ms pulses. This is only useful if you want pulses that are multiples of 6ms or so long that a 6ms error doesn't matter. 

There is a microsecond delay function wait_us() which is part of the mbed software that the framework is built on and we can call it from a micro:bit program. Unfortunately there is an overhead in calling the function.

So for example:

    while(1) {
        uBit.io.P0.setDigitalValue(1);
        wait_us(20);
        uBit.io.P0.setDigitalValue(0);
        wait_us(20);     
    }

produces pulses that last just over 32 micro seconds. 

If you look at how the delay time relates to the average pulse length things seem fairly simple and we have 

pulse length = delay+12

What this means is that if you want to set a delay less than 12 microseconds don't use wait_us. For delays bigger than this it works reasonably well. 

You can try various modifications to the basic wait_us approach but the best you can do is to get the overheads down to about 7 microseconds. 

Busy Wait

For pulses of less than about 50 microseconds it is better to use a busy wait i.e. a loop that does nothing.  You have to be a little careful about how you insert a loop that does nothing because optimizing compiler have a tendency to take a null loop out in an effort to make your program run faster.

To stop an optimizing from removing busy wait loops make sure you always declare loop variables as volatile.

To generate a pulse of a given length you can use 

   volatile int i;
        while (1) {
            for (i = 0; i < n; i++) {
            };
            uBit.io.P0.setDigitalValue(1);
            for (i = 0; i < n; i++) {
            };
            uBit.io.P0.setDigitalValue(0);          
        }

Where n is set to a value that depends on the delay you want to generate.

The relationship between n and t the time delay is very linear:

n=1.333t - 5.83

Notice that you can't generate pulses less than about 6 microseconds.

For example if you want 10 microsecond pulses then n=7.5 and using 7 the result can be seen below:

 

 

Notice that the pulses are around 9.75 microsecond.

We will revisit the busy wait option and using timers in the next chapter.