One way around the problem of getting a fast response from a microcontroller is to move the problem away from the processor. In the case of the Pi's processor there are some builtin devices that can use GPIO lines to implement protocols without the CPU being involved. In this chapter we take a close look at pulse width modulation PWM including, sound, driving LEDs and servos.
Now On Sale!
You can now buy a print or ebook edition of Raspberry Pi IoT in C from Amazon.
For Errata and Listings Visit: IO Press
This our ebook on using the Raspberry Pi to implement IoT devices using the C programming language. The full contents can be seen below. Notice this is a first draft and a work in progress.
Chapter List
-
Introducing Pi (paper book only)
-
Getting Started With NetBeans In this chapter we look at why C is a good language to work in when you are creating programs for the IoT and how to get started using NetBeans. Of course this is where Hello C World makes an appearance.
-
First Steps With The GPIO
The bcm2835C library is the easiest way to get in touch with the Pi's GPIO lines. In this chapter we take a look at the basic operations involved in using the GPIO lines with an emphasis on output. How fast can you change a GPIO line, how do you generate pulses of a given duration and how can you change multiple lines in sync with each other? -
GPIO The SYSFS Way
There is a Linux-based approach to working with GPIO lines and serial buses that is worth knowing about because it provides an alternative to using the bcm2835 library. Sometimes you need this because you are working in a language for which direct access to memory isn't available. It is also the only way to make interrupts available in a C program. -
Input and Interrupts
There is no doubt that input is more difficult than output. When you need to drive a line high or low you are in command of when it happens but input is in the hands of the outside world. If your program isn't ready to read the input or if it reads it at the wrong time then things just don't work. What is worse is that you have no idea what your program was doing relative to the event you are trying to capture - welcome to the world of input. -
Memory Mapped I/O
The bcm2835 library uses direct memory access to the GPIO and other peripherals. In this chapter we look at how this works. You don't need to know this but if you need to modify the library or access features that the library doesn't expose this is the way to go. -
Near Realtime Linux
You can write real time programs using standard Linux as long as you know how to control scheduling. In fact it turns out to be relatively easy and it enables the Raspberry Pi to do things you might not think it capable of. There are also some surprising differences between the one and quad core Pis that make you think again about real time Linux programming. -
PWM
One way around the problem of getting a fast response from a microcontroller is to move the problem away from the processor. In the case of the Pi's processor there are some builtin devices that can use GPIO lines to implement protocols without the CPU being involved. In this chapter we take a close look at pulse width modulation PWM including, sound, driving LEDs and servos. -
I2C Temperature Measurement
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. -
A Custom Protocol - The DHT11/22
In this chapter we make use of all of the ideas introduced in earlier chapters to create a raw interface with the low cost DHT11/22 temperature and humidity sensor. It is an exercise in implementing a custom protocol directly in C. -
One Wire Bus Basics
The Raspberry Pi is fast enough to be used to directly interface to 1-Wire bus without the need for drivers. The advantages of programming our own 1-wire bus protocol is that it doesn't depend on the uncertainties of a Linux driver. -
iButtons
If you haven't discovered iButtons then you are going to find of lots of uses for them. At its simples an iButton is an electronic key providing a unique coce stored in its ROM which can be used to unlock or simply record the presence of a particular button. What is good news is that they are easy to interface to a Pi. -
The DS18B20
Using the software developed in previous chapters we show how to connect and use the very popular DS18B20 temperature sensor without the need for external drivers. -
The Multidrop 1-wire bus
Some times it it just easier from the point of view of hardware to connect a set of 1-wire devices to the same GPIO line but this makes the software more complex. Find out how to discover what devices are present on a multi-drop bus and how to select the one you want to work with. -
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. -
SPI MCP3008/4 AtoD (paper book only)
-
Serial (paper book only)
-
Getting On The Web - After All It Is The IoT (paper book only)
-
WiFi (paper book only)
The GPIO lines at their most basic output function can be set high or low by the processor. How fast they can be set high or low depends on the speed of the processor.
Using the GPIO line in its Pulse Width Modulation (PWM) mode you can generate pulse trains up to 4.8MHz, i.e. pulses as short as just a little more than 0.08 microseconds. You can even go faster if you are prepared to do some additional work.
The reason for the increase in speed is that the GPIO is connected to a pulse generator and once set to generate pulses of a specific type the pulse generator just gets on with it without needing any intervention from the GPIO line or the processor. In fact the pulse output can continue after your program has ended if you forget to reset it.
Of course, even though the PWM line can generate pulses as short as 0.1 microseconds, it can only change the pulses it produces each time that processor can modify it. For example, you can't use PWM to produce a single 0.1 microsecond pulse because you can't disable the PWM generator in just 0.1 microsecond.
Some Basic Pi PWM Facts
There are some facts worth getting clear right from the start, although some of the meanings will only become clear as we progress.
First what is PWM?
The simple answer is that a Pulse Width Modulated signal has pulses that repeat at a fixed rate - say one pulse every millisecond but the width of the pulse can be changed.
There are two basic things to specify about the pulse train that is generated - its repetition rate and the width of each pulse. Usually the repetition rate is set as a simple repeat period and the width of each pulse is specified as a percentage of the repeat period the duty cycle.
So for example a 1ms repeat and a 50% duty cycle specifies a 1ms period which is high for 50% of the time i.e. a pulse width of 0.5ms. The two extremes are 100% duty cycle i.e. the line is always high and 0% duty cycle i.e. the line is always low.
Notice it is the duty cycle that carries the information in PWM and not the frequency.
What this means is that generally you select a repeat rate and stick to it and what you change as the program runs is the duty cycle.
In many cases PWM is implemented using special PWM generator hardware that is built either into the processor chip or provided by an external chip. The processor simply sets the repeat rate by writing to a register and then changes the duty cycle by writing to another register. This generally provides the best sort of PWM with no load on the processor and generally glitch free operation. You can even buy addon boards that will provide additional channels of PWM without adding to the load on the processor.
The alternative to dedicated PWM hardware is to implement it in software. You can quite easily work out how to do this. All you need is to set a timing loop to set the line high at the repetition rate and then set it low again according to the duty cycle. You can implement this either using interrupts or a polling loop or even in more advanced ways such as using a DMA channel .
In the case of the Pi the PWM lines are implemented using special PWM hardware.
The big problem is that although there are two PWM implementations both are only available on the Pi2/3 and even in this case the second PWM is used for sound generation.
However as long as you don't want to use sound you can use both PWM channels and this is enough to run two servos or motor say.
The main PWM channel zero is brought out on the connector on pin12 via GPIO 18. The secondary channel which is also used by the system for sound generation is brought out on the connector on pin 35 via GPIO 13. As already mentioned you can only use GPIO 13 if you don't want sound generation.
Both PWM lines are driven by the same clock and this sometime causes people to conclude that the two lines must work at the same repeat rate. Although there are some restrictions both PWM lines can be set to their own repeat rate.
Two PWM lines is limiting and often forces users to either use hardware expansion or implement PWM in software.
As you can guess - there are no PWM inputs, just the one output. If for some reason you need to decode or respond to a PWM input then you need to program it using the GPIO input lines and the pulse measuring techniques introduced in the previous chapters.
The PWM Modes Mark/Space and Balanced
The PWM hardware is more sophisticated than you might have encountered before.
The first big difference is that it can work in two modes - mark/space or balanced. In fact there are even more options but these are the only two that have a common use.
In mark/space mode you simply have a repeat rate and a duty cycle. This is precisely what you need if you want to drive a traditional PWM device such as a servo motor. However for other applications where the PWM signal is being used as a simple D to A convertor then this is not the best way to send out a pulse train with a given duty cycle.
For example suppose you want 1KHz pulse train with a 50% duty cycle pulse train to say deliver 50% power or voltage to a device. The mark/space way of doing this switches the GPIO line on for 500 microseconds and off for 500 microseconds. The fluctuations in voltage are very slow and this causes problems for the driven device. A better way would be to divide the time into say 1 microsecond blocks and spread the on and off times throughout the block. In this case the device would be high for 1 microsecond and low for one microsecond and the filtering problems would be much easier to solve.
This idea of distributing the duty cycle over the full repeat period is what balanced mode implements. It is much more suitable to applications such as DtoA conversion and driving loads such as LEDs or motors.
The clock that is supplied to the PWM hardware doesn't set the repeat rate but the smallest unit of change on the PWM output line. In balanced mode it specifies the shortest high or low times for the pulse train.
The way that this works is that you specify a repeat rate in terms of a range parameter which is used to count down to zero at the clock rate. For example, if you set a clock rate of 1KHz and a range of 1024 then the repeat rate is 1/1024 KHz i.e. 0.99Hz - because the PWM cycle starts over after 1024 clock pulses.
In mark/space mode a 50% duty cycle would simply mean holding the line high for 512 clock pulses and then low for 512 clock pulses and so on.
In balanced mode a 50% duty cycle would mean pulses 500 microseconds high and 500 microseconds low. Note that the repeat rate would still be 0,99Hz but the signal would be much higher frequency even though it was a 50% duty cycle.
It is clear that the range sets the repeat rate. What sets the duty cycle? The answer is there is a data parameter which specifies how long the line should be held high. So for example if range is 1024 and data is 512 the duty cycle is 50%. In mark/space mode the line is held high for the first 512 counts and low for the remainder of the 1024 counts. In balanced mode the line is held high for 512 counts spread over the the total 1024 counts.
Obviously using this way of specifying repeat rate and duty cycle means that you can select more than one range and data value for any given duty cycle. For example a range of 4 and data of 2 is also a 50% duty cycle. So how do you choose a suitable pair of values.
The range gives you the possible number of duty cycle steps you can use. For example for a range of 2 you modify the duty cycle to 0, 50 or 100% by setting data to 0,1 or 2. A range of 512 gives you 512 steps and is usually called 8 bit PWM and a range of 1024 gives you 1024 duty cycle steps and is usually called 16 bit PWM.
So to summarize
- The PWM clock gives the smallest unit of change of the PWM line.
- The range gives the repeat rate of the pulse train
- repeat frequency = clock frequency/(2*range)
- The range gives the resolution of the duty cycle 1/range% or the number of duty cycle steps that are available.
- If you want to set duty cycle d% then data=range*d/100
You can now see why the clock frequency being supplied to both PWM units doesn't constrain them to the same repeat rate. They can work at different rates as long as they can tolerate different duty cycle resolutions and hence range values.
PWM Functions
The the library provides PWM in an easy to use form. They do allow you to set the parameters of both PWM channels even though you can't use both on Pi 1s.
The fundamental frequency of the PWM is set by:
void bcm2835_pwm_set_clock (uint32_t divisor)
You can't set the clock speed directly. Instead you have to specify a divisor to reduce the 19.2MHz clock to a lower rate. The library provides some standard settings:
BCM2835_PWM_CLOCK_DIVIDER_2048 9.375kHz BCM2835_PWM_CLOCK_DIVIDER_1024 18.75kHz BCM2835_PWM_CLOCK_DIVIDER_512 37.5kHz BCM2835_PWM_CLOCK_DIVIDER_256 75kHz BCM2835_PWM_CLOCK_DIVIDER_128 150kHz BCM2835_PWM_CLOCK_DIVIDER_64 300kHz BCM2835_PWM_CLOCK_DIVIDER_32 600.0kHz BCM2835_PWM_CLOCK_DIVIDER_16 1.2MHz BCM2835_PWM_CLOCK_DIVIDER_8 2.4MHz BCM2835_PWM_CLOCK_DIVIDER_4 4.8MHz BCM2835_PWM_CLOCK_DIVIDER_2 9.6MHz BCM2835_PWM_CLOCK_DIVIDER_1 4.6875kHz
The largest divider you can specify is 0xFFF or 4096 which gives the same frequency as specifying 1 i.e. 4.6875kHz
An important point to remember is that the clock rate is not the PWM repeat rate.
How the PWM pulses are created depends on the selected mode:
void bcm2835_pwm_set_mode (uint8_t channel, uint8_t markspace, uint8_t enabled)
channel has to be 0 or 1, markspace is 1 for markspace mode and 0 for balanced and enabled is 1 to start the PWM pulses running.
Finally you can set the range using:
void bcm2835_pwm_set_range (uint8_t channel, uint32_t range)
And you can set data using
void bcm2835_pwm_set_data (uint8_t channel, uint32_t data)
The largest value of range that seems to work in practice is 0xFF FFFF which is over 268 million clock pulses which at the highest clock rate is around 30 seconds per repeat.
Selecting A Clock and Range
One of the basic problems in using the Pi's PWM is selecting a clock divider and range value. There is a fairly standard way of doing this.
The best way to work out a configuration that gives a particular repeat rate is to start at the highest clock rate, i.e. 9.6MHz with a divider of 2, and work out the range needed.
If you need a particular repeat rate given in seconds then:
max range=repeat time*clock frequency
= repeat time* 9.6 *1000
if the repeat time is in milliseconds.
Similarly if the repeat rate is specified as a frequency then the max range is
max range =clock frequency/ repeat frequency
= 9.6/repeat frequency
This gives you the maximum range you can use.
If you actually want a smaller range you can reduce the clock to a lower frequency and the divider you need is:
divider =2* max range/desired range
The two is because the smallest divider you can use is 2.
If after rounding the divider and range don't give you an accurate enough signal you have no choice but to try multiples of the range - i.e. try range*2, range*3 with the corresponding dividers i.e. divider/2, divider/3 and so on. Of course this means you have to work with data*2, data*3 and so on but this isn't difficult.
Using PWM
So now you know how to make use of the PWM lines. All you have to do is set the frequency, range and data.
You also have to set the mode of the GPIO pin that you are using the correct Alt mode.
As we only have two PWM pins you set GPIO 18 to ALT5 and GPIO 13 to ALT0:
bcm2835_gpio_fsel(18,BCM2835_GPIO_FSEL_ALT5 ); bcm2835_gpio_fsel(13,BCM2835_GPIO_FSEL_ALT0 );
The simplest PWM program you can write is:
#include <bcm2835.h>
int main(int argc, char** argv) {
if (!bcm2835_init())
return 1;
bcm2835_gpio_fsel(18,BCM2835_GPIO_FSEL_ALT5 );
bcm2835_pwm_set_clock(2);
bcm2835_pwm_set_mode(0, 1, 1);
bcm2835_pwm_set_range(0,2);
bcm2835_pwm_set_data(0,1);
return (EXIT_SUCCESS);
}
The clock is set to its fastest 9.6MHz and PWM0 is set to mark/space mode with range 2 and data 1.
This produces the fastest pulse train with one clock pulse high and one low:
You have to set the value first and then set the period - otherwise the PWM line isn't setup properly. You also have to set the period for each pin you are using even if it does set the same period.
Notice that there is no need to put the program into an infinite loop. Once the PWM line has been set up and enables it just gets on with generating the pulse train no matter what the processor does. In this case the pulse generation continues long after the program has ended.
Of course this may be fast but as a PWM signal it is completely useless because you cannot change its duty cycle to anything useful. In practice you need to set range to something greater than one.
Just to demonstrate that two PWM lines can be used independently of one another here is a program that sets each one of the PWM GPIO lines - to a different duty cycle and repeat rate:
#include <stdio.h> #include <stdlib.h> #include <bcm2835.h> int main(int argc, char** argv) { if (!bcm2835_init()) return 1; bcm2835_gpio_fsel(18,BCM2835_GPIO_FSEL_ALT5 ); bcm2835_gpio_fsel(13,BCM2835_GPIO_FSEL_ALT0 ); bcm2835_pwm_set_clock(2); bcm2835_pwm_set_mode(0, 1, 1); bcm2835_pwm_set_range(0,2); bcm2835_pwm_set_data(0,1); bcm2835_pwm_set_mode(1, 1, 1); bcm2835_pwm_set_range(1,8); bcm2835_pwm_set_data(1,2); return (EXIT_SUCCESS); }
Yyou can see the result in the following logic analyzer display:
How Fast Can You Modulate?
Of course in most cases the whole point is to vary the duty cycle or the period of the pulse train for reasons that will be discussed later. This means that the next question is how fast can you change the characteristic of a PWM line?
In other words how fast can you change the duty cycle say?
There is no easy way to give an exact answer and in most applications an exact answer isn't of much value. The reason is that for a PWM signal to convey information it generally has to deliver a number of complete cycles with a given duty cycle. This is because of the way pulses are often averaged in applications.
We also have another problem - synchronization.
There is no way to swap from one duty cycle to another exactly when a complete duty cycle has just finished. What this means is that there is going to be a glitch when you switch from one duty cycle to another. Of course this glitch becomes less important as you slow the rate of duty cycle change and exactly what is useable depends on the application.
For example if you try changing the duty cycle about every 200 microseconds and the repeat is 25 microseconds then you are going to see roughly three to four pulses per duty cycle:
bcm2835_gpio_fsel(18, BCM2835_GPIO_FSEL_ALT5); bcm2835_pwm_set_clock(2); bcm2835_pwm_set_mode(0, 1, 1); bcm2835_pwm_set_range(0, 256); for (;;) { bcm2835_pwm_set_data(0, 10); bcm2835_delayMicroseconds(10); bcm2835_pwm_set_data(0, 200); bcm2835_delayMicroseconds(10); }
This results in a fairly irregular pulse pattern:
Notice that now we are using an 8 bit duty cycle resolution and setting the duty cycle from 10/256 to 200/256.
The timing of the change and the time it takes to make the change cause the glitches between duty cycles so sometimes we get two and sometimes just one cycle of each duty cycle.
The point is that there is time lost in changing the duty cycle and you simply cannot use PWM with modulation that involves just a few cycles.
If you change and generate duty cycle changes every 500 microseconds or so then the pattern looks a lot better because you are going to see 19 to 20 pulses before each change.
What all this is really about is trying to lower you expectation of how sophisticated you can be in using PWM.
The fastest PWM repetition rate that you can use is about 25 microseconds for an 8 bit resolution and to minimize the glitches you need to leave the duty cycle stable for 10 to 20 pulses i.e. about 200-600 microseconds.
In many applications this is very acceptable but don't expect to use PWM to send coded data and using it for waveform synthesis as a DtoA converter is limited to around 4KHz.
Uses of PWM - DtoA
What sorts of things do you use PWM for?
There are lots of very clever uses for PWM. However there are two use cases which account for most PWM applications - voltage or power modulation and signaling to servos.
The amount of power delivered to a device by a pulse train is proportional to the duty cycle. A pulse train that has a 50% duty cycle is delivering current to the load only 50% of the time and this is irrespective of the pulse repetition rate.
So duty cycle controls the power but the period still matters in many situations because you want to avoid any flashing or other effects - a higher frequency smoothes out the power flow at any duty cycle.
If you add a low pass filter to the output of a PWM signal then what you get is a voltage that is proportional to the duty cycle. This can be looked at in many different ways but it is again the result of the amount of power delivered by a PWM signal. You can also think of it as using the filter to remove the high frequency components of the signal leaving only the slower components due to the modulation of the duty cycle.
How fast you can work depends on the duty cycle resolution you select. If you work with 8 bit resolution your DtoA will have 256 steps which at 3.3V gives a potential resolution of 3.3/256 or about 13mV. This is often good enough. At the fastest clock rate this gives a repetition rate of 37.5KHz which sounds as if it should be enough for audio synthesis.
The PWM output in this configuration mimics the workings of an eight bit AtoD converter. You set the duty cycle using the data value 0 to 256 and you get a voltage output that is 3.3*data/256V.
The repetition rate is around 25 microseconds and the usual rule of thumb is that you need ten pulses to occur per conversion i.e. the maximum frequency you can produce is pulse rate/10.
This means that the fastest signal you can create about 3KHz - which isn't fast enough for a great many applications and you can't use it for sound synthesis in particular.
To demonstrate the sort of approach that you can use to DtoA conversion the following program creates a triangle or ramp waveform. :
for (;;) { for (i = 0; i < 256; i = i + 10) { bcm2835_pwm_set_data(0, i); bcm2835_delayMicroseconds(100); } }
The inner for loop sets a value of 0 to 256 in steps of 10 i.e. 300mV per step and the delay of 100 microseconds produces an total loop time of over 106 microseconds and this means that around three or four pulses of each 25 microsecond duty cycle should be produced - which is far to few but it is interesting to see what happens. The waveform repeats after around 2.5 ms which makes the frequency around 400Hz. The measured repeat, using an oscilloscope, time is more like 2.3ms or 430Hz.
To see the analog wave form we need to put the digital output into a low pass filter. A simple resistor and capacitor work reasonably well:
The filter's cut off is 17KHz and might be a little on the high side for this low frequency output.
You can create a sine wave or any other waveform you need using the same techniques but 400-800Hz is a practical upper limit on what you can manage.
If you already know that the PWM outputs are used to create audio on the Pi you might be wondering how this is possible with a 800Hz upper limit? The answer is that there are other clock sources, up to 500MHz that can be use to drive the PWM lines.
If you change to mode 0 i.e. balanced output the oscilloscope trace looks little different but there is a lot more spurious RF inference generated.
Music?
So how can the Pi generate musical notes?
The simplest solution is not to try to generate anything other than a square wave. That is use the pulse period rather than duty cycle.
As the frequency of middle C is 281.6Hz the period using a clock rate of 9.6Mhz you would have to set range to 34091 to get the correct frequency.
range= clock frequency/target frequency=34091
So to generate middle C you could use:
bcm2835_gpio_fsel(18, BCM2835_GPIO_FSEL_ALT5); bcm2835_gpio_fsel(13, BCM2835_GPIO_FSEL_ALT0); bcm2835_pwm_set_clock(2); bcm2835_pwm_set_mode(0, 1, 1); int C=34091; bcm2835_pwm_set_range(0, C); bcm2835_pwm_set_data(0, C/2);
The resulting output is a square wave with a measured frequency of 281.5Hz which isn't particularly nice to listen to. You can improve it by feeding it though a simple low pass filter like the one in used for waveform synthesis.
You can lookup the frequencies for other notes and use a table to generate them.
Controlling An LED
You can use a PWM supply to control the brightness of an LED for example, or the rotation rate of a DC motor. The only differences in applications such as these are to do with the voltage and current you need to control and the way duty cycle relates to what ever the physical effect is. In other words if you want to change some device by 50% how much do you need to change the duty cycle?
For example how do we "dim an LED"?
By changing the duty cycle of the PWM pulse train you can set the amount of power delivered to an LED, or any other device, and hence change its brightness.
For example for an LED we might use a 3.3V supply and a current of a few tens of milliamps. In the case of an LED the connection between duty cycle and brightness is a complicated matter but the simplest approach uses the fact that the perceived brightness is roughly proportional to the cube of the input power. The exact relationship is more complicated but this is good enough for most applications. As the power supplied to the LED is proportional to the duty cycle we have:
b=kd3
where b is the perceived brightness and d is the duty cycle.
Notice that as the LED when powered by a PWM signal is either full on or full off there is no effect of the change in LED light output with current - the LED is always run at the same current.
What all of this means is that if you want an LED to fade in a linear fashion you need to change the duty cycle in a non-linear fashion. Intuitively it means that changes when the duty cycle is small produce bigger changes in brightness than when the duty cycle is large.
For a simple example we need to connect a standard LED to the PWM line.
Given that all of the Pi'sGPIO lines work at 3.3V and ideally only supply a few milliamps we need a transistor to drive the LED which typically draws 20mA.
You could use an FET of some sort but for this sort of application an old fashioned Bipolar Junction Transistor works very well and is cheap and available in a through hole mount - i.e. it comes with wires.
Almost any general purpose npn transistor will work but the 2N2222 is very common:
R1 restricts the current to 0.48mA which is very low and assuming that the transistor has a minimum hfe of 50 this provides 24mA to power it. R2 limits the current to 20mA. Notice that all of these values are for a red LED with forward voltage drop of 1.8V and typical current 20mA. LEDs of different color have different forward voltage drops and currents.
If you are using the 2N2222 then the pin outs are:
And of course, as always the positive terminal on the LED is the long pin.
Assuming that you have this circuit constructed then a simple PWM program to modify its brightness from low to high and back to low in a loop is;
if (!bcm2835_init())
return 1;
bcm2835_gpio_fsel(18, BCM2835_GPIO_FSEL_ALT5);
bcm2835_pwm_set_clock(2);
bcm2835_pwm_set_mode(0, 1, 1);
bcm2835_pwm_set_range(0, 1024);
int w = 1;
int inc = 1;
for (;;) {
bcm2835_pwm_set_data(0, w);
w = w + inc;
if (w > 1024 || w <= 0)inc = -inc;
bcm2835_delayMicroseconds(5000);
}
The basic idea is to set up a pulse train with a period of 1ms. Next in the for loop the duty cycle is set to 0% to 100% and then back down to 0%.
If you watch the flashing you will see that it changes brightness very quickly and then seems to spend a long time "stuck" at almost full brightness and then suddenly starts to dim rapidly. This is a consequence of the way the human eye perceives light output as a function of input power.
Changing the LED brightness
What about a linear change in brightness?
To achieve this reasonably accurately isn't difficult all we need to do is increase the power or equivalently the duty cycle in steps that are cubic. If we just use 0 to 10 cubed we get a pulse width of 0 to 1000 which is ideal for our 1024 pulse range used in the previous example i.e. 0 to close to 100% duty cycle.
If you need the ultimate speed you could precompute the powers but for simplicity we will just use integer multiplication:
int w = 0;
int inc = 1;
for (;;) {
bcm2835_pwm_set_data(0, w*w*w);
w = w + inc;
if (w > 10 || w <= 0)inc = -inc;
bcm2835_delayMicroseconds(50000);
}
As this produces 10 cubic steps a wait_us of 50000 makes each step last 5ms and so it takes 50ms to go from low to high.
If you try the program you should find that you see the LED increase steadily towards a maximum and then decrease steadily to a minimum.
If you replace the delay with a value of 100,000 then you will get a 1 second cycle - which using only ten steps starts to look a little flashy in a not too good sense.
You can increase the number of steps by simply dividing by a suitable factor. If you cube 1 to 20 you get 1 to 8000 and dividing by 8 gives 0 to 1000. Dividing by 8 is just a matter of three right shifts and so while not very accurate does allow fast computation with integer arithmetic:
int w = 0;
int inc = 1;
for (;;) {
bcm2835_pwm_set_data(0, (w*w*w)>>3);
w = w + inc;
if (w > 20 || w <= 0)inc = -inc;
bcm2835_delayMicroseconds(50000);
}
Notice that now as there are twice as many steps we only need each one to last half the time, i.e. 50,000 microseconds.
You can also set the PWM to mode 0 i.e. balanced mode
bcm2835_pwm_set_mode(0, 0, 1);
This does make the LED look even smoother but the increase in RF noise makes it essential to put the Pi in a metal case.
In most cases exactly how linear the response of the LED is is irrelevant - a rough approximation looks as smooth to the human eye. The only exception is when you are trying to drive LEDs to create a grey level or color display when color calibration is another level of accuracy.
Controlling a Servo
Hobby servos, the sort used in radio control models, are very cheap and easy to use and the Pi has enough PWM lines to control two of them without much in the way of extras.
A basic servo has just three connections - usually ground and power line and a signal line. The colors used vary but the power is usually red and the ground line is usually black or brown. The signal line is white, yellow or orange.
The power wire has to be connected to 5V supply capable of providing enough current to run the motor - anything up to 500mA or more depending on the servo. In general you cannot power a full size servo from the Pi's 5V pin, you need a separate power supply. You can power some micro servos directly from the Pi's 5V line but you need to check the specifications.
The good news is that the servo signal line generally needs very little current, although it does need to be switched between 0 and 5V using a PWM signal.
You can assume that the signal line needs to be driven as a voltage load and so the appropriate way to drive the servo is:
The servo's + line needs to be connected to an external 5V power supply unless it is a micro servo when it might be possible to power it from the Pi's 5V line.
The 10K resistor R1 can be a lot larger for most servos - 47K often works. The 5.6K resistor limits the base current to slightly less than 0.5mA.
Now all we have to do is set the PWM line to produce 20ms pulses with pulse widths ranging from 0.5 to 2.5 ms.
As described earlier the best way to work out a configuration that gives a particular repeat rate is to start at the highest clock rate and work out the range needed.
For example, with a clock of 9.6MHz the range needed to give a 20ms repeat rate is:
max range=repeat time*clock frequency
= 192000
This gives you the maximum range you can use. If you actually want a smaller range you can reduce the clock to a lower frequency and the divider you need is:
divider =2* max range/desired range
The two is because the smallest divider you can use is 2.
So to get a range of 1024 i.e. a 16 bit resolution we need a clock divider of:
divider=2*192000/1024= 375
If you use a divider of 375 and a range of 1024 you will find that the repetition rate is, as promised, 20ms.
Most servos work on a pulse width ranging from 500 microseconds to 2.5 milliseconds or a duty cycle of 2.5% to 12.5% but this varies quite a lot according to the servo.
Using a range of 1024 this corresponds to a range of data from 25 to 128.
So the simplest servo program you can write is something like:
if (!bcm2835_init()) return 1; bcm2835_gpio_fsel(18, BCM2835_GPIO_FSEL_ALT5); bcm2835_pwm_set_clock(375); bcm2835_pwm_set_mode(0, 1, 1); bcm2835_pwm_set_range(0, 1024); for(;;){ bcm2835_pwm_set_data(0,25); bcm2835_delayMicroseconds(2000000); bcm2835_pwm_set_data(0, 50); bcm2835_delayMicroseconds(2000000); bcm2835_pwm_set_data(0, 128); bcm2835_delayMicroseconds(2000000); }
This moves the servo to three positions and pauses between each. If you run the program using the circuit given earlier you will discover that the servo does nothing at all - apart perhaps from vibrating.
The reason is that the transistor voltage driver is an inverter. When the PWM line is high the transistor is fully on and the servo's pulse line is effectively grounded. When the PWM line is low the transistor is fully off and the servo's pulse line is pulled high by the resistor.
A common solution to this problem is to drive the servo using an emitter follower configuration, but in this case this isn't possible because the maximum voltage an emitter follower configuration would generate is 3.3-0.6=2.7V, which is too low to drive most servos.
The standard solution in this case is to use two transistors to generate a non-inverted pulse but it is possible to use a single transistor in a non-inverting configuration.
The simplest solution of all is to ignore the problem in hardware and solve the problem in software.
This is generally good advice. Before you consider modifying the hardware always see if there is an easier software fix.
Instead of generating 20ms pulses with pulse widths 0.5 to 2.5ms, you can generate an inverted pulse with 20ms pulses with widths in the range 17.5 to 19 ms. The principle is that if the servo needs a 10% duty cycle we supply it with a 90% duty cycle which the inverter converts back to a 10% duty cycle.
The range of duty cycles we need goes from 17.5 to 19.5.ms which in percentages is 87.5% to 97.5% or as values 895 to 997.
Simple math can convert an angle T to a value in the range 895 to 997:
value = (997-895)/180*T+895
= 138*T/180+895
To make sure this works with all servos it is a good to restrict the range to 1ms to 2ms or inverted 18ms to 19ms and hence values from 920 to 972
value=52*T/190+920
So we can write the same testing program as:
if (!bcm2835_init()) return 1; bcm2835_gpio_fsel(18, BCM2835_GPIO_FSEL_ALT5); bcm2835_pwm_set_clock(375); bcm2835_pwm_set_mode(0, 1, 1); bcm2835_pwm_set_range(0, 1024); for(;;){ bcm2835_pwm_set_data(0,52 * 0 / 180 + 920); bcm2835_delayMicroseconds(2000000); bcm2835_pwm_set_data(0, 52 * 90 / 180 + 920); bcm2835_delayMicroseconds(2000000); bcm2835_pwm_set_data(0, 52 * 180/ 180 + 920); bcm2835_delayMicroseconds(2000000); }
If you run this program you should find that the servo moves as promised - however it might not reach its limits of movement.
Servos differ in how they respond to the input signal and you might need to calibrate the pulse widths. Many robot implementations, for example, calibrate the servos to find their maximum movement using either mechanical switches to detect when the servo is at the end of its range or a vision sensor.
You can see from the logic analyzer plot that the PWM pulse train at the GPIO pin is "inverted" as desired.
Non-Inverting Drivers
The software solution to driving a servo via a simple inverting buffer is elegant if slightly messy when it comes to computing the duty cycle needed.
The traditional solution is to use two transistors to create a non-inverting buffer:
There is a way to use a single transistor as a non-inverting buffer using a common base configuration:
In this variation on a common base mode the transistor's base is connected to the 3.3V line and its collector to the 5V supply. Note that the two power supplies have to share a common earth.
If the GPIO output is low then R6 sets the base emitter voltage to 0.6V and the transistor is hard on, pulling the output to the servo low.
If the GPIO output is high the base emitter voltage is zero and the transistor is cut off, making the output to the servo high.
You can see that this is non-inverting, but the problem is that the current that flows through R7 is also the emitter current, which is the current the GPIO line has to sink. What this means is that the current in R7 is limited to around 1mA and this circuit provides no amplification. Of course you could add another transistor to provide current amplification but in this case you would be better off going back to the standard 2-transistor arrangement.
This circuit does, however, work with most servos so in this role it is useful. The current in the GPIO line is 1mA and this is well within the drive power of a standard GPIO line.
Here is a "non-inverted" version of the test program given earlier. The range of duty cycle is limited to make sure it works with all servos. Change the range or center value to make it work with your servo:
if (!bcm2835_init()) return 1; bcm2835_gpio_fsel(18, BCM2835_GPIO_FSEL_ALT5); bcm2835_pwm_set_clock(375); bcm2835_pwm_set_mode(0, 1, 1); bcm2835_pwm_set_range(0, 1024); for(;;){ bcm2835_pwm_set_data(0,30); bcm2835_delayMicroseconds(2000000); bcm2835_pwm_set_data(0, 70); bcm2835_delayMicroseconds(2000000); bcm2835_pwm_set_data(0, 110); bcm2835_delayMicroseconds(2000000); }
What Else Can You Use PWM For?
PWM lines are incredibly versatile and it is always worth asking the question "could I use PWM?" when you are considering almost any problem.
The LED example shows how you can use PWM as s power controller. You can extend this idea to a computer controlled switch mode power supply. All you need is a capacitor to smooth out the voltage and perhaps a transformer to change the voltage.
You can also use PWM to control the speed of a DC motor and if you add a simple bridge circuit you can control its direction and speed.
Finally, you can use a PWM signal as a modulated carrier for data communications. For example, most infrared controller make use of a 38KHz carrier, which is roughly a 26 microseconds. This is switched on and off for 1ms and this is well within the range that the PWM can manage. So all you have to do is replace the red LED in the previous circuit with an infrared LED and you have the start of a remote control or data transmission link.
Some Hardware Details
Advanced Topic
The PWM hardware is more complicated than what is required just to produced a simple PWM signal. It also has a serialize mode which can clock a 32 bit word out of a register to create a pulse train with the characteristic you require. To make this even more useful it also supports the use of DMA to keep the serialization queue full. These features are beyond the scope of this book but if you understand how things work they are not difficult to use.
The PWM has a large number of registers associated with it.
A control register and status register for both channels and a pair of data and range registers for each channel, plus a DMA and a FIFO register for serialization.
The offsets from bcm2835_pwm as word addresses are:
BCM2835_PWM_CONTROL 0 BCM2835_PWM_STATUS 1 BCM2835_PWM_DMAC 2 BCM2835_PWM0_RANGE 4 BCM2835_PWM0_DATA 5 BCM2835_PWM_FIF1 6 BCM2835_PWM1_RANGE 8 BCM2835_PWM1_DATA 9
There is also a single clock which has the same registers as the GP clocks described in the previous chapter with word offset from bcm2835_clk given by:
BCM2835_PWMCLK_CNTL 40 BCM2835_PWMCLK_DIV 41
You can look up the details of the control and status registers and write your own functions to make use of the addition modes of operation.
One easy to implement and useful addition is to modify the PWM clock source and rate. The function given at the end of the previous chapter is easy to modify to work with the PWM clock:
void bcm2835_pwm_set_clock_source( uint32_t source, uint32_t divisorI, uint32_t divisorF) { if (bcm2835_clk == MAP_FAILED || bcm2835_pwm == MAP_FAILED) return; divisorI &= 0xfff; divisorF &= 0xfff; source &= 0xf; uint8_t mask = bcm2835_peri_read( bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0xffffffef; bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | mask); while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0) {}; bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisorI << 12) | divisorF); bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | source | 0x200); bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x210 | source); }
This works in the same way as the previous function. If first disables the clock, then sets the new divisor and source and then enables the clock again.
If you use PLLD then the clock can be as high as 500MHz. This allows you to set a range of 256 and get an effective repeat frequency of 1.9MHz which means you can get a PWM sample every half a microsecond.
Repeating the previous triangle wave generator given earlier but at this new frequency pushes the upper limit on audio generation using this method to around 20KHz.
bcm2835_gpio_fsel(18, BCM2835_GPIO_FSEL_ALT5); bcm2835_pwm_set_clock_source(6, 2, 0); bcm2835_pwm_set_mode(0, 1, 1); bcm2835_pwm_set_range(0, 256); int i; for (;;) { for (i = 0; i < 256; i = i + 10) { bcm2835_pwm_set_data(0, i); bcm2835_delayMicroseconds(2); } }
Using the same filter circuit given earlier the output looks a better when you take into account the frequency is now 20KHz:
There are more sophisticated coding methods that can create higher quality audio than this simple approach. However this is at least gives you the basic tools to do the job.
Now On Sale!
You can now buy a print or ebook edition of Raspberry Pi IoT in C from Amazon.
For Errata and Listings Visit: IO Press
This our ebook on using the Raspberry Pi to implement IoT devices using the C programming language. The full contents can be seen below. Notice this is a first draft and a work in progress.
Chapter List
-
Introducing Pi (paper book only)
-
Getting Started With NetBeans In this chapter we look at why C is a good language to work in when you are creating programs for the IoT and how to get started using NetBeans. Of course this is where Hello C World makes an appearance.
-
First Steps With The GPIO
The bcm2835C library is the easiest way to get in touch with the Pi's GPIO lines. In this chapter we take a look at the basic operations involved in using the GPIO lines with an emphasis on output. How fast can you change a GPIO line, how do you generate pulses of a given duration and how can you change multiple lines in sync with each other? -
GPIO The SYSFS Way
There is a Linux-based approach to working with GPIO lines and serial buses that is worth knowing about because it provides an alternative to using the bcm2835 library. Sometimes you need this because you are working in a language for which direct access to memory isn't available. It is also the only way to make interrupts available in a C program. -
Input and Interrupts
There is no doubt that input is more difficult than output. When you need to drive a line high or low you are in command of when it happens but input is in the hands of the outside world. If your program isn't ready to read the input or if it reads it at the wrong time then things just don't work. What is worse is that you have no idea what your program was doing relative to the event you are trying to capture - welcome to the world of input. -
Memory Mapped I/O
The bcm2835 library uses direct memory access to the GPIO and other peripherals. In this chapter we look at how this works. You don't need to know this but if you need to modify the library or access features that the library doesn't expose this is the way to go. -
Near Realtime Linux
You can write real time programs using standard Linux as long as you know how to control scheduling. In fact it turns out to be relatively easy and it enables the Raspberry Pi to do things you might not think it capable of. There are also some surprising differences between the one and quad core Pis that make you think again about real time Linux programming. -
PWM
One way around the problem of getting a fast response from a microcontroller is to move the problem away from the processor. In the case of the Pi's processor there are some builtin devices that can use GPIO lines to implement protocols without the CPU being involved. In this chapter we take a close look at pulse width modulation PWM including, sound, driving LEDs and servos. -
I2C Temperature Measurement
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. -
A Custom Protocol - The DHT11/22
In this chapter we make use of all of the ideas introduced in earlier chapters to create a raw interface with the low cost DHT11/22 temperature and humidity sensor. It is an exercise in implementing a custom protocol directly in C. -
One Wire Bus Basics
The Raspberry Pi is fast enough to be used to directly interface to 1-Wire bus without the need for drivers. The advantages of programming our own 1-wire bus protocol is that it doesn't depend on the uncertainties of a Linux driver. -
iButtons
If you haven't discovered iButtons then you are going to find of lots of uses for them. At its simples an iButton is an electronic key providing a unique coce stored in its ROM which can be used to unlock or simply record the presence of a particular button. What is good news is that they are easy to interface to a Pi. -
The DS18B20
Using the software developed in previous chapters we show how to connect and use the very popular DS18B20 temperature sensor without the need for external drivers. -
The Multidrop 1-wire bus
Some times it it just easier from the point of view of hardware to connect a set of 1-wire devices to the same GPIO line but this makes the software more complex. Find out how to discover what devices are present on a multi-drop bus and how to select the one you want to work with. -
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. -
SPI MCP3008/4 AtoD (paper book only)
-
Serial (paper book only)
-
Getting On The Web - After All It Is The IoT (paper book only)
-
WiFi (paper book only)