Saturday, August 2, 2014

GPIO 05-pwm

Raspberry PI has the ability to use PWM (pulse width modulation) to control width of the pulse sent on the GPIO pin, which provides better granularity to control connected devices, in this case LED.
This feature allows to control LEDs intensity, which can be used with RGB LED to mix colors.

First piece of code (05a-pwm.c) uses Raspberry's one and only hardware PWM pin (P1-12 GPIO 1). It is written in C and uses wiringPi.h library to control pulse width.
#include <wiringpi .h>

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main (void)
{
  int pin ;
  int l ;

  printf ("Raspberry Pi wiringPi PWM test program\n") ;
  if (wiringPiSetup () == -1)
    exit (1) ;

  for (pin = 0 ; pin < 8 ; ++pin)
  {
    pinMode (pin, OUTPUT) ;
    digitalWrite (pin, LOW) ;
  }

  pinMode (1, PWM_OUTPUT) ;

  for (;;)
  {
    for (l = 0 ; l < 1024 ; ++l)
    {
      pwmWrite (1, l) ;
      delay (1) ;
    }
    for (l = 1023 ; l >= 0 ; --l)
    {
      pwmWrite (1, l) ;
      delay (1) ;
    }
  }
  return 0 ;
}

However Raspberry Pi offers only one hardware PWM pin, it is possible to use software PWM with other GPIO pins.
Next code (05b-pwm.py) is written in python and it demonstrates how software PWM could be implemented. In this example, one RGB LED is connected to GPIO pins as shown on the following figure.
The pyhon script simply cycles through red, green and blue color and exits.
#!/usr/bin/env python3
# script by Alex Eames http://RasPi.tv
#http://RasPi.tv/2013/how-to-use-soft-pwm-in-rpi-gpio-pt-2-led-dimming-and-motor-speed-control
# Using PWM with RPi.GPIO pt 2 - requires RPi.GPIO 0.5.2a or higher
  
import RPi.GPIO as GPIO # always needed with RPi.GPIO
from time import sleep # pull in the sleep function from time module
  
GPIO.setmode(GPIO.BCM) # choose BCM or BOARD numbering schemes. I use BCM
  
GPIO.setup(17, GPIO.OUT)
GPIO.setup(27, GPIO.OUT)
GPIO.setup(22, GPIO.OUT)
  
r = GPIO.PWM(17, 50) # create object red for PWM on port 17 at 50 Hertz
g = GPIO.PWM(27, 50) # create object green for PWM on port 27 at 50 Hertz
b = GPIO.PWM(22, 50) # create object blue for PWM on port 22 at 50 Hertz
  
r.start(0) # start red led on 0 percent duty cycle (off)
g.start(0) # start green led on 0 percent duty cycle (off)
b.start(0) # start blue led on 0 percent duty cycle (off)
  
# now the fun starts, we'll vary the duty cycle to
# dim/brighten the leds, so one is bright while the other is dim
  
pause_time = 0.05 # you can change this to slow down/speed up
  
try:
# while True:
    for i in range(0,101): # 101 because it stops when it finishes 100
        r.ChangeDutyCycle(i)
        sleep(pause_time)
    for i in range(100,-1,-1): # from 100 to zero in steps of -1
        r.ChangeDutyCycle(i)
        sleep(pause_time)
    for i in range(0,101): # 101 because it stops when it finishes 100
        g.ChangeDutyCycle(i)
        sleep(pause_time)
    for i in range(100,-1,-1): # from 100 to zero in steps of -1
        g.ChangeDutyCycle(i)
        sleep(pause_time)
    for i in range(0,101): # 101 because it stops when it finishes 100
        b.ChangeDutyCycle(i)
        sleep(pause_time)
    for i in range(100,-1,-1): # from 100 to zero in steps of -1
        b.ChangeDutyCycle(i)
        sleep(pause_time)
  
except KeyboardInterrupt:
    r.stop() # stop the red PWM output
    g.stop() # stop the green PWM output
    b.stop() # stop the blue PWM output
    GPIO.cleanup() # clean up GPIO on CTRL+C exit

This intensity control is used in the following script (05c-pwn-colors.py) which adds a few features. It enables to control each color (and prints current value of colors from -100 to +100, where -100 and +100 is 0% pulse width and 0 is 100% pulse width).
Usage:
r RED+
e RED-
g GREEN+
f GREEN-
b BLUE+
b BLUE-
a add colors
s sub colors
h show this help
q quit

#!/usr/bin/env python3

import RPi.GPIO as GPIO
from time import sleep
import signal

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO.setup(17, GPIO.OUT)
GPIO.setup(27, GPIO.OUT)
GPIO.setup(22, GPIO.OUT)

# define LED connection @ 75Hz
r = GPIO.PWM(17, 75)
g = GPIO.PWM(27, 75)
b = GPIO.PWM(22, 75)

r.start(0)
g.start(0)
b.start(0)

# initial values
col_r=-100
col_g=-100
col_b=-100

def usage():
    print ('''
krisko 2014
Script for adjusting RGB LED color intensity (uses software PWM)

USAGE:
r RED+
e RED-
g GREEN+
f GREEN-
b BLUE+
b BLUE-
a add colors
s sub colors
h show this help
q quit

''')

# set color intensity (operation add/sub, col_X -100 - +100, color r/g/b)
def col_adjust(op, col_r, col_g, col_b, color):
    if (op == "add"):
        if (color == "r") and (col_r < 100):
            col_r+=1
        elif (color == "g") and (col_g < 100):
            col_g+=1
        elif (color == "b") and (col_b < 100):
            col_b+=1
    elif (op == "sub"):
        if (color == "r") and (col_r > -100):
            col_r-=1
        elif (color == "g") and (col_g > -100):
            col_g-=1
        elif (color == "b") and (col_b > -100):
            col_b-=1
    set_color(col_r, col_g, col_b)
    return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}

def set_color(col_r, col_g, col_b):
    if (col_r >= 0):
        col_r=(col_r*(-1))+100
    else:
        col_r+=100
    if (col_g >= 0):
        col_g=(col_g*(-1))+100
    else:
        col_g+=100
    if (col_b >= 0):
        col_b=(col_b*(-1))+100
    else:
        col_b+=100
    r.ChangeDutyCycle(col_r)
    g.ChangeDutyCycle(col_g)
    b.ChangeDutyCycle(col_b)

def col_spectrum(op, col_r, col_g, col_b):
    if (op == "add"):
        if (col_b < 50) and (col_r != 100 ) and (col_g != 100):
            col_b+=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        elif (col_b < 100) and (col_r != 100) and (col_g != 100):
            col_b+=1
            col_g+=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        elif (col_g < 0) and (col_r != 100):
            col_g+=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        elif (col_r < 0) and (col_r != 100):
            col_g+=1
            col_r+=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        elif (col_r < 100) and (col_r != 100) and (col_b == 100):
            col_r+=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        elif (col_g == 100) and (col_r > 0) and (col_b > 0):
            col_b-=1
            col_r-=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        else:
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
    elif (op == "sub"):
        if (col_r != 100) and (col_b != 100) and (col_g == 100):
            col_b+=1
            col_r+=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        elif (col_r > 0):
            col_r-=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        elif (col_r > -100):
            col_r-=1
            col_g-=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        elif (col_g > -50):
            col_g-=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        elif (col_g > -100):
            col_b-=1
            col_g-=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        elif (col_b > -100):
            col_b-=1
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}
        else:
            return {'col_b':col_b, 'col_g':col_g, 'col_r':col_r}


#def signal_handler(signal, frame):
# print('You pressed Ctrl+C!')
# sys.exit(0)
#signal.signal(signal.SIGINT, signal_handler)

#def init_worker():
# signal.signal(signal.SIGTERM, signal.SIG_TERM)

# do the cleanup before exit
def cleanup():
    r.stop()
    g.stop()
    b.stop()
    GPIO.cleanup()

# read one char from stdin
def func():
    import sys, tty, termios
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
        tty.setraw(sys.stdin.fileno())
        ch = sys.stdin.read(1)
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    if ( ch == "q" ):
        cleanup()
        sys.exit(0)
    return ch

print ("Press 'q' to quit, 'h' to show help.")

# read stdin in a loop
while True:
    char=func()
    if char == "r":
        col_new=col_adjust("add", col_r, col_g, col_b, "r")
    elif char == "e":
        col_new=col_adjust("sub", col_r, col_g, col_b, "r")
    elif char == "g":
        col_new=col_adjust("add", col_r, col_g, col_b, "g")
    elif char == "f":
        col_new=col_adjust("sub", col_r, col_g, col_b, "g")
    elif char == "b":
        col_new=col_adjust("add", col_r, col_g, col_b, "b")
    elif char == "v":
        col_new=col_adjust("sub", col_r, col_g, col_b, "b")
    elif char == "a":
        col_new=col_spectrum("add", col_r, col_g, col_b)
    elif char == "s":
        col_new=col_spectrum("sub", col_r, col_g, col_b)
    elif char == "h":
        usage()
    else:
        print ("Unknown function or color out of range. Type 'q' to quit")
    col_b=col_new['col_b']
    col_g=col_new['col_g']
    col_r=col_new['col_r']
    set_color(col_r, col_g, col_b)
    print("COLOR R:", col_r, "G:", col_g, "B:", col_b)
Function func is used to capture input from keyboard.
Function col_adjust handles individual R, G and B color change.
Function col_spectrum uses simple algorithm to go through color spectrum. It has far from being perfect, but it's sufficient as a simple example. Following scheme demonstrates approximately how the colors are mixed up:




No comments:

Post a Comment