16xPWM?! Na Raspberry Pi?

Generowanie sprzętowego PWM z Raspberry jest trochę problematyczne. Wersje 'A/B' umożliwiały wystawienie tylko jednego takiego sygnału. Dla 'A+/B+/Pi 2′ rozszerzono te możliwości o jeden dodatkowy kanał z możliwością uruchomienia 'kopii' tego sygnału na kolejnych 2 pinach. Oczywiście dwa sprzętowe PWMy to niewiele a te programowe potrafią sprawić dużo (nieprzyjemnych) niespodzianek.
Rozwiązaniem tego problemu może być użycie zewnętrznych rozszerzeń (generatorów). Jedno z takich właśnie do mnie dotarło.

Rozszerzenie do generowania sygnału PWM
Rozszerzenie do generowania sygnału PWM

Rozszerzenie to umożliwia generacje nawet 16 niezależnych sygnałów w granicach 40Hz – 1kHz. Co więcej, można je obsługiwać za pomocą i2c. Układ akceptuje napięcia logiki Raspberry i Arduino.

Pinologia

Urządzenie oferuje 16 wyjść PWM. Wyprowadzone są one w formie pinów pasujących do wtyczek modelarskich: 3 rzędów pinów w kolumnach kolejno sterowanie-napięcie-masa. Dzięki temu serwa można bezpośrednio przyłączać do płytki.
Kolejne porty służą do komunikacji z hostem (SCL i SDA) i zasilania czipu sterującego rozszerzeniem (Vcc i GND; PCA9685).
Warto również zwrócić uwagę na wyprowadzenie „V+”. Jest to zasilanie podłączonych do rozszerzenia elementów (środkowa linia). Jako że mogą one konsumować stosunkowo duże ilości prądu, bezpieczniej jest do tego celu użyć dodatkowego terminalu i zewnętrznego zasilania. Unikniecie wtedy spalenia Rasberry, gdy kilka serw uderzy jednocześnie  w ograniczniki.

Pin Funkcja Podłącz: Raspberry Podłącz: Arduino
GND Masa Pin 6: GND GND
OE Normalnie niepodłączone
SCL Linia zegara scl Pin 5: SCL A5
SDA Linia danych i2c Pin 3: SDA A4
Vcc Zasilanie czipu Pin 1: 3,3v 5v
V+ Zasilanie linii V+ dla podłączonych urządzeń Najlepiej nie podłączaj – zasilaj zewnętrznie przez terminal

Instalacja oprogramowania – Raspberry

Jedną  z zalet tego rozszerzenia jest to, że z hostem komunikuje się za pomocą interfejsu i2c. W ten sposób wystarcza mu minimalna ilość połączeń.

  1. Uruchomcie insterfejs i2c:
    • W pliku /boot/config.txt dopiszcie:
      dtparam=i2c1=on
      dtparam=i2c_arm=on
    • W pliku /etc/modules dopiszcie:
      i2c-dev
  2. Zainstalujcie narzędzia i2c i wrapery Pythona:
    • $ sudo apt-get install i2c-tools
    • $ sudo apt-get install python-smbus

Najnowsze kernele 3.18+ używają tzw. device tree. Generalnie powinno wystarczyć dodanie i2c w pliku /boot/config.txt. Niestety u mnie narzędzia i2c zadziałały dopiero po uzupełnieniu pliku /etc/modules.

Po restarcie użyjcie polecenia:
$ i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 -- -- -- -- -- -- --

Widzicie tu, że do Raspberry podłączone są 3 urządzenia o adresach 0x40, 0x50 i 0x70. U mnie 0x50 to podłączona do i2c pamięć 24fc512. 0x40 to podstawowy adres rozszerzenia. 0x70 to tzw. 'All Call' – powala na sterowanie całym łańcuchem podłączonych urządzeń równocześnie.

Jakiś przykład?

Jasne, Adafruit:
$git clone https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git
$cd Adafruit-Raspberry-Pi-Python-Code
$cd Adafruit_PWM_Servo_Driver
$sudo python Servo_Example.py

Przykład w skrócie:

from Adafruit_PWM_Servo_Driver import PWM
import time
#Rozszerzenie znajduje sie na adresie 0x40
pwm = PWM(0x40)
#Wybieramy znaną częstotliwość 50Hz
pwm.setPWMFreq(50)
#ustaw sygnał o na wyjściu 0 rozszerzenia
pwm.setPWM(0, 0, 307)

Wystarczy podłączyć serwo i powinno ustawić się w pozycji neutralnej… Niestety tak się nie stanie. Problem polega na tym, że biblioteka nie skaluje dokładnie częstotliwości. W rezultacie zamiast 50Hz, generuje sygnał 57Hz. Jeżeli podzielicie to na przedziały (domyślnie 4096), różnica dla stanu neutralnego jest dość znaczna. Proponuję dodać małą poprawkę do pliku Adafruit_PWM_Servo_Driver.py:

def setPWMFreq(self, freq):
    #Dodaj:
    freq *= 0.9
    "Sets the PWM frequency"
    prescaleval = 25000000.0    # 25MHz
    prescaleval /= 4096.0       # 12-bit
    prescaleval /= float(freq)
    ...

Dzielnik sprawi, że sygnał będzie generowany zgodnie z zadaną częstotliwością. Usprawnienie to jest obecne w kodzie dla Arduino.

Dodaj komentarz