PCF8574 czyli jak łatwo zwiększyć liczbę pinów w Arduino

Wstęp

Arduino z rodziny UNO posiada 14 pinów cyfrowych. Jest to wystarczająca ilość do większości prostych zastosowań z dziedziny automatyki. Zdarzają się jednak sytuacje, jak np. podłączenie wyświetlacza, które drastycznie tą liczbę mogą zmniejszyć.

Co wtedy robić? Można przesiąść się na 2 razy droższe Arduino MEGA i mieć ponad 50 dodatkowych pinów. Można też użyć rejestru przesuwnego 74HC595, który da dodatkowe 8 wyjść cyfrowych za cenę 2 zł i użycia 3 pinów cyfrowych. Można też użyć układu PCF8574 i mieć dodatkowe 8 pinów (lub nawet 128 łącząc więcej układów) wejścia/wyjścia o możliwościach przekraczających te w Arduino za cenę użycia 2 pinów analogowych.

Układ scalony PCF8574

  • Vcc – Pin zasilania, podłączany w Arduino do pinu 5V.

  • GND – Pin masy zasilania podłączany w Arduino do GND.

  • P0..P7 – Cyfrowe piny do własnego wykorzystania.

  • SDA – Sygnał danych magistrali I2C podłączany w Arduino do Analog In 4.

  • SCL – Sygnał zegara magistrali I2C podłączany w Arduino do Analog In 5.

  • A0, A1, A2 – Wybór adresu układu, jeśli używasz jednego układu, wszystkie można podłączyć do GND.

  • /INT – Zanegowany sygnał przerwania. Można go używać do wykrywania zmiany stanu na jednym z cyfrowych wejść.

Magistrala I2C

Układ PCF8574 komunikuje się z Arduino za pomocą magistrali I2C. Jest to synchroniczna magistrala szeregowa wykorzystywana powszechnie w sprzęcie RTV. Szeregowa oznacza, że bity są przesyłane jednym pinem po kolei – pinem SDA. Synchroniczna oznacza, że każdy wysłany bit jest zatwierdzany sygnałem na drugim pinie – SCL. Synchroniczność przyczynia się do zwiększenia prędkości komunikacji i eliminuje błędy transmisji.

Magistrala I2C ma też własny protokół komunikacji, dzięki któremu do jej sygnałów SDA/SCL można podłączyć więcej niż jeden układ scalony. Każdy układ w magistrali ma swój adres. Adres jest to liczba, która identyfikuje układ, wybierając tą liczbę masz pewność, że dane które wysyłasz trafią do właściwego układu. Układ PCF8574 ma dodatkowo piny A0, A1, A2 za pomocą których możesz konfigurować część adresu. Pozwala to na podłączenie do magistrali więcej takich samych układów scalonych, dzięki modyfikacji stanów logicznych na tych pinach. Trzy piny adresu oznaczają, że możesz podłączyć do magistrali 8 takich układów scalonych, co da 64 dodatkowe piny cyfrowe. Jeśli i to jest za mało to istnieje też wersja układu PCF8574A, która ma zmieniony adres, co sprawia, że oprócz tamtych 8 można podłączyć jeszcze 8 układów i mieć w sumie 128 pinów.

W Arduino magistrala I2C ze względu na problemy licencyjne nazywa się TWI (TwoWires [dwa przewody] – od liczby przewodów używanych przez nią). Mikrokontroler AVR zawarty na płytce zawiera sprzętową obsługę tej magistrali. Jej wyprowadzenia znajdują się na dwóch ostatnich pinach grupy “ANALOG IN”. Sygnał danych SDA znajduje się na pinie 4, a sygnał zegarowy SCL na pinie 5.

Adres układu PCF8574

Układy z rodziny PCF8574 mają 7 bitowy adres. Pierwsze 3 bity adresu nadaje mu użytkownik, przez ustawienie pinów A0, A1, A2. Kolejne 4 są nadane fabrycznie na stałe. Układ PCF8574 ma je ustawione na 0100, a układ PCF8574A ustawione na 0111.

Binarny adres układu PCF8574

0

1

0

0

A2

A1

A0

Co oznacza, że minimalny adres (dla wszystkich A = LOW) to dziesiętnie 32, szesnastkowo 0x20. Maksymalny adres (dla wszystkich A = HIGH) to dziesiętnie 39, a szesnastkowo 0x27.

Binarny adres układu PCF8574A

0

1

1

1

A2

A1

A0

Co oznacza, że minimalny adres (dla wszystkich A = LOW) to dziesiętnie 56, szesnastkowo 0x38. Maksymalny adres (dla wszystkich A = HIGH) to dziesiętnie 63, a szesnastkowo 0x3F.

Podłączenie do Arduino

Podłączenie nie sprawia trudności i wykorzystuje tylko 4 przewody. Vcc układu łącze z pinem 5V Arduino, GND układu łącze z pinem GND Arduino w sekcji zasilania. Sygnały SDA łącze z pinem 4, a SCL z pinem 5 sekcji “ANALOG IN” Arduino.

Ponieważ używam w tej konfiguracji tylko jednego układu, podłączyłem wszystkie linie adresowe w stan niski (LOW) łącząc je z GND. Jeśli w twojej konfiguracji jest więcej takich samych układów, powinieneś do linii adresowych stosować różne kombinacje stanów (LOW – GND, HIGH – 5V) innych dla każdego układu. Pozostałe piny P0 do P7 możesz wykorzystywać dowolnie jako piny cyfrowe (DIGITAL), jak to robisz w Arduino.

Programowanie układu PCF8574

Przygotowanie

#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander;

void setup()
{
  expander.begin(0x20);
}

void loop()
{
}

Jak zawsze społeczność Arduino nie zawiodła i jeden z miłośników platformy przygotował odpowiednią bibliotekę. Aby rozpocząć komunikacje z układem PCF8574 musisz najpierw podłączyć 2 biblioteki – PCF8574.h (dostarczającą funkcje obsługi układu) i Wire.h (do obsługi magistrali TWI).

Następnie trzeba zadeklarować obiekt klasy “PCF8574”, który nazwałem “expander”. Deklaruje się to jak inne zmienne czyli “PCF8574 expander;”.

Potem w funkcji “setup” należy za pomocą metody “expander.begin” ustawić adres układu, który ma reprezentować zadeklarowany obiekt. W tym przypadku ustawiłem wartość szesnastkową 0x20 odpowiadającą układowi z liniami adresowymi w stanie “LOW” – jak na schemacie.

Ustawienie kierunku działania pinów

#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander;

void setup()
{
  expander.begin(0x20);
  expander.pinMode(0, OUTPUT);
  expander.pinMode(1, INPUT);
}

void loop()
{
}

Nazwy funkcji biblioteki obsługującej układ zostały tak dobrane, by przypominać te z biblioteki obsługującej piny Arduino. z tym, że zamiast “pinMode” jest “expander.pinMode”, gdyż dotyczy obiektu układu. Działanie funkcji jest bliźniaczo podobne do tych w Arduino. Pierwszy argument to numer pinu układu od 0 do 7, a drugi to kierunek działania pinu “INPUT” – wejście, “OUTPUT” – wyjście.

W tym przykładzie ustawiłem pin 0 na wyjście, a pin 1 na wejście.

Ustawienie stanu pinów

#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander;

void setup()
{
  expander.begin(0x20);
  expander.pinMode(4, OUTPUT);
}

void loop()
{
  expander.digitalWrite(4, LOW);
  delay(1000);
  expander.digitalWrite(4, HIGH);
  delay(1000);
}

Podobnie jak w Arduino, tak i tutaj do ustawienia stanu pinów służy bliźniacza funkcja “expander.digitalWrite”. Jej pierwszym argumentem jest numer pinu od 0 do 7, a kolejnym stan jaki ma być ustawiony. “LOW” to stan niski odpowiadający GND, “HIGH” to stan wysoki odpowiadający 5V. Oczywiście można ustawiać tylko stan pinów wyjściowych “OUTPUT”.

Ten przykład co sekundę zmienia stan pinu 4 układu.

Jeśli teraz podłączysz do pinu 4 (noga układu 9) rezystor 220 Ohm, a drugie wyprowadzenie rezystora do katody diody LED, potem anodę diody LED do zasilania 5V, to powinna zgodnie z programem mrugać.

Odwracanie stanu pinów wyjściowych

#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander;

void setup()
{ 
  expander.begin(0x20);
  expander.pinMode(4, OUTPUT);
}

void loop()
{
  expander.toggle(4);
  delay(1000);
}

Przykład z poprzedniego rozdziału można odrobinę skrócić. Biblioteka PCF8574 zawiera dodatkową funkcję “expander.toggle”, która automagicznie odwraca stan na wyjściu układu na przeciwny. Argumentem metody jest numer pinu od 0 do 7, którego stan chcesz odwrócić.

