piątek, 13 października 2017

Moduł sterownika PWM PCA9685

Opisywany moduł zawiera 16 kanałowy 12 bitowy sterownik PWM oparty na układzie PCA9685 sterowany po magistrali I2C. Może przydać się kiedy zabraknie pinów w Arduino, a tych w przypadku UNO od PWM jest sześć. Zajmując tylko dwa piny (do sterowania magistralą I2C) dostajemy kolejne 16. Moduł z definicji przeznaczony jest do sterowania diodami LED, ale może posłużyć też do sterowania silnikami (poprzez mostek H), czy serwami.


Sterownik PWM PCA9685



Specyfikacja
  • 16 niezależnych kanałów PWM
  • 12 bitowa rozdzielczość generowania sygnałów PWM
  • interfejs I2C
  • domyślny adres 0x40 (konfigurowalny za pomocą zworek na wyprowadzonych punktach lutowniczych)
  • zasilanie 3,3 - 5V
  • 6 zworek do ustawiania adresu modułu na magistrali I2C
  • regulacja częstotliwości PWM w przedziale 24Hz - 1526Hz (taka sama dla wszystkich kanałów). Domyślnie 200Hz.
  • wbudowane rezystory 220Om chroniące wyjścia, dzięki czemu można bezpośrednio podłączać LED-y
  • konfiguracja wszystkich wyjść jako push-pull lub open-drain. Domyślnie push-pull (dokładnie nazywa się to totem pole od charakterystycznego układu wyjścia układów TTL).
  • dopuszczalny prąd na wyjściu każdego z kanałów (dla zasilania 5V) wynosi 25mA (przy prądzie wpływającym - current sink) lub 10mA (przy prądzie wypływającym - current source)
  • łańcuchowy projekt płytki - komplementarna para pinów sterowania i zasilania umożliwia podłączenie kolejnego modułu
  • dodatkowe zasilanie V+ napięciem max. 6V (o jego funkcji będzie poniżej) podłączane pod pin V+ lub pod złącze ARK (z dodanym zabezpieczeniem chroniącym przed odwrotną polaryzacją)
  • miejsce na dodatkowy kondensator elektrolityczny, którego pojemność wylicza się wg. wzoru n * 100uF, gdzie n to ilość podpiętych serw (6)

Sterownik w akcji


Uwagi
  • Opcja zasilania dodatkowym napięciem V+ jest przydatna jeśli zasilamy moduł niskim napięciem, np 3,3V a chcielibyśmy móc sterować LED-ami wysokimi napięciami przewodzenia - kolor świecenia biały i niebieski. Wtedy LED wpina się między piny V+ i PWM, podłączając anodę do V+.

    Podobnie ma się rzecz z zasilaniem serw - z V+ zasilane jest serwo, a do jego sterowania powinno wystarczyć 3,3V podawane na pinie PWM.
  • Stosowanie serw wymusza ustawienie częstotliwości 50Hz.


Stosowana biblioteka Adafruit-PWM-Servo-Driver-Library dostarcza m.in. poniższe funkcje:


Funkcja Opis
Adafruit_PWMServoDriver(addr) Konstruktor - ustawia adres modułu (domyślnie 0x40)
begin() Inicjalizacja komunikacji z modułem
setPWMFreq(freq) Ustawia częstotliwość pracy modułu freq. Wartość z przedziału 40 - 1000
setPWM(num, on, off) Ustawia wartość PWM dla kanału o numerze num. On wyznacza początek pojawienia się impulsu (zmianę stanu z niskiego na wysoki), a off jego koniec (zmiana stanu z wysokiego na niski). On i off przyjmują wartości z przedziału 0 - 4095
setPin(num, val, invert); Ustawia stan niski 0 lub wysoki 4095 dla kanału num. Opcjonalny parametr invert odwraca impuls (domyślna wartość to false)


Piny

Pin(y) Opis
VCC, GND Zasilanie 3,3V lub 5V
SDA, SCL Piny dla podłączenia magistrali I2C (domyślnie dla aruduino uno łączą się odpowienio z pinami A4, A5)
V+ Dodatkowe napięcie zasilania, max +6V
OE Odłączenie wyjść po podaniu stanu wysokiego
ARK (V+) Dodatkowe napięcie zasilania, max +6V z zabezpieczeniem przed odwróconą polaryzacją


