Sterowanie diodami RGB z Raspberry Pi

Sterowanie diodami RGB z Raspberry Pi

Dioda RGB pozwala na wyświetlenie dowolnego koloru. Może funkcjonować jako kontrolka, a w postaci modułów i taśm z wieloma diodami służy zwykle jako źródło oświetlenia. W artykule opiszemy, jak sterować diodami RGB z komputerka Raspberry Pi.

Dioda RGB składa się z trzech diod elektroluminescencyjnych o różnych barwach, umieszczonych w jednej obudowie. Uzyskany wypadkowy kolor światła wynika z niezależnego sterowania jasnością każdej ze składowych barw. A ponieważ barwy zlewają się w jednym obszarze, mamy wrażenie płynnej zmiany kolorów w niemalże całym zakresie widzenia ludzkiego oka. Istnieje wiele sposobów na uzyskanie takiego sterowania z użyciem minikomputera Raspberry Pi (lub jednego z podobnych modułów, pracujących pod kontrolą systemu operacyjnego Linux). Opiszemy dwa z nich, które sterują diodą za pomocą trzech przebiegów PWM.

Mieszanie barw w RGB

Model kolorów RGB jest addytywnym modelem opisu tworzenia kolorów, w którym światło czerwone, zielone i niebieskie jest dodawane do siebie na różne sposoby w celu odtworzenia szerokiego spektrum kolorów. Addytywny model kolorów oznacza, że wynikowe widmo koloru jest sumą widm poszczególnych jego składników. Nazwa modelu pochodzi od skrótów angielskich nazw trzech podstawowych kolorów, używanych w tym modelu – Red (czerwony), Green (zielony) i Blue (niebieski). W uproszczeniu model ten zobrazowany jest na rysunku 1.

Rysunek 1. Uproszczone przedstawienie addytywnego mieszania barw w modelu RGB

Głównym celem stosowania modelu kolorów RGB jest opis, reprezentacja i wyświetlanie kolorów w systemach elektronicznych, zwłaszcza takich jak telewizory i komputery, aczkolwiek jego historia sięga początków fotografii analogowej. Model RGB ma za sobą solidną teorię w zakresie postrzegania kolorów przez ludzi.

Aby utworzyć dowolny kolor z barw składowych, należy nałożyć na siebie trzy wiązki światła. Każda z nich może mieć dowolną intensywność, od całkowitego wyłączenia (0) do pełnego włączenia (100%). Zerowa intensywność dla każdego komponentu daje najciemniejszy kolor – czarny (bez światła), a pełna intensywność wszystkich barw daje w sumie kolor biały. Jakość tej bieli jest zależna od precyzyjnego doboru składowych źródeł światła i ich widm. Gdy intensywność wszystkich składowych barw jest taka sama, wynikiem jest kolor szary o jasności zależnej od intensywności barw. Gdy intensywności są różne, rezultatem jest zabarwiony odcień, mniej lub bardziej nasycony w zależności od różnicy najsilniejszej i najsłabszej intensywności zastosowanych kolorów podstawowych.

Gdy jeden ze składników ma najwyższą intensywność, wynikowy kolor jest odcieniem zbliżonym do tego podstawowego koloru, a gdy dwa składniki mają tę samą najsilniejszą intensywność, wówczas kolor jest odcieniem jednego z kolorów uzupełniających (cyjan, magenta lub żółty). Widać to dokładnie na wykresie przedstawionym na rysunku 2. Jest to tzw. gamut, czyli przestrzeń barwna – w tym przypadku sRGB. Na wykresie zaznaczone są cztery punkty – trzy to barwy podstawowe, a czwartym jest tzw. D65, czyli umowny kolor biały; odpowiada on kolorowi emisji ciała doskonale czarnego o temperaturze 6500 K.

Rysunek 2. Wykres przestrzeni barwnej CIExy1931 do przedstawiania gamutu (przestrzeni barwowej) w modelu RGB

Na wykresie przedstawiono gamut sRGB z zaznaczonymi trzema punktami – składowymi kolorami oraz kolorem białym o temperaturze barwowej 6500 K (D65).)

