Skocz do zawartości

Przeszukaj forum

Pokazywanie wyników dla tagów 'Raspberry Pico'.

  • Szukaj wg tagów

    Wpisz tagi, oddzielając przecinkami.
  • Szukaj wg autora

Typ zawartości


Kategorie forum

  • Elektronika i programowanie
    • Elektronika
    • Arduino i ESP
    • Mikrokontrolery
    • Raspberry Pi
    • Inne komputery jednopłytkowe
    • Układy programowalne
    • Programowanie
    • Zasilanie
  • Artykuły, projekty, DIY
    • Artykuły redakcji (blog)
    • Artykuły użytkowników
    • Projekty - DIY
    • Projekty - DIY roboty
    • Projekty - DIY (mini)
    • Projekty - DIY (początkujący)
    • Projekty - DIY w budowie (worklogi)
    • Wiadomości
  • Pozostałe
    • Oprogramowanie CAD
    • Druk 3D
    • Napędy
    • Mechanika
    • Zawody/Konkursy/Wydarzenia
    • Sprzedam/Kupię/Zamienię/Praca
    • Inne
  • Ogólne
    • Ogłoszenia organizacyjne
    • Dyskusje o FORBOT.pl
    • Na luzie

Kategorie

  • Quizy o elektronice
  • Quizy do kursu elektroniki I
  • Quizy do kursu elektroniki II
  • Quizy do kursów Arduino
  • Quizy do kursu STM32L4
  • Quizy do pozostałych kursów

Szukaj wyników w...

Znajdź wyniki, które zawierają...


Data utworzenia

  • Rozpocznij

    Koniec


Ostatnia aktualizacja

  • Rozpocznij

    Koniec


Filtruj po ilości...

Data dołączenia

  • Rozpocznij

    Koniec


Grupa


Imię


Strona

Znaleziono 7 wyników

  1. Pytanko jak w temacie: ktoś to może uruchomił albo zna działającą bibliotekę? Mi się jakoś nie udaje (tzn. moduł działa ślicznie, w rejestrach ma wszystko co trzeba, ogólnie miód i malinki tylko odbierać nie chce)...
  2. Cześć forumowicze, Steruję buzzerem (bez generatora) z Raspberry Pi Pico (3,3V). Buzzera podłączam bezpośrednio do GIO0, generuję sygnał o częstotliwości 250-900 Hz, ładnie gra marsz imperialny😃 Mój problem pojawia się, gdy chcę mieć głośniejszy dźwięk. Stwierdziłem, że by uzyskać większą amplitudę sygnału użyję baterii 9V (nowy Phillips alkaiczny) i tranzystora BC547 w roli przełącznika. Połączenie jak na załączonym schemacie. Efekt jest taki, że w tej nowej konfiguracji generowany dźwięk jak dużo cichszy niż poprzednio (bezpośrednie połączenie buzzer-mikrokontroler), a założenie było, żeby było głośniej😁 Buzzer jest dokładnie taki - https://botland.com.pl/buzzery-generatory-dzwieku/4187-buzzer-bez-generatora-23mm-1-40v-w-obudowie-5904422366070.html I pytanie, czemu gra ciszej w nowej konfiguracji, gdzie popełniam błąd, jak zrobić by było głośniej w stosunku do bezpośredniego połączenie buzzera i mikrokontrolera?
  3. Ten artykuł jest częścią serii "Kurs? Raspberry Pi Pico" #0 - Wstęp, spis treści #1 - GPIO #2 - UART #3 - PWM, ADC, IRQ na GPIO W tym rozdziale Dowiesz się czym jest magistrala I2C oraz SPI oraz nauczysz się jak z nich korzystać przy wykorzystaniu wygodnego API 😉 I2C Jest to drugi często spotykany rodzaj magistrali. Z niej często korzystają interfejsy dla wyświetlaczy alfanumerycznych (takich małych 16 znaków w 2 liniach) oraz np pamięci EEPROM (programowalna pamięć, którą można modyfikować z poziomu zewnętrznych urządzeń wysyłających do niej dane). To właśnie ten drugi element będzie naszym pacjentem badawczym. W moim przypadku będzie to konkretnie model AT24C64A. Zajmiemy się zapisem i odczytem jednego bajta z pamięci, do czego według specyfikacji producenta należy przesłać poprzez I2C 2 bajty adresu oraz dane do zapisania lub 2 bajty adresu i zażądać odczytu danych. Ten artykuł bierze udział w naszym konkursie! 🔥 Na zwycięzców czekają karty podarunkowe Allegro, m.in.: 2000 zł, 1000 zł i 500 zł. Potrafisz napisać podobny poradnik? Opublikuj go na forum i zgłoś się do konkursu! Czekamy na ciekawe teksty związane z elektroniką i programowaniem. Sprawdź szczegóły » Adres? Tak - I2C jest jedną z magistrali pozwalających adresować urządzenia. Do dyspozycji użytkownika jest 128 adresów (0 - 127). Wyżej wymieniona pamięć ma adres 0x50. Oprócz tego ma 3 piny pozwalające dodać do adresu maksymalnie 7 - czyli zakres adresowy jest od 0x50 do 0x57. Zajmijmy się więc podłączeniem pamięci - do GP4 podłączamy SDA, do GP5 podłączamy SCL. Do samej kości pamięci podłączamy zasilanie oraz masę. Oprócz tego do masy zwieramy pin WP. Piny A1,A2,A3 zostawiamy “wiszące”. Teraz omówmy funkcje - tutaj również powstała wygodna biblioteka - IIC.h. void begin(i2c_inst_t *inst, int baudRate); // Inicjacja I2C void end(); // koniec I2C void setBaud(int baudRate); // ustawianie baudrate I2C int available(); // sprawdzanie czy są dane ;) uint8_t* read(uint8_t address, size_t amount); // Odczyt danych - odczytujemy sekwencję danych binarnych void write(uint8_t address, uint8_t* data, size_t amount); // Zapis sekwencji binarnej na SPI void free_memory(); // Wyczyść pamięć - po wykorzystaniu odczytanych danych zalecam wykonać tę metodę - w celu oszczędności RAM’u ;) void set_slave(bool mode, uint8_t addr); // ustawia tryb niewolnika No i kurs został zdemonetyzowany (żart). Pewnie zastanawiasz się co to jest “tryb niewolnika” - otóż w magistrali I2C (oraz SPI, które omówimy za chwilę) istnieje master i slave. Master wysyła żądanie do slave’a, który na nie odpowiada. Czyli za początek komunikacji zawsze odpowiada master. W przypadku slave - który w naszym przykładowym programie to kość pamięci EEPROM oczekujemy na żądanie i na nie odpowiadamy. Jeżeli ustawimy tryb na true możemy ustawić adres naszego pico i wysyłać do niego żądania tak samo jak do pamięci. Tego tematu nie będziemy poruszać w podstawowej wersji kursu, gdyż jest rzadko stosowany. Tymczasem zajmijmy się naszym programem 😉 Na początku polecam poczytać o funkcjach oraz tablicach w C/C++. Teraz za zadanie mamy zgodnie ze specyfikacją producenta odczytać bajt do pamięci i zapisać bajt do pamięci. By zapisać bajt zapisujemy na I2C adres komórki pamięci (dwa bajty) oraz jeden bajt, który jest wartością. W celu odczytu zapisujemy na I2C adres komórki pamięci (dwa bajty) oraz odczytujemy jeden bajt (po czasie t, który ustalmy na 10ms - jest on znacznie nad wyrost, ale i tak nie zauważymy tego, a pozwoli uniknąć błędów wynikających z tego, że kość nie zdążyła przygotować danych dla magistrali). Przykładowy kod: #include <cstdio> #include "pico/stdlib.h" #include "IIC.h" #include <string> void i2c_eeprom_write_byte( uint8_t addr, uint16_t mem_addr, uint8_t data ) { // Przekonweruj adres ze słowa na bajty uint8_t addr_msb = mem_addr >> 8; uint8_t addr_lsb = mem_addr & 0xFF; // Zbuduj tablicę do wysłania uint8_t data_to_write[] = {addr_msb, addr_lsb, data}; // Zapisz bajt do pamięci EEPROM i2c.write(addr, data_to_write, 3); } uint8_t i2c_eeprom_read_byte( uint8_t addr, uint16_t mem_addr) { // Konwertuj adres do bajtów uint8_t addr_msb = mem_addr >> 8; uint8_t addr_lsb = mem_addr & 0xFF; // Zbuduj tablicę do wysłania uint8_t data_to_write[] = {addr_msb, addr_lsb}; i2c.write(addr, data_to_write, 2); // Odczekaj chwilę sleep_ms(10); // Odczytaj zwrócone dane... uint8_t* data = i2c.read(addr, 1); return data[0]; // Zwróć pierwszy bajt } Kod sam się opisuje, więc za bardzo nie będę w to wnikał 😉 Na koniec przykładowy program wykorzystujący nasze funkcje: int main() { stdio_init_all(); i2c.begin(i2c0, 100*1000); // Zainicjuj I2C i2c_eeprom_write_byte(ADDR, 0, 0x47); // Zapisz do EEPROM[0] wartość 0x47 sleep_ms(10); // Odczekaj chwilę ;) // Można i tak :D puts(std::to_string(i2c_eeprom_read_byte(ADDR, 0)).c_str()); // Wyślij na UART wartość EEPROM[0] puts(std::to_string(i2c_eeprom_read_byte(ADDR, 1)).c_str()); // Wyślij na UART wartość EEPROM[1] puts(std::to_string(i2c_eeprom_read_byte(ADDR, 2)).c_str()); // Wyślij na UART wartość EEPROM[2] // EEPROM[X] - bajt X w pamięci EEPROM } Jak widzimy korzystam z innej wersji wysyłania danych na UART - wersji bez biblioteki. Jeżeli masz kość EEPROM z serii AT24C możesz sam spróbować / sama spróbować i zobaczyć czy zwracane dane będą poprawne (0x47, 0x0, 0x0) lub (0x47, 0xFF, 0xFF). Oczywiście dane będą tak wyglądać, o ile nikt wcześniej nic nie zapisał na kości pamięci 😉 SPI Trzeci rodzaj magistrali, niestety tutaj będzie bez przykładu, gdyż nie mam żadnego chipu, który mogę podpiąć pod Pico z tą magistralą (chipy mam, ale niestety wszystkie są wlutowane w płytki). No to czas pokazać, że czegoś możesz się nauczyć nawet bez praktyki. SPI jest magistralą dupleksową, która ZAWSZE przesyła dane w obie strony. Czyli nawet jak odczytujesz dane to w tym momencie przesyłasz dane przez SPI. W specyfikacji dostępu poprzez tę magistralę do chipu producent zwykle wymienia ustawienia: CPOL, CPHA, kolejność bitów oraz ich ilość. API też pomaga z tymi ustawieniami. Po prostu ustawiasz wartości takie jak podaje producent i nie musisz wnikać w szczegóły. Jednakowoż jeżeli chcesz wnikać - CPOL określa czy zegar w stanie standardowym ma stan wysoki czy niski, a CPHA przy jakim rodzaju zbocza zegara pobierane są dane. Warto nadmienić, że w SPI również możemy obsłużyć wiele urządzeń, do tego służy pin CS, który musi mieć (zazwyczaj) stan niski, by dany slave był aktywowany (odbierał wiadomości). Pamiętaj o tym podczas podłączania swojego chipu/urządzenia do Pico. Dodatkowo Pico w trybie Slave ma dodatkowe piny (patrz pinout w rozdziale #0), które określają czy ma odbierać wiadomości. Patrz poniższa grafika: W naszym API pinami SPI0 są odpowiednio piny GP16 - MISO, GP18 - SCK, GP19 - MOSI. I tego możemy się trzymać. Funkcje dostępne w API to: SPI* begin(spi_inst_t *inst, int baudRate); // Inicjuje SPI ;) SPI* cpha(bool isHigh); // Ustawia CPHA SPI* cpol(bool isHigh); // Ustawia CPOL SPI* data_bits(uint8_t bits); // Ustawia ilość bitów SPI* msb_first(); // MSB_FIRST SPI* lsb_first(); // LSB_FIRST void setup(); // aktualizuje ustawienia void end(); // Konczy pracę SPI void setBaud(int baudRate); // Ustawia baudrate uint8_t read(); // odczytuje bajt wysyłając 0x0 void write(uint8_t data); // wysyła bajt uint8_t read_write(uint8_t data); // równocześnie wysyła i odczytuje bajt (dupleks) void set_slave(bool mode); // Włącza tryb slave Przykładowy syntetyczny program może wyglądać następująco: #include <cstdio> #include "pico/stdlib.h" #include "SPI.h" SPI spi; int main() { stdio_init_all(); spi.begin(spi0, 12000000)->cpol(false)->cpha(false)->data_bits(8)->msb_first()->setup(); // Inicjacja SPI ;) spi.write(0x40); // Wyślij komendę 0x40 for(int q = 0; q < 4; q++) { spi.write(0x0); // Odczekaj 4 cykle wysyłając pustą treść } uint8_t data = spi.read(); // Odczytaj dane } Jest to syntetyczny program, który nie robi zupełnie nic, ale pokazuje jak korzystać z API. Teraz wystarczy, że znajdziesz w swojej szufladzie coś korzystającego z magistrali SPI, podążysz za wskazaniami producenta i gotowe - komunikacja SPI z Pi Pico. To by było na tyle w tym rozdziale Jeżeli masz jakieś pytania śmiało je zadawaj, od tego tutaj jesteśmy 😉 Zadania domowe Przećwicz magistralę I2C Przećwicz magistralę SPI
  4. Giętarka do tworzyw sztucznych to urządzenie służące do kształtowania tworzyw termoplastycznych, które w handlu występują najczęściej w postaci płaskich arkuszy (dotyczy głównie PMMA, czyli popularnej pleksi/plexy). Giętarkę do tworzyw sztucznych postanowiłem zbudować na własny użytek, aby mieć możliwość zaginania wyciętych na laserze elementów, z których następnie będę budować prototypy swoich robotów. Udział w tegorocznym konkursie Forbota potraktowałem jako motywację do ukończenia projektu w ściśle określonym terminie. Ten artykuł bierze udział w naszym konkursie! 🔥 Na zwycięzców czekają karty podarunkowe Allegro, m.in.: 2000 zł, 1000 zł i 500 zł. Potrafisz napisać podobny poradnik? Opublikuj go na forum i zgłoś się do konkursu! Czekamy na ciekawe teksty związane z elektroniką i programowaniem. Sprawdź szczegóły » Tworząc ten artykuł miałem w planach udostępnienie pełnej dokumentacji technicznej urządzenia, aby każdy czytelnik był w stanie je samodzielnie zbudować w swoim warsztacie. W trakcie budowy i podczas pierwszych testów okazało się jednak, że prototyp jest jeszcze mocno niedoskonały i wymaga licznych modyfikacji. Postanowiłem zmienić konwencję – zamiast instrukcji krok-po-kroku zaprezentuję poszczególne rozwiązania wraz z uwagami, które uwzględnię w drugiej wersji urządzenia. W trakcie projektowania Zapraszam do pytań i dyskusji – może wykluje się z tego pełnoprawny projekt open hardware 🙂 Giętarka do tworzyw sztucznych – zasada działania Aby zagiąć płaski arkusz tworzywa pod zadanym kątem należy podgrzać go na linii zgięcia do odpowiedniej temperatury, a następnie po prostu uformować i utrzymać w zadanej pozycji, aż materiał znów się utwardzi. Materiałem, który będzie kształtowany na tym urządzeniu jest polimetakrylan metylu (PMMA, czyli potocznie plexiglas, pleksi lub pleksa) o grubości 3 mm. Internetowe źródła podają, że pleksa staje się plastyczna w temperaturze 115 °C, jednak testy pokazały, że arkusz można giąć już po krótkim podgrzewaniu w temperaturze ok. 70 °C. Elementem grzewczym w giętarkach jest najczęściej drut oporowy, spirala lub grzałka podobna do tych stosowanych w piekarnikach lub kuchenkach elektrycznych. W swoim urządzeniu zastosowałem drut oporowy Kanthal A1 ze względu na łatwość jego obróbki i stosunkowo niską cenę. Fotografia 1: Giętarka do tworzyw sztucznych gotowa do pracy | Fotografia 2: Zagięty element z pleksy Konstrukcja urządzenia Elementy obudowy urządzenia wykonałem z płyty HDF o grubości 3 mm. Jest materiał względnie tani i łatwo poddający się obróbce na popularnych laserach CO2. Ramę wykonałem z drewnianych kantówek – głównie z tego względu, że drewno nie przewodzi prądu i mam pewne doświadczenie w jego obróbce. Aby zapewnić sobie dostęp do wnętrza urządzenia po jego zmontowaniu, obudowa nie jest przymocowana do ramy na sztywno za pomocą wkrętów – zastosowałem tzw. mufy, czyli odpowiednik nitonakrętek do drewna, w które można wkręcać zwykłe śruby metryczne. Mimo, że drewno rzadko kiedy jest dobrym towarzystwem dla wysokich temperatur, w tym przypadku nie ma ryzyka pożaru. Temperatura zapłonu surowego drewna to 210–350 °C, a HDFu jeszcze więcej. Maksymalna przewidywana temperatura pracy urządzenia to 120 °C, więc istnieje jeszcze duży margines bezpieczeństwa. Fotografia 3: Gotowa rama urządzenia Rzeczy do poprawy O ile elementy obudowy można zaprojektować i wyciąć na laserze z dużą dokładnością, o tyle przy samej ramie należy przyjąć dosyć duże tolerancje wymiarowe. Drobne przesunięcia podczas klejenia kantówek spowodowały, że elementy obudowy nie zbiegały się ze sobą idealnie w miejscach łączenia. Rama kolejnej wersji urządzenia powinna zostać zbudowana z materiału o bardziej przewidywalnych wymiarach pierwotnych. Na myśl przychodzą mi profile aluminiowe, ale trzeba będzie zadbać o dobrym odizolowaniu ich od elementów przewodzących. Zasilanie giętarki Nie czułem się na siłach, aby zasilać giętarkę bezpośrednio z gniazdka 220 V, więc postanowiłem zastosować zasilacz obniżający napięcie do wartości bezpiecznej. Wybrałem tani zasilacz laboratoryjny z zakresem pracy 30V/10A, co dało mi możliwość regulowania temperatury drutu oporowego bez dodatkowych elementów typu regulator mocy w samym urządzeniu. Zasilanie doprowadziłem do giętarki za pomocą przewodów bananowych, a następnie rozprowadziłem: (A) Bezpośrednio do obu końców drutu oporowego (jeden biegun do jednego, drugi do drugiego), (B) Do przetwornicy step-up/step-down, stabilizującej napięcie na poziomie 12 V. Komponent ten ma za zadanie utrzymywać napięcie zasilające mikrokontroler na stałym poziomie, niezależnym od bieżących parametrów zewnętrznego zasilacza. Napięcie z przetwornicy doprowadziłem następnie do zespołu 3 połączonych szeregowo stabilizatorów napięcia (C) obniżających 12 V do kolejno: 9V, 5 V i 3,3 V. Dwa ostatnie rozprowadziłem do osobnych szyn na płytce, aby w prosty sposób można było zasilać pozostałe podzespoły. Stabilizator 12 V służy jedynie bardziej równomiernemu rozprowadzaniu ciepła. Fotografia 4: Wnętrze urządzenia Rzeczy do poprawy Dużo tańszym rozwiązaniem byłoby zastąpienie zasilacza laboratoryjnego prostym regulatorem mocy oraz wyeliminowanie przetwornicy, która – ze względu na duży zakres obsługiwanych napięć – okazała się jednym z najdroższych komponentów całego urządzenia. Raspberry Pi Pico i peryferia Sercem całej elektroniki jest Raspberry Pi Pico. Za wyborem tego mikrokontrolera stały dwie przesłanki: Cena – dużo niższa niż Arduino, co miało mieć przełożenie na finalny koszt urządzenia. Założenie to okazało się jednak fałszywe – o ile sam mikrokontroler faktycznie jest tańszy, o tyle kompatybilne z nim peryferia znacząco podwyższyły budżet. Mowa tu o ekspanderze wyprowadzeń (którego Arduino nie potrzebuje) oraz wyświetlacz 3,3 V. Co prawda teoretycznie można zastosować „zwykły” wyświetlacz 5 V, ale konieczny jest wówczas dodatkowy konwerter stanów logicznych. Język programowania – Pico można programować w Pythonie, w którym czuję się pewniej niż w C++. Oszczędność czasu jednak znów okazała się pozorna. Wydaje się, że firmware Pico jest jeszcze mocno niedopracowany, przez co niestabilny. Ilość problemów, które pojawiły się przy okazji tworzenia stosunkowo nieskomplikowanego programu jest ogromna w porównaniu do analogicznego rozwiązania, które napisałem na Arduino: Moduł Timer nie działa prawidłowo – „gubi” zadaną częstotliwość i potrafi zawiesić program. Nie ma działających gotowych bibliotek do obsługi wyświetlaczy LCD i termometru – komendy obsługujące te peryferia musiałem zaszyć ręcznie w kodzie głównym, po uprzednim przekopaniu się przez biblioteki dla innych platform. Komunikacja poprzez magistralę I2C szwankuje – interpreter co chwila sypie błędami o braku podłączonych urządzeń lub nieprawidłowo wybranych pinach. Pico regularnie się zawiesza i nie ma to związku z temperaturą pracy, która cały czas pozostaje w normie. Do Raspberry Pi Pico – oprócz przycisków sterujących – podłączyłem 3 urządzenia peryferyjne: Buzzer z generatorem (D) – wydający dźwięk w momencie zakończenia odliczania czasu. Wyświetlacz LCD (E) – należy pamiętać o konieczności wyboru modelu obsługiwanego przez napięcie 3,3 V lub zastosowanie dodatkowego konwertera stanów logicznych. Termoparę (F) wraz ze sterownikiem (G) – nie wiedziałem jaką temperaturę będę musiał wytworzyć, aby urządzenie działało poprawnie, więc wybrałem czujnik z dużym zakresem odczytu (wg dokumentacji: od -270 °C do 1372 °C). Fotografia 5: Wnętrze urządzenia | Fotografia 6: Termopara | Fotografia 7: Front urządzenia Rzeczy do poprawy Kwestią dyskusyjną jest sam wybór Pico, jako głównego mikrokontrolera. Tak długo, jak firmware będzie niestabilne oraz gama akcesoriów będzie stosunkowo niewielka to wybór Pico do zastosowań półprofesjonalnych będzie raczej bezzasadne. W przypadku tego projektu identyczną funkcjonalność można uzyskać dużo niższym kosztem za pomocą starego poczciwego Arduino. Przerostem formy nad treścią okazał się również zakup termopary. Z uwagi na to, że używane tworzywo sztuczne staje się plastyczne już w 70 °C, to w zupełności wystarczyłby dużo tańszy i prostszy w obsłudze termometr cyfrowy DS18B20. Kod źródłowy Oprogramowanie sterownika giętarki do tworzyw sztucznych pełni następujące funkcje: W regularnych odstępach czasu (co 1 sekundę) odczytuje temperaturę z czujnika i jeśli nastąpiła zmiana to wyświetla ją na wyświetlaczu. Aby odczyt i wyświetlanie temperatury było niezależne od głównej pętli, ten fragment kodu wykonywany jest na drugim rdzeniu Pico. Po naciśnięciu przycisku „Start” program zaczyna odliczać w dół od ustawionej liczby sekund. Dzięki temu użytkownik ma możliwość dokładnego odmierzania czasu potrzebnego do uplastycznienia obrabianego materiału. Po osiągnięciu „0” uruchamia się buzzer sygnalizujący koniec odliczania. Aktualna pozycja timera wyświetlana jest na wyświetlaczu. Przycisk „Reset” umożliwia przerwanie odliczania w dowolnym momencie. Przyciski „Plus” i „Minus” umożliwiają zmianę zaprogramowanego czasu odliczania i wyświetlenie aktualnej wartości na wyświetlaczu. import machine import utime import _thread # Deklaracja zmiennych globalnych global time time = 60 global prev_time prev_time = 0 global temp temp = 0 global prev_temp prev_temp = 0 global reset reset = False global counting counting = False # Deklaracja pinow led = machine.Pin(25, machine.Pin.OUT) start_btn = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_DOWN) reset_btn = machine.Pin(3, machine.Pin.IN, machine.Pin.PULL_DOWN) plus_btn = machine.Pin(4, machine.Pin.IN, machine.Pin.PULL_DOWN) minus_btn = machine.Pin(5, machine.Pin.IN, machine.Pin.PULL_DOWN) buzzer = machine.Pin(15, machine.Pin.OUT) # Funkcje def PrintData(): global LCD_i2c global TEM_i2c global time global prev_time global temp global prev_temp if (prev_time != time) or (prev_temp != temp): #jezeli aktualny czas lub temperatura rozni sie od poprzedniego odczytu to wyswietl aktualny. Warunek zapobiega ciaglemu wysylaniu danych na LCD temp_prt = str(temp) temp_prt = temp_prt[0:4] temp_str = "Temp: " + temp_prt + " " time_str = "Time: " + str(time) lcd_str = temp_str + time_str print(temp_str) print(time_str) LCD_i2c.writeto(114, '\x7C') LCD_i2c.writeto(114, '\x2D') LCD_i2c.writeto(114, lcd_str) prev_time = time prev_temp = temp def StopCounting(): global reset for i in range(6): buzzer.toggle(); utime.sleep(0.1) reset = False def ResetCounting(x): global reset reset = True reset_btn.irq(trigger=machine.Pin.IRQ_RISING, handler=ResetCounting) #przerwanie "nasluchujace", czy przycisk RESET zostal wcisniety def CheckTemp(): global temp TEM_i2c=machine.SoftI2C(sda=machine.Pin(12), scl=machine.Pin(13), freq=10000, timeout=1000) #nie wiem dlaczego musi byc SoftI2C - normalny nie dzialal utime.sleep(1) while True: a = TEM_i2c.readfrom_mem(16,0x00,1)[0] b = TEM_i2c.readfrom_mem(16,0x01,1)[0] d = TEM_i2c.readfrom_mem(16,0x03,1)[0] temp = (((a << 8) | b) >> 2)*0.25 #wzor zaczerpniety z biblioteki termopary - przeliczanie odczytow na stopnie utime.sleep(1) _thread.start_new_thread(CheckTemp, ()) #uruchomienie funkcji na drugim rdzeniu # Inicjalizacja LCD global TEM_i2c global LCD_i2c LCD_i2c=machine.SoftI2C(sda=machine.Pin(16), scl=machine.Pin(17), freq=10000, timeout=1000) #znow SoftI2C, bo zwykly nie dzialal utime.sleep(1) #Petla glowna while True: PrintData() #Funkcja wyswietli dane (czas, temperatura) tylko jesli ulegly zmianie if start_btn.value() == 1: #po nacisnieciu Start uruchom odliczanie... i = time while (i > 0) and (reset == False): #... ale tylko do momentu dojscia do zera lub wcisniecia resetu print("Counting...") print("Time: " + str(i)) lcd_str = "Counting... " + "Time: " + str(i) LCD_i2c.writeto(114, '\x7C') LCD_i2c.writeto(114, '\x2D') LCD_i2c.writeto(114, lcd_str) utime.sleep(1) i -= 1 prev_time = i StopCounting() if (plus_btn.value() == 1) and (time < 1000): #dodanie sekundy do czasu odliczania utime.sleep(0.25) time += 1 if (minus_btn.value() == 1) and (time > 1): #odjecie sekundy od czasu odliczania utime.sleep(0.25) time -= 1 Moduł grzewczy Moduł grzewczy to najważniejszy komponent giętarki do tworzyw sztucznych, ponieważ od jakości jego działania zależy, czy urządzenie w ogóle będzie spełniać swoją funkcję. Niestety w trakcie testów okazało się, że jest to najsłabszy punkt całego prototypu i jego opis to w zasadzie jedna długa lista koniecznych poprawek: Dobór drutu oporowego – mimo usilnych chęci nie udało mi się usystematyzować wiedzy dot. doboru odpowiedniego drutu oporowego. Największym problemem jest określenie mocy, za pomocą której będzie można osiągnąć zakładaną temperaturę samego drutu, a co za tym idzie jego otoczenia. Znając potrzebną moc oraz parametry źródła zasilania będzie można następnie dobrać średnicę oraz długość drutu. Na ten moment jednak nie podejmę się tego zadania – potraktujmy to jako „zawieszony” temat do kolejnego artykułu. Drut do prototypowej giętarki dobrałem eksperymentalnie. Najlepiej sprawdził się Kanthal A1 o średnicy 0,32 mm. Przy długości wynoszącej ok. 30 cm optymalną temperaturę do gięcia pleksy uzyskałem po zasileniu go mocą 100 W (ok. 25V i 4 A). Do zagięcia pleksy o grubości 3 mm należy podgrzewać ją przy zadanych parametrach przez ok. 60 sekund. Mocowanie drutu oporowego – jeden koniec drutu oporowego można zamocować „na sztywno”, ale drugi koniecznie musi kończyć się sprężyną ściągającą. Wraz ze wzrostem temperatury drut rozciąga się, a naciąg ma za zadanie to kompensować i zapobiegać jego przerwaniu po zetknięciu z obudową. Dosyć problematyczne okazało się zarobienie końcówek drutu. Wykorzystałem do tego celu zwykłe konektory zaciskowe, jednak utrudniają one dokładne wypoziomowanie – wraz ze wzrostem temperatury, gdy sprężyna zaczyna pracować, konektory wraz z mocowaniem zaczynają niekontrolowanie „poruszać się”, co zwiększa ryzyko pęknięcia. W drugiej wersji urządzenia konieczne będzie podpatrzenie profesjonalnych rozwiązań, które utrzymują drut w idealnie równej pozycji niezależnie od jego temperatury oraz zapewniają odpowiedni naciąg po jego rozciągnięciu. Zasilanie drutu oporowego – miejsce podłączenia końcówek zasilających będzie pochodną zastosowanego sposobu mocowania. W pierwszej wersji (niepokazanej na zdjęciach) jeden biegun podłączyłem bezpośrednio do sprężyny naciągowej. Jednak z powodu wzrostu temperatury sprężyna traciła swoje właściwości. Warto więc pamiętać o tym, aby pomiędzy dwoma biegunami zasilającymi nie było żadnego innego przewodnika oprócz samego drutu oporowego. Fotografia 8: Mocowanie drutu oporowego | Fotografia 9: Mocowanie drutu oporowego Dodatki usprawniające pracę Jednym z ciekawszych dodatków usprawniających pracę na giętarce, którego nie podpatrzyłem w urządzeniach profesjonalnych, jest ruchomy profil aluminiowy blokowany za pomocą nakrętek skrzydełkowych. Dzięki wygrawerowanej podziałce na górnej obudowie użytkownik ma możliwości dokładnego ustalenia pozycji giętego elementu na urządzeniu. Fotografia 10: Linijka Podsumowanie Patrząc na ostatnią eksplozję rakiety SpaceX jeden z jej twórców stwierdził podobno ze stoickim spokojem, że każda kolejna porażka tylko przybliża go do sukcesu. Mimo, że prototyp mojego pierwszego w życiu urządzenia DIY jest daleki od założonego ideału to i tak cieszę się, że udało się go ukończyć, a efektami i wnioskami jestem w stanie podzielić się na szerszym forum. O autorze Łukasz Jędrasiak - z wykształcenia politolog, z zawodu marketing manager, z pasji robotyk. Uważam, że najlepszą metodą przewidywania przyszłości jest jej tworzenie, więc chcę zbudować robota, który weźmie udział w misji na Marsa.
  5. Z konieczności utworzenia małego mobilnego systemu pomiarowego, pojawiła się potrzeba połączenia mikrokontrolera z telefonem pracującym na Androidzie, tak aby ten drugi otrzymywał dane z pierwszego. Wybór podstawowej platformy (Android) padł z uwagi na jej powszechność, a także brak konieczności zakupu płytek, czujników itp. Dlatego też chciałbym ośmielić tym artykułem osoby, które noszą się z zamiarem tworzenia projektów, które wymagają dużej ilości dostępnych w telefonach czujników, interfejsów komunikacyjnych i możliwości obliczeniowych, które czasem się marnują, a samo przedsięwzięcie nie przewiduje trwałego montażu telefonu w projektowanym urządzeniu (choć pewnie dużo osób i tak ponownie wykorzystuje te same mikrokontrolery/płytki z czujnikami itd. do kolejnych projektów). Jedną z wad takiego rozwiązania, jest brak zestawu pinów ogólnego przeznaczenia (GPIO) w telefonach. Ten artykuł bierze udział w naszym konkursie! 🔥 Na zwycięzców czekają karty podarunkowe Allegro, m.in.: 2000 zł, 1000 zł i 500 zł. Potrafisz napisać podobny poradnik? Opublikuj go na forum i zgłoś się do konkursu! Czekamy na ciekawe teksty związane z elektroniką i programowaniem. Sprawdź szczegóły » W kilku słowach niniejszy poradnik przeprowadzi przez proces tworzenia aplikacji na Androida (Java), która będzie komunikować się poprzez wirtualny port szeregowy za pomocą przewodu USB z całkiem nowym Raspberry Pi Pico. Oczywiście, omówiony zostanie również kod w języku C, który będzie sterował stanami logicznymi pinów mikrokontrolera (włączony/wyłączony) na podstawie odczytu wysłanych z telefonu danych. Ponadto będzie on również na żądanie telefonu odczytywał stan logiczny wybranych wejść i wysyłał te informacja, które wyświetlone zostaną w aplikacji mobilnej. Dodatkowo zaznajomi on z przygotowaniem środowiska do kompilacji programów w C/C++ na Raspberry Pi Pico jak i podpowie jak do pewnego stopnia zautomatyzować proces kompilacji i wgrywania programu do mikrokontrolera. Przygotowanie aplikacji na telefon z Androidem Po pierwsze należy pobrać i zainstalować zintegrowane środowisko programistyczne (IDE) Android Studio. Po utworzeniu nowego projektu z domyślną opcją "Empty Activity" i następnie wybranym "API21: Android 5.0 (Lollipop)" w pozycji "Minimum SKD" można zacząć od utworzenia pliku o nazwie "usb_devices.xml" o następującej zawartości (widoczne w pliku wartości wyjaśnione zostaną później): <?xml version="1.0" encoding="utf-8"?> <resources> <usb-device product-id="10" vendor-id="11914" /> </resources> Należy skopiować go do katalogu "app\res\xml" tak jak na zrzucie poniżej. Albo skopiować plik w Eksploratorze Windows i wkleić w Android Studio (może być wymagane utworzenie katalogu "xml" - patrz opcje menu kontekstowego), albo utworzyć bezpośrednio w edytorze Android Studio, czy znaleźć jego lokalizację na dysku (domyślnie "Litera:\Users\Użytkownik\AndroidStudioProjects\NazwaProjektu\app\src\main\res\xml"). Następnie można przystąpić do konfiguracji pliku "AndroidManifest.xml" (do znalezienia jak na zdjęciu powyżej w katalogu "manifests"). Należy wkleić poniższe linijki kodu: <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/usb_device" /> tak jak pokazano to na zrzucie ekranu: Aplikacja będzie korzystała z zewnętrznej biblioteki usb-serial-for-android, którą należy podłączyć do aplikacji. Zrobić można to edytując najpierw plik "build.gradle (Project)", następnie - "build.gradle (Module)" tak, jak przedstawiono na zdjęciach poniżej: wklejając poniższe linijki: maven { url 'https://jitpack.io' } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } implementation 'com.github.mik3y:usb-serial-for-android:3.3.0' Teraz można zsynchronizować projekt klikając na "Synch Now" (ostatni rysunek powyżej). Teraz należy wkleić poniższy kod do pliku "app\res\layout\activity_main.xml". Będzie opisywał on wygląd ekranu aplikacji - pole tekstowe i dwa przyciski. <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/tekst" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="vendorID productID" android:textSize="29sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/on" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="50dp" android:layout_marginTop="50dp" android:text="ON" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/off" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="50dp" android:layout_marginEnd="50dp" android:text="OFF" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout> Teraz przyszła pora na kod programu. Należy otworzyć plik "MainActivity.java" z katalogu "app\java" i zastąpić jego zawartość (poza pierwszą linijką rozpoczynającą się od "package...") tą podaną poniżej. Komentarz zawarte w kodzie opisują "co jest od czego" i ich przestudiowanie da pogląd na to, jak to wszystko działa. // Zaimportowanie używanych przez aplikację klas import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.WindowManager; import android.widget.Button; import android.widget.TextView; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; // Zaimportowanie zewnętrzenej biblioteki "usb-serial-for-android" import com.hoho.android.usbserial.driver.CdcAcmSerialDriver; import com.hoho.android.usbserial.driver.ProbeTable; import com.hoho.android.usbserial.driver.UsbSerialDriver; import com.hoho.android.usbserial.driver.UsbSerialPort; import com.hoho.android.usbserial.driver.UsbSerialProber; import com.hoho.android.usbserial.util.SerialInputOutputManager; public class MainActivity extends AppCompatActivity { // Deklaracja zmiennych globalnych - opis w dalszej części kodu TextView poleTekstowe; BroadcastReceiver broadcastReceiver; UsbSerialPort usbSerialPort; SerialInputOutputManager serialInputOutputManager; ScheduledFuture co100Ms; ExecutorService rx; Button on; Button off; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Załadowanie pliku układu okna i elementów aplikacji setContentView(R.layout.activity_main); // Ekran będzie włączony tak długo, jak aplikacja będzie widoczna na głównym planie getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // Przypisanie pola tekstowego z pliku układu do zmiennej w kodzie aplikacji poleTekstowe = findViewById(R.id.tekst); // Definicja obiektu, który będzie nasłuchiwał zdarzeń związanych z podłączeniem i odłączeniem // miktokontrolera do telefonu broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // Przełącznik "switch" - program nie musi sprawdzać po kolei wszystkich "if'ów" czy // "else if'ów", tylko kieruje się do właściwego miejsca za pierwszym podejściem switch (intent.getAction()) { // Obsługa akcji podłączenia urządzeni USB do telefonu case "android.hardware.usb.action.USB_DEVICE_ATTACHED": UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); // Wartości, które należy umieścić we wspomnianym wcześniej pliku "usb_device.xml" poleTekstowe.setText(usbDevice.getVendorId() + " " + usbDevice.getProductId()); ProbeTable probeTable = new ProbeTable(); // Przypisanie odpowiedniego dla Pico sterownika probeTable.addProduct(usbDevice.getVendorId(), usbDevice.getProductId(), CdcAcmSerialDriver.class); UsbSerialProber usbSerialProber = new UsbSerialProber(probeTable); UsbSerialDriver usbSerialDriver = usbSerialProber.probeDevice(usbDevice); UsbDeviceConnection usbDeviceConnection = usbManager.openDevice(usbSerialDriver.getDevice()); // Przypisanie wcześniej zdefiniowanego sterownika do wirtualnego portu // szeregowego USB usbSerialPort = usbSerialDriver.getPorts().get(0); try { // Próba otwarcia portu szeregowego dla mikrokontrolera usbSerialPort.open(usbDeviceConnection); // Zdefiniowanie parametrów komunikacji usbSerialPort.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); // Sygnał Data Terminal Ready - Pico i Android rozpocznął komunikację usbSerialPort.setDTR(true); // Sygnał Request To Send - wymaga go np. Arduino do rozpoczęcia komunikacji z Androidem usbSerialPort.setRTS(true); } catch (Exception ignored) {} // Obiekt nasłuchujący danych przychodzących SerialInputOutputManager.Listener serialInputOutputListener = new SerialInputOutputManager.Listener() { @Override public void onRunError(Exception ignored) {} @Override public void onNewData(byte[] data) { runOnUiThread(() -> poleTekstowe.setText(new String(data))); } }; serialInputOutputManager = new SerialInputOutputManager(usbSerialPort, serialInputOutputListener); serialInputOutputManager.setReadTimeout(0); // Definicja pozyższego obiektu jako oddzielnego wątku programu... rx = Executors.newSingleThreadExecutor(); // ...i jego uruchomienie rx.submit(serialInputOutputManager); // Zdefiniowanie osobnego wątku, który będzie wywoływał się do 100 ms wysyłając // porcję danych co100Ms = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> { try { usbSerialPort.write("o".getBytes(), 0); } catch (Exception ignored) {} }, 0, 100, TimeUnit.MILLISECONDS); break; // Obsługa akcji podłączenia urządzeni USB do telefonu case "android.hardware.usb.action.USB_DEVICE_DETACHED": // Sprzątanie po ustanowionej wcześniej komunikacji do odłączeniu Pico od Androida if (co100Ms != null && rx != null) { co100Ms.cancel(false); serialInputOutputManager.stop(); rx.shutdown(); poleTekstowe.setText("Odłączono"); } break; } } }; // Definicja filtrów podłączone/odłączone urządzenie USB IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); // Uruchomienie nasłuchiwania podłączenia/odłączenia urządzenia USB this.registerReceiver(broadcastReceiver, intentFilter); // Definicja przycisków "ON" i "OFF" on = findViewById(R.id.on); on.setOnClickListener(v -> { try { // Wysłanie ciągu znaków do mikrokontrolera usbSerialPort.write("n".getBytes(), 0); } catch (Exception ignored) {} }); off = findViewById(R.id.off); off.setOnClickListener(v -> { try { usbSerialPort.write("f".getBytes(), 0); } catch (Exception ignored) {} }); } // Sprzątanie po ustanowionej komunikacji w momencie zamknięcia (strzałką wstecz) aplikacji @Override protected void onDestroy() { if (co100Ms != null && rx != null) { co100Ms.cancel(false); serialInputOutputManager.stop(); rx.shutdown(); } this.unregisterReceiver(broadcastReceiver); super.onDestroy(); } } Ostatnim krokiem w tej części będzie skompilowanie pliku *.apk aplikacji, który to będzie można zainstalować w telefonie. Wybieramy z menu "Build" pozycję "Generate Signed APK...", wybrać "APK", utworzyć nowy klucz ("Create new..."), wybrać opcję "Build Variants" "release" oraz "Signature Versions" "V2..." i zakończyć kreatora przyciskiem "Finish" - zrzuty poniżej. W powiadomieniu wyświetli się lokalizacja do pliku *.apk. Za jego pomocą można zainstalować aplikację w telefonie. Gotowe! Przynajmniej androidowa aplikacja 😉 Pliki do znalezienia tutaj: Android.zip Program dla Raspberry Pi Pico Za wszelką cenę chciałem uruchomić Pico SDK, aby móc zaprogramować "Małą Malinkę" w C i finalnie kopiując plik *.uf2 do widocznej pamięci masowej po podłączeniu jej do komputera trzymając przyciśnięty przycisk "BOOTSEL". Było to zanim powstał skrypt dla Windowsa, a że na Windowsie 7 nie chciał współpracować żaden instalator Microsoftu, to rozwiązałem to w inny sposób. Jak to mówią "the hard way". Po pierwsze nigdy nie zrobiłem nic w C, co więcej oficjalna instrukcja do C/C++ mówiła np. o łatwej konfiguracji chociażby CLion'a. Nie wspominała tylko, że na Linuksie... A więc do rzeczy. Pobieramy następujące składniki: ARM GCC Compiler CMake MinGW Python 3 Raspberry Pi Pico SDK TinyUSB Następnie wypakowujemy je w swojej ulubionej lokalizacji (ja utworzyłem oddzielny folder, gdzie przechowuję binarki programów do "devovania"). Nie ma instalatorów, więc otwarcie mogę się przyznać, że mam niechęć do nich, jak ja coś zrobię, to wiem, gdzie naśmieciłem i łatwiej później posprzątać. Można się przyznawać się, kto też tak ma 🤷‍♂️ Co zrobić... A nie, jest MinGW, instalujemy architekturę x86_64. Pierwsze trzy pozycje będą zawierały podkatalog "bin", których ścieżki należy umieścić w systemowej zmiennej "PATH" (Komputer>Właściwości>Zaawansowane ustawienia systemu>Zmienne środowiskowe>Zmienne systemowe) oddzielając je średnikiem. Python 3, tutaj należy podać ścieżkę katalogu głównego. Zawartość TinyUSB kopiujemy do podkatalogu "lib" folderu gdzie wypakowano Raspberry Pi Pico SDK. Można się też pokusić o utworzenie zmiennej użytkownika "PICO_SDK_PATH" zawierającej ścieżkę do wspomniajego w poprzednim zdaniu katalogu. Na zdjęciu tak to wygląda: Pora przygotować właściwe pliki. Najpierw "CMakeLists.txt", który będzie miał zawartość jak poniżej. Należy wspomnieć, że plik"pico_sdk_import.cmake" należy umieścić w katalogu projektu, a znaleźć można go w katalogu zawierającym Raspberry Pi Pico SDK w podfolderze "external". cmake_minimum_required(VERSION 3.17) include(pico_sdk_import.cmake) project(project_name) # nazwa projektu set(CMAKE_C_STANDARD 11) pico_sdk_init() add_executable(project_name file_name.c) # dodanie plików wykonywalnych projektu target_link_libraries(project_name pico_stdlib) pico_enable_stdio_usb(project_name 1) # włączenie połączenia szeregowego USB pico_enable_stdio_uart(project_name 0) # wyłączenie UART pico_add_extra_outputs(project_name) Teraz tworzymy długo wyczekiwany plik *.c taki jak wskazany w "CMakeLists.txt" plik wykonywalny. Jego zawartość będzie jak poniżej. Komentarze rozjaśnią sprawę. #include <stdio.h> #include "pico/stdlib.h" int main() { stdio_init_all(); // Użycie pinu GP25 (dioda LED na płytce) gpio_init(25); // Ustawienie GP25 jako wyjścia gpio_set_dir(25, GPIO_OUT); gpio_init(0); // Ustawienie GP0 jako wejścia gpio_set_dir(0, GPIO_IN); gpio_init(1); gpio_set_dir(1, GPIO_IN); // Podciąga GP1 poprzez wbudowany w czip RP2040 opornik - stan wysoki (1), gdy nie podłączony do masy (GND) gpio_pull_up(1); // Bufor dwóch znaków. Efektywnie jednego, drugi to znak terminalny "\0" // Jeśli chcesz odczytać 5 znaków, ustaw należy ustawić bufor na 6 char wiadomosc[2]; while (true) { // Odczytuje dwa znaki (łącznie z terminalnym) w wejścia. Jeśli chcesz odczytywać wiadomości // ze zmienną ilością znaków, użyj "gets(wiadomosc)" zamiast "fgets(wiadomosc, 2, stdin)" // i zakończ wysyłane wiadomości znakiem kowej linii - "\n" fgets(wiadomosc, 2, stdin); // Odczytuje pierwszy znak (indeks [0]) wektora zawierającego wiadomość. // Java może porównać cały ciąg w instrukcji "switch" ;P switch(wiadomosc[0]) { case 'n': // Włącza diodę led przy wiadomości "n" ze standardowego wejścia gpio_put(25, 1); break; case 'f': // Wyłącza diodę led przy wiadomości "f" ze standardowego wejścia gpio_put(25, 0); break; case 'o': // Odczytuje wartości stanów GP0 i GP1 - wysoki (1) lub niski (0) // i wysyła je przez połączenie szeregowe printf("GP0: %d, GP1: %d", gpio_get(0), gpio_get(1)); break; } } } Do kompilacji przygotujemy plik wsadowy *.bat, który ułatwi zautomatyzuje kompilację. Jego zawartość będzie następująca: @echo off mode con: cols=130 lines=32 for %%i in (%1) do (set sciezka=%%~pi) for %%i in (%1) do (set litera=%%~di) cd /d "%litera%%sciezka%" cmake.exe -DCMAKE_BUILD_TYPE=Release -G "CodeBlocks - MinGW Makefiles" "%litera%%sciezka%" cmake.exe --build "%litera%%sciezka%" robocopy "%litera%%sciezka% " "G:\ " "*.uf2" /nfl /ndl /njh /njs /np pause Należy zmienić literę "G:\ " na tą, pod którą pojawi się dysk wymienny Pico. Spacje nie są błędem. Dziwna sprawa, ale inaczej nie chce działać, przynajmniej na Siódemce, a dodanie spacji na końcu było rozwiązaniem. Przygotowany plik chowamy przed wzrokiem i np. programem Default Programs Editor tworzymy pozycję w menu kontekstowym pliku *.c a jeszcze lepiej tworzymy nowe rozszerzenie dla plików C Pico. Albo kompilujemy wykorzystując dwie linijki zaczynające się od "cmake.exe", albo korzystamy ze szpanerskiego menu, a efekt kompilacji będzie następujący: Pliki do znalezienia tutaj: Pico.zip Pierwsze uruchomienie Po uruchomieniu aplikacji na telefonie i podłączeniu Pico przez kabel USB i adapter OTG należy zgodzić się, aby aplikacja uruchamiała się wraz z podłączeniem mikrokontrolera do smartfona. Nada to uprawnienia do korzystania z połączenia USB aplikacji. Pola "vendorID" i "productID" zmienią swoje wartości na te, które trzeba było umieścić we wspomnianym wcześniej pliku "usb_devices.xml". Po odłączeniu i ponownym podłączeniu (w trakcie działania aplikacji mobilnej, gdyż w androidowej aplikacji odczyt od cykliczne nadawanie zakodowane jest po podłączeniu urządzenia, ale tym zdefiniowanym w pliku "MainActivity.java", który wykonuje się po jej uruchomieniu) Malinki będzie można włączyć i wyłączyć jej diodę LED (GP25) i co 100 ms (na żądanie telefonu) będzie odczytywany stan wejść GP0 i GP1. Jak to działa, na zdjęciach poniżej. Zwarcie pinów GP0 (1) i 3V3OUT (36) ustawi stan wysoki na wejściu GP0, a połączenie GP1 (2) z GND (3) przesteruje normalnie wysoki stan do niskiego. Gotowe pliki wykonywalne tutaj: Binarki.zip Podsumowanie Niniejszy artykuł nie jest projektem od A do Z jakiegoś przedsięwzięcia, tylko przedstawia zagadnienie komunikacji portem szeregowym pomiędzy mikrokontrolerem (tutaj Raspberry Pi Pico, choć nie musi to być on) a telefonem komórkowym z Androidem wykorzystując natywne rozwiązania tworzenia programów na te platformy, tym samym nie będąc na "łasce" np. rozwiązań modułowych tworzenia aplikacji z klocków, wykorzystując wszystkie możliwości jakie daje Android, Pico, Java i C. Oczywiście to tylko zalążek, ale nie znalawszy gotowego rozwiązania, zmotywowało mnie to, żeby takie zrobić i zaprezentować, ktoś może mieć łatwiej. Ja już mam łatwiej, bo takiego potrzebuję 😁 PS. Nie spodziewałem się, że tak długo zajmie mi napisanie tego tekstu... PS2. Nazwa aplikacji mobilnej to taka losowo wklepana na klawiaturze. PS3. Pierwszy obrazek, drugi kod i trzy następne obrazki to trochę ludzik, trochę android 🤖
  6. Ten artykuł jest częścią serii "Kurs? Raspberry Pi Pico" #0 - Wstęp, spis treści #1 - GPIO #2 - UART #3 - PWM, ADC, IRQ na GPIO Czym jest Raspberry Pi Pico? Raspberry Pi Pico - w skrócie “Pico” lub “Pi Pico” to płytka z mikrokontrolerem RP2040 w całości opracowana przez Raspberry Pi Foundation. Jej mikrokontroler bazuje na rdzeniu ARM-Cortex M0+, posiada taktowanie bazowe 48MHz, a maksymalne 133 MHz. Oprócz tego posiada 3 wejścia 12-bitowego przetwornika ADC. Oczywiście Pico korzysta z maksymalnego taktowania mikrokontrolera. Warto też nadmienić, że sam mikrokontroler podobnie do ESP8266 czy ESP32 posiada aż dwa rdzenie, co czyni go bardzo praktycznym do projektów przetwarzania danych, mimo jego niskiej wydajności. Warto też wspomnieć, że aplikacje na płytkę możemy pisać zarówno w C/C++ jak i w Pythonie. Na potrzeby tego poradnika pójdziemy ścieżką C/C++, gdyż zakładam, iż większość osób posiada już doświadczenie z Arduino i niekoniecznie z Pythonem, stąd taki wybór. Ten artykuł bierze udział w naszym konkursie! 🔥 Na zwycięzców czekają karty podarunkowe Allegro, m.in.: 2000 zł, 1000 zł i 500 zł. Potrafisz napisać podobny poradnik? Opublikuj go na forum i zgłoś się do konkursu! Czekamy na ciekawe teksty związane z elektroniką i programowaniem. Sprawdź szczegóły » Instalacja zestawu narzędzi - Windows By zainstalować narzędzia deweloperskie dla Pi Pico możemy skorzystać z gotowego skryptu dostępnego tutaj. Pobieramy plik pico-setup-windows-*.*-x64.exe. Jeżeli ten plik nie chce nam się uruchomić to pobieramy plik z końcówką x32 (wtedy prawdopodobnie Twój system jest 32-bitowy, co jest obecnie straszną rzadkością). Jeżeli korzystasz z Windowsa 8 lub nowszego możesz być praktycznie pewien / pewna, że Twój system ma 64 bity. Uwaga: Podczas instalacji polecam zaznaczyć, "Clone Raspberry Pi Pico Repositiories and Build" lub podobnie brzmiącą nazwę. Automatycznie zainstaluje ona dodatkowe narzędzia, które będą nam przydatne. Instalacja zestawu narzędzi - Linux W tym przypadku odeślę do oficjalnej dokumentacji Pi Pico dostępnej tutaj - rozdział drugi. Sądzę, iż osoby korzystające z Linuxa są zazwyczaj doświadczone od strony informatycznej i potrafią poradzić sobie same 😉 A jeżeli nawet nie, to powyższy dokument perfekcyjnie tłumaczy co i jak. Wybór IDE Większość z czytaczy prawdopodobnie będzie wykorzystywała Visual Studio Code, aczkolwiek ja posiadam pełną licencję na pakiet JetBrains, więc skorzystam z CLion’a. Z ciekawych informacji zaznaczę, iż pakiet JetBrains jest dostępny dla studentów całkowicie za darmo poprzez GitHub Student Developer Pack. Pinout Pi Pico Polecam zapisać powyższy plik gdzieś na dysku lub wydrukować - płytka nie ma oznaczeń na górze więc po włożeniu w płytkę prototypową może być mały problem z koniecznością liczenia pinów 🙂 - warto zaznaczyć, iż pady GND mają specjalne oznaczenie (są kwadratowe nie okrągłe) Ale do czego mi to Pi Pico może się przydać? Podobnie jak Arduino do wszelkiego rodzaju projektów - patrząc na cenę Pi Pico, które kosztuje (na chwilę pisania artykułu) ~20 PLN jest to znakomita alternatywa dla STM32 czy Arduino - które kosztują odpowiednio ~17 PLN (Blue Pill) lub ~14 PLN (Arduino Nano) za płytkę. Zadania domowe Napięcia na pinach zasilania Sprawdź napięcia na pinach VBUS, VSYS, 3V3 - czy są one takie jak byś oczekiwał? Czy na pewno pin VBUS ma 5V czy 4.8V? Czym to może być spowodowane? Zalecane zapoznanie z podstawami C/C++. Postaram się tłumaczyć pobieżnie większość terminów, aczkolwiek będę ciągle odsyłał do kursu C/C++ 😉 Jeżeli opanujesz podstawy będziesz miał łatwiej realizować ten kurs. W tym kursie Wstęp / Spis treści GPIO UART I2C, SPI PWM, ADC, Przerwania GPIO
  7. Dziś pierwszy mój samodzielny wpis: jak wysterować trójkolorowy wyświetlacz ePaper z Raspberry Pico. Przykłady są przeprowadzane na czarno-biało-czerwonej matrycy GDEW075Z09 (zestaw WaveShare 13505, matryca z płytką kontrolera SPI w formie shielda na Raspberry Pi), ale tak samo programuje się czarno-biało-żółtą matrycę GDEW075C21 (zestaw WaveShare 14229). Teoria Wyświetlacze typu e-papier w najczęstszej formie nazywają się wyświetlaczami elekroforetycznymi i działają tak: Wyświetlacz zawiera miliony miniaturowych, przezroczystych kapsułek zawierających dwa lub trzy pigmenty zawieszone w oleju. Cząsteczki pigmentów są naładowane elektrycznie (w wyświetlaczach dwukolorowych - dodatnio i ujemnie, w trójkolorowych słabo i silnie dodatnio i ujemnie) Kapsułki są umieszczone pomiędzy przezroczystą elektrodą wspólną u góry a matrycą elektrod indywidualnych na dole. Jeden piksel składa się z wielu kapsułek (są one dużo mniejsze od jednej elektrody) i widać to pod dużym powiększeniem. Zmieniając napięcie na elektrodach indywidualnych można wypychać pożądane pigmenty w kierunku górnej części wyświetlacza, wtedy piksel zmienia kolor. W 3-kolorowych jest trudnej, bo dwa pigmenty poruszają się razem (czarny i kolorowy), ale z różnymi prędkościami i trzeba sztuczek, żeby te kolory finalnie rozdzielić - więcej w tym artykule. Napięcia sterujące są dość wysokie (jak na współczesną elektronikę), rzędu ±7-15V i wyświetlacz pozostawiony "pod napięciem" przez dłuższy czas może ulec uszkodzeniu (jak burn-in w OLED czy plaźmie). Pamiętaj, żeby po każdym odświeżeniu przełączyć kontroler w stan power-down. Kontroler Matryce można kupić w formie zestawu z shieldem dla Raspberry Pi (testowałem z Pi0W i Pi3). Płytka shielda ma pod spodem styki do standardowego łącza GPIO-40, ale ma też osobne gniazdo SPI do używania z innymi komputerami nadzorczymi. I właśnie to gniazdo zostało wykorzystane do podpięcia się do Raspberry Pico. W zestawie z shieldem jest nawet odpowiedni kabel z żeńskimi końcówkami do podpięcia pod szpilki. Shield jest uniwersalny (do wszystkich "gołych" matryc ePaper sprzedawanych przez WaveShare), należy tylko odpowiednio na nim ustawić mikroswitche. Interfejs elektryczny Shield jest wyposażony w standardowy, jednokierunkowy port SPI (matryca ma co prawda łącze dwukierunkowe, ale część odbiorcza nie jest "przepuszczana" przez shield), kilka sygnałów sterujących (CS, CD, RST) oraz jeden monitorujący (BUSY). Zasilany jest jednym napięciem 3.3V. Przykładowy kod zakłada następujące połączenia (kolorowe kable na dole powyższego zdjęcia, licząc od lewej): BUSY -> GP6 (pin 9 Pico) RST <- GP1 (pin 2 Pico) DC <- GP0 (pin 1 Pico) CS <- GP5/SPI0)CSn (pin 7 Pico) CLK <- GP2/SPI0_SCK (pin 4 Pico) DIN <- GP3/SPI0_TX (pin 5 Pico) GND -- GND (pin 38 Pico) VCC -- 3V3_OUT (pin 36 Pico) Protokół Do wyświetlacza możemy przesyłać ciągi komend opcjonalnie uzupełnionych o dane. O tym, co jest transmitowane, decyduje stan linii DC. Sekwencja wysłania komendy wygląda tak: Wysterować linię DC na stan niski (komenda), Wysterować linię CS na stan niski, Przesłać bajt komendy Wysterować linię CS na stan wysoki, Jeżeli komenda wymaga danych, to później następuje taka sekwencja: Wysterować linię DC na stan wysoki (dane), Wysterować linię CS na stan niski, Przesłać bajty danych Wysterować linię CS na stan wysoki, Na końcu trzeba próbkować linię BUSY, stan wysoki sygnalizuje zakończenie operacji. Dokumentacja jawnie wskazuje, że należy "zdjąć" na chwilę CS po komendzie, jednak zaobserwowałem, że nie ma to wpływu na działanie wyświetlacza. Program przykładowy skonstruowany jest zgodnie z dokumentacją. Inicjalizacja Proces inicjalizacji wyświetlacza zawiera dużo czarnej magii: poszczególne rejestry dla poszczególnych matryc muszą być ustawione konkretnie, bo producent tak mówi - bez głębszego wyjaśniania. Dotyczy to szczególnie zależności czasowych i konfiguracji układu konwersji napięcia. Ma być tak, bo inaczej zepsujesz matrycę. Sekwencja komend konfiguracyjnych w przykładowym programie jest dostosowana do konkretnych dwóch matryc - parametry dla innych należy ściągnąć z przykładowych programów na wiki WaveShare'a. Aktualizacja Matryca ma wbudowaną pamięć obrazu i jeżeli chcemy zmienić jej zawartość, musimy przesłać cały nowy obraz. Po przesłaniu nowej ramki wydaje się polecenie odświeżenia, które pierw czyści cały ekran, a następnie na podstawie zawartości pamięci "pompuje" odpowiednie piksele. W przypadku opisywanych matryc cały cykl trwa bolesne 15 sekund. Matryce dwukolorowe mają możliwość częściowego odświeżenia prostokątnego okienka - trwa to około 1/3s. Niestety, żadna matryca trójkolorowa nie ma takiej funkcjonalności. Zasilanie Aby uniknąć uszkodzenia materiały matrycy, należy bezwzględnie wydawać polecenie wyłączenia zasilania po każdym odświeżeniu. Program """ Driver do wyświetlaczy GDEW075Z09 (czarno-biało-czerwony) i GDEW075C21 (czarno-biało-żółty), występujących odpowiednio w zestawach WaveShare 13505 i 14229 Copyright (c) 2021 Paweł Kraszewski Bazuje na pracy: Copyright (c) 2017 Waveshare Copyright (c) 2018 Mike Causer MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ class EPaper_GDEW075Z09: from micropython import const EPD_WIDTH = const(640) EPD_HEIGHT = const(384) COMMAND = const(0) DATA = const(1) def __init__(self, spi, cs, dc, rst, busy): """ Inicjalizacja wyświetlacza :param spi: Skonfigurowany interfejs SPI :param cs: Pin do sygnału wyjściowego ~CS (chip select) :param dc: Pin do sygnału wyjściowego DC (data/command) :param rst: Pin do sygnału wyjściowego ~RST (reset) :param busy: Pin do sygnału wejściowego ~BUSY (gotowość wyświetlacz) """ import framebuf self.spi = spi self.cs = cs self.dc = dc self.rst = rst self.busy = busy self.cs.init(self.cs.OUT, value=1) self.dc.init(self.dc.OUT, value=0) self.rst.init(self.rst.OUT, value=0) self.busy.init(self.busy.IN) self._setup_pixel_lut() # Format framebuf.GS2_HMSB to grafika w 4 odcieniach szarości. Tablica # LUT przetwarza kolory 0, 1, 2 i 3 odpowiednio na biały, # czerwony/żółty, czarny, czarny (tak, dwa czarne) # Bajt tablicy obrazu to spakowane 4 piksle, dlatego wymaga 1/4 bajtów # Bufor pamięciowy self.buf = bytearray(self.EPD_WIDTH * self.EPD_HEIGHT // 4) # Framebuffer (operacje graficzne, itd) self.fb = framebuf.FrameBuffer( self.buf, self.EPD_WIDTH, self.EPD_HEIGHT, framebuf.GS2_HMSB ) def _wait_for_ready(self): from time import sleep_ms while self.busy.value() == 0: sleep_ms(100) def _setup_pixel_lut(self): """ Funkcja wypełnia tablice do szybkiej transformacji obrazu w formacie 2:2:2:2 (bajt zawiera cztery pikele po dwa bity każdy) na dwa bajty w formacie 4:4 4:4 (piksele po cztery bity, ale tylko 3 kombinacje dają sensowny wynik, kolory czarny, biały i czerwony/żółty) """ LUT = [3, 4, 0, 0] self.lut_high = bytearray(256) self.lut_low = bytearray(256) for i in range(0, 256): p1 = LUT[(i >> 6) & 3] p2 = LUT[(i >> 4) & 3] p3 = LUT[(i >> 2) & 3] p4 = LUT[(i >> 0) & 3] self.lut_high[i] = (p2 << 4) | p1 self.lut_low[i] = (p4 << 4) | p3 def _send_command(self, command, data=None): """ Funkcja wysyła komendę (z linią DC=0) za którą występują opcjonalne dane (z linią DC=1) :param command: komenda wyświetlacz :param data: opcjonalne dane dla komendy """ self.dc(self.COMMAND) self.cs(0) self.spi.write(bytearray([command])) self.cs(1) if data is not None: self.dc(self.DATA) self._send_raw_data(data) def _send_raw_data(self, data): """ Komenda wysyła surowe dane :param data: dane """ self.dc(self.DATA) self.cs(0) self.spi.write(data) self.cs(1) def _send_pixel_data(self, data): """ Komenda wysyła bufor wyświetlacza w formacie 2:2:2:2 przeliczając kolory według prekalkulowanych tablic LUT :param data: framebuffer """ self.dc(self.DATA) self.cs(0) for quad_pixel in data: self.spi.write(bytearray([ self.lut_low[quad_pixel], self.lut_high[quad_pixel] ])) self.cs(1) def setup(self): """ Inicjalizacja wyświetlacza. Poprawne dla modułów GDEW075Z09 (czarno-biało-czerwony) i GDEW075C21 (czarno-biało-żółty), występujących odpowiednio w zestawach WaveShare 13505 i 14229 """ import ustruct self.hard_reset() # Power setting self._send_command(0x01, b'\x37\x00') # Panel setting self._send_command(0x00, b'\xCF\x08') # PLL Control self._send_command(0x30, b'\x3A') # VCM DC Setting self._send_command(0x82, b'\x28') # Booster soft start self._send_command(0x06, b'\xC7\xCC\x15') # VCOM and data interval setting self._send_command(0x50, b'\x77') # TCON setting self._send_command(0x60, b'\x22') # SPI flash control self._send_command(0x65, b'\x00') # TCON resolution self._send_command(0x61, ustruct.pack(">HH", self.EPD_WIDTH, self.EPD_HEIGHT)) # FLASH mode self._send_command(0xE5, b'\x03') # Temp calibration self._send_command(0x41, b'\x00') # Power on self._send_command(0x04) self._wait_for_ready() def hard_reset(self): """ Twardy reset wyświetlacza (zeruje wszystkie rejestry), impuls niski sygnału ~RST przez 200ms i dodatkowe 200ms czekania """ from time import sleep_ms self.rst(0) sleep_ms(200) self.rst(1) sleep_ms(200) def update(self): """ Aktualizuje zawartość wyświetlacza na podstawie bieżącej zawartości framebuffera. Dla opisywanych wyświetlaczy trwa to ~10s :( """ # Start pixel transmission self._send_command(0x10) # Pixels self._send_pixel_data(self.buf) # Stop pixel transmission self._send_command(0x11) # Display refresh self._send_command(0x12) self._wait_for_ready() def power_down(self): """ Wyłączenie zasilania wyświetlacza (zawartość oczywiście pozostaje na ekranie) """ # Power off self._send_command(0x02) self._wait_for_ready() # Deep sleep self._send_command(0x07, b'\xA5') def get_framebuffer(self): """ Zwraca klasę framebuf.FrameBuffer() do rysowania na buforze :return: framebuf.FrameBuffer() """ return self.fb def get_size(self): """ Zwraca rozmiar wyświetlacza w pikselach :return: (width,height) """ return self.EPD_WIDTH, self.EPD_HEIGHT def demo(): from machine import Pin, SPI # Konfiguracja pinów zgodnie z artykułem sck = Pin(2) miso = Pin(4) mosi = Pin(3) dc = Pin(0) cs = Pin(5) rst = Pin(1) busy = Pin(6) spi = SPI(0, baudrate=20000000, polarity=0, phase=0, sck=sck, miso=miso, mosi=mosi) # Podpięcie pod wyświetlacz e = EPaper_GDEW075Z09(spi, cs, dc, rst, busy) # Inicjalizacja e.setup() # Pobranie rozmiaru wyświetalcza w pikselach w, h = e.get_size() # Pobranie instancji klasy FrameBuffer do rysowania fb = e.get_framebuffer() # Przypisanie kolorów black = 2 red = 1 white = 0 # Wypełnienie białym kolorem fb.fill(white) # Ramki for i in range(0, 40, 4): fb.rect(i, i, w - 1 - 2 * i, h - 1 - 2 * i, black) fb.rect(i + 2, i + 2, w - 1 - 4 - 2 * i, h - 1 - 4 - 2 * i, red) # "Szachownica" for x in range(12): for y in range(12): c = (red, black, white)[(x + y) % 3] fb.fill_rect(50 + x * 20, 50 + y * 20, 19, 19, c) # Tekst fb.text('RED', w // 2, h // 2, red) fb.text('BLACK', w // 2, h // 2 + 16, black) # Wysłanie bufora do matrycy i polecenie odświeżenia e.update() # Bardzo, BARDZO ważne! Wyłączenie zasilania e.power_down() if __name__ == "__main__": demo() Miłej zabawy! A, skryptu można używać jako biblioteki w swoich programach. Jak zapiszecie na Malince jako "epaper.py" to z własnego skryptu użyjecie tego przez "from epaper import EPaper_GDEW075Z09" PS. Niebawem to samo, tylko w C++. A potem inne peryferia: OLED monochromatyczny, OLED kolorowy i LCD kolorowy, w wersjach pierw Python, potem C++.
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.