Uruchomienie

Układ uruchomieniowy, będący przymiarką do realizacji "samochodu robota",  zasilany jest w sumie trzema napięciami:

  • 5V (1) - napięcie brane z arudino zasilające "logikę" modułów:  PCA9685 i mostka H L298N
  • 5V (2) - napięcie z zasilacza zasilające wyjście V+ PCA9685 (zasilania serw, ewentualnie diod LED). Zasilanie jest oddzielne z racji na możliwy znacznie większy pobierany prąd, niż ma to miejsce dla części "logicznej"
  • 8,2V (3) - dwa akumulatory Li-ion 18650 zasilające silniki podłączone do mostka H. To samo co wyżej odnośnie prądu. Poza tym silniki mogą wymagać większego napięcia (użyte przeze mnie tego wymagały).
Schemat połączeń

Diody LED wpięte są pod piny V+ (anoda) i PWM (katoda), co pozwala na użycie większego prądu, maksymalnie do 25mA. Zmierzona wartość prądu dla użytej diody LED świecącej na czerwono przy rezystorze 220Om i zasilaniu 5V wyniosła ok. 16mA. To oznacza, że dość łatwo może być przekroczona dopuszczalna wartość 10mA przy wpięciu LED między piny PWM i GND. Tym bardziej, że rezystory zabezpieczające wyjścia mają własnie po 220 Om.

Podpięcie serwa SG90 wymusza jeszcze jedną istotną rzecz w konfiguracji modułu PCA9685. Chodzi o częstotliwość pracy wszystkich kanałów PWM, która zgodnie z dokumentacją serwa wynosi 50Hz.

Zastosowanie modułu daje jeszcze jeszcze tę zaletę, że mamy wyższą rozdzielczość regulacji sygnału PWM (4096 stopni)

Test działania modułu


Test działania modułu
Kod dla "zestawu uruchomieniowego" zamieszczam poniżej.

Oryginalnie w przykładzie do modułu dotyczącym serwa SERVOMIN i SERVOMAX przyjmują odpowiednio wartości: 150 i 600. Ja musiałem je zmniejszyć z racji na problem z serwem opisany na końcu postu. Dzięki temu dla wartości 170, 280, 400 mam odpowiednio kąty obrotu serwa: 0, 90, 180 stopni.

Może nie jest to najprostszy możliwy układ uruchomieniowy, ale jakby nie patrzeć, cztery LEDy, jedno serwo i mostek H "pochłaniają" aż 10 kanałów/pinów PWM opisywanego modułu. To obrazuje, w jakim stopniu opisywany moduł "odciąża" Arduino.

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

//stałe dla pozycji serwa
#define SERVOMIN   170 //minimalna długość pulsu (z 4096)
#define SERVOMAX   400 //maksymalna długość pulsu (z 4096)
#define SERVMIDDLE 280 //długość pulsu dla pozycji środkowej

//piny LED-ów
const int LED_FRONT_RIGHT = 0;
const int LED_FRONT_LEFT = 1;
const int LED_BACK_RIGHT = 2;
const int LED_BACK_LEFT = 3;

//piny mostka
const int ENA = 4;
const int ENB = 5;
const int IN1 = 6; 
const int IN2 = 7;
const int IN3 = 8;
const int IN4 = 9;

//piny serwo
const int SERVO = 10;

//utworzenie obiektu sterownika
//funkcja przyjmuje jako wartość adres modułu (domyślnie 0x40)
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

//stany niski i wysoki na potrzeby sterowania mostkiem
const int SD_LOW = LOW;
const int SD_HIGH = 4095;

//przechowuje pozycję serwa (kąt obrotu)
int pos = 0;

//odpowiada za jednorazowe wykonanie kodu w loop()
boolean run;

