Phased Pulses

As a simple example of using the output functions lets try to write a short program that pulses two lines - one high and one low and then one low and one high i.e. two pulse trains out of phase by 180 degrees.

The simplest program to do this job is:

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

Notice that there is no delay in the loop so the pulses are produced at the fastest possible speed.

Using a logic analyzer reveals that the result isn't what you might expect: You can also see that the pulse trains are not 180 degrees out of phase. The top train switches on and the bottom train takes about half a pulse before it switches on - the intent is for both actions to occur at the same time. You should also notice that now the pulse time is about 7.75 microseconds which is about double the time for a pulse train on a single GPIO line.

The point is that it does take quite a long time to access and change the state of an output line.

Of course if we include a delay to increase the pulse width then the delay caused by accessing the GPIO lines in two separate actions isn't so obvious: In this case the loop now n=100 busy wait loops:

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

You will notice that the pulses are now roughly 80 microseconds wide and they are changing at what looks like nearer to being the same time - of course they aren't.

There is still a lag, but in many applications it might not be important. In other applications it could be crucial.

For example, if the two pulse trains were driving different halves of a motor controller bridge there would be a significant time when both were high - so shorting the power supply. It might only be for 10 microseconds but over time it could well damage the power supply.  Of course, any sensible, cautious, engineer wouldn't feed a motor control bridge from two independently generated pulse trains unless they were guaranteed not to switch both sides of the bridge on at the same time.

A better way to generate multiple pulses is to write directly to the hardware which is what we do in the next chapter.

Summary

If you want to create accurate pulses then:

• for delays greater than 6ms to an accuracy of 6ms you can use sleep().
• for delays from around 12 microseconds you can use wait_us accurate to around 1 microsecond.
• for delays greater than 7 microseconds you can use a busy wait accurate to around 1 microsecond.
• switching GPIO lines takes time and achieving synchronized switching better than ten microseconds is not possible using the framework.

GPIO Input

GPIO input can be a much more difficult problem than output. At least for output you can see the change in the signal on a logic analyzer and know the exact time that it occurred. This makes if possible to track down timing problems and fine tune things with good accuracy.

Input on the other hand is "silent" and unobservable. When did you read in the status of the line. Usually the timing of the read is with respect to some other action that the micro:bit has taken. For example, read the input line 20 microseconds after setting the output line high. The usual rule of thumb is to assume that it takes as long to read a GPIO line as it does to set it. This means we can use the delay mechanisms that we looked at with output in mind for input.

In some applications the times are long and/or unimportant but in some they are critical.

Basic Input Circuit - The Switch

One of the most common input circuits is the switch or button - the micro:bit has two built in buttons A and B.  If you want another external button you can use any GPIO line and the following circuit: The 10K resistor isn't critical in value. It simply pulls the GPIO line high when the switch isn't pressed. When it is pressed a current of a little more than 0.3 flows in the resistor. If this is too much increase the resistance to 100K or even more - but notice that the higher the resistor value the noisier the input to the GPIO and the more it is susceptible to RF interference.

If you want a switch that pulls the line high instead of low, to reverse the logic just swap the positions of the resistor and the switch in the diagram.

Although the switch is the simples input device it is also very difficult to get right. When a user clicks a switch of any sort the action isn't clean - the switch bounces. What this means is that the logic level on the GPIO line goes high then low and high and bounces between the two until it settles down. There are electronic ways of debouncing switches but software does the job much better. All you have to do is but a delay of a millisecond or so after detecting a switch press and read the line again - if it is still low then record a switch press. Similarly when the switch is released read the state twice with a delay. You can very the delay to modify the perceived characteristics of the switch.

The Potential Divider

If you have an input that is outside of the range of 0 to 3.3V then you can reduce it using a simple potential divider.

V is the input from the external logic and Vout it the connection to the GPIO input line: You can spend a lot of time on working out good values of R1 and R2. For loads that take a lot of current you need R1+R2 to be small and divided in the same ratio as the voltages.

For example for a 5V device R1=18K and R2=33K work well to drop the voltage to 3.3V.

The problem with a resistive divider is that it can round off fast pulses due to the small capacitive effects. This usually isn't a problem, but if it is then the solution is to use an FET buffer again. Notice that this is an inverting buffer but you can usually ignore this and simply correct in software i.e. read a 1 as a low and a 0 as a high state. The role of R1 is to make sure the FET is off when the 5V signal is absent and R2 limits the current in the FET to about 0.3mA.

In most case you should try the simple voltage divider and only move to an active buffer if it doesn't work.