Przykładowy program robi to samo co poprzedni z tym, że krócej.

Mruganie

#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander;

void setup()
{ 
  expander.begin(0x20);
  expander.pinMode(4, OUTPUT);

  expander.blink(4, 10, 500);
}

void loop()
{
}

Metoda “expander.blink” odpowiada za mruganie stanem pinu. Jej pierwszy argument to numer pinu od 0 do 7, drugi argument to ilość mrugnięć, a trzeci, czas w jakim ma trwać jeden stan mrugania w milisekundach.

Przykład sprawia, że na pinie 4 (noga 9) pojawi się 10 impulsów – naprzemiennych stanów LOW – HIGH. Każdy stan będzie trwał 500 milisekund, czyli 0,5 s.

Odczytywanie stanu pinów

#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander;

void setup()
{
  Serial.begin(9600);

  expander.begin(0x20);
  expander.pinMode(0, INPUT);
}

void loop()
{
  byte value = expander.digitalRead(0);

  Serial.println(value, DE);
  delay(100);
}

Odczyt stanu pinu ustawionego na wejście “INPUT” odczytujesz za pomocą “expander.digitalRead”. Jedynym argumentem tej metody jest numer pinu od 0 do 7.

Powyższy przykład odczytuje wartość pinu 0 (noga 4) i zapisuje go do zmiennej “value”. Jeśli na pinie 0 jest stan niski, to zmienna “value” będzie miała wartość “LOW” (0), a jeśli wysoki, to zmienna będzie miała wartość “HIGH” (1). Wartość zmiennej jest wysyłana do komputera (dzięki Serial.println), dzięki czemu możesz ją podglądać w Serial Monitor z Arduino IDE.

Rezystory PullUp wejść układu PCF8574

#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander;

void setup()
{
  Serial.begin(9600);

  expander.begin(0x20);
  expander.pinMode(4, INPUT);
  expander.pullUp(4);
}

void loop()
{
  byte value = expander.digitalRead(4);

  Serial.println(value, DEC);
  delay(100);
}

Wejścia cyfrowych układów scalonych mogą mieć 3 stany. LOW – wejście ustawione w stan niski np. przez podłączenie do GND, HIGH – wejście ustawione w stan wysoki np. podłączone do 5V i stan nieustalony. Stan nieustalony jest wtedy, kiedy w przewodzie wejścia cyfrowego nie ma żadnego konkretnego stanu wysyłanego przez inne podłączone układy. Stan nieustalony jest interpretowany przez wejście układu jako stan losowy w zależności od zakłóceń ustawiany raz na LOW, a raz na HIGH.

Wiele elementów jak np. przełącznik, albo przewodzą prąd, albo nie. Jeśli podłączysz przełącznik do wejścia cyfrowego, to gdy nie będzie przewodził, będzie stan nieustalony i układ może dostawać błędne sygnały.

Rezystory pullup wymyślono po to, aby gdy wejście nie ma ustalonego stanu, same ustalały stan domyślny, dopóki na wejściu nie pojawi się konkretny stan. Pullup oznacza “podciągnij w górę”, znaczy to, że domyślnym stanem niepodłączonego wejścia będzie stan wysoki “HIGH”. Dlatego na schemacie podłączyłem przełącznik do pin 4 układu, a jego drugie wyprowadzenie do GND. Wobec czego gdy przełącznik nie przewodzi, dzieki pullup jest domyślnie stan wysoki. Gdy przełącznik przewodzi, zwiera wejście do GND przez co ustala stan na niski “LOW”.

Za ustalenie domyślnie stanu wysokiego na wejściu układu PCF8574 odpowiada funkcja “expander.pullUp”, której argumentem jest numer pinu układu od 0 do 7.

Przykład programu ustawia pullup na wejście numer 4 (noga 9) i wysyła jego stan do komputera.

Rezystory Pull Down układu PCF8574

#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander;

void setup()
{
  Serial.begin(9600);

  expander.begin(0x20);
  expander.pinMode(4, INPUT);
  expander.pullDown(4);
}

void loop()
{
  byte value = expander.digitalRead(4);

  Serial.println(value, DEC);
  delay(100);
}

Układ PCF8574 jest na tyle uniwersalny, że zapewnia też rezystory pulldown. Ustalają one domyślnie wejście cyfrowe na stan niski “LOW”. Zatem teraz podłączenie wejścia do 5V ustala jego stan na wysoki “HIGH”, a brak stanu to domyślnie stan niski.

Równoległy dostęp do pinów

Równoległy dostęp do pinów to funkcje dzięki którym za jednym wywołaniem program może obsłużyć wszystkie piny układu PCF8574 na raz.

  • expander.write(value)
    Pozwala ustawić wartość binarną argumentu “value” na wszystkich pinach wyjściowych układu.
  • expander.read()
    Pozwala odczytać wartość binarną wszystkich pinów.
  • expander.clear()
    Ustawia na wszystkich wyjściowych pinach stan niski “LOW”.
  • expander.set()
    Ustawia na wszystkich pinach stan wysoki “HIGH”.
#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander;

void setup()
{ 
  expander.begin(0x20);
  for (byte i=0; i<8; i++)
    expander.pinMode(i, OUTPUT);
}

void loop()
{
  byte value = random(0, 255);

  expander.write(value);
  delay(100);
}

Przykład ustawia wszystkie piny jako wyjścia i zmienia ich stan losowo.

Przerwania – układ elektryczny

Układ PCF8574 na nodze 13 posiada sygnał /INT. Jest to sygnał przerwania, informujący Arduino, “że coś się dzieje”, bez potrzeby ciągłego sprawdzania stanu pinu układu. Sygnał ten wykorzystywany jest do tego, że gdy na pinie wejściowym pojawi się odpowiedni stan, sygnał /INT ustawia się w stan niski. Sygnał ten jest w standardzie “otwarty dren”. Oznacza to, że jego wyjście ma albo stan niski, albo nieustalony. Umożliwia to podłączenie do tego samego pinu Arduino wyjść przerwań z większej ilości układów PCF8574, gdyż nie następuje kolizja stanów logicznych.

Sygnał przerwania /INT możesz podłączyć do dowolnego pinu cyfrowego Arduino. Ja wybrałem pin numer 2, do którego prowadzi niebieski przewód.

Przerwania – program podstawowy

#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander;

void onInterrupt()
{
  Serial.println("Przerwanie");
}

void setup()
{
  Serial.begin(9600);

  expander.begin(0x20);

  expander.pinMode(4, INPUT);
  expander.pullUp(4);

  pinMode(2, INPUT);
  digitalWrite(2, HIGH);

  expander.enableInterrupt(2, onInterrupt);
}

void loop()
{
}

W przykładzie ustawiłem pin 4 układu PCF8574 jako wejście i włączyłem mu domyślny stan wysoki (“pullUp”).

Następnie ustawiłem pin 2 Arduino na wejście. Ponieważ wyjście /INT może mieć albo stan niski, albo nieustalony, ustawiłem wejście pin 2 Arduino też jako “pullup” za pomocą “digitalWrite”.

Za pomocą metody “expander.enableInterrupt” wyberasz pin w Arduino do którego przyłączyłeś sygnał /INT układu (pierwszy argument) i rejestrujesz funkcję, która będzie automatycznie wywoływana po nastąpieniu przerwania (drugi argument). Ja zarejestrowałem funkcje o nazwie “onInterrupt”, która wysyła do komputera napis “Przerwanie”, gdy zostanie wywołana.

Po uruchomieniu programu, zmiana stanu pinu 4 układu (noga 9) na przeciwny niż domyślny, powoduje uruchomienie zarejestrowanej funkcji. Jak widzisz bez żadnego sprawdzania stanu pinów można dzięki przerwaniu automatycznie wywoływać funkcje. To rozwiązanie ma jedną wadę. Tą samą funkcję wywołuje dowolny pin wejściowy układu.

Powiązanie przerwania z funkcją można zlikwidować za pomocą funkcji “expander.disableInterrupt”, która nie ma żadnego argumentu.

Przerwania – program rozszerzony

#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander;

void onPin0()
{
  Serial.println("Pin 0");
}

void onPin1()
{
  Serial.println("Pin 1");
}

void onInterrupt()
{
  Serial.println("Przerwanie");
  expander.checkForInterrupt();
}

void setup()
{
  Serial.begin(9600);

  expander.begin(0x20);

  expander.pinMode(0, INPUT);
  expander.pullUp(0);
  expander.pinMode(1, INPUT);
  expander.pullUp(1);

  pinMode(2, INPUT);
  digitalWrite(2, HIGH);

  expander.enableInterrupt(2, onInterrupt);

  expander.attachInterrupt(0, onPin0, FALLING);
  expander.attachInterrupt(1, onPin1, FALLING);
}

void loop()
{
}

Jeśli w funkcji obsługującej przerwanie (“onInterrupt”) umieścisz wywołanie metody “expander.checkForInterrupt”, biblioteka zdobędzie nowe możliwości.

Od tej chwili możesz używać metod “expander.attachInterrupt” do rejestrowania funkcji wywoływanych osobno dla każdego pinu układu PCF8574. Ułatwia to pisanie nowych programów.

Metoda “expander.attachInterrupt” ma 3 argumenty. Pierwszym z nich jest numer pinu wejściowego układu, do którego chcesz podłączyć swoją funkcję. Kolejny argument to nazwa funkcji, która chcesz podłączyć. Ostatnim jest rodzaj zdarzenia jaki ma wywoływać funkcję. Istnieją 4 rodzaje zdarzeń.

  • CHANGE – wywołuje funkcje przy każdej zmianie stanu pinu

  • LOW – wywołuje funkcje jeśli pin układu ma stan niski

  • FALLING – wywołuje funkcję przy zmianie stanu z wysokiego na niski

  • RISING – wywołuje funkcje przy zmianie stanu z niskiego na wysoki

W przykładzie ustawiłem piny 0 i 1 układu na wejścia o domyślnym stanie wysokim. Potem zarejestrowałem dla nich funkcje “OnPin0” i “OnPin1” wywoływane podczas zmiany stanu z wysokiego “HIGH” na niski “LOW” (“FALLING”).

Powiązanie pinu z funkcją można zlikwidować za pomocą funkcji “expander.detachInterrupt”. Jej jednym parametrem jest numer pinu układu.

Podłączenie większej ilości układów PCF8574

Układ elektryczny

Włączając w obwód dodatkowe układy PCF8574 należy połączyć ich linie sygnałowe i zasilania. SDA, łączysz z SDA pierwszego układu. SCL łączysz z SCL pierwszego układu. /INT łączysz z /INT pierwszego układu. To samo z zasilaniem. 5V łączysz z 5v, a GND z GND.

Nieco inaczej ma się sprawa z liniami adresowymi. Tu należy ustawić binarnie inny adres niż układu pierwszego. W przykładzie podłączyłem linię A0 do 5V – czyli wymusiłem na niej stan wysoki “HIGH”. A1 i A2 zostawiłem bez zmian, czyli podłączone do GND. Sprawia to, że adres drugiego układu zwiększył się o 1. Teraz pierwszy układ ma adres szesnastkowy 0x20, a drugi 0x21.

Programowanie

#include <PCF8574.h>
#include <Wire.h>

PCF8574 expander1;
PCF8574 expander2;

void onInterrupt()
{
  expander1.checkForInterrupt();
  expander2.checkForInterrupt();
}

void setup()
{

  expander1.begin(0x20);
  expander2.begin(0x21);

  pinMode(2, INPUT);
  digitalWrite(2, HIGH);

  expander1.enableInterrupt(2, onInterrupt);
}

void loop()
{
}

Po dodaniu drugiego układu program też trzeba zmodyfikować. Teraz każdy układ reprezentują 2 niezależne obiekty – expander1 i expander2. Każdy z obiektów trzeba połączyć z innym układem przez adres danego układu. Zajmuje się tym metoda “begin” w funkcji “setup”. Jak ustaliłem na schemacie jeden układ ma adres 0x20, a kolejny 0x21. W funkcji “onInterrupt” trzeba rozdzielić metodę “checkForInterrupt” na 2 układy. Cała reszta programowania jest taka sama, z tym, że należy pamiętać który pin należy do którego układu i co za tym idzie do którego obiektu. Piny w obydwu układach liczone są od 0 do 7.

Inne rodzaje Arduino

Arduino MEGA

W Arduino MEGA magistrala TWI wyprowadzona jest w nieco innym miejscu. Dlatego jeśli jesteś użytkownikiem tej płytki to musisz nieco zmodyfikować schemat połączeń. Sygnał SDA jest tam na pinie 20, a SCL na pinie 21 z grupy “COMMUNICATION”.

Arduino R3

Nadchodzące wersje Arduino z serii R3 mają powiększoną ilość pinów. Powyższe schematy nie wymagają modyfikacji, jednak dla wygody użytkowników wyprowadzono tam sygnały TWI w dodatkowe miejsce. Znajdują się one w grupie “DIGITAL” na lewo od pinu “AREF”. Pierwszy z nich licząc od lewej to SCL, a następny to SDA.

Zdjęcie Arduino UNO R3 należy do zespołu Arduino (www.arduino.cc). Zostało pobrane z http://arduino.cc/en/Main/ArduinoBoardUno i zmodyfikowane.

Zakończenie

Teraz jeśli brak ci pinów masz już o jedno rozwiązanie więcej. Jest to rozwiązanie o tyle elastyczne, że bez problemu w razie potrzeb możesz dokładać i dokładać, aż do 128 pinów.

Zatem zapraszam do lutownicy i życzę nowych oraz ekscytujących projektów. No i nie zapomnij się nimi pochwalić.

Linki