Sam model kolorów RGB nie definiuje, co oznacza dokładnie kolor czerwony, zielony czy niebieski. Z uwagi na to wyniki ich mieszania nie są bezwzględnie określone, ale zależą od kolorów podstawowych. Dopiero po zdefiniowaniu dokładnych kolorów podstawowych ogólny model kolorów staje się konkretną absolutną przestrzenią kolorów (gamutem), taką jak wspominane sRGB czy Adobe RGB. Zdefiniowanie ścisłej przestrzeni barwowej jest szczególnie istotne w systemach telewizyjnych, oprogramowaniu graficznym etc. W przypadku kontroli prostej diody RGB nie jest to krytyczne, o ile nasza aplikacja nie musi ściśle odwzorowywać pewnych kolorów.

Sterowanie PWM

Sterowanie diod elektroluminescencyjnych za pomocą impulsów o zmiennym wypełnieniu jest bardzo praktycznym rozwiązaniem. Do sterowania diodą LED w ten sposób wystarczy pojedyncze wyjście cyfrowe, które może generować przebieg prostokątny. Modulacja PWM polega na zmianie wypełnienia przebiegu prostokątnego, który podawany jest na wyjście cyfrowe układu. W dużym uproszczeniu tego rodzaju sterowanie polega na naprzemiennym włączaniu i wyłączaniu zasilania diody. Stosunek czasu, gdy zasilanie jest załączone i jest podawane na wyjściu, do ogólnego czasu trwania jednego okresu nazywa się właśnie wypełnieniem sygnału. Wartość ta podawana jest w procentach. Dla wypełnienia 50% zasilanie będzie podawane na diodę LED przez połowę czasu trwania okresu. Na rysunku 3 zaprezentowano trzy przebiegi tego rodzaju, różniące się wypełnieniem (pokazano przebiegi o wypełnieniu (od góry): 10%, 50% oraz 90%).

Rysunek 3. Przykładowe przebiegi PWM o wypełnieniu 10% (na górze), 50% (pośrodku) i 90% (na dole)

Jeśli zasilanie jest przełączane na diodzie dostatecznie często, to mruganie diod nie będzie w ogóle widoczne dla człowieka – wystarczy około kilkuset herców, aby mruganie było niezauważalne, nawet jeżeli dioda lub obserwator są w ruchu. Rekomenduje się używanie częstotliwości co najmniej 1 kHz do kluczowania sygnału PWM dla diod LED.

W systemach cyfrowych sygnał PWM generuje się najczęściej, wykorzystując przebieg prostokątny o znacznie krótszym okresie (wyższej częstotliwości) niż wyjściowy przebieg PWM. Układ odpowiedzialny za generowanie tego sygnału zlicza okresy tego zegara – przez określoną liczbę cykli sygnał na wyjściu jest załączony, potem wyłącza się, po to tylko, żeby po określonej liczbie cykli zresetować się, załączyć zasilanie i ponownie rozpocząć zliczanie cykli. To, ile maksymalnie cykli zliczać może dany system, mówi nam o rozdzielczości modułu PWM – na przykład 10-bitowy licznik jest w stanie zliczać do 1024, co oznacza, że cały cykl sygnału podzielony może być na 1024 fragmentów, co daje nam rozdzielczość PWM równą ok. 0,1%.

Generowanie PWM na Raspberry Pi

Aby wygenerować przebieg PWM, skorzystamy z pythonowej biblioteki GPIO, która pozwala na obsługę wyjść cyfrowych RPi. W bibliotece znajdziemy moduł GPIO.PWM, który pozwala na generowanie przebiegu o zmiennej modulacji. Przebieg ten generowany jest programowo – tworzony jest osobny wątek dla każdego wyjścia PWM w systemie, co zapewnia dostateczną precyzję generowania przebiegu PWM, wymaganą do zadań takich jak sterowanie diodami LED itp.