void setup() {
  pwm.begin();
  pwm.setPWMFreq(50);
  
  //białe LED-y
  pwm.setPWM(LED_FRONT_RIGHT, 0, 0);
  pwm.setPWM(LED_FRONT_LEFT, 0, 0);
                                                                                                                                                                                                                                                                                      
  //czerwone LED-y - normalne światło (tj. nie stop)
  pwm.setPWM(LED_BACK_RIGHT, 0, 3500);
  pwm.setPWM(LED_BACK_LEFT, 0, 3500);

  //mostek H
  pwm.setPin(ENA, 0);
  pwm.setPin(ENB, 0);

  run = true;
}

void loop() {
  if (run) {
    //czerwone LED-y - światło stop (większa jasność) 
    pwm.setPWM(LED_BACK_RIGHT, 0, 0);
    pwm.setPWM(LED_BACK_LEFT, 0, 0);
    delay(2000);
    
    //czerwone LED-y (nie stop)
    pwm.setPWM(LED_BACK_RIGHT, 0, 3500);
    pwm.setPWM(LED_BACK_LEFT, 0, 3500);
  
    //serwo - obrót od 0 do 180 stopni i z powrotem
    for (pos = SERVOMIN; pos <= SERVOMAX; pos += 1) {
      pwm.setPWM(SERVO, 0, pos);
      delay(15);
    }
    
    for (pos = SERVOMAX; pos >= SERVOMIN; pos -= 1) {
      pwm.setPWM(SERVO, 0, pos);
      delay(15);
    }   

     //operacje na silnikach (przód, tył, obrót w lewo i prawo)
     motorForward(4095);
     delay(2000);
     motorFastBrake();
     delay(2000);
     motorBackward(4095);
     delay(2000);
     motorFastBrake();
     delay(2000);
     motorLeft(2000);
     delay(2000);
     motorRight(2000);
     delay(2000);
     motorFastBrake();     
    
     run = false;
  }
}

//ruch silników do przodu
void motorForward(int speed)
{
  pwm.setPWM(ENA, 0, speed);
  pwm.setPWM(ENB, 0, speed);
  pwm.setPin(IN1, SD_LOW);
  pwm.setPin(IN2, SD_HIGH);
  pwm.setPin(IN3, SD_LOW);
  pwm.setPin(IN4, SD_HIGH);
}

//ruch silników do tyłu
void motorBackward(int speed)
{
  pwm.setPWM(ENA, 0, speed);
  pwm.setPWM(ENB, 0, speed);
  pwm.setPin(IN1, SD_HIGH);
  pwm.setPin(IN2, SD_LOW);
  pwm.setPin(IN3, SD_HIGH);
  pwm.setPin(IN4, SD_LOW);
}

//skręt w lewo
void motorLeft(int speed)
{
  pwm.setPWM(ENA, 0, speed);
  pwm.setPWM(ENB, 0, speed);
  pwm.setPin(IN1, SD_LOW);
  pwm.setPin(IN2, SD_HIGH);
  pwm.setPin(IN3, SD_HIGH);
  pwm.setPin(IN4, SD_LOW);
}

//skręt w prawo
void motorRight(int speed)
{
  pwm.setPWM(ENA, 0, speed);
  pwm.setPWM(ENB, 0, speed);
  pwm.setPin(IN1, SD_HIGH);
  pwm.setPin(IN2, SD_LOW);
  pwm.setPin(IN3, SD_LOW);
  pwm.setPin(IN4, SD_HIGH);
}

//zatrzymanie silników
void motorFastBrake()
{
  pwm.setPWM(ENA, 0, SD_HIGH);
  pwm.setPWM(ENB, 0, SD_HIGH);
  pwm.setPin(IN1, SD_HIGH);
  pwm.setPin(IN2, SD_HIGH);
  pwm.setPin(IN3, SD_HIGH);
  pwm.setPin(IN4, SD_HIGH);
  delay(500);
}


Źródła
1) Schemat elektryczny modułu
2) Opis i specyfikacja na stronie Adafruit
3) Dokumentacja układu PCA9685
4) Biblioteka sterująca modułem (Adafruit-PWM-Servo-Driver-Library)
5) Dokumentacja do biblioteki Adafruit-PWM-Servo-Driver-Library
6) Podpinanie serwa

1 komentarz: