Skocz do zawartości

Przeszukaj forum

Pokazywanie wyników dla tagów 'PiPico'.

  • 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 2 wyniki

  1. Cześć! Mam nadzieję, że przyda się koleżankom i kolegom. Na github.com/dotcypress/ula jest firmware do Raspberry Pi Pico, który zamienia je w sondę logiczną. Parametry: 16 kanałów (GPIO 0..15) [można podrasować do 30 kanałów) 100MHz częstotliwość próbkowania [można podkręcić do 250MHz przy przetaktowaniu procka] 200kB pamięci próbek Triggery na PIO Kompatybilny z oprogramowaniem PulseView Minusy: Nie pracuje w trybie strumieniowym, więc pomiar musi się zmieścić do bufora.
  2. Dla zielonych 😉 (BASIC CRASH COURSE) O czym w tym artykule? W tym artykule poznasz podstawy pracy z Raspberry Pi Pico w języku C++. Obsłużysz podstawowe narzędzia - PWM, delay, piny, UART. Ten poradnik jest skierowany dla osób, które są kompletnie zielone i chcą postawić swoje pierwsze kroki z RP2040. Na wstępie Uwaga: jeżeli korzystasz z Windowsa zaparz sobie szklankę melisy, gdyż instalacja środowiska na Windowsie to horror 😉 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 środowiska - Linux sudo apt update sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential // To powinieneś mieć sudo apt install gcc g++ // A to dla fanatyków Debiana :) sudo apt install libstdc++-arm-none-eabi-newlib Tutaj odeślę do oficjalnej dokumentacji 🙂 Instalacja środowiska - Windows Alternatywa: skrypt instalujący Pi Pico Toolchain na Windowsie 😉 (nie testowałem) README dla tego programu. Dobrze no to czas na tę tragedię - przede wszystkim zaparz sobie melisę... To jest bardzo ważne 🙂 Wybierz środowisko programistyczne Visual Studio Code Clion Zainstaluj CMake Pamiętaj by podczas instalacji zaznaczyć opcję "Add the CMake to system PATH for all users" Pobierz i zainstaluj Build Tools for Visual Studio, pamiętaj by zaznaczyć "C++ build tools" w sekcji "Workloads" Zainstaluj Python3, pamiętaj by zaznaczyć opcję by dodać go do PATH 😉 Zainstaluj Git - również pamiętając o dodaniu do PATH 😉 Zainstaluj kompilator ARM 😉 Tak samo należy pamiętać o dodaniu go do zmiennej PATH 😉 Zmienna PATH powinna wyglądać w sposób zbliżony: Myślałeś, że to koniec? 😄 Teraz czas pobrać i zainstalować SDK Pobieramy archiwum (zielony przycisk) Wypakowujemy do jakiegoś folderu na dysku Kopiujemy ścieżkę dysku Wchodzimy w "Mój komputer" > "Ten Komputer" > PPM > "Właściwości" > "Zaawansowane ustawienia systemu" > "Zmienne środowiskowe" Dodajemy nową zmienną o nazwie PICO_SDK_PATH i wartości skopiowanej ścieżki (np. "D:\SDK\Pi Pico") Dodajemy nową zmienną o nazwie PICO_TOOLCHAIN_PATH i ścieżce do ARM Toolchain (np. "C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major\bin") Do osobnego folderu pobieramy i wypakowujemy następujące narzędzie, znacząco ułatwi nam tworzenie projektów 😉 Wszystko powinno być już zainstalowane, jeżeli masz problemy odeślę Cię do tego poradnika, który opisuje wszystko krok po kroku dla Visual Studio 😉 Tworzenie projektu Tutaj nie będę poruszał tworzenia pliku Makefile, gdyż dla większości osób jest to strasznie skomplikowane, a ma być prosto nieprawdaż? Stąd pobraliśmy Pico Project Generator. Jest to bardzo wygodne narzędzie, dzięki któremu możemy tworzyć projekty w interfejsie graficznym. By uruchomić narzędzie uruchamiamy "Wiersz Poleceń" (cmd.exe) i przechodzimy do folderu, w którym znajduje się nasz Pico Project Generator (np. "D:\pico-project-generator-master"). Następnie używamy komendy: python pico_project.py --gui by uruchomić nasz program. Przykład: Używanie Pico Project Generator Pojawia nam się następujące okienko: W zakładce "Project Name" wpisujemy nazwę projektu, UWAGA: NAZWA PROJEKTU NIE MOŻE ZAWIERAĆ SPACJI W sekcji Location możemy wybrać folder nadrzędny dla naszego projektu (w nim znajdzie się folder z projektem), ja go wrzucę razem z generatorem. Oprócz tego możemy wybrać dodatkowe biblioteki, ustawienia konsoli czy ustawienia kodu. Jako, że ja będę korzystał z CLiona użyję standardowych ustawień i kliknę przycisk OK. Czekamy aż projekt się wygeneruje i zamykamy generator 😉 Teraz uruchamiamy środowisko... Jak widzimy nasz program wygenerował standardowy plik. Nie będę tutaj tłumaczył podstaw języka C, aczkolwiek powiem jak podmienić go na C++. W tym celu zmieniamy rozszerzenie pliku na .cpp Oraz musimy poprawić plik CMakeLists.txt - czasami nie jest on poprawiany automatycznie. Szukamy linijki "add_executable(Forbot Forbot.c)" i podmieniamy nazwę "Forbot.c" na "Forbot.cpp" - linijka powinna wyglądać tak: "add_executable(Forbot Forbot.cpp)" 😉 Uwaga: dla Visual Studio Code zalecane jest zainstalowanie pluginu CMake Tools. Dokumentacja Warto sobie dodać do zakładek dokumentację C++ dla Raspberry Pi Pico - jest tam wszystko dokładnie i elegancko opisane 😉 Warto sobie też zapisać gdzieś pinout, gdyż na górze płytki nie ma oznaczeń i jak ją wepniemy w płytkę stykową... będzie problem 😉 GPIO - Pierwszy program... No to co? Może napiszemy pierwszy program? W tym celu do GPIO16 podpinamy diodę LED z rezystorem ograniczającym prąd. Jak na załączonym obrazku 😉 Tak wiem, że rezystor nie jest najpiękniej zamontowany, ale kto by się przejmował... To teraz czas na miganie diodą. #include <stdio.h> #include "pico/stdlib.h" int main() { stdio_init_all(); // inicjacja STDIO gpio_init(16); // inicjacja pinu GP16 gpio_set_dir(16, true); // ustawienie pinu GP16 jako wyjścia // loop() while(true){ gpio_put(16, true); // ustaw stan wysoki na pinie GP16 sleep_ms(250); // Odczekaj 250ms gpio_put(16, false); // ustaw stan niski na pinie GP16 sleep_ms(250); // Odczekaj 250ms } return 0; } Jak widzimy kod w Pico nie ma loop() ani setup(), a ma wyłącznie main(). W tym celu sami musimy stworzyć sobie loop'a - po inicjacji I/O dodajemy niekończącą się pętlę while. W niej możemy wykonywać kod tak samo jak w loop() w Arduino, a wszystko powyżej będzie działało jak setup(). Funkcje gpio_init(<id>) - inicjuje pin, PIN MUSI BYĆ ZAINICJOWANY! gpio_set_dir(<id>, <wartość>) - ustawia kierunek pinu: false - wejście, true - wyjście gpio_pull_up(<id>) - podciągnięcie pinu do VCC gpio_pull_down(<id>) - podciągnięcie pinu do GND gpio_put(<id>) - ustawia stan pinu: false - niski, true - wysoki sleep_ms(<time>) - czeka określoną ilość milisekund sleep_us(<time>) - czeka określoną ilość mikrosekund 😉 gpio_set_function(<gpio>, <func>) - ustawia funkcję GPIO GPIO_FUNC_SPI GPIO_FUNC_UART GPIO_FUNC_I2C GPIO_FUNC_PWM GPIO_FUNC_SIO (default) - zwykły pin GPIO_FUNC_PIO0 GPIO_FUNC_PIO1 GPIO_FUNC_GPCK GPIO_FUNC_USB GPIO_FUNC_NULL - brak funkcji (wyłączone) GPIO_FUNC_XIP To są podstawowe funkcje, z których będziesz najczęściej korzystać. Wgrywanie programu. By wgrać program na Pi Pico musisz go skompilować. Jeżeli wygenerowałeś go przy użyciu podanego wcześniej narzędzia powinno to pójść bez większych problemów. Alternatywnie komenda do zbudowania programu wygląda następująco: cmake -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - MinGW Makefiles" <ŚCIEŻKA> cmake --build <ŚCIEŻKA> --target all -- -j <ILOŚĆ RDZENI> // Pamiętaj by podmienić <ŚCIEŻKA> i <ILOŚĆ RDZENI> na odpowiednie parametry! // UWAGA: te komendy nie gwarantują poprawnego builda :( Niestety cmake to narzędzie, które jest toporne i zwykle wyrzuca miliard błędów zanim łaskawie coś skompiluje ;) // W przypadku powyższych komend musisz jeszcze ustawić kompilatory C/C++ i ASM ;) // Dlatego zalecam używanie CLion'a, który to wszystko automatyzuje // Alternatywa (często sypie błędami cmake) cd <ŚCIEŻKA>/build nmake Wreszcie gdy uda nam się to skompilować wgrywamy program. By to zrobić odłączamy Pi Pico od USB, przytrzymujemy przycisk BOOTSEL oraz podłączamy Pi Pico do USB (lub używamy przełącznika w HUBie) 😉 Następnie wchodzimy do folderu ze skompilowanymi plikami i szukamy pliku o nazwie <coś>.uf2 i ten plik przenosimy na dysk przenośny, który powstał z naszego Raspberry Pi Pico. Dysk powinien zniknąć, a nasza dioda powinna zacząć migać. UART Obsługa UART'a jest banalna. W Pi Pico mamy do dyspozycji dwa UART'y. uart0 i uart1. Przykład będzie korzystał ze standardowego uart0. #include <stdio.h> #include "pico/stdlib.h" int main() { stdio_init_all(); // Init GPIO gpio_init(0); gpio_init(1); // Ustaw funkcje GPIO jako UART gpio_set_function(0, GPIO_FUNC_UART); gpio_set_function(1, GPIO_FUNC_UART); // Zainicjuj uart0 z transmisją 115200 bps uart_init(uart0, 115200); // Jeżeli na uart0 są jakieś dane if(uart_is_readable(uart0)){ // tablica do odczytu pojedynczego bajtu uint8_t data[1]; // odczytaj bajt uart_read_blocking(uart0, data, 1); // wyślij bajt spowrotem na uart0 uart_write_blocking(uart0, data, 1); } return 0; } Funkcje uart_init(<uart>, <baud>) - inicjuje UART (Serial.begin(<baudrate>)) uart_set_baudrate(<uart>, <baud>) - zmiana baudrate UART'a w locie 😉 uart_is_writable(<uart>) - sprawdza czy bufor zapisu ma miejsce uart_is_readable(<uart>) - sprawdza czy w buforze odczytu są dane uart_write_blocking(<uart>, <data>, <length>) - wysyła bajty na UART, data to wskaźnik do tablicy z danymi uart_read_blocking(<uart>, <data>, <length>) - oczytuje bajty z UART, data to wskaźnik do tablicy z danymi uart_putc(<uart>, <char>) - wysyła znak na UART uart_puts(<uart>, <string>) - wysyła tekst na UART uart_getc(<uart>) - odczytuje znak z UART I2C (IIC) By zainicjować I2C musimy się chwilę natrudzić - po pierwsze musimy dodać bibliotekę hardware_i2c do pliku CMakeLists.txt. Szukamy linijki "target_link_libraries(<NAZWA_PROJEKTU> pico_stdlib)" i dodajemy pod nią "target_link_libraries(<NAZWA_PROJEKTU> hardware_i2c)". Teraz musimy wykonać całą procedurę inicjacji - zainicjować I2C, podłączyć pullupy oraz ustawić piny w tryb I2C. Przykład poniżej: // Inicjacja I2C i2c_init(i2c0, 1000*100); gpio_set_function(4, GPIO_FUNC_I2C); gpio_set_function(5, GPIO_FUNC_I2C); // I2C wymaga pull-upów na liniach ;) gpio_pull_up(4); gpio_pull_up(5); Jak widzimy moje I2C jest podłączone do pinów 4 i 5. Pin 4 to SDA, pin 5 to SCL. Baudrate to 100kbit. By odczytać dane używamy funkcji i2c_read_blocking, a by zapisać i2c_write_blocking. Widać podobieństwo do UARTa? #include <stdio.h> #include "pico/stdlib.h" #include "hardware/i2c.h" int main() { stdio_init_all(); printf("Witaj I2C!\n"); // Inicjacja I2C i2c_init(i2c0, 1000*100); gpio_set_function(4, GPIO_FUNC_I2C); gpio_set_function(5, GPIO_FUNC_I2C); // I2C wymaga pull-upów na liniach ;) gpio_pull_up(4); gpio_pull_up(5); uint8_t data[4]{'t', 'e', 's', 't'}; // Wyślij dane poprzez I2C i2c_write_blocking(i2c0, 0x01, data, 4, false); i2c_read_blocking(i2c0, 0x01, data, 4, false); } Przykładowy kod powyżej 😉 - wysyła słowo "test" na adres 0x01 po czym pobiera 4 bajty z adresu 0x01. Funkcje i2c_init (<i2c>, <baudrate>) - inicjuje I2C 😉 i2c_set_baudrate(<i2c>, <baudrate>) - zmiana baudrate w locie i2c_set_slave_mode(<i2c>, <status>, <adres>) - przełącza Pi Pico w tryb slave dla I2C - jeżeli status jest true to urządzenie jest slave, jeżeli false to jest masterem. Adres jest adresem urządzenia slave, na którym ma działać nasze Pico 😉 i2c_write_blocking(<i2c>, <adres>, <dane>, <długość>, <nostop>) - wysyła dane poprzez I2C - dane to wskaźnik do tablicy bajtów (uint8_t), jeżeli nostop jest true to po wysłaniu nie zostanie wysłany sygnał stop (z reguły false) i2c_read_blocking(<i2c>, <adres>, <tablica>, <długość>, <nostop>) - pobiera dane poprzez I2C - tablica to wskaźnik do miejsca, gdzie mają one zostać zapisane w pamięci (np. tablica uint8_t). Parametr nostop podobnie jak w górnym przypadku opisuje czy program ma pominąć sygnał STOP. i2c_get_write_available(<i2c>) - sprawdza ilość bajtów dostępnych do zapisu na danej magistrali I2C i2c_get_read_available(<i2c>) - sprawdza ilość bajtów dostępnych do odczytu na danej magistrali I2C. SPI Musimy na początku dołączyć bibliotekę hardware_spi. Po tym możemy stworzyć już swój program z obsługą SPI 😉 Przykład: #include <stdio.h> #include "pico/stdlib.h" #include "hardware/spi.h" int main() { stdio_init_all(); // Inicjacja SPI spi_init(spi0, 12000000); // 12 MHz spi_set_format(spi0, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST); gpio_set_function(6, GPIO_FUNC_SPI); // SCK gpio_set_function(7, GPIO_FUNC_SPI); // TX gpio_set_function(8, GPIO_FUNC_SPI); // RX uint8_t write_data[4] = {'t', 'e', 's', 't'}; uint8_t read_data[4]; // Wyślij 4 bajty na SPI spi_write_blocking(spi0, write_data, 4); // Wyślij 4 razy 0 na SPI do odczytu 4 bajtów. spi_read_blocking(spi0, 0x0, read_data, 4); // Pełny duplex SPI ;) spi_write_read_blocking(spi0, write_data, read_data, 4); } Jak widzimy program inicjuje SPI z prędkością 12Mbit/s, po czym przesyła słowo 'test' na magistralę, odczytuje 4 bajty (przesyłając 4 razy 0x0 by odczytać treść) oraz dokonuje transmisji duplex - wysyła słowo 'test' oraz odczytuje w tym samym czasie 4 bajty. Funkcje spi_init(<spi>, <baudrate>) - inicjuje SPI spi_set_format(<spi>, <data_bits>, <cpol>, <cpha>, <order>) - ustawia tryb SPI - ilość bitów oraz parametry CPOL, CPHA i określa czy przesyłamy najpierw MSB czy LSB. spi_set_slave(<spi>, <aktywny>) - jeżeli parametr "aktywny" ustawimy na true to nasze Pi Pico stanie się "slave" SPI zamiast "masterem". spi_is_writable(<spi>) - sprawcza czy na SPI możemy zapisać dane (ilość miejsca w buforze) 😉 spi_is_readable(<spi>) - sprawdza ilość danych odczytanych przechowywanych w buforze SPI spi_read_blocking(<spi>, <bajt>, <dane>, <ilość>) - odczytuje dane z SPI jednocześnie przesyłając określoną ilość razy parametr "bajt" np. 0x0 (zgodnie z standardem SPI). Dane to wskaźnik do tablicy, gdzie dane mają zostać odczytane, a ilość to długość tej tablicy 🙂 spi_write_blocking(<spi>, <dane>, <ilość>) - przesyła dane na SPI - dane to wskaźnik do tablicy z danymi, ilość określa długość tej tablicy 🙂 spi_write_read_blocking(<spi>, <dane_zapis>, <dane_odczyt>, <ilość>) - połączenie read i write - transmisja duplex SPI. dane_zapis to wskaźnik do danych do wysłania na magistralę SPI, a dane_odczyt to wskaźnik do miejsca, gdzie dane mają zostać odczytane. Ilość określa ile danych ma być przesłane. PWM Pi Pico ma całkiem przyjazny w konfiguracji PWM (to jest półsarkazm). Jest on obsługiwany sprzętowo, więc potrzebujemy dodać bibliotekę do pliku CMakeLists.txt. Szukamy linijki "target_link_libraries(<NAZWA_PROJEKTU> pico_stdlib)" i dodajemy pod nią "target_link_libraries(<NAZWA_PROJEKTU> hardware_pwm)". Potem aktualizujemy plik CMake - w Clionie u góry pojawia się do tego ładny pasek z przyciskiem 😉 Klikamy reload changes i gotowe 😉 Teraz czas użyć PWM... #include <stdio.h> #include "pico/stdlib.h" #include "hardware/pwm.h" int main() { stdio_init_all(); gpio_init(0); // Init GPIO gpio_set_function(0, GPIO_FUNC_PWM); // Setup PWM // Pobierz dane PWM - slice i kanał uint slice_num = pwm_gpio_to_slice_num(0); uint channel = pwm_gpio_to_channel(0); // Ustaw długość trwania cyklu PWM na 65536 (granica licznika) pwm_set_wrap(slice_num, 65535); // Kanał PWM będzie miał stan wysoki tylko przez 32767 cykli licznika (ok 50%) pwm_set_chan_level(slice_num, channel, 32767); // Aktywuj PWM pwm_set_enabled(slice_num, true); return 0; } Efekt? Funkcje pwm_gpio_to_slice_num(<gpio>) - pobiera adres slice'a dla PWM pwm_gpio_to_channel(<gpio>) - pobiera adres kanału PWM pwm_set_wrap(<slice>, <cycles>) - ustala ilość cykli PWM dla danego kanału pwm_set_chan_level(<slice>, <channel>, <cycles>) - przez tyle cykli licznika PWM będzie miało stan wysoki pwm_set_enabled(<slice>, <stan>) - aktywuje PWM na danym slice 😉 ADC By odczytać ADC musimy załączyć odpowiednią bibliotekę - hardware_adc do pliku CMakeLists.txt. Szukamy linijki "target_link_libraries(<NAZWA_PROJEKTU> pico_stdlib)" i dodajemy pod nią "target_link_libraries(<NAZWA_PROJEKTU> hardware_adc)". Następnie musimy zainicjować adc, wybrać odpowiedni kanał oraz odczytać wartość. adc_init(); // zainicjuj ADC (wykonaj na początku main()) adc_gpio_init(gpio); // zainicjuj pin ADC adc_select_input(gpio - 26); // wybierz wejście ADC adc_read(); // Odczytaj wartość z ADC Piny ADC na Raspberry Pi Pico to: GP26, GP27, GP28 - odpowiednio wejście 0, 1, 2 Oprócz tego mamy również wejście 3 - wewnętrzny sensor temperatury, gdyby nasza płytka pracowała w mniej sprzyjających warunkach 😉 Funkcje adc_init() - inicjuje ADC, musi być wykonane przed odczytem adc_gpio_init(<gpio>) - inicjacja pinu GPIO adc_select_input(<id>) - wybiera wejście ADC (0 - GP26, 1 - GP27, 2 - GP28, 3 - sensor temperatury) adc_read() - odczytuje wartość z ADC (12-bit) Przerwania na GPIO GPIO obsługuje przerwania - podczas narastania, opadania zbocza oraz również przerwania "stanów" - wysokiego i niskiego. By dodać przerwanie na GPIO używamy funkcji gpio_set_irq_enabled_with_callback(<gpio>, <rodzaj przerwania>, true, <handler>); Oraz określamy handler przerwania np. void gpio_irq_handler(uint gpio, uint32_t events) { status = !status; gpio_put(15, status); } I wtedy nasza funkcja wygląda przykładowo: gpio_set_irq_enabled_with_callback(2, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, gpio_irq_handler); W tym przypadku przerwanie nastąpi podczas narastania lub opadania zbocza i wykona powyższą funkcję przerwania. Uwaga: na jeden rdzeń może być tylko jedno przerwanie SIO. Przykładowy program z przerwaniami: #include <stdio.h> #include "pico/stdlib.h" bool status = false; void gpio_irq_handler(uint gpio, uint32_t events) { status = !status; // Zmień status gpio_put(15, status); // Przełącz diodę } int main() { stdio_init_all(); // Zainicjuj pin z diodą gpio_init(15); gpio_set_dir(15, true); gpio_put(15, status); // Ustal przerwanie gpio_set_irq_enabled_with_callback(2, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, gpio_irq_handler); // Nieskończona pętla - WYMAGANA!!! while (1) { tight_loop_contents(); } } UWAGA: przerwania wymagają, by funkcja main wciąż się wykonywała - można to uzyskać w sposób podany powyżej! Alarm Alarm - jednorazowe przerwanie, Timer - powtarzające się wykonanie przerwania - tak można to najprościej opisać 😉 Timery omówimy w następnej sekcji, tymczasem zajmiemy się tematem tej... Przykładowe wywołanie alarmu: add_alarm_in_ms(1000, alarm, NULL, false); Alarm pozwala nam wykonać fragment kodu po określonym czasie - uwaga, dalszy kod wykonuje się pomimo ustawienia alarmu. W celu odczekania na wykonanie przerwania należy ustawić zmienną flagową i pętlę opartą na tej zmiennej. Przykładowy kod: #include <stdio.h> #include "pico/stdlib.h" // Alarm int64_t alarm(alarm_id_t id, void *user_data){ gpio_put(15, true); return 0; } int main() { stdio_init_all(); // Zainicjuj pin z diodą gpio_init(15); gpio_set_dir(15, true); gpio_put(15, false); // Dodaj alarm add_alarm_in_ms(1000, alarm, NULL, false); // Nieskończona pętla - WYMAGANA!!! while (1) { tight_loop_contents(); } } Jak widzimy w alarmie musimy podać czas (w tym przypadku ms, istnieje również wersja tej funkcji pracująca z mikrosekundami - add_alarm_in_us), callback naszego alarmu - funkcję, która będzie wykonana. Jej deklaracja musi być zgodna z tą powyżej (oprócz nazwy). Do tego możemy przesłać jakieś dane w formie wskaźnika do np. struktury. Ostatni parametr definiuje czy alarm ma się wykonać, kiedy jego czas już dawno minął. #include <stdio.h> #include "pico/stdlib.h" int64_t alarm(alarm_id_t id, void *user_data){ uint* pinId = (uint*) user_data; gpio_put(*pinId, true); return 0; } int main() { stdio_init_all(); // Zainicjuj pin z diodą gpio_init(15); gpio_set_dir(15, true); gpio_put(15, false); // Zmienna tymczasowa uint pinId = 15; add_alarm_in_ms(1000, alarm, &pinId, false); // Nieskończona pętla - WYMAGANA!!! while (1) { tight_loop_contents(); } } Jak widać na tym kodzie - możemy bez problemu przesłać np. numer gpio, które chcemy zmodyfikować w naszym alarmie 😉 Możemy również pobrać ID alarmu i w dowolnym momencie przerwać nasz alarm 😉 alarm_id_t id = add_alarm_in_ms(1000, alarm, &pinId, false); cancel_alarm(id); // Przerwij alarm o określonym ID Funkcje add_alarm_in_ms(<opóźnienie_ms>, <callback>, <user_data>, <aktywuj_przeszły>) - dodaje alarm opóźniony o podany czas wyrażony w milisekundach, ostatni parametr opisuje, że jeżeli dany moment nastąpi zanim ta metoda się wykona to callback również zostanie wykonany add_alarm_in_us(<opóźnienie_us>, <callback>, <user_data>, <aktywuj_przeszły>) - to samo co powyżej tylko zamiast ms są us 😉 cancel_alarm(<alarm_id>) - anuluje alarm o określonym ID pozyskanym z funkcji dodawania alarmu Oprócz alarmów są też timery... Teraz czas na timery - alarmy powtarzające się co pewien czas 😉 Timer inicjujemy podobnie do alarmu 🙂 struct repeating_timer timer; add_repeating_timer_ms(100, tCallback, &pin, &timer); Z tym, że na początku musimy stworzyć strukturę timera, potem możemy go dodać podając czas, przerwanie, dane użytkownika oraz adres naszej struktury. bool tCallback(repeating_timer_t * timer){ status = !status; uint* pinId = (uint*) timer->user_data; gpio_put(*pinId, status); return true; } Funkcja przerwania też jest ciut inna - ma inny typ zwracany i tylko jeden parametr - wskaźnik do timera. Jak widzimy w powyższym przykładzie dane są przechowywane wewnątrz naszego timera, więc by je uzyskać musimy odwołać się do pola zawartego wewnątrz struktury. Przykładowy kod: #include <stdio.h> #include "pico/stdlib.h" bool status = false; bool tCallback(repeating_timer_t * timer){ status = !status; uint* pinId = (uint*) timer->user_data; gpio_put(*pinId, status); return true; } int main() { stdio_init_all(); // Zainicjuj pin z diodą gpio_init(15); gpio_set_dir(15, true); gpio_put(15, false); uint pin = 15; struct repeating_timer timer; add_repeating_timer_ms(100, tCallback, &pin, &timer); // Nieskończona pętla - WYMAGANA!!! while (1) { tight_loop_contents(); } } Funkcje add_repeating_timer_ms(<czas_ms>, <callback>, <dane_usera>, <timer>) - uruchamia powtarzający się co określony w ms czas timer. Wcześniej należy stworzyć strukturę timera, patrz przykład. add_repeating_timer_us(<czas_us>, <callback>, <dane_usera>, <timer>) - to samo co powyżej tylko dla us 😉 cancel_repeating_timer(<timer>) - anuluje wykonywanie danego timera 😉 Wielowątkowość Miało nie być... jednak jest... Do obsługi wielowątkowości musimy dołączyć bibliotekę - pico_multicore do pliku CMakeLists.txt. Szukamy linijki "target_link_libraries(<NAZWA_PROJEKTU> pico_stdlib)" i dodajemy pod nią "target_link_libraries(<NAZWA_PROJEKTU> pico_multicore)". Następnie możemy stworzyć prosty program, który uruchomi kod na drugim rdzeniu - przykład? #include <stdio.h> #include "pico/stdlib.h" #include "pico/multicore.h" void core1_entry() { printf("To jest rdzeń 2!\n"); while (1) tight_loop_contents(); } int main() { stdio_init_all(); printf("Witaj wiele rdzeni!\n"); printf("To jest rdzeń 1!\n"); multicore_launch_core1(core1_entry); } Do uruchomienia funkcji na innym rdzeniu używamy multicore_launch_core1. W środku umieszczamy nazwę funkcji, którą chcemy uruchomić. Jej deklaracja musi się pokrywać z tą zaprezentowaną powyżej - musi to być funkcja nie zwracająca żadnej wartości i bez parametrów. Ale jak przekazać informacje między rdzeniami? Do tego należy wykorzystać funkcje multicore_fifo_push_blocking(<value>) oraz multicore_fifo_pop_blocking(). Pierwszą umieszczamy w kodzie wątku źródłowego, a drugą w docelowym. Pierwsza z nich ustawia wartość semafora, druga odczytuje. Dopóki semafor nie zostanie odczytany z wątku docelowego, wtedy kod na wątku źródłowym oczekuje (nie wykonuje innych operacji). Oprócz tego istnieje funkcja, która ustawia semafor z timeoutem, aczkolwiek tutaj odeślę do oficjalnej dokumentacji Pi Pico 😉 Przykład kodu z semaforami? #include <stdio.h> #include "pico/stdlib.h" #include "pico/multicore.h" #define FLAG_VALUE 123 void core1_entry() { multicore_fifo_push_blocking(FLAG_VALUE); uint32_t g = multicore_fifo_pop_blocking(); if (g != FLAG_VALUE) // Porównanie 1 printf("Semafor (0) nie jest OK :(\n"); else printf("Semafor (0) jest OK ;)\n"); while (1) tight_loop_contents(); } int main() { stdio_init_all(); printf("Witajcie rdzenie!\n"); multicore_launch_core1(core1_entry); sleep_ms(100); uint32_t g = multicore_fifo_pop_blocking(); if (g == FLAG_VALUE){ // Porównanie 2 multicore_fifo_push_blocking(FLAG_VALUE); printf("Semafor (1) jest OK ;)\n"); } else { printf("Semafor (1) nie jest OK :(\n"); } } Kod wykona się całkowicie poprawnie. Jego efekt na konsoli UART powinien wyglądać następująco: Działanie kodu w uproszczony sposób można opisać następująco: Rdzeń 1 Wyślij semafor o wartości FLAG_VALUE i oczekuj aż zostanie odebrany Oczekuj na semafor Jeżeli otrzymany semafor ma wartość FLAG_VALUE poinformuj, że semafor 0 JEST OK, w innym przypadku poinformuj, że semafor NIE JEST OK Zapętl się w nieskończoność Rdzeń 0 Wyświetl powitanie Uruchom funkcję core1_entry() na rdzeniu 1 Odczekaj 100ms Oczekuj na semafor Jeżeli semafor ma wartość FLAG_VALUE Poinformuj, że semafor 1 JEST OK Wyślij semafor o wartości FLAG_VALUE i oczekuj na odbiór Jeżeli punkt 5 nie został spełniony Poinformuj, że semafor 1 NIE JEST OK Jak można wywnioskować z powyższego opisu, gdy rdzeń 1 wyśle niepoprawny semafor do rdzenia 0, to kod z rdzenia drugiego się nie wykona. Możesz sam to sprawdzić podmieniając wartość FLAG_VALUE w funkcji multicore_fifo_push_blocking w core1_entry 😉 Wtedy rezultat w konsoli będzie wyglądał następująco: Funkcje multicore_launch_core1(<funkcja>) - uruchom funkcję na drugim rdzeniu (wyłącznie void bez parametrów) multicore_fifo_push_blocking(<wartość>) - wyślij semafor do drugiego rdzenia i oczekuj na jego pobranie multicore_fifo_pop_blocking() - pobierz wartość semafora (wykonywane z drugiego rdzenia) Zaawansowane Przerwania? Inne tematy? Temat zaawansowanych przerwań opisuje biblioteka hardware_irq. Tutaj odsyłam do oficjalnej dokumentacji Pi Pico 😉 Nie mogę przecież wszystkiego podawać na dłoni 😉 Ten poradnik miał omówić najbardziej podstawowe zagadnienia z pisania na Pi Pico. Powyższych do nich nie zaliczam... Dodatkowo warto też zapoznać się z wbudowanym RTC 🙂 Dodatkowe funkcje sleep_ms(<czas>) - odczekaj określony w ms czas sleep_us(<czas>) - odczekaj n mikrosekund Visual Studio - alternatywna metoda kompilacji (zalecana) Alternatywnie projekt można skompilować poprzez konsolę developerską (Developer Command Prompt). Wchodzimy do folderu z plikami, potem do podfolderu "build" np. cd C:\Users\nov11\Documents\Pico\pico-project-generator\Blinky\build Potem wpisujemy komendę generującą pliki cmake -G "NMake Makefiles" .. Potem możemy skompilować program nmake I gotowe 😉 Pytania? Po to jest sekcja komentarzy 😉
×
×
  • 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.