100 myśli nt. „PCF8574 czyli jak łatwo zwiększyć liczbę pinów w Arduino

  1. coyote01

    Świetny tekst!

    Zmienił bym tylko w przykładzie (Ustawienie stanu pinów):

    void setup()
    {
    expander.begin(0x20);
    expander.pinMode(0, OUTPUT);
    }

    void loop()
    {
    expander.digitalWrite(0, LOW);
    delay(1000);
    expander.digitalWrite(0, HIGH);
    delay(1000);
    }

    pozinien być ustawiany pin 4, a nie 0, bo później jest ciągle mowa o pinie 4 i cięzko to dopasować do tego co widać w przykładzie.

    W Arduino R3 TWI jest dostępne zarówno w nowych wyprowadzeniach jak i A4, A5? Nie rozumiem w takim razie po co te nowe wyprowadzenia.

  2. WRonX

    Ślepota postępująca nie dała mi znaleźć wersji EN na stronie projektu, więc spytam tutaj – jaka jest możliwa do uzyskania częstotliwość przełączania wejść/wyjść tego układu? Chodzi mi konkretnie o możliwość uzyskania wyjścia PWM.
    Maksymalna częstotliwość zegara magistrali układu wynosi 100kHz, jak wynika z noty katalogowej, zaś częstotliwość PWM na Arduino wynoki około 500Hz, więc teoretycznie powinno się udać uzyskać jakieś programowe PWM?

  3. Michał

    Dziękuję! Kilka dni temu dostałem paczkę/list z arduino uno smd i starter kitem i zastanawiałem się jak to popodłączać.

  4. netmaniac

    @coyote
    R3 i TWI – podejrzewam, że chodzi o kompatybilnosc ‚w przyszłośc’. R3 ma być referencyjnym produktem dla 1.0 – tzn jeśli będziesz budował shieldy, biblioteki, materiały szkoleniowe w oparciu o układ R3 to ma to być długo kompatybilne

    Podejrzewam, że Leonardo i może Due nie będą miały na A4 i A5 TWI aka I2C, i wtedy TWI na dwóch pinach ma sens – shieldy korzystające z TWI będą kompatybilne.

    Nie będzie takiego zamieszania jak z SPI i Arduino ‚małym’ i Mega, póki shieldy nie zaczęły korzystać z SPI na ICSP pośrodku, to shieldy z SPI nie działały na Mega (np stary Ethernet Shield)

  5. sprae

    Dziękuję za miłe komentarze. Wszystkie wezmę pod uwagę :-)

    WRonX: Ten zegar 100 kHz trzeba jeszcze podzielić na ilość bitów potrzebną do przestawiania pinu. One są przesyłane w paczkach 9 bitowych. Za pomocą biblioteki zmiana pinu zajmuje chyba 18 bitów (pierwsza paczka to adres, druga ustawienie portu). Zatem maksymalna częstotliwość zmiany pinu to 100 kHz / 18 – 5 Hz.
    Zatem do PWM najlepiej byłoby zastosować układ TLC5940NT http://nettigo.pl/product/TLC5940NT,TLC5940NT
    Podłączany przez SPI.

    Michał: Gratulacje i powodzenia na nowej drodze życia :-)

  6. sprae Autor wpisu

    coyote01: Gratuluję spostrzegawczości :-) i dziękuję za zwrócenie uwagi. Już poprawiłem.

  7. WRonX

    @sprae:
    (kurde, nie wiedziałem, że piszesz na Nettigo :D)
    Faktycznie, dedykowany sterownik PWM zawsze będzie lepszy, ale myślałem o czymś uniwersalnym – jeśli nie zużyję wszystkich pinów na PWM, użyłbym ich jako I/O.
    Założyłem, że zegar trzeba podzielić przez coś, chociaż nie zagłębiałem się w strukturę ramki I2C. Niemniej jednak, jeśli mamy 18 bitów na porcję danych, to wychodzi nie 5Hz, tylko 5.5kHz. Czyli może jednak by się dało?

  8. sprae Autor wpisu

    WRonX: Jeśli ręcznie obsłużysz TWI, to (nie testowałem) można raz ustalić adres, a potem wysyłać strumień ustawień pinów. To powinno dać 2x większą częstotliwość.
    Tak na serio to chyba łatwiej użyć PWM w Arduino, a resztę pinów zrekompensować za pomocą PCL8574.
    Jak napiszesz co robisz to może łatwiej będzie znaleźć rozwiązanie :-). Nie wiem też co będzie jak się przetaktuje TWI :-). Wydaje mi się, że najwyżej jakieś przekłamania.
    W tym dokumencie

    http://www.atmel.com/dyn/resources/prod_documents/doc8271.pdf

    na stronie 216 zaczyna się dział o TWI. Na stronie 223 jest opis Bit Rate Generator Unit ze wzorem na szybkość transmisji. Odpowiada za niego rejestr TWBR (strona 243) i bity TWPS rejestru TWSR (strona 245). Jak znajdę trochę czasu to zrobię test na ile da się podnieść szybkość :-).

    Michał: Możesz się tez pochwalić swoimi eksperymentami :-)

  9. Mchl

    Spieszę donieść, że biblioteka zamieszczona na nettigo nie kompiluje się z bibliotekami dostarczonymi do oprogramowania Arduino 1.0. Najważniejsza zmiana, to konieczność dołączenia Arduino.h zamiast WProgram.h. Druga rzecz to zmiana nazw funkcji biblioteki TwoWire z send() na write() iz receive() na read().

  10. sprae Autor wpisu

    Mchl: Fachowość twojego zgłoszenia powala :-) Dziękujemy!

  11. Mchl

    Skoro już spędziłem kilkanaście minut nad rozwikłaniem zagadki, to czemu miałbym się nie podzielić? Dodam, że uaktualniona biblioteka jest już na stronie autora.

    Przy okazji podziękowania dla #arduino na freenode za pomoc :)

  12. Marek

    nie ma nigdzie tej biblioteki do PCF8574 a wasze linki nie działają

  13. Marek

    dobra znalazłem…dobry poradnik, dzięki, sporo się nauczyłem

  14. Marek

    szkoda, że nie można edytować komentarzy ale myślę, że w pierwszym przykładzie z diodą lepiej jest zastosować inne rozwiązanie. Przy takim połączeniu praktycznie nie widać jak dioda się zapala i gaśnie. Lepiej dać expander.pullDown() na 4 pin i do niego podłączyć katodę, a anodę dać bezpośrednio do Vcc. wtedy mamy prąd z Arduino ( jakieś 40 mA o ile dobrze pamiętam ) zamiast 100uA a PCF… .

  15. sprae Autor wpisu

    Marek: Dziękuję za komcie :-).

    W opisie podłączyłem diodę katodą do wyjścia tak, że stan niski ją zapala. Wg katalogu wyjście układu ma wtedy 25 mA.
    Jakbym podłączał anodą to racja wtedy wyjście ma wydajność jedynie 300 uA.

  16. lukasz

    Wyskakuje mi taki błąd w kompilacji:
    wiecejwejsc5 error ISO C++ forbids declaration of ‚voidsetup’ with no type
    ktoś pomoże?

  17. Darek

    Bardzo pomocne są te artykuły. Wielkie dzięki i prosimy o jeszcze więcej i częściej (;
    Dla pełnego zrozumienia mam pytanie:
    Czy zapisy w funkcji setup():
    expander.digitalWrite(4, HIGH);
    expander.pullUp(4);
    w działaniu byłyby równoważne?.

    Może warto na początku artykułu wspomnieć o wydajności prądowej portów w stanie L i H. Skapnąłem się dopiero po kilku godzinach walki zanim doczytałem w komentarzach info o 300uA (;

  18. sprae

    Darek: Dziękuję i przepraszam za brak tych informacji. Będę brał to pod uwagę w następnych wpisach.
    Myślę, że masz rację z tymi funkcjami. Przeglądałem źródło i wyszło mi, że komunikacja z układem polega tylko na odczytaniu wszystkich bitów portu, albo wysłaniu wszystkich bitów do ustawienia. Cała reszta to tylko nakładka dla komfortu użytkowników Arduino.

  19. Kuba

    Czy do tak ustawionego wejscia mozna podlaczac i odczytywac wartosci z fotorezystora? W artykule jest tylko mowa o stanach niskich i wysokich.

  20. Subman

    @Kuba
    W skrocie: nie, powiela porty cyfrowe a nie analogowe.
    A dłużej: Nie, powiela ono tylko porty cyfrowe- o wartościach ALBO logiczne 1 (stan wysoki) ALBO logiczne 0 (stan niski), a nie analogowe- dzialajace (w Arduino) na napięciach OD 0 DO 5 V.
    Mam nadzieję, że pomogłem, pozdrawiam
    Subman.

  21. sprae Autor wpisu

    To są takie wtyki na przewody jak w Arduino :) Umieściłem je tam, żeby można było łatwiej dostrzec gdzie są wyjścia.

  22. Tomek

    Witam
    Podłączyłem przerwania (metoda bardziej zaawansowana w opisie – „Przerwania – program rozszerzony”). Przerwanie uruchamiane jest przyciskiem na PIN 6 – PCF8574 i dostarczane do PIN 5 Arduino, coś w tym stylu…


    expander.pullUp(6);
    expander.enableInterrupt(5, onInterrupt);
    expander.attachInterrupt(6, onButtonOneInt, FALLING);

    Mam 2 problemy z przerwaniami:

    1) Obserwuje taki problem że, za pierwszym razem wywoływana jest tylko funkcja onInterrupt dopiero drugie naciśniecie przycisku wywołuje onButtonOneInt – ze względu na brak debugera zdebugowanie biblioteki sterującej jest trudne. Czy ktoś może napotkał problem z przerwaniami i poprawiał coś w tej bibliotece ponieważ pierwsze wywołanie przerwania chyba jest źle interpretowane przez funkcję „checkForInterrupt” ?

    2) Po wywołaniu przerwania muszę na końcu obsługi przerwania ponownie ustawić jeszcze raz: expander.pullUp(6), inaczej nie obsługuje dalej przerwań. Dodatkowo losowo gubione jest podłączenie funkcji przerwań i muszę wznawiać obsługę przerwań przez ponowny attachInterrupt – czy ktoś miał może podobne problemy?

  23. sprae

    Jeśli to nie jest spowodowane drganiami styków przełącznika to może spróbuj napisać swoją wersję. W artykule o czujniku przyspieszenia masz opis podstaw komunikacji I2C z biblioteką Wire.h, a przerwanie możesz odbierać za pomocą funkcji attachInterrupt z pinów 2 lub 3. Całość nie powinna zająć kilkunastu linii. Jakbyś miał pytania to chętnie pomogę.

  24. YonasH

    @Tomek
    Ad.1) Mam to samo. Rozwiązanie jest banalne. Otóż u mnie problem występuje tylko w czasie debugowania przez Serial. Ostateczna wersja z wykomentowanym Serialem działa od pierwszego przerwania.
    Ad.2) Nie pomogę, ale dzięki za trop, bo męczę się z tym już miesiąc! Mam tak, że gdy ustawię pullUp na pin, który przy starcie układu ma LOW, to nie wykrywa już potem HIGH.

  25. YonasH

    Jeszcze jedno. Z przerwaniami w ogóle sobie nie poradziłem. Teoria całkowicie minęła się z praktyką. Mój program wykrywał najwyżej jedno ze zdarzeń (CHANGE, LOW, FALLING, RISING). Poradziłem sobie inaczej:

    void setup()
    {
    //inicjacja PCF itp.
    lastPcf = PCF.read();
    }

    void loop()
    {
    if (pcfInterrupt)
    {
    checkPcf();
    }
    }

    void onInterrupt()
    {
    pcfInterrupt = true;
    }

    void checkPcf()
    {
    pcfInterrupt = false;

    tempPcf = PCF.read();

    for (byte i = 0; i <= 7; i++)
    {
    if (bitRead(tempPcf, i) != bitRead(lastPcf, i))
    {
    //wykryta zmiana stanu na pinie i

    //na koniec resetuję bit lastPcf
    bitWrite(lastPcf, i, bitRead(tempPcf, i));
    }
    }
    }

  26. Wydra

    Witam,
    uczę się dopiero elektroniki oraz arduino, więc proszę mnie nie kamieniować za moje błędy. Chciałbym podłączyć ekran 16bit do arduino uno, ale wiem że ma za mało pinów. Chcę zrobić oddzielnie shield (lub kupić gotowy), ale wiem że mam w UNO za mało pinów. Czy ten układ PCF8574 będzie na tyle szybki by obsłużyć ekran? Bo słyszałem że liczy się prędkość i takie powielenie pinów jest złym rozwiązaniem?

  27. sprae

    On jest raczej zbyt wolny. Lepiej poradzi sobie jakiś szybki rejestr przesuwny podłączony do interfejsu SPI.
    SPI wyciąga 16 Mbit/s, zatem dla 16 bitów będziesz mógł zmienić wartość miliona pikseli w ciągu sekundy. To teoretyczna wartość bo trzeba jeszcze szybko zatwierdzać pewnie te piksele jakimś innym pinem.

  28. marcinsud

    Witam!
    Da się w ten sposób sterować w łatwy sposób (czyli przy jak najmniejszych przeróbkach bibliotek arduino) wyświetlaczem LCD 2×16? Pytam ponieważ w moim projekcie zaczyna mi brakować portów I/O. Mam Arduino, motorshield, 2 silniki dc, 2 potencjometry (wbudowane w shield) i 2 bariery świetlne własnej roboty (każda zabiera jeden port). Muszę do tego dodać jeszcze jedną barierę, która będzie użyta do liczenia przesuwających się obiektów (tu przydatna funkcja wykrywania zmiany stanu) i lcd by wyświetlać liczbę elementów. Myślałem nad wyświetlaczem 7seg, ale jakoś portów bym nie oszczędził, a LCD daje jednak większe możliwości.
    Pozdrawiam MS

  29. coyote01

    Możesz spróbować tego: http://nettigo.pl/products/111
    Szczególnie dobrze się sprawdza gdy wykorzystujesz już jakieś urządzenia sterowane po SPI, zużywasz wtedy tylko jeden dodatkowy port do wskazywania urządzenia.

  30. marcinsud

    Czyli jeśli dobrze rozumiem z tamtym układem korzystam z przerobionej biblioteki liquidcrystal, a jednocześnie mogę korzystać z PCF8574 tak jak w tym artykule i wszystko będzie śmigać cacy po i2c?

  31. sprae Autor wpisu

    Tak powinno :) Dla pewności możesz sprawdzić czy jej funkcja print obsługuje też dane z pamięci flash.

    lcd.print(F(„ala ma kota”));

    To oszczędza zawsze trochę RAM-u i jak działa to miło :)

  32. tomee

    W temacie przerwań, bo też z tym walczyłem:
    1. expander.attachInterrupt() i expander.enableInterrupt() w ogóle nie chciały prawidłowo mi działać, losowo gubiły przerwania lub generowały po dwa (dla aktualnego i poprzedniego pinu, na przykład)
    2. przerwania w Arduino R3 są bodajże 2, 0 i 1. 0 jest przypisane do PINU 2, a 1 do (bodajże) PINU 3.
    3. wystarczy ustawić pin2 na INPUT, podciągnąć go (digitalWrite(HIGH)) i zrobić attachInterrupt(0, funkcjaPrzerwanie, FALLING)
    4. w funkcji obsługi przerwań NIE DZIAŁA delay(), Serial może powodować problemy, a licznik millis() nie będzie się inkrementował!
    5. wszystkie zmienne zmieniane w funkcji obsługi przerwania powinny być zdeklarowane jako volatile.
    6. przyciski, o ile nie podłączone do układu RC+bramki, będą powodowały bouncing który w najlepszym razie spowoduje, że odczyty będą niewiarygodne, a w najlepszym zawieszą kontroler. Jak mówiłem, delay() i millis() nie działa, więc software’owy debouncing w ten sposób odpada.
    7. działające rozwiązanie w moim przypadku wygląda tak:
    volatile counter = 0;

    funkcjaPrzerwanie() {
    detachInterrupt(0);
    counter = 1;
    }

    loop() {
    if (count == 1) {
    int stany = expander.read(); // np. B11111010 dla wciśniętych przycisków P7 i P5. Lepiej odczytać raz wszystkie stany, niż robić digitalRead() na każdym pinie, I2C do szybkich nie należy.
    zrobCosZeStanami(stany); // ta funkcja robi np. bitowy AND maską na odczycie żeby wydobyć stan danego pinu i coś z nim robi
    attachInterrupt(0, funkcjaPrzerwanie, FALLING);
    }
    8. Teraz wystarczy jeszcze trzymać boolean poprzedniego stanu pinu, uaktualniany na końcu zrobCosZeStanami(), porównywać go z aktualnym stanem i wiemy, czy przycisk został wciśnięty, czy wyciśnięty.
    9. Być może zamiast detach/attachInterrupt zadziałałoby też noInterrupts() i interrupts(), nie próbowałem.
    }

    Uff, to się naprodukowałem, myślę że nadaje się do umieszczenia jako suplement do głównego posta ;-)

    PS. PCF8574 ma taki sam adres jak MCP23008 używany w shieldach do kontroli LCD po I2C! Na to też trzeba uważać.

  33. tomee

    PS. Oczywiście B11111010 odpowiada pinom P0 i P2, nie P7 i P5.

  34. tomee

    PPS. Cholera, przed końcem loop() zabrakło:
    counter = 0;
    }
    a (if count == 1) powinien brzmieć (if counter == 1).
    Przepraszam za niechlujność.

  35. tomee

    I jeszcze jedno. Ta biblioteka zalaczona do posta jest fatalnie napisana. Przykladowo, stan rezystorow pullup/pulldown przechowuje w 2 tablicach int[8], co zuzywa nam „na dzien dobry” 32b RAMu, podczas gdy da sie to zalatwic w DWOCH bajtach.

  36. sprae Autor wpisu

    Tomee: We wszystkim masz rację i dziękuję za twoje komcie. Od jakiegoś czasu zbieram się na nowe wydanie tego wpisu z opisem niskopoziomowym. Jeśli masz jakiegoś bloga lub firmę to się pochwal chętnie o tym wspomnę :-)

  37. Milky

    Witam.
    Bardzo fajny poradnik.
    Mam pytanie o wydajność prądową wyjść. Na stronie jest podane 25mA. Czy jak dam np. 20mA x 8 wyjść to będzie ok? (steruje diody, LOW na wyjście, diody z rezystorami do VCC). Do w tej chwili mam trzy wyjścia – razem ok 30mA i po 10min układ osiąga temp „nie dotykaj mnie”…

  38. sprae

    W dokumentacji jest napisane, że cały układ może maksymalnie rozproszyszyć 400 mW mocy.
    Moc oblicza się przez P = U * I, gdzie P to moc w Watach, U to napięcie w Voltach, a I to prą w Amperach.
    Bebechy układu pobierają średnio 40 uA (mikro Amperów) co daje 0.00004 A. Układ jest zasilany napięciem 5 V. Czyli sam układ wydziela moc 5 * 0,00004 = 0,0002 W czyli 200 uW (mikro Wattów). Czyli to rzecz pomijalna (chciałem się upewnić, przepraszam za dygresję).

    Teraz podłączając do jego wyjść odbiorniki prądu, zwiększamy tą moc.
    Piszesz, że podłączyłeś do nich w sumie 30 mA. Sprawdźmy ile to daje mocy.
    5 * 0.03 = 0,15 W czyli 150 mW Chyba nie powinno sprawiać problemu.

    A gdyby to były 3 diody świecące po 20 mA, to wyjdzie:
    P = U * I = 5 * 3 * 0.02 = 0,3 W = 300 mW. Raczej też nie powinno być problemów.
    Akurat mam pod ręką ten układ i 8 LED to sprawdzę i odpiszę ci w kolejnym komciu :-)

  39. sprae

    Milky: Zrobiłem test, podłączyłem układ i do niego 8 LEDów przez rezystory 220 Ohm. Katody do wyjść układu, a anody do zasilania 5V. Układ też zasilam z wyjścia 5V. Nie czuję, żeby coś się grzało. Układ jest cały czas chłodny, mimo, że obciążyłem go prądem 160 mA. Zatem moc w układzie powinna być na poziomie 5 * 0.16 = 0.8 W = 800 mW.
    Mam układ o dokładnym oznaczeniu PCF8574F firmy NXT.
    Nie wiem co się stało u ciebie. Może podłączyłeś zasilanie do 3.3V?
    Ale jeśli zrobiłeś wszystko dobrze to układ może być uszkodzony.
    Jak by co zrób dobrą fotkę i podeślij.

  40. Milky

    Połączyłem jeszcze raz, 5 LED, razem zmierzyłem 50mA, zrobiłem piękne zdjęcie, 30sek i temperaturka :-) Mam PCF8574AP Philips. Ale dzięki Twojej inspiracji udało mi się dojść co jest nie tak! To nie układ jest uszkodzony, tylko płytka stykowa ma gdzieś coś zwarte albo co bo jak przesunąłem układ w inne miejsce to wszystko ok!!! Podsumowując, dziękuje!

  41. Milky

    Jedne problemy znikają, inne się pojawiają :-) Biblioteka PCF8574 nie kompiluje się razem z SoftwareSerial… konflikt timerów/przerwań jeśli dobrze rozumiem komunikaty. Da się to obejść?

  42. suchy

    Witam. U mnie na wyjściach układu PCF8574 jest napięcie 1,6V. Czym może być to spowodowane?

  43. sprae

    Sprawdziłem u siebie i zmierzyłem napięcie na jednym z wyjśc P względem GND, gdy układ pracował.
    W stanie wysokim napięcie było bliskie 5 V, a w stanie niskim 0 V.
    Może zapomniałeś podłączyć zasilania do układu.

  44. suchy

    złożyłem układ jeszcze raz i działa. Widocznie płytka stykowa w niektórych miejscach nie łączyła.

  45. Zabq

    Witam mam problem z dwoma układami, kiedy mam podłączone np. diody do obu PCF świecą bez zarzutu na obu, natomiast przy bardziej rozbudowanej wersji:
    do jednego mam podłączone diody (wyjścia) i przewody, które się zewrą np. w wodzie (wejścia)i zapalą ww. diody, pod drugi układ podłączony mam wyświetlacz LCD 16×2. Przy pracy pojedynczego układu oba rozwiązania działają bez zarzutu, natomiast wspólnie niby jest OK, wyświetlacz działa wyświetla to co ma, ale czasem wyskakują błędy(blubry) tak jakby wzrosła temperatura i wczytuje niewłaściwą część kodu lub po zwarciu przewodów z układu nr 1 wszystko wariuje (wyświetlacz wyświetla bzdury diody na pierwszym układzie się nie zapalają).
    Proszę mnie potraktować pobłażliwie, jestem nowicjuszem, ryłem u wujka G odnośnie dwóch układów, ale nie znalazłem pomocnej informacji.
    Podłączenie układów zgodnie z opisem powyżej.
    Przy okazji super przekazana wiedza.
    Poniżej kod, w którym coś mam pewnie nie tak.

    #include
    #include
    #include
    #include //dotyczy czujnika temperatury
    #include //dotyczy czujnika temperatury

    #define ONE_WIRE_BUS 8 //dotyczy czujnika temperatury
    OneWire oneWire(ONE_WIRE_BUS); //dotyczy czujnika temperatury
    DallasTemperature sensors(&oneWire); //dotyczy czujnika temperatury

    #include //wyświetlacz LCD
    PCF8574 expander1; //ekspander
    PCF8574 expander2; //ekspander

    LiquidCrystal_I2C lcd1(33, 16,2); //wyświetlacz LCD

    void onInterrupt() //ekspander
    {
    expander1.checkForInterrupt(); //ekspander
    expander2.checkForInterrupt(); //ekspander
    } //ekspander

    void temp_LCD ()
    {
    if (sensors.getTempCByIndex(0) < 26 && sensors.getTempCByIndex(1) < 26
    && sensors.getTempCByIndex(2) < 26 && sensors.getTempCByIndex(3) < 26) {
    lcd1.clear();
    lcd1.print("T1: ");
    lcd1.print(sensors.getTempCByIndex(0));
    lcd1.print("*C / T2: ");
    lcd1.print(sensors.getTempCByIndex(1));
    lcd1.print("*C");
    lcd1.setCursor(0,1);
    lcd1.print(" T3: ");
    lcd1.print(sensors.getTempCByIndex(2));
    lcd1.print("*C / T4: ");
    lcd1.print(sensors.getTempCByIndex(3));
    lcd1.print("*C");
    // scroll 13 positions (string length) to the left
    // to move it offscreen left:
    for (int positionCounter = 0; positionCounter < 10; positionCounter++) {
    // scroll one position left:
    lcd1.scrollDisplayLeft();
    // wait a bit:
    delay(550);}

    for (int positionCounter = 0; positionCounter < 10; positionCounter++) {
    // scroll one position right:
    lcd1.scrollDisplayRight();
    // wait a bit:
    delay(550);
    }

    // scroll 16 positions (display length + string length) to the left
    // to move it back to center:
    for (int positionCounter = 0; positionCounter < 10; positionCounter++) {
    // scroll one position left:
    lcd1.scrollDisplayLeft();
    // wait a bit:
    delay(750);
    }

    // wait a bit:
    } else {
    lcd1.clear();
    lcd1.print("POZAR!!!POZAR!!!");
    lcd1.setCursor(0,1);
    lcd1.print("POZAR!!!POZAR!!!");
    // Turn off the display:
    lcd1.noDisplay();
    delay(100);
    // Turn on the display:
    lcd1.display();
    delay(100);
    tone(9, 100, 1000);}}

    void temp() //dla sprawdzenia temperatur na ekranie

    {
    sensors.requestTemperatures();
    Serial.println("");
    delay(5000);
    Serial.print("Czujnik-1: ");
    Serial.println(sensors.getTempCByIndex(0));

    Serial.print("Czujnik-2: ");
    Serial.println(sensors.getTempCByIndex(1));

    Serial.print("Czujnik-3: ");
    Serial.println(sensors.getTempCByIndex(2));

    Serial.print("Czujnik-4: ");
    Serial.println(sensors.getTempCByIndex(3));
    }

    void powodz(){
    // sprawdzanie czy na pinie BUTTON jest stan niski
    if (expander1.digitalRead(0) == LOW)
    {
    // jesli tak to zapala diode LED
    expander1.digitalWrite(4, LOW);
    }
    else
    {
    // jesli nie to wylacza
    expander1.digitalWrite(4, HIGH);
    }
    // sprawdzanie czy na pinie BUTTON jest stan niski
    if (expander1.digitalRead(1) == LOW)
    {
    // jesli tak to zapala diode LED
    expander1.digitalWrite(5, LOW);
    }
    else
    {
    // jesli nie to wylacza
    expander1.digitalWrite(5, HIGH);}
    // sprawdzanie czy na pinie BUTTON jest stan niski
    if (expander1.digitalRead(2) == LOW)
    {
    // jesli tak to zapala diode LED
    expander1.digitalWrite(6, LOW);
    tone(9, 100, 20);
    }
    else
    {
    // jesli nie to wylacza
    expander1.digitalWrite(6, HIGH);
    }}

    void setup()
    {

    Serial.begin(9600); //dotyczy czujnika temp
    sensors.begin(); //dotyczy czujnika temp

    pinMode(8, OUTPUT);
    digitalWrite(8, LOW);

    expander1.begin(0x20); //expander
    expander2.begin(0x21); //expander
    pinMode(2, INPUT); //expander
    digitalWrite(2, HIGH); //expander
    expander1.enableInterrupt(2, onInterrupt); //ekspander

    expander1.pinMode(0, INPUT);
    expander1.pinMode(1, INPUT);
    expander1.pinMode(2, INPUT);
    expander1.pinMode(3, INPUT);
    expander1.pinMode(4, OUTPUT);
    expander1.pinMode(5, OUTPUT);
    expander1.pinMode(6, OUTPUT);
    expander1.pinMode(7, OUTPUT);
    expander1.pullUp(0);
    expander1.pullUp(1);
    expander1.pullUp(2);
    expander1.pullDown(3);
    expander1.pullDown(4);
    expander1.pullDown(5);
    expander2.pinMode(0, OUTPUT);
    expander2.pinMode(1, OUTPUT);
    expander2.pinMode(2, OUTPUT);
    expander2.pinMode(3, OUTPUT);
    expander2.pinMode(4, OUTPUT);
    expander2.pinMode(5, OUTPUT);
    expander2.pinMode(6, OUTPUT);
    expander2.pinMode(7, OUTPUT);

    lcd1.init();
    lcd1.backlight();

    }

    void loop()
    {
    powodz();
    temp();
    temp_LCD();

    }

    Dziękuję

  46. sprae

    Hej. Przeanalizowałem twój kod i wydaje mi się, że wystarczy wywalić linię:

    expander1.enableInterrupt(2, onInterrupt); //ekspander
    i funkcję onInterrupt.
    A wszystkie ciągi tekstowe w stylu:
    Serial.print(„Czujnik-1: „);
    zamienić na:
    Serial.print(F(„Czujnik-1: „));
    także te od LCD
    Wtedy będą zajmowały mniej pamięci.

    Wydaje mi się, że problem dotyczył przerwania. Gdy je wywołujesz procesor zajmuje sobie kawałek pamięci na jego obsługę, aż skończy się jego procedura. Problem polega na tym, że jak zwierasz styki to powstają setki impulsów przerwań od zakłóceń przy zwieraniu i zajmowane są setki takich fragmentów pamięci, które mogą dojść do pamięci danych programu i wszystko wysypać tak jak to opisałeś.
    Dlatego powyżej poradziłem usunięcie tego fragmentu z przerwaniem, który o ile się nie mylę nic nie robi.
    Możesz też odłączyć przewód od pinu 2.

    Dodanie F(„napis”) sprawia, że program będzie trzymał napisy w pamięci Flash i nie będzie zajmował pamięci RAM od danych. W ten sposób będzie mniejsze ryzyko przepełnienia.

    Powodzenia :-)

  47. Zabq

    Wielkie dzięki za fachowa pomoc, dziś po robocie dokonam testów.

    pozdrawiam

  48. sprae

    Fajnie, że ci się podoba.
    Szkoda, że to F() nie jest dobrze rozpropagowane. Bo pomaga wielu osobom.

  49. Zabq

    Wprowadziłem zasugerowane zmiany, co przyniosło pewne efekty jednak muszę jeszcze pozmieniać kod, bo nie działa tak jak bym sobie życzył. Jeżeli sobie nie będę wstanie poradzić poproszę o pomoc.
    Bardzo dziękuję i pozdrawiam

  50. kon

    chciałem spróbować z „nowych” pinow cyfrowych uzyc funkcji tone. nie potrafie w zaden sposob nazwac pinu, ktory mialby tworzyc ton o zadanej czestotliwosci. czy taka operacja jest mozliwa?

  51. sprae

    Silnik pobiera dużo prądu. Najlepiej go podłączyć przez jakiś tranzystor. Najłatwiej użyć tranzystora MOSFET, bo przekazuje znaczną moc i łatwo się go podłącza. :-)

  52. sprae

    Sam PCF przekazuje niewielką moc, która w stanie niskim ledwie starcza na zasilanie diody LED.

  53. jacc

    Witam wszystkich.
    Pomimo wielu lat dłubania w elektronice do czynienia z Arduino mam zaledwie od trzech tygodni. Prawdę mówiąc wszedłem w ten temat dzieki rekomendacji kolegi z Włoch, który w konkretnym przypadku zaproponował do sterowania użycie właśnie Arduino. Już pojaśniam…
    Planujemy uruchomić tablicę zbudowaną z klapkowych modułów informacyjnych zdemontowanych i odzyskanych przeze mnie(legalnie) z dworców kolejowych. Tzw. wózki tych modułów mają dość skomplikowane sterowanie i nie ma sensu jego modyfikacja, więc nie będę się nad tym rozpisywał. Dużo prościej, a przede wszystkim – elastyczniej będzie użyć do tego Arduino. Każdy tzw. „wózek”, czyli jeden moduł (litera, cyfra, rodzaj pociągu, kierunek, itd.) ma jednak tarczę w dwoma transoptorami ustawionymi tak, że dają impulsy na zmianę (odległość między nacięciami tarczy sterujacej ma się do odległości między transoptorami, jak 1:1,5). Każdy impuls obojętnie z którego transoptora, to kolejna klapka. Silnik 230V AC uruchamiany jest przez sygnał na optotriaki do momentu, gdy transoptory położenia odczytają odpowiednio zadeklarowaną pozycję. Dla przykładu, aby wyświetlić z położenia zerowego literkę A uruchamia się silnik sygnałem na optotriak i czeka aż przyjdzie z pierwszego transoptora jeden impuls, B uruchamia się podobnie, ale czeka, aż przyjdzie jeden impuls z transoptora 1 i jeden z transoptora 2. C, to impulsy z transoptorów: 1,2,1. D, to impulsy z transoptorów: 1,2,1,2 itd. Klapek jest 40. Jest jeszcze trick z położeniem zero. Jest to dodatkowe nacięcie, które ustawione jest tak (w połowie pomiedzy dwoma „normalnymi” nacięciami), że w położeniu stop włączają się obydwa transpotory jednocześnie. Utrudnieniem jest fakt, że takie położenia „zero” są oczywiście dwa, bo najpierw to dodatkowe nacięcie przechodzi przez transoptor 1, a potem przez 2. To drugie zero zapewne da się zignorować programowo. Dlaczego na tym forum o tym piszę? Bo uruchomienie prostej tablicy zbudowanej z dwóch wierszy po 16 wózków da razem 96 koniecznych pinów (po 2 na wózek do odczytu transoptorów i po jednym do włacznia silnika, czyli razem 3, co przy 32-óch wózkach da właśnie 96), z czego 64 będą odczytywać impulsy z transoptorów, a 32 sterować silnikami. Myślę zatem o uruchomieniu tego w oparciu o ekspandery. Projekt nie jest banalny i dość ambitny. Czy znajdzie się ktoś, kto się przyłączy do jego uruchomienia pod kątem oprogramiowania? Moje umiejętności w zakresie programowania Arduino w takim przypadku są jeszcze niewystarcząjace (ale szybko sie uczę).;-)
    Pozdrawiam

  54. sprae

    jacc: Mam taki pomysł.
    Każdy wózek sterować kontrolerem ATTiny45. To taki mały kontroler w 8 nóżkowej obudowie.
    2 nogi portu do odbierania sygnałów fotoelementów
    1 noga do sterowania silnikiem
    2 nogi do odbierania danych magistralą i2c.

    Wszystkie wózki można połączyć razem tą magistralą, na której końcu będzie Arduino odbierające dane z komputera po USB i rozdzielające je na poszczególne wózki.

    I2C zapewni to, że wszystko będzie połączone tylko 2 przewodami danych + GND + Vcc. Każdy wózek będzie miał swój adres w magistrali.

  55. ard

    Interesujący artykuł. Zabrakło mi w nim tylko informacji o tym, że w pierwszym przykładzie z diodą prąd trzeba brać z procesora a nie z PCF8574, ponieważ w przeciwnym przypadku dioda prawie nie świeci.

    Po zapoznaniu się z podanymi przykładami powróciłem do jednego z nich – przycisku z rezystorem pull up. Podłączyłem ten rezystor na płytce stykowej (w programie nie stosowałem expander.PullUp()), przycisk podłączyłem do jednego z wejść PCF8574. Jednokrotne przyciśnięcie przycisku powoduje, że przechodzi on w stan LOW i pomimo puszczenia przycisku pozostaje on w tym stanie. Dodam, że gdy ten sam przycisk (z tak samo podłączonym rezystorem i identycznym programem) podłączę do dowolnego pinu cyfrowego Arduino, to działa on tak, jak powinien (powraca ze stanu LOW do stanu HIGH po puszczeniu przycisku). Co robię źle? Gdzie znajduje się błąd?

    Z góry dziękuję za odpowiedź.

    Pozdrawiam

  56. sprae

    Pin wyjściowy PCF w stanie niskim potrafi przepuścić do 25 mA, a w stanie wysokim tylko 300 uA. Dlatego lepiej podłączać mu diody katodą do wyjścia, a anodą do zasilania.

    Nie rozumiem czemu stosowałeś rezystor skoro układ ma wbudowane PullUp. Wystarczy ustawić mu stan wysoki na wyjściu które chcesz używać.

  57. ard

    Opornik podłączyłem tylko po to, żeby zobaczyć czy tak też będzie działać ;-)

    Po paru dniach przerwy powróciłem do PCF i próbowałem zrobić przykład ze strony http://nettigo.pl/products/121. Natrafiłem identyczny problem z tym, który opisywałem poprzednio – pierwsze przyciśnięcie przycisku powoduje, że zmienia się jego stan, natomiast nie powraca on do stanu początkowego po puszczeniu przycisku (wejście się zatrzaskuje). Prosiłbym o sugestię, co robię źle.

  58. sprae

    Napisałem sobie prosty kod na odczyt i zapis do portu PCF.
    https://gist.github.com/sprae/4999861
    Za pomocą „pcfWrite” w funkcji „setup” ustawiam wszystkie piny PCF w stan wysoki – argument 0b11111111 (wszystkie bity na 1)
    Potem w funkcji „loop” odczytuje za pomocą „pcfRead” stany pinów i podglądam je w „Monitorze portu szeregowego” jako wartości binarne.
    Za każdym razem, kiedy dotknę którymś pinem PCF do GND, widzę, że stan bitu zmienia się z 1 na 0. Kiedy przestaję zwierać do GND, wartość bitu wraca do 1.

  59. w

    Witam,
    Mam arduino due i podłączyłem sobie PCF8574p do arduino tak jak w przykladzie „Ustawienie stanu pinów” z diodą
    zainstalowałem wcześniej starszą wersje bibloteki PCF8574.h i zmieniłem WProgram.h na Arduino.h a potem wgrałem nowszą wersje bibloteki v1 i takie same błędy mi cały czas wyskakują:
    In file included from D:\Arduino\arduino-1.5.2\libraries\PCF8574\PCF8574.cpp:2:
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h:4: error: ‚PCMSK0’ was not declared in this scope
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h:5: error: ‚PCMSK1’ was not declared in this scope
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h:6: error: ‚PCMSK2’ was not declared in this scope
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h: In function ‚void PCattachInterrupt(uint8_t, void (*)(), int)’:
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h:23: error: invalid conversion from ‚Pio* const’ to ‚uint8_t’
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h:49: error: ‚PCICR’ was not declared in this scope
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h: In function ‚void PCdetachInterrupt(uint8_t)’:
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h:54: error: invalid conversion from ‚Pio* const’ to ‚uint8_t’
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h:70: error: ‚PCICR’ was not declared in this scope
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h: In function ‚void PCint(uint8_t)’:
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h:83: error: base operand of ‚->’ is not a pointer
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h: At global scope:
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h:108: error: expected constructor, destructor, or type conversion before ‚(‚ token
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h:111: error: expected constructor, destructor, or type conversion before ‚(‚ token
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\/PCint.h:114: error: expected constructor, destructor, or type conversion before ‚(‚ token
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\PCF8574.cpp: In member function ‚void PCF8574::checkForInterrupt()’:
    D:\Arduino\arduino-1.5.2\libraries\PCF8574\PCF8574.cpp:112: error: ‚sei’ was not declared in this scope

    co może być tego powodem?

  60. sprae

    Mało która biblioteka jest zgodna z IDE 1.5. Można spróbować wrzucić pliki do podkatalogu sam w katalogu libraries.

    Ja polecam użyć mojego sposobu, bo jest uniwersalny.
    https://gist.github.com/sprae/4999861
    Tylko pamiętaj, żeby w DUE zasilać układ napięciem 3,3V i podłączyć SDA, SDL do przeznaczonych do tego pinów.

  61. w

    Możesz mi powiedział jak mam to zastosować dla przykladu z diodą? bo niestety nie podołałem. początkujący to początkujący elektronik ;\

  62. sprae

    pcfWrite ustawia wszystkie bity portu układu na raz.
    Zatem jeśli dioda jest podłączona do 4 bitu (licząc od 0) to fragment w loop powinien wyglądać jakoś w tym stylu.

    pcfWrite(0b00000000); // Włączenie
    delay(500); // czekanie 0.5 s
    pcfWrite(0b00010000); // wyłączenie
    delay(500);

    Ustawienie bitu na 1 wyłącza diodę, a na 0 włącza.
    0b00000000 to zapis w systemie binarnym jego cyfry po 0b odpowiadają kolejnym bitom od 7 do 0

    00010000 – stan bitow
    76543210 – numer bitow

  63. sprae Autor wpisu

    Dziękuję. Jak by co odpisałem ci na swoim blogu pod dwoma wpisami :-)

    Nie wyrzucaj DUO, jeszcze się dużo naumiesz.

  64. wysior

    Mam problem. Wpisując na wyjście LOW, dioda mi się zapala i na odwrót, jak dam stan HIGH, to gaśnie…

    O co tu może chodzić?

  65. sprae

    Tak powinno być. Dioda najlepiej działa kiedy anodę ma podłączona do zasilania, a katodę przez rezystor do wyjścia układu.
    Czyli musisz wyjście ustawić na przeciwny stan niż zasilanie. LOW to tak jakby GND, a HIGH to tak jakby 5V.

  66. wysior

    Ahaaaa. No tak do testów ma to zrobione. Bo docelowo chcę tym układem sterować przekaźnikami. To z kolei mam inny problem w związku z tym, że program pętli nie chce działać. Chociażby to włączanie skody led po wciśnięciu przycisku. Po wciśnięciu się włącza, ale nie wyłącza… O co mogło być nie tak?

  67. Radziu

    Witam

    Właśnie buduję urządzenie, które będzie się składało z 2 modułów połączonych przewodem. W jednym będzie Mikrokontroler, zasilacz i układ wykonawczy, oraz wejścia z kilku czujników. W drugim będzie LCD, kilka przycisków, kontrolki LED i czujnik temperatury DS18B20. Zależy mi by całość była połączona jak najmniejszą ilością żył, i tu idealne rozwiązanie PCF8574, bo całość połączę za pomocą 4 żył. W prototypie już mam działający LCD, jutro będę podłączał na drugim PCF przyciski, LED, no i… pytanie: Czy czujnik DS18B20 też da się obsłużyć w ten sposób?

    Pozdrawiam

  68. sprae Autor wpisu

    Raczej nie. PCF jest zbyt wolny by odczytać sygnały DS. Ale możesz podpiąć do I2C jakiś mały kontroler typu ATTiny i do niego podłączyć DS.

  69. Compiesto

    Do rozszerzenia wyjść pwm można użyć 74HC595. Programowo można cuda zrobić o ile procek wydoli wysłać dane w czasie.

  70. Manwe

    Cześć,

    podłączyłem układ z diodą (1wszy praktyczny przykład) i niestety moja dioda nie mruga w żaden sposób. Co mogłem zrobić źle? Dioda ani nie mruga, ani się nie zapala.

  71. Manwe

    ok. Jak to w życiu bywa: problem rozwiązuje się sam, kiedy poprosisz o pomoc. Mam mega adk, i tutaj sda i scl są na pinie 20 i 21 a A4 i A5

  72. Manwe

    Jeszcze jedno pytanie.

    W przykładzie z obsługą diody LED anodę podłączamy pod anodę (poprzedzoną rezystorem) a katodę pod masę. Diodę zapalamy poprzez ustawienie digitalWrite(pin,HIGH)

    Dla czego jeśli dla przykładu 1wszego, diodę podłączę anodą do P4, a katodę do masy to dioda nie mruga?

  73. sprae Autor wpisu

    Ten układ scalony ma wydajny tylko stan LOW. Wtedy można jego wyjście obciążyć do 25 mA. Dlatego widać świecenie kiedy katoda jest do pinu przez rezystor, a anoda do zasilania.

    Kiedy podłączasz odwrotnie, to przez stan HIGH na pinie płynie za mały prąd, żeby zaświecić diodę (chyba 200 uA).
    To jest 100x za mało.

  74. vidmo

    w bibliotece jest napisane, że układ NIE MA rezystorów pull up i down, a funkcje pullUp i pullDown są tylko dla kompatybilności wstecznej i nie robią niczego

  75. Radek

    Witam,
    mam pytanie , jak odwołać się do układu jak do portu, a nie do poszczególnych pinów ?

  76. kk

    Prawdopodobnie jest to bardzo głupie pytanie, ale dlaczego mam odwrócone stany? Ustawiając LOW dioda się zapala. tak samo set() gasi wszystkie diody, a clear() wszystkie je zapala.

  77. sprae Autor wpisu

    Bo anoda diody jest podłączona do zasilania. Sterujesz katodą. Dioda się świeci, jeśli na katodzie jest GND. Stan wysoki sprawia, że z pinu wychodzi zasilanie, a stan niski sprawia, że wychodzi GND.

  78. Mac

    Wykorzystuję PCF wylącznie jako output. Wygodnie mi wysyłać do PCFa dziesiętną wartość z zakresu 0-8 – i pięknie mi na czterech pinach ustawia wyjścia do dalszego sterowania.
    Pozostaje pytanie – jak ustawiać niezależnie pozostałe piny – nie wpływając na ustawienia tych „czterech”.
    Myślałem o odczytaniu stanu PCFa, dodaniu wartości i wysłaniu z powrotem do PZF – ale może jest jakaś mądrzejsza metoda.
    HElp please.

    Na marginesie – jak zamienić w Arduino wartość dziesiętną (np 2) na binarną (B00000010), tak, żeby ją wysłać do PCF (po ewentualnych binarnych manipulacjach)

  79. sprae Autor wpisu

    są operacje logiczne
    & – and,
    | – or,
    ^ – xor,
    ! – not,

  80. sprae Autor wpisu

    Można zrobić zmienną globalną
    byte state;

    a potem w programie zmieniać połówki bajtu
    (bity 0-3)
    state |= value;

    (bity 4-7)
    state |= (value << 4);

  81. Artur

    Adres układu PCF jest ośmiobitowy.
    To biblioteka jest głupio napisana, że takie kwiatki wychodzą.
    Przyjmując specyfikację samego I2C można próbować stosować naciąganą teorię o siedmiu bitach, ale po magistrali przesyłane jest osiem. W związku z tym powyższe adresy zastosowane bez powyższej biblioteki nie będą działać.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *