Wednesday, November 12, 2014

56. Exploring the Pi B+'s Ports II - Pulse Width Modulation

25,000 page views!!
In a previous post, http://smokespark.blogspot.co.uk/2013/04/15-programming-attiny85-with.html I experimented with RGB LEDs and control of LED brightness using Pulse Width Modulation (PWM).  That post includes explanations of how our human Persistence of Vision (POV) enables us to perceive a flashing LED as having less than maximum brightness.  That was using the ATMega 328 (Arduino) chip to program an ATTiny85 microchip to produce Pulse Width Modulation.

The difference here, of course, is that we are programming the Raspberry Pi in the Python language.  Not only are there coding differences, but we are working with 3V3 from the Pi's GPIO logic levels instead of 5V.


The circuit is a bit simpler than the last post at
http://smokespark.blogspot.co.uk/2014/10/55-exploring-pis-gpio-ports.html.  The switches have been removed as there are now no input GPIO ports, only outputs corresponding to the red, green and blue anodes of the RGB LED.  So the RGB LED is wired exactly the same as before - with its common cathode going to GND through a 
325 resistor.  You may have noticed that I still have wires running to GPIO ports 21, 22 and 23.  These are not connected to anything on the breadboard - I just forgot to remove them when I took off the switches!

What the experiment does is simply to increase the brightness of the red LED from zero to maximum, decrease it back to zero, move on the green LED, do the same, and then do this all over again for the blue LED.  The routine is in an infinite loop, but can be terminated gracefully by pressing CTRL-C on the keyboard:




For this video, I turned the ambient lighting down, as the LED colours all look white to the camera, in bright light.  You can see that there is a little bit of flicker, and this is intentional.  With a frequency of 40 hertz (40 Hz), my personal vision can detect flicker when the LEDs are in the dim part of their cycle.  A higher frequency will easily remove this flicker.  




It's interesting that this looping program doesn't use much in the way of processor resources.  You can see from the above image of the Pi's desktop, that the Raspberry Pi's CPU indicator is fairly activity-free, presumably because the code keeps the processor in the idle state for most of the time with the sleep() command. 

#!/usr/bin/env python2.7
# script by Sparks N Smoke for SOFTWARE control of LEDs with Pulse Width Modulation
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM)
GPIO.setup(24, GPIO.OUT) # LED Red PWM port - can use any GPIO port !
GPIO.setup(25, GPIO.OUT) # LED Green PWM port
GPIO.setup(26, GPIO.OUT) # LED Blue PWM port
frequency = 40 # set the LED pulse cycle frequency (40 Hertz)
delay = 0.02 # set no of secs to wait on duty cycle change
Red = GPIO.PWM(24, frequency) # set port 24 to 40 Hertz
Green = GPIO.PWM(25, frequency) # set port 13 to 40 Hertz
Blue = GPIO.PWM(26, frequency) # set port 18 to 40 Hertz
Red.start(0) # start with 0% duty cycle
Green.start(0)
Blue.start(0)
try:
while True:
for i in range (0, 101, 1): # 100 is the last value executed
Red.ChangeDutyCycle(i) # change duty cycle to the value of i (going UP)
sleep(delay) # linger a while at each step with RED
for i in range (100, -1, -1): # 0 is the last value executed
Red.ChangeDutyCycle(i) # value of duty cycle going DOWN
sleep(delay)
sleep(delay*5) # linger a bit longer with the LED off completely
for i in range (0, 101, 1): # now deal with GREEN
Green.ChangeDutyCycle(i)
sleep(delay)
for i in range (100, -1, -1):
Green.ChangeDutyCycle(i)
sleep(delay)
sleep(delay*5)
for i in range (0, 101, 1): # now deal with BLUE
Blue.ChangeDutyCycle(i)
sleep(delay)
for i in range (100, -1, -1):
Blue.ChangeDutyCycle(i)
sleep(delay)
sleep(delay*5)
except KeyboardInterrupt:
print " CTRL-C detected"
Red.stop()
Green.stop()
Blue.stop()
GPIO.cleanup() # Reset ports on CTRL-C
view raw KCPWMLED hosted with ❤ by GitHub
This experiment has been using software controlled PWM.  There are some GPIO pins (GPIO12, GPIO13 & GPIO18 - although I have also seen GPIO18 described as the PWM pin*) which are labelled PWM and therefore seem to have a special role in pulse width modulation.  When I find out how and when to use these, I'll post it up.

* servos require accurately timed pulses - which software alone cannot always provide, but GPIO18 with special software can (not sure I understand the subtleties here).

No comments:

Post a Comment