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.
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ć.
Ś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.
@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)
Ś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?
Dziękuję! Kilka dni temu dostałem paczkę/list z arduino uno smd i starter kitem i zastanawiałem się jak to popodłączać.
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 :-)
coyote01: Gratuluję spostrzegawczości :-) i dziękuję za zwrócenie uwagi. Już poprawiłem.
@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?
@sprae Dziekuję :-)
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 :-)
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().
Mchl: Fachowość twojego zgłoszenia powala :-) Dziękujemy!
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 :)
nie ma nigdzie tej biblioteki do PCF8574 a wasze linki nie działają
dobra znalazłem…dobry poradnik, dzięki, sporo się nauczyłem
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… .
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.
Wyskakuje mi taki błąd w kompilacji:
wiecejwejsc5 error ISO C++ forbids declaration of 'voidsetup’ with no type
ktoś pomoże?
brakuje Ci spacji między void a setup
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 (;
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.
Czy do tak ustawionego wejscia mozna podlaczac i odczytywac wartosci z fotorezystora? W artykule jest tylko mowa o stanach niskich i wysokich.
@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.
https://starter-kit.nettigo.pl/2012/02/akcelerometry-zyroskopy-i-kompasy-czyli-badanie-polozenia-z-arduino-cz-1/#comment-3019 tutaj więcej napisałem o tym co trzeba, jeśli brakuje wejść analogowych.
Dzięki Subman :-)
Przepraszam, ale jestem bardziej niż zielony w tym. Co to jest?: http://storino.pl/p/CHr2K (screenshot)
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.
Mogłem sie domyślić. Dzięki za odpowiedź.
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?
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ę.
@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.
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));
}
}
}
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?
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.
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
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.
Marcinsud: Jasne, że możesz nawet cała infrastruktura w sofcie jest gotowa :)
http://garagelab.com/profiles/blogs/tutorial-lcd-using-only-2-arduino-pins-with-pcf8574-and-i2c
http://playground.arduino.cc/Code/LCDi2c
http://hmario.home.xs4all.nl/arduino/LiquidCrystal_I2C/
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?
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 :)
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ć.
PS. Oczywiście B11111010 odpowiada pinom P0 i P2, nie P7 i P5.
PPS. Cholera, przed końcem loop() zabrakło:
counter = 0;
}
a (if count == 1) powinien brzmieć (if counter == 1).
Przepraszam za niechlujność.
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.
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ę :-)
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”…
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 :-)
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.
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!
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ść?
Zależy co chcesz zrobić. Jeśli tylko sterować wyjściami to ja to zrobiłem tak:
https://gist.github.com/sprae/4999861
do funkcji pcfWrite przekazujesz wartość binarną wszystkich wyjść. W programie dałem 0 czyli wszystkie wyjścia na LOW.
Oooo… czyli jest nadzieja. Super! Zaraz wypróbuje. Dzięki!
Działa. Idealnie! Dzięki.
Ja też dziekuję
Witam. U mnie na wyjściach układu PCF8574 jest napięcie 1,6V. Czym może być to spowodowane?
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.
złożyłem układ jeszcze raz i działa. Widocznie płytka stykowa w niektórych miejscach nie łączyła.
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ę
Odpowiem ci wieczorem, ale już zasięgiem się twoim problemem :-)
Dziękuję i będę czekał
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 :-)
Wielkie dzięki za fachowa pomoc, dziś po robocie dokonam testów.
pozdrawiam
Fajnie, że ci się podoba.
Szkoda, że to F() nie jest dobrze rozpropagowane. Bo pomaga wielu osobom.
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
Ok, Powodzenia :)
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?
Witam! Zastanawiałem się, czy ten układ można wykorzystać jako przekaźnik do np. silnika, chodzi mi o np taki układ http://img541.imageshack.us/img541/3180/93248650.jpg , tutaj dioda zamiast silnika, czy coś takiego zadziała i czy przypadkiem nie zniszczy Arduino? Z góry dzięki za odpowiedź :)
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. :-)
Sam PCF przekazuje niewielką moc, która w stanie niskim ledwie starcza na zasilanie diody LED.
Rozumiem ;)
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
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.
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
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ć.
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.
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.
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?
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.
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 ;\
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
Wielkie dzięki :) a już chciałem wyżucać moje arduino duo :P
Dziękuję. Jak by co odpisałem ci na swoim blogu pod dwoma wpisami :-)
Nie wyrzucaj DUO, jeszcze się dużo naumiesz.
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ć?
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.
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?
Pokaż program albo jego fragment to będzie nam łatwiej.
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
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.
Do rozszerzenia wyjść pwm można użyć 74HC595. Programowo można cuda zrobić o ile procek wydoli wysłać dane w czasie.
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.
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
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?
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.
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
Witam,
mam pytanie , jak odwołać się do układu jak do portu, a nie do poszczególnych pinów ?
Przepraszam, nie doczytałem, już wszystko jasne
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.
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.
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)
są operacje logiczne
& – and,
| – or,
^ – xor,
! – not,
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);
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ć.
Witam. Jeśli dobrze przeanalizowałem tekst nikt nie napisał o to o co ja chcę zapytać.
Zwracam uwagę, że jestem amatorem.
Mam PCFa wykorzystanego na maxa jeśli chodzi o sztuki (i wersje A co razem daje 128 We/Wy).
Chodzi o to, że próbuję zrobić sterowanie światłem w mieszkaniu. Wszystko jako tako działa ale kiedy robię reset wszystkie stany przechodzą z LOW do HIGH bo tak mam w setupie ustawione (HIGH domyślnie). Czyli wszystkie światłą w mieszkaniu mi mrugną. Steruje optotriakiem. Próbowałem wywołać funkcję SET ale nic nie daje, efekt ten sam (aniżeli manualnie po kolei). Zawsze biblioteka wywołuje wpierw sama od siebie LOW.
Zerkłem na moje polskie oko do datashteet i widze jakiś kondensator pomiędzy A0 i VCC i choć wydaje mi się to to tylko jeden przypadek tam opisany to jednak może mogłem gdzieś coś dać ? W TLC5940 jak mnie pamięć nie myli trzeba było wlutować opornik na jednym z wejść aby tego problemu nie było.
O ile reset robi się rzadko o tyle w przypadku ciągłych zmian i aktualizacji na początku robi się to irytujące kiedy cały sprzęt wariuje. Proszę o podpowiedź :)
Jest poważny problem z Arduino IDE 1.8.2 i płytka Leonardo oraz obsluga pcf8574.
Na dzień dzisiejszy nie znalazłem żadnej biblioteki która obsługiwałaby expandera – IDE wyrzuca błąd
PCF8574/PCint.h:3:50: error: 'PCMSK1′ was not declared in this scope
volatile uint8_t *port_to_pcmask[] = { &PCMSK0, &PCMSK1, &PCMSK2 }; – przy uzyciu biblioteki z GitHub pobranej.
Po dwóch dniach bezowocnej walki – wracam do IDE 1.7 – tutaj wszystko dziala dobrze -ten sam plik do kompilacji, ta sama biblioteka – i tutaj działa.
Jeśli ktoś wie jak to obejść – proszę o kontakt.
@Artur: Ostatnio bit pakietu, którym wysyłany jest adres to wybranie czy dane mają być do odczytu czy do zapisu. Typ argumentu to byte/uint8_t. Myślę, że na jakimś poziomie biblioteki jest on ucinany/nadpisywany do 7 bitów.
@lukas: Wydaje mi się, że układ ustawia się na LOW, kiedy używasz metody „pinMode”. Spróbuj z nich zrezygnować i używać samego ustawiania stanu.
@marek: Próbowałeś użyć samej biblioteki Wire.h? https://www.arduino.cc/en/Reference/Wire
Jeśli nie, to chętnie ci pomogę :-)
@Łukasz Tretyn
Dziękuje za odpowiedź. Z uwagi na harmonogram prac – w ciągu 10 dni sprawdzę w/w bibliotekę.
Mam pewne zastrzeżenia ale nie będę zapeszał.
Jeszcze raz dziękuje za odzew.
A jak to ugryźć z ESP32 DEVKiT C? Trochę za mało mam wolnych pinów (8 relay switch-y) a potrzebuję 16. Ktoś pomoże? Mam już zakupiony moduł PCF8574 tylko pod SCL i SDA mam podpięty już LCD. Bardzo proszę apaczy o pomoc bo nie wiem jak do tego się zabrać
Jeśli jeszcze tematu nie ugryzłeś, to do szyny I2C możesz podłączyć jednocześnie wiele urządzeń. Kwestia adresu. Jeśli zarówno Twój PCF jak i ten w LCD jest taki sam (bo są LCD z PCF8574 i PCF8574A różniącymi się adresami bazowymi) to pozostaje zmiana adresu w PCFie którym chcesz sterować przekaźnikami. Jeśli to goły scalak, to są to piny A0-A2. Podciągnij je odpowiednio pod GND i Vcc, by uzyskać różne adresy. Sprawdź kartę katalogową po szczegóły. Jeśli korzystasz z gotowego modułu (np takiego https://nettigo.pl/products/modul-pcf8574-8-bitowy-port-sterowany-i2c) to masz tam DIPswitche do przełączania adresu.