Na rysunku 4 zaprezentowano dwa schematy podłączenia diody RGB do modułu Raspberry Pi, aby sterować nią poprzez sygnały PWM. W zależności od tego, czy zastosowana przez nas dioda ma wspólną katodę (schemat po lewej, katoda do masy – czarny przewód), czy wspólną anodę (schemat po prawej, anoda do zasilania 3,3 V – czerwony przewód), bazujemy na odpowiednim diagramie z rysunku 4. Jeśli chcemy diody zasilać z napięcia 5 V, koniecznie trzeba dodać rezystory na wszystkich kanałach RGB. Oporniki stosowane są do ograniczenia prądu płynącego przez LED-y. Dla typowych elementów wartość ta powinna wynosić 150 V dla kanału czerwonego i po 100 V dla kanałów zielonego i niebieskiego.

Rysunek 4. Dwa schematy podłączenia diody RGB do modułu Raspberry Pi, dla modelu ze wspólną katodą (po lewej) oraz ze wspólną anodą (po prawej)

Na listingu 1 zaprezentowano kod programu do generowania przebiegów PWM na pinach, do których podłączone są poszczególne diody w diodzie RGB, według schematu z rysunku 4 . Aby zademonstrować działanie, wystarczy uruchomić program.

Listing 1. Kod programu do sterowania dyskretną diodą RGB z wykorzystaniem przebiegów PWM na komputerze jednopłytkowym Raspberry Pi
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM) # konfiguracja konwencji oznaczenia pinów GPIO
GPIO.setwarnings(False) # wyłączenie ostrzeżeń podczas pracy kodu
RUNNING = True

# definicja pinów wyjściowych dla poszczególnych kanałów RGB
green = 20
red = 21
blue = 22

# definiujemy wszystkie kanały jako wyjścia
GPIO.setup(red, GPIO.OUT)
GPIO.setup(green, GPIO.OUT)
GPIO.setup(blue, GPIO.OUT)

# wybór częstotliwości przebiegu PWM
Freq = 100

# definicja kanałów PWM na poszczególnych pinach
RED = GPIO.PWM(red, Freq)
GREEN = GPIO.PWM(green, Freq)
BLUE = GPIO.PWM(blue, Freq)

try:
# główna pętla programu
while RUNNING:
# uruchomienie PWM, 100 oznacza 100% wypełnienia
RED.start(100)
GREEN.start(1)
BLUE.start(1)

for x in range(1,101):
# zmiana wypełnienia sygnału PWM
GREEN.ChangeDutyCycle(x)
# opóźnienie pomiędzy każdą zmianą
# (czas w sekundach)
time.sleep(0.05)

for x in range(1,101):
RED.ChangeDutyCycle(101-x)
time.sleep(0.025)

for x in range(1,101)
GREEN.ChangeDutyCycle(101-x)
BLUE.ChangeDutyCycle(x)
time.sleep(0.025)

for x in range(1,101):
RED.ChangeDutyCycle(x)
time.sleep(0.025)

# aby przerwać działanie programu, wystarczy
# nacisnąć dowolny klawisz na klawiaturze
except KeyboardInterrupt:

RUNNING = False
GPIO.cleanup() # wyłączenie wszystkich kanałów PWM

Scalone diody RGB ze zintegrowanym sterownikiem

Inne podejście do sterowania diodą RGB oferują scalone moduły, które integrują w jednej obudowie trzy diody – czerwoną, zieloną i niebieską –oraz kontroler, który generuje przebiegi PWM dla każdego z tych kanałów. Do komunikacji pomiędzy takim modułem a sterownikiem w postaci minikomputera Raspberry Pi służy interfejs cyfrowy.

Jednymi z najpopularniejszych diod RGB ze zintegrowanym sterownikiem są układy WS28xx. Przykładowa dioda tego rodzaju pokazana jest na rysunku 5a. Są one dostępne w szerokiej gamie elementów, różniących się liczbą diod w strukturze – niektóre z nich zawierają trzy diody – czerwoną, zieloną i niebieską, a inne dodatkowo uzupełnione są diodą białą, tworząc tzw. diodę RGBW. Symbol elektryczny takiej diody pokazano na rysunku 5b. Ma ona cztery piny, dwa z nich (VCC i GND) służą do zasilania, a dwa pozostałe (DIN oraz DOUT) do komunikacji.

Rysunek 5. Dioda RGB ze zintegrowanym sterownikiem, typu WS28xx

Protokół komunikacji z diodami RGB WS2812B

Protokół komunikacji z modułami tego rodzaju bazuje na kodowaniu NRZ, czyli bez powrotu do zera. Jest to rodzaj kodowania, który zera i jedynki pozwala zapisać jako przejścia pomiędzy dwoma stanami (napięciami), a to, czy dane zbocze jest zerem, czy jedynką (czy resetem), uwarunkowane jest zależnościami czasowymi. Na rysunku 6 zaprezentowano takie przejścia dla układu WS2812B.

Rysunek 6. Kodowanie NRZ dla modułu WS2812

Moduł WS2812B ma wewnętrzny, 24-bitowy zatrzask, który zatrzaskuje dane wejściowe, w momencie gdy na wejście DIN podany zostanie komplet bitów po resecie. Kolejne bity podawane na wejście podawane są do układu, który wzmacnia je, regeneruje zbocza etc. i podaje na wyjście – DOUT. Dzięki temu można łączyć kaskadowo ze sobą diody RGB tego rodzaju w szereg, podłączając wyjście DOUT jednej diody do wejścia DIN kolejnej diody. Na rysunku 7 zaprezentowano schemat kaskadowego układu tego typu.

Rysunek 7. Schemat kaskadowego połączenia modułów

Dane przesyłane do układu są uporządkowane w formacie GRB – zielony/czerwony/niebieski, gdzie pierwszy w ramce jest najstarszy bit. Każdej składowej odpowiada osiem bitów, co pozwala uzyskać 16777216 kolorów (224). Aby sterować diodą WS2812B z  minikomputera Raspberry Pi, podłączamy jej wejście danych do jednego pinu danych. Jeśli chcemy podłączyć więcej modułów RGB, łączymy je kaskadowo, jak pokazano na rysunku 7. Do sterowania takim modułem można użyć biblioteki neopixel (którą trzeba przed uruchomieniem programu pobrać i zainstalować za pomocą narzędzia pip:

sudo pip3 install rpi_ws281x
adafruit-circuitpython-neopixel

Kod programu do sterowania łańcuchem diod LED RGB WS2812 pokazano na listingu 2. Program to proste demo, które prezentuje możliwości kontrolowania kolorów.

Listing 2. Kod programu do sterowania łańcuchem diod LED RGB WS28
import time
from neopixel import *
import argparse

# Konfiguracja listwy LED
LED_COUNT = 16 # Liczba kaskadowo połączonych diod
LED_PIN = 18 # Numer pinu GPIO podłączonego do diody
LED_FREQ_HZ = 800000 # Częstotliwość komunikacji z LED-ami w hercach (typowo 800 kHz)
LED_DMA = 10 # Kanał DMA wykorzystywany do generowania sygnału (typowo 10)
LED_BRIGHTNESS = 255 # Ustawienie jasności - 0 to najciemniej a 255 najjaśniej
LED_INVERT = False # Odwrócenie sygnałów, ustawmy na True, jeżeli np. odwracamy sygnał na translatorze poziomów
LED_CHANNEL = 0 # set to ‘1’ for GPIOs 13, 19, 41, 45 or 53


# Definicja funkcji, które w różny sposób animować będą diody

def colorWipe(strip, color, wait_ms=50):
”””Przejście koloru przez diody, jedna po drugiej.”””
for i in range(strip.numPixels()):
strip.setPixelColor(i, color)
strip.show()
time.sleep(wait_ms/1000.0)

def theaterChase(strip, color, wait_ms=50, iterations=10):
”””Animacja z przechodzącymi i goniącymi się kolorami.”””
for j in range(iterations):
for q in range(3):
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, color)
strip.show()
time.sleep(wait_ms/1000.0)
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, 0)

def wheel(pos):
”””Generacja tęczowego wzoru przez wszystkie pozycje.”””
if pos < 85:
return Color(pos * 3, 255 - pos * 3, 0)
elif pos < 170:
pos -= 85
return Color(255 - pos * 3, 0, pos * 3)
else:
pos -= 170
return Color(0, pos * 3, 255 - pos * 3)

def rainbow(strip, wait_ms=20, iterations=1):
”””Wyświetlanie wszystkich kolorów tęczy na wszystkich pikselach naraz.”””
for j in range(256*iterations):
for i in range(strip.numPixels()):
strip.setPixelColor(i, wheel((i+j) & 255))
strip.show()
time.sleep(wait_ms/1000.0)

def rainbowCycle(strip, wait_ms=20, iterations=5):
”””Wyświetlanie wszystkich kolorów tęczy w postaci jednorodnego rozkładu na wszystkich diodach.”””
for j in range(256*iterations):
for i in range(strip.numPixels()):
strip.setPixelColor(i, wheel((int(i * 256 / strip.numPixels()) + j) & 255))
strip.show()
time.sleep(wait_ms/1000.0)

def theaterChaseRainbow(strip, wait_ms=50):
”””Przechodzące kolory tęczy przez diody.”””
for j in range(256):
for q in range(3):
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, wheel((i+j) % 255))
strip.show()
time.sleep(wait_ms/1000.0)
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, 0)

# Program główny
if __name__ == ‘__main__’:
# Przetwarzanie argumentów, z jakimi uruchomiono skrypt
parser = argparse.ArgumentParser()
parser.add_argument(‘-c’, ‘--clear’, action=’store_true’, help=’wyczyść ekran wychodząc z programu’)
args = parser.parse_args()

# Utworzenie obiektu NeoPixel z odpowiednią konfiguracją
strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
# Inicjalizacja biblioteki - musi być ona uruchomiona, zanim zaczniemy korzystać z jej funkcji
strip.begin()

print (‘Naciśnij Ctrl-C aby wyjść.’)
if not args.clear:
print(‘Wykorzystaj argument „-c” aby wyczyścić LEDy wychodząc’)

try:

while True:
print (‘Animacje z przechodzeniem koloru.’)
colorWipe(strip, Color(255, 0, 0)) # Przejście czerwonego
colorWipe(strip, Color(0, 255, 0)) # Przejście niebieskiego
colorWipe(strip, Color(0, 0, 255)) # Przejście zielonego
print (‘Animacja z goniącymi się kolorami.’)
theaterChase(strip, Color(127, 127, 127)) # Animacja z kolorem białym
theaterChase(strip, Color(127, 0, 0)) # Animacja z kolorem czerwonym
theaterChase(strip, Color( 0, 0, 127)) # Animacja z kolorem niebieskim
print (‘Rainbow animations.’)
rainbow(strip)
rainbowCycle(strip)
theaterChaseRainbow(strip)

except KeyboardInterrupt:
if args.clear:
colorWipe(strip, Color(0,0,0), 10)

Podsumowanie

Diody RGB dają duże możliwości – mogą świecić na dowolny kolor, dzięki czemu np. po zastosowaniu ich jako kontrolki w naszym urządzeniu jedną diodą można przekazać użytkownikowi wiele informacji. Z użyciem komputera jednopłytkowego Raspberry Pi sterowanie taką diodą (czy samym elementem dyskretnym, czy modułem z wbudowanym sterownikiem) jest bardzo proste i nie wymaga dużych nakładów pracy.

Nikodem Czechowski, EP

Źródła:
1. http://bit.ly/2p31ro7,
2. http://bit.ly/2MJlvVu

 

Artykuł ukazał się w
Elektronika Praktyczna
listopad 2019

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik wrzesień 2021

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio wrzesień - październik 2021

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka Podzespoły Aplikacje wrzesień 2021

Automatyka Podzespoły Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna wrzesień 2021

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich wrzesień 2021

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów