Popularny post pmochocki Napisano Grudzień 4, 2022 Popularny post Udostępnij Napisano Grudzień 4, 2022 (edytowany) Kupiłeś płytkę do kursu STM32L? Skończyłeś kurs, a płytka teraz się kurzy? A może poznawanie HALa STM32 już trochę Cię nudzi i chciałbyś spróbować czegoś zupełnie innego? Z pomocą może przyjść Zefir (gr. Ζέφυρος) - grecki bóg i uosobienie wiatru zachodniego. Wikipedia twierdzi, że wiosną budzi do życia przyrodę, a latem przynosi orzeźwiające ochłodzenie. Ja myślę, że tej zimy sprawi, że przestanie wiać nudą i że znów będziesz mógł pobawić się płytką NUCLEO-L476RG w sposób jaki pewnie tego nie planowałeś. Zephyr jest małym systemem czasu rzeczywistego dla urządzeń wbudowanych o ograniczonych zasobach. Wspiera różne architektury i może być wykorzystywany przez urządzenia oparte na mikrokontrolerach. Szczególny nacisk jest w nim położony jest na bezpieczeństwo, łączność i pobór mocy. Należy nadmienić, że projekt zarządzany jest przez The Linux Foundation. Niektóre cechy charakterystyczne Zephyr RTOS: Małe jądro Aplikacja i jądro działają w tej samej przestrzeni adresowej - kod aplikacji i kod jądra, połączone w jeden plik binarny. Wysoki stopień modułowości i konfigurowalności - używa Kconfig oraz devicetree podobnie jak jądro Linuxa, jednak implementacja jest w Pythonie Zasoby są definiowane statycznie w czasie kompilacji Różne algorytmy planowania Szeroki wachlarz wspieranych protokołów komunikacyjnych: (IPv4 oraz IPv6, Constrained Application Protocol (CoAP), LwM2M, MQTT, 802.15.4, Thread, Bluetooth Low Energy, CAN) Interfejs wirtualnego systemu plików z kilkoma systemami plików do przechowywania nieulotnego (FatFs, LittleFS, NVS) Mechanizmy zarządzania aktualizacją oprogramowania Pierwsze wydanie Zephyr RTOS miało miejsce w roku 2016. Sam fakt powstania tego projektu został także zauważony na Forbocie https://forbot.pl/blog/projekt-zephyr-otwarty-system-dla-rynku-iot-id13085 Osobiście kibicuję temu projektowi prawie od początku, ale do tej pory robiłem to raczej biernie. Obserwowałem jak angażują się kolejne firmy i jak dodawane jest wsparcie dla imponującej liczby płyt rozwojowych i "development kitów". Miałem jednak wątpliwości. Zastanawiałem się, czy ktoś porzuci stare rozwiązania "bare metal", czy dedykowany HAL i przejdzie na Zephyr RTOS bez obawy o wydajność i narzut jaki wprowadza dodatkowa warstwa. Interesująco zaczęło się robić gdy Nordic Semiconductor dla nowych układów nRF53 nie dodał wsparcia w ich sławnym nRF5 SDK. Nowe układy są wspierane tylko przez nRF Connect SDK opartym na Zephyr RTOS. Tu można poczytać dlaczego firma zdecydowała się na taki ruch: https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/nrf-connect-sdk-and-nrf5-sdk-statement Jestem ciekaw czy Nordic Semiconductor postawił na właściwą kartę. Dekadę temu STMicroelectronics tworząc HALa zdobył przewagę biznesową nad konkurencją. Czy tak będzie z Nordic Semiconductor, który praktycznie zmusza klientów do używania Zephyra? Od kilku tygodni staram się zgłębiać ten system operacyjny. Pierwsze wrażenia są bardzo pozytywne. Dokumentacja jest dość dobrej jakości. Sam kod źródłowy czyta się z przyjemnością. Chyba na razie wystarczy teoretyzowania. Na podstawie strony: https://docs.zephyrproject.org/latest/develop/getting_started/index.html zestawiłem sobie środowisko do pracy z tym systemem na Ubuntu 22.04 LTS. Jest też opis dla Maca i Windowsa. Proszę zwrócić uwagę, że WSL nie jest obecnie wspierany: "Due to issues finding executables, the Zephyr Project doesn’t currently support application flashing using the Windows Subsystem for Linux (WSL)" Nie będę kopiował tu komend z powyższej strony. Nadmienię, że wszystko zadziałało od ręki. Płytka NUCLEO-L476RG występuje w projekcie pod nazwą: "nucleo_l476rg", więc aby skompliować pierwszy sample pod nazwą "blinky" wywołujemy: west build -p always -b nucleo_l476rg samples/basic/blinky -- west build: making build dir /home/piotrek/zephyrproject/zephyr/build pristine -- west build: generating a build system Loading Zephyr default modules (Zephyr base). -- Application: /home/piotrek/zephyrproject/zephyr/samples/basic/blinky -- Found Python3: /usr/bin/python3.10 (found suitable exact version "3.10.6") found components: Interpreter -- Cache files will be written to: /home/piotrek/.cache/zephyr -- Zephyr version: 3.2.99 (/home/piotrek/zephyrproject/zephyr) -- Found west (found suitable version "0.14.0", minimum required is "0.7.1") -- Board: nucleo_l476rg -- ZEPHYR_TOOLCHAIN_VARIANT not set, trying to locate Zephyr SDK -- Found host-tools: zephyr 0.15.2 (/home/piotrek/zephyr-sdk-0.15.2) -- Found toolchain: zephyr 0.15.2 (/home/piotrek/zephyr-sdk-0.15.2) -- Found Dtc: /home/piotrek/zephyr-sdk-0.15.2/sysroots/x86_64-pokysdk-linux/usr/bin/dtc (found suitable version "1.6.0", minimum required is "1.4.6") -- Found BOARD.dts: /home/piotrek/zephyrproject/zephyr/boards/arm/nucleo_l476rg/nucleo_l476rg.dts -- Generated zephyr.dts: /home/piotrek/zephyrproject/zephyr/build/zephyr/zephyr.dts -- Generated devicetree_generated.h: /home/piotrek/zephyrproject/zephyr/build/zephyr/include/generated/devicetree_generated.h -- Including generated dts.cmake file: /home/piotrek/zephyrproject/zephyr/build/zephyr/dts.cmake Parsing /home/piotrek/zephyrproject/zephyr/Kconfig Loaded configuration '/home/piotrek/zephyrproject/zephyr/boards/arm/nucleo_l476rg/nucleo_l476rg_defconfig' Merged configuration '/home/piotrek/zephyrproject/zephyr/samples/basic/blinky/prj.conf' Configuration saved to '/home/piotrek/zephyrproject/zephyr/build/zephyr/.config' Kconfig header saved to '/home/piotrek/zephyrproject/zephyr/build/zephyr/include/generated/autoconf.h' -- The C compiler identification is GNU 12.1.0 -- The CXX compiler identification is GNU 12.1.0 -- The ASM compiler identification is GNU -- Found assembler: /home/piotrek/zephyr-sdk-0.15.2/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc CMake Warning at /home/piotrek/zephyrproject/zephyr/CMakeLists.txt:824 (message): No SOURCES given to Zephyr library: zdsp Excluding target from build. -- Configuring done -- Generating done -- Build files have been written to: /home/piotrek/zephyrproject/zephyr/build -- west build: building application [1/156] Preparing syscall dependency handling [2/156] Generating include/generated/version.h -- Zephyr version: 3.2.99 (/home/piotrek/zephyrproject/zephyr), build: zephyr-v3.2.0-2246-g041e85303c7f [146/156] Linking C executable zephyr/zephyr_pre0.elf [150/156] Linking C executable zephyr/zephyr_pre1.elf [156/156] Linking C executable zephyr/zephyr.elf Memory region Used Size Region Size %age Used FLASH: 14728 B 1 MB 1.40% RAM: 4288 B 96 KB 4.36% IDT_LIST: 0 GB 2 KB 0.00% Następnie "flashujemy", ponieważ płytka NUCLEO-L476RG jest wyposażona w programator ST-Link, wystarczy podpiąć kabel USB i wszystko działa "out of the box". west flash -- west flash: rebuilding ninja: no work to do. -- west flash: using runner openocd -- runners.openocd: Flashing file: /home/piotrek/zephyrproject/zephyr/build/zephyr/zephyr.hex Open On-Chip Debugger 0.11.0+dev-00725-gc5c47943d (2022-11-22-06:32) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD Info : clock speed 500 kHz Info : STLINK V2J30M19 (API v2) VID:PID 0483:374B Info : Target voltage: 3.253193 Info : [stm32l4x.cpu] Cortex-M4 r0p1 processor detected Info : [stm32l4x.cpu] target has 6 breakpoints, 4 watchpoints Info : starting gdb server for stm32l4x.cpu on 3333 Info : Listening on port 3333 for gdb connections TargetName Type Endian TapName State -- ------------------ ---------- ------ ------------------ ------------ 0* stm32l4x.cpu hla_target little stm32l4x.cpu running Info : Unable to match requested speed 500 kHz, using 480 kHz Info : Unable to match requested speed 500 kHz, using 480 kHz target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x0800294c msp: 0x20000520 Info : device idcode = 0x10076415 (STM32L47/L48xx - Rev 4 : 0x1007) Info : RDP level 0 (0xAA) Info : flash size = 1024kbytes Info : flash mode : dual-bank Warn : Adding extra erase range, 0x08003988 .. 0x08003fff auto erase enabled wrote 14728 bytes from file /home/piotrek/zephyrproject/zephyr/build/zephyr/zephyr.hex in 0.471472s (30.506 KiB/s) Info : Unable to match requested speed 500 kHz, using 480 kHz Info : Unable to match requested speed 500 kHz, using 480 kHz target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x080010cc msp: 0x20001080 verified 14728 bytes in 0.086148s (166.955 KiB/s) Info : Unable to match requested speed 500 kHz, using 480 kHz Info : Unable to match requested speed 500 kHz, using 480 kHz shutdown command invoked W ten sposób zamrugaliśmy diodą za pomocą 32 bitowego mikrokontrolera z RTOSem 🙂 W najbliższym czasie nadal będę zajmował się Zephyr RTOSem, bo na nim ma bazować mój nowy projekt. Nie będzie on wprawdzie oparty o STM32, ale część eksperymentów planuję właśnie przeprowadzić na NUCLEO-L476RG. Jeśli temat was zaciekawił i chcielibyście abym umieszczał tu informacje o Zephyr RTOS to dajcie znać. Jeśli ktoś inny zna lub chciałby że mną wspólnie odkrywać ten system operacyjny też dajcie znać. Edytowano Grudzień 4, 2022 przez pmochocki 6 Link do komentarza Share on other sites More sharing options...
_LM_ Grudzień 5, 2022 Udostępnij Grudzień 5, 2022 Ja się chętnie dowiem czegoś nowego 2 Link do komentarza Share on other sites More sharing options...
Treker (Damian Szymański) Grudzień 5, 2022 Udostępnij Grudzień 5, 2022 @pmochocki dzięki za założenie tego tematu! Zagadnienie jest ciekawe, dosłownie kilka dni temu rozmawiałem z @Elvis m.in. o tym temacie - chyba ktoś nas podsłuchiwał 😉 Również ciekawi mnie jakie będzie zainteresowanie ze strony innych osób. 2 Link do komentarza Share on other sites More sharing options...
MR1979 Grudzień 11, 2022 Udostępnij Grudzień 11, 2022 Ja jestem zainteresowany 🙂 2 Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Produkcja i montaż PCB - wybierz sprawdzone PCBWay! • Darmowe płytki dla studentów i projektów non-profit • Tylko 5$ za 10 prototypów PCB w 24 godziny • Usługa projektowania PCB na zlecenie • Montaż PCB od 30$ + bezpłatna dostawa i szablony • Darmowe narzędzie do podglądu plików Gerber Zobacz również » Film z fabryki PCBWay
szczygiel256 Grudzień 11, 2022 Udostępnij Grudzień 11, 2022 Ja też jestem zainteresowany. Zawsze warto poczytać o czymś nowym. 2 Link do komentarza Share on other sites More sharing options...
pmochocki Grudzień 19, 2022 Autor tematu Udostępnij Grudzień 19, 2022 (edytowany) Zastrzeżenie: Poniższy opis powstał na bazie mojej najlepszej wiedzy, ale z związku z tym, że dopiero poznaję Zephyr RTOS może mieć błędy. Kolejnym, naturalnym krokiem byłoby stworzenie własnej aplikacji. W sumie to mógłbym napisać przeczytajcie sobie to: https://docs.zephyrproject.org/latest/develop/application/index.html Niby tam wszystko jest, ale mi osobiście brakowało szerszej perspektywy. No więc zanim zaczniemy tworzyć własną aplikację, zobaczmy czy rozumiemy jak działa toolchain Zephyr RTOS. Pod podanym wyżej linkiem czytamy, że system budowania Zephyra opiera się na CMake'u. Dla osób, które go nie znają podpowiem, że ze jest to program do generacji plików z regułami kompilacji. Ogólnie CMake może tworzyć makefile'e czy pliki solution Visual Studio. Dla Zephyr RTOS, niezależnie od systemu operacyjnego na którym przeprowadzamy kompilację, CMake generuje reguły budowania dla Ninja. Następnie system budowania Ninja wykonuje reguły, budując aplikację za pomocą GCC. Należy podkreślić, że opisuję tu tylko podstawowy toolchain. Bardziej zaawansowane osoby pewnie chętnie wypróbują np.: ARM Compiler w wersji 6, który bazuje na Clang/LLVM. Czyli ogólnie wygląda to tak: Jeśli ktoś nie zna CMake'a i/lub Ninja to gorąco zachęcam do nadrabiania zaległości. Osoby, które znają te narzędzia mogą się zastanawiać, o co chodzi z szerszą perspektywą, skoro combo CMake + Ninja jest powszechnie stosowane. Należy więc rozważyć co jest wejściem do CMake'a w Zephyr RTOS: Kconfig - Dla osób, które kiedyś same kompilowały jądro Linuxa ten koncept nie powinien być nowy. W Zephyr RTOS każda aplikacja musi mieć plik konfiguracyjny, zwykle nazywany prj.conf, który opisuje używane moduły oprogramowania i usługi jądra oraz ich ustawienia. Plik konfiguracyjny jest tekstowy i zawiera opcje konfiguracyjne (zwane po angielsku symbols) w postaci: CONFIG_<symbol>=<wartość> Na przykład, aby dodać moduł UARTa i GPIO w pliku konfiguracyjnym umieszczamy: CONFIG_SERIAL=y CONFIG_GPIO=y Tak jak pisałem w poprzednim poście aplikacja i jądro działają w tej samej przestrzeni adresowej, a kod aplikacji i kod jądra, połączone w jeden plik binarny. Dzięki Kcofig do jądra wkompilowane są tylko niezbędne moduły, więc wyjściowa binarna jest mała. Informacja, które to są moduły, znana jest przed rozpoczęciem procesu kompilacji, dzięki temu mimo, że musimy zawsze skompilować jądro i sterowniki proces ten jest bardzo sprawny. Device Tree - Dla mnie to podejście jest zupełną nowością. Nie wiem ile osób czytając mój poprzedni post pokusiło się, aby uruchomić przykłąd na płytce. Nie wiem też czy ktoś może zerknął do kodu tego przykładu: https://github.com/zephyrproject-rtos/zephyr/blob/main/samples/basic/blinky/src/main.c /* * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include <zephyr/kernel.h> #include <zephyr/drivers/gpio.h> /* 1000 msec = 1 sec */ #define SLEEP_TIME_MS 1000 /* The devicetree node identifier for the "led0" alias. */ #define LED0_NODE DT_ALIAS(led0) /* * A build error on this line means your board is unsupported. * See the sample documentation for information on how to fix this. */ static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios); void main(void) { int ret; if (!device_is_ready(led.port)) { return; } ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); if (ret < 0) { return; } while (1) { ret = gpio_pin_toggle_dt(&led); if (ret < 0) { return; } k_msleep(SLEEP_TIME_MS); } } Kod jest banalnie prosty, więc czemu go tu umieszczam? Ponieważ całą potęgę Device Tree można zrozumieć jak się spojrzy na ten kod i uświadomi sobie, że gdy zamiast wywołać: west build -p always -b nucleo_l476rg samples/basic/blinky wywołam: west build -p always -b nrf52840dongle_nrf52840 samples/basic/blinky to ten kod zadziała na dongle'u nRF52840. Tak się składa, że posiadam ten dongle i po wypaleniu dioda mruga. Więc jak spojrzymy na to szerzej, wszystkie wspierane płytki, które mają w Device Tree opisanego led0, będą w stanie uruchomić ten przykład. Muszą oczywiście mieć poprawnie skonfigurowany timmer by też wspierać k_msleep. To tyle na dziś. Więcej o strukturze Device Tree następnym razem. Edytowano Grudzień 19, 2022 przez pmochocki 2 Link do komentarza Share on other sites More sharing options...
pmochocki Styczeń 3, 2023 Autor tematu Udostępnij Styczeń 3, 2023 (edytowany) Czas na Device Tree. Szczegółowe informacje są tu: https://docs.zephyrproject.org/latest/build/dts/intro.html, ale całość działa dość intuicyjnie. Sprzęt opisywany jest w postaci drzewa, z którego generowany jest plik nagłówkowy. Obrazuje to rysunek zaczerpnięty z linka powyżej. Oto przykład drzewa z jednym węzłem i podwęzłem, który ma też labelkę: /dts-v1/; / { wezeł { podwęzeł_labelka: podwęzeł { foo = <3>; }; }; }; Pierwsza linijka /dts-v1/; oznacza, że jest to Device Tree w wersji 1. Jest to o tyle ważne, gdyż, bez niej składnia byłaby interpretowana jako wersja 0. Teraz przeanalizujmy jakiś rzeczywisty przykład. Tu jest drzewo do płytki nRF52840 DK: https://github.com/zephyrproject-rtos/zephyr/blob/main/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840.dts /* * Copyright (c) 2017 Linaro Limited * * SPDX-License-Identifier: Apache-2.0 */ /dts-v1/; #include <nordic/nrf52840_qiaa.dtsi> #include "nrf52840dk_nrf52840-pinctrl.dtsi" / { model = "Nordic nRF52840 DK NRF52840"; compatible = "nordic,nrf52840-dk-nrf52840"; chosen { zephyr,console = &uart0; zephyr,shell-uart = &uart0; zephyr,uart-mcumgr = &uart0; zephyr,bt-mon-uart = &uart0; zephyr,bt-c2h-uart = &uart0; zephyr,sram = &sram0; zephyr,flash = &flash0; zephyr,code-partition = &slot0_partition; zephyr,ieee802154 = &ieee802154; }; leds { compatible = "gpio-leds"; led0: led_0 { gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; label = "Green LED 0"; }; led1: led_1 { gpios = <&gpio0 14 GPIO_ACTIVE_LOW>; label = "Green LED 1"; }; led2: led_2 { gpios = <&gpio0 15 GPIO_ACTIVE_LOW>; label = "Green LED 2"; }; led3: led_3 { gpios = <&gpio0 16 GPIO_ACTIVE_LOW>; label = "Green LED 3"; }; }; pwmleds { compatible = "pwm-leds"; pwm_led0: pwm_led_0 { pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_INVERTED>; }; }; buttons { compatible = "gpio-keys"; button0: button_0 { gpios = <&gpio0 11 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; label = "Push button switch 0"; }; button1: button_1 { gpios = <&gpio0 12 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; label = "Push button switch 1"; }; button2: button_2 { gpios = <&gpio0 24 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; label = "Push button switch 2"; }; button3: button_3 { gpios = <&gpio0 25 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; label = "Push button switch 3"; }; }; arduino_header: connector { compatible = "arduino-header-r3"; #gpio-cells = <2>; gpio-map-mask = <0xffffffff 0xffffffc0>; gpio-map-pass-thru = <0 0x3f>; gpio-map = <0 0 &gpio0 3 0>, /* A0 */ <1 0 &gpio0 4 0>, /* A1 */ <2 0 &gpio0 28 0>, /* A2 */ <3 0 &gpio0 29 0>, /* A3 */ <4 0 &gpio0 30 0>, /* A4 */ <5 0 &gpio0 31 0>, /* A5 */ <6 0 &gpio1 1 0>, /* D0 */ <7 0 &gpio1 2 0>, /* D1 */ <8 0 &gpio1 3 0>, /* D2 */ <9 0 &gpio1 4 0>, /* D3 */ <10 0 &gpio1 5 0>, /* D4 */ <11 0 &gpio1 6 0>, /* D5 */ <12 0 &gpio1 7 0>, /* D6 */ <13 0 &gpio1 8 0>, /* D7 */ <14 0 &gpio1 10 0>, /* D8 */ <15 0 &gpio1 11 0>, /* D9 */ <16 0 &gpio1 12 0>, /* D10 */ <17 0 &gpio1 13 0>, /* D11 */ <18 0 &gpio1 14 0>, /* D12 */ <19 0 &gpio1 15 0>, /* D13 */ <20 0 &gpio0 26 0>, /* D14 */ <21 0 &gpio0 27 0>; /* D15 */ }; arduino_adc: analog-connector { compatible = "arduino,uno-adc"; #io-channel-cells = <1>; io-channel-map = <0 &adc 1>, /* A0 = P0.3 = AIN1 */ <1 &adc 2>, /* A1 = P0.4 = AIN2 */ <2 &adc 4>, /* A2 = P0.28 = AIN4 */ <3 &adc 5>, /* A3 = P0.29 = AIN5 */ <4 &adc 6>, /* A4 = P0.30 = AIN6 */ <5 &adc 7>; /* A5 = P0.31 = AIN7 */ }; /* These aliases are provided for compatibility with samples */ aliases { led0 = &led0; led1 = &led1; led2 = &led2; led3 = &led3; pwm-led0 = &pwm_led0; sw0 = &button0; sw1 = &button1; sw2 = &button2; sw3 = &button3; bootloader-led0 = &led0; mcuboot-button0 = &button0; mcuboot-led0 = &led0; watchdog0 = &wdt0; spi-flash0 = &mx25r64; }; }; &adc { status = "okay"; }; &gpiote { status = "okay"; }; &gpio0 { status = "okay"; }; &gpio1 { status = "okay"; }; &uart0 { compatible = "nordic,nrf-uarte"; status = "okay"; current-speed = <115200>; pinctrl-0 = <&uart0_default>; pinctrl-1 = <&uart0_sleep>; pinctrl-names = "default", "sleep"; }; arduino_serial: &uart1 { status = "okay"; current-speed = <115200>; pinctrl-0 = <&uart1_default>; pinctrl-1 = <&uart1_sleep>; pinctrl-names = "default", "sleep"; }; arduino_i2c: &i2c0 { compatible = "nordic,nrf-twi"; status = "okay"; pinctrl-0 = <&i2c0_default>; pinctrl-1 = <&i2c0_sleep>; pinctrl-names = "default", "sleep"; }; &i2c1 { compatible = "nordic,nrf-twi"; /* Cannot be used together with spi1. */ /* status = "okay"; */ pinctrl-0 = <&i2c1_default>; pinctrl-1 = <&i2c1_sleep>; pinctrl-names = "default", "sleep"; }; &pwm0 { status = "okay"; pinctrl-0 = <&pwm0_default>; pinctrl-1 = <&pwm0_sleep>; pinctrl-names = "default", "sleep"; }; &spi0 { compatible = "nordic,nrf-spi"; /* Cannot be used together with i2c0. */ /* status = "okay"; */ pinctrl-0 = <&spi0_default>; pinctrl-1 = <&spi0_sleep>; pinctrl-names = "default", "sleep"; }; &spi1 { compatible = "nordic,nrf-spi"; status = "okay"; pinctrl-0 = <&spi1_default>; pinctrl-1 = <&spi1_sleep>; pinctrl-names = "default", "sleep"; }; &spi2 { compatible = "nordic,nrf-spi"; status = "disabled"; pinctrl-0 = <&spi2_default>; pinctrl-1 = <&spi2_sleep>; pinctrl-names = "default", "sleep"; }; &qspi { status = "okay"; pinctrl-0 = <&qspi_default>; pinctrl-1 = <&qspi_sleep>; pinctrl-names = "default", "sleep"; mx25r64: mx25r6435f@0 { compatible = "nordic,qspi-nor"; reg = <0>; /* MX25R64 supports only pp and pp4io */ writeoc = "pp4io"; /* MX25R64 supports all readoc options */ readoc = "read4io"; sck-frequency = <8000000>; jedec-id = [c2 28 17]; sfdp-bfp = [ e5 20 f1 ff ff ff ff 03 44 eb 08 6b 08 3b 04 bb ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52 10 d8 00 ff 23 72 f5 00 82 ed 04 cc 44 83 68 44 30 b0 30 b0 f7 c4 d5 5c 00 be 29 ff f0 d0 ff ff ]; size = <67108864>; has-dpd; t-enter-dpd = <10000>; t-exit-dpd = <35000>; }; }; arduino_spi: &spi3 { status = "okay"; cs-gpios = <&arduino_header 16 GPIO_ACTIVE_LOW>; /* D10 */ pinctrl-0 = <&spi3_default>; pinctrl-1 = <&spi3_sleep>; pinctrl-names = "default", "sleep"; }; &ieee802154 { status = "okay"; }; &flash0 { partitions { compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; boot_partition: partition@0 { label = "mcuboot"; reg = <0x00000000 0x0000C000>; }; slot0_partition: partition@c000 { label = "image-0"; reg = <0x0000C000 0x00076000>; }; slot1_partition: partition@82000 { label = "image-1"; reg = <0x00082000 0x00076000>; }; /* * The flash starting at 0x000f8000 and ending at * 0x000fffff is reserved for use by the application. */ /* * Storage partition will be used by FCB/LittleFS/NVS * if enabled. */ storage_partition: partition@f8000 { label = "storage"; reg = <0x000f8000 0x00008000>; }; }; }; zephyr_udc0: &usbd { compatible = "nordic,nrf-usbd"; status = "okay"; }; Należy zwrócić uwagę, że drzewko to opisuje development kit: nRF52840 DK. Czyli jest to opis LEDów, przycisków, złącz, itd. To jest drzewo jakie tworzy się projektując płytkę. Warto zwrócić uwagę na sekcję aliases. To jest łatwy sposób na zrobienie mapowania i tak button0 może stać się sw0. Chodzi o to, aby mając gotowy kod, który ma działać na różnych płytkach i używa jakieś konwencji nazw, dostosować do innej płytki, nie koniecznie robiąc brutalny rename wszędzie. Teraz spójrzmy na pierwszy include w tym drzewie: #include <nordic/nrf52840_qiaa.dtsi> W ten sposób dołączamy informację o samym układzie nRF52840. Informacje o CPU, mapa pamięci, informacje o rejestrach i priorytetach przerwań, itd... https://github.com/zephyrproject-rtos/zephyr/blob/main/dts/arm/nordic/nrf52840.dtsi /* SPDX-License-Identifier: Apache-2.0 */ #include <arm/armv7-m.dtsi> #include "nrf_common.dtsi" / { chosen { zephyr,entropy = &rng; zephyr,flash-controller = &flash_controller; }; cpus { #address-cells = <1>; #size-cells = <0>; cpu@0 { device_type = "cpu"; compatible = "arm,cortex-m4f"; reg = <0>; #address-cells = <1>; #size-cells = <1>; itm: itm@e0000000 { compatible = "arm,armv7m-itm"; reg = <0xe0000000 0x1000>; swo-ref-frequency = <32000000>; }; }; }; soc { ficr: ficr@10000000 { compatible = "nordic,nrf-ficr"; reg = <0x10000000 0x1000>; status = "okay"; }; uicr: uicr@10001000 { compatible = "nordic,nrf-uicr"; reg = <0x10001000 0x1000>; status = "okay"; }; sram0: memory@20000000 { compatible = "mmio-sram"; }; clock: clock@40000000 { compatible = "nordic,nrf-clock"; reg = <0x40000000 0x1000>; interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; radio: radio@40001000 { compatible = "nordic,nrf-radio"; reg = <0x40001000 0x1000>; interrupts = <1 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; ieee802154-supported; ble-2mbps-supported; ble-coded-phy-supported; tx-high-power-supported; ieee802154: ieee802154 { compatible = "nordic,nrf-ieee802154"; status = "disabled"; }; }; uart0: uart@40002000 { /* uart can be either UART or UARTE, for the user to pick */ /* compatible = "nordic,nrf-uarte" or "nordic,nrf-uart"; */ compatible = "nordic,nrf-uarte"; reg = <0x40002000 0x1000>; interrupts = <2 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; }; i2c0: i2c@40003000 { /* * This i2c node can be TWI, TWIM, or TWIS, * for the user to pick: * compatible = "nordic,nrf-twi" or * "nordic,nrf-twim" or * "nordic,nrf-twis". */ compatible = "nordic,nrf-twim"; #address-cells = <1>; #size-cells = <0>; reg = <0x40003000 0x1000>; clock-frequency = <I2C_BITRATE_STANDARD>; interrupts = <3 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; }; spi0: spi@40003000 { /* * This spi node can be SPI, SPIM, or SPIS, * for the user to pick: * compatible = "nordic,nrf-spi" or * "nordic,nrf-spim" or * "nordic,nrf-spis". */ compatible = "nordic,nrf-spim"; #address-cells = <1>; #size-cells = <0>; reg = <0x40003000 0x1000>; interrupts = <3 NRF_DEFAULT_IRQ_PRIORITY>; max-frequency = <DT_FREQ_M(8)>; status = "disabled"; }; i2c1: i2c@40004000 { /* * This i2c node can be TWI, TWIM, or TWIS, * for the user to pick: * compatible = "nordic,nrf-twi" or * "nordic,nrf-twim" or * "nordic,nrf-twis". */ compatible = "nordic,nrf-twim"; #address-cells = <1>; #size-cells = <0>; reg = <0x40004000 0x1000>; clock-frequency = <I2C_BITRATE_STANDARD>; interrupts = <4 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; }; spi1: spi@40004000 { /* * This spi node can be SPI, SPIM, or SPIS, * for the user to pick: * compatible = "nordic,nrf-spi" or * "nordic,nrf-spim" or * "nordic,nrf-spis". */ compatible = "nordic,nrf-spim"; #address-cells = <1>; #size-cells = <0>; reg = <0x40004000 0x1000>; interrupts = <4 NRF_DEFAULT_IRQ_PRIORITY>; max-frequency = <DT_FREQ_M(8)>; status = "disabled"; }; nfct: nfct@40005000 { compatible = "nordic,nrf-nfct"; reg = <0x40005000 0x1000>; interrupts = <5 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; }; gpiote: gpiote@40006000 { compatible = "nordic,nrf-gpiote"; reg = <0x40006000 0x1000>; interrupts = <6 5>; status = "disabled"; }; adc: adc@40007000 { compatible = "nordic,nrf-saadc"; reg = <0x40007000 0x1000>; interrupts = <7 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; #io-channel-cells = <1>; }; timer0: timer@40008000 { compatible = "nordic,nrf-timer"; status = "okay"; reg = <0x40008000 0x1000>; cc-num = <4>; interrupts = <8 NRF_DEFAULT_IRQ_PRIORITY>; prescaler = <0>; }; timer1: timer@40009000 { compatible = "nordic,nrf-timer"; status = "okay"; reg = <0x40009000 0x1000>; cc-num = <4>; interrupts = <9 NRF_DEFAULT_IRQ_PRIORITY>; prescaler = <0>; }; timer2: timer@4000a000 { compatible = "nordic,nrf-timer"; status = "okay"; reg = <0x4000a000 0x1000>; cc-num = <4>; interrupts = <10 NRF_DEFAULT_IRQ_PRIORITY>; prescaler = <0>; }; rtc0: rtc@4000b000 { compatible = "nordic,nrf-rtc"; reg = <0x4000b000 0x1000>; cc-num = <3>; interrupts = <11 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; clock-frequency = <32768>; prescaler = <1>; }; temp: temp@4000c000 { compatible = "nordic,nrf-temp"; reg = <0x4000c000 0x1000>; interrupts = <12 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; rng: random@4000d000 { compatible = "nordic,nrf-rng"; reg = <0x4000d000 0x1000>; interrupts = <13 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; ecb: ecb@4000e000 { compatible = "nordic,nrf-ecb"; reg = <0x4000e000 0x1000>; interrupts = <14 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; ccm: ccm@4000f000 { compatible = "nordic,nrf-ccm"; reg = <0x4000f000 0x1000>; interrupts = <15 NRF_DEFAULT_IRQ_PRIORITY>; length-field-length-8-bits; status = "okay"; }; wdt: wdt0: watchdog@40010000 { compatible = "nordic,nrf-wdt"; reg = <0x40010000 0x1000>; interrupts = <16 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; rtc1: rtc@40011000 { compatible = "nordic,nrf-rtc"; reg = <0x40011000 0x1000>; cc-num = <4>; interrupts = <17 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; clock-frequency = <32768>; prescaler = <1>; }; qdec: qdec0: qdec@40012000 { compatible = "nordic,nrf-qdec"; reg = <0x40012000 0x1000>; interrupts = <18 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; }; comp: comparator@40013000 { /* * This comparator node can be COMP or LPCOMP, * for the user to pick: * compatible = "nordic,nrf-comp" or * "nordic,nrf-lpcomp". */ compatible = "nordic,nrf-comp"; reg = <0x40013000 0x1000>; interrupts = <19 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; #io-channel-cells = <1>; }; egu0: swi0: egu@40014000 { compatible = "nordic,nrf-egu", "nordic,nrf-swi"; reg = <0x40014000 0x1000>; interrupts = <20 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; egu1: swi1: egu@40015000 { compatible = "nordic,nrf-egu", "nordic,nrf-swi"; reg = <0x40015000 0x1000>; interrupts = <21 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; egu2: swi2: egu@40016000 { compatible = "nordic,nrf-egu", "nordic,nrf-swi"; reg = <0x40016000 0x1000>; interrupts = <22 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; egu3: swi3: egu@40017000 { compatible = "nordic,nrf-egu", "nordic,nrf-swi"; reg = <0x40017000 0x1000>; interrupts = <23 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; egu4: swi4: egu@40018000 { compatible = "nordic,nrf-egu", "nordic,nrf-swi"; reg = <0x40018000 0x1000>; interrupts = <24 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; egu5: swi5: egu@40019000 { compatible = "nordic,nrf-egu", "nordic,nrf-swi"; reg = <0x40019000 0x1000>; interrupts = <25 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; }; timer3: timer@4001a000 { compatible = "nordic,nrf-timer"; status = "okay"; reg = <0x4001a000 0x1000>; cc-num = <6>; interrupts = <26 NRF_DEFAULT_IRQ_PRIORITY>; prescaler = <0>; }; timer4: timer@4001b000 { compatible = "nordic,nrf-timer"; status = "okay"; reg = <0x4001b000 0x1000>; cc-num = <6>; interrupts = <27 NRF_DEFAULT_IRQ_PRIORITY>; prescaler = <0>; }; pwm0: pwm@4001c000 { compatible = "nordic,nrf-pwm"; reg = <0x4001c000 0x1000>; interrupts = <28 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; #pwm-cells = <3>; }; pdm0: pdm@4001d000 { compatible = "nordic,nrf-pdm"; reg = <0x4001d000 0x1000>; interrupts = <29 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; }; acl: acl@4001e000 { compatible = "nordic,nrf-acl"; reg = <0x4001e000 0x1000>; status = "okay"; }; flash_controller: flash-controller@4001e000 { compatible = "nordic,nrf52-flash-controller"; reg = <0x4001e000 0x1000>; partial-erase; #address-cells = <1>; #size-cells = <1>; flash0: flash@0 { compatible = "soc-nv-flash"; erase-block-size = <4096>; write-block-size = <4>; }; }; ppi: ppi@4001f000 { compatible = "nordic,nrf-ppi"; reg = <0x4001f000 0x1000>; status = "okay"; }; mwu: mwu@40020000 { compatible = "nordic,nrf-mwu"; reg = <0x40020000 0x1000>; status = "okay"; }; pwm1: pwm@40021000 { compatible = "nordic,nrf-pwm"; reg = <0x40021000 0x1000>; interrupts = <33 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; #pwm-cells = <3>; }; pwm2: pwm@40022000 { compatible = "nordic,nrf-pwm"; reg = <0x40022000 0x1000>; interrupts = <34 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; #pwm-cells = <3>; }; spi2: spi@40023000 { /* * This spi node can be SPI, SPIM, or SPIS, * for the user to pick: * compatible = "nordic,nrf-spi" or * "nordic,nrf-spim" or * "nordic,nrf-spis". */ compatible = "nordic,nrf-spim"; #address-cells = <1>; #size-cells = <0>; reg = <0x40023000 0x1000>; interrupts = <35 NRF_DEFAULT_IRQ_PRIORITY>; max-frequency = <DT_FREQ_M(8)>; status = "disabled"; }; rtc2: rtc@40024000 { compatible = "nordic,nrf-rtc"; reg = <0x40024000 0x1000>; cc-num = <4>; interrupts = <36 NRF_DEFAULT_IRQ_PRIORITY>; status = "okay"; clock-frequency = <32768>; prescaler = <1>; }; i2s0: i2s@40025000 { compatible = "nordic,nrf-i2s"; #address-cells = <1>; #size-cells = <0>; reg = <0x40025000 0x1000>; interrupts = <37 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; }; usbd: usbd@40027000 { compatible = "nordic,nrf-usbd"; reg = <0x40027000 0x1000>; interrupts = <39 NRF_DEFAULT_IRQ_PRIORITY>; num-bidir-endpoints = <1>; num-in-endpoints = <7>; num-out-endpoints = <7>; num-isoin-endpoints = <1>; num-isoout-endpoints = <1>; status = "disabled"; }; uart1: uart@40028000 { compatible = "nordic,nrf-uarte"; reg = <0x40028000 0x1000>; interrupts = <40 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; }; qspi: qspi@40029000 { compatible = "nordic,nrf-qspi"; #address-cells = <1>; #size-cells = <0>; reg = <0x40029000 0x1000>, <0x12000000 0x8000000>; reg-names = "qspi", "qspi_mm"; interrupts = <41 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; }; pwm3: pwm@4002d000 { compatible = "nordic,nrf-pwm"; reg = <0x4002d000 0x1000>; interrupts = <45 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; #pwm-cells = <3>; }; spi3: spi@4002f000 { compatible = "nordic,nrf-spim"; #address-cells = <1>; #size-cells = <0>; reg = <0x4002f000 0x1000>; interrupts = <47 NRF_DEFAULT_IRQ_PRIORITY>; max-frequency = <DT_FREQ_M(32)>; rx-delay-supported; rx-delay = <2>; status = "disabled"; }; gpio0: gpio@50000000 { compatible = "nordic,nrf-gpio"; gpio-controller; reg = <0x50000000 0x200 0x50000500 0x300>; #gpio-cells = <2>; status = "disabled"; port = <0>; }; gpio1: gpio@50000300 { compatible = "nordic,nrf-gpio"; gpio-controller; reg = <0x50000300 0x200 0x50000800 0x300>; #gpio-cells = <2>; ngpios = <16>; status = "disabled"; port = <1>; }; cryptocell: crypto@5002a000 { compatible = "nordic,nrf-cc310"; reg = <0x5002A000 0x1000>; status = "okay"; #address-cells = <1>; #size-cells = <1>; cryptocell310: crypto@5002b000 { compatible = "arm,cryptocell-310"; reg = <0x5002B000 0x1000>; interrupts = <42 NRF_DEFAULT_IRQ_PRIORITY>; }; }; }; sw_pwm: sw-pwm { compatible = "nordic,nrf-sw-pwm"; status = "disabled"; generator = <&timer2>; clock-prescaler = <0>; #pwm-cells = <3>; }; }; &nvic { arm,num-irq-priority-bits = <3>; }; Kolejny include: #include "nrf52840dk_nrf52840-pinctrl.dtsi" https://github.com/zephyrproject-rtos/zephyr/blob/main/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840-pinctrl.dtsi /* * Copyright (c) 2022 Nordic Semiconductor * SPDX-License-Identifier: Apache-2.0 */ &pinctrl { uart0_default: uart0_default { group1 { psels = <NRF_PSEL(UART_TX, 0, 6)>, <NRF_PSEL(UART_RTS, 0, 5)>; }; group2 { psels = <NRF_PSEL(UART_RX, 0, 8)>, <NRF_PSEL(UART_CTS, 0, 7)>; bias-pull-up; }; }; uart0_sleep: uart0_sleep { group1 { psels = <NRF_PSEL(UART_TX, 0, 6)>, <NRF_PSEL(UART_RX, 0, 8)>, <NRF_PSEL(UART_RTS, 0, 5)>, <NRF_PSEL(UART_CTS, 0, 7)>; low-power-enable; }; }; uart1_default: uart1_default { group1 { psels = <NRF_PSEL(UART_RX, 1, 1)>; bias-pull-up; }; group2 { psels = <NRF_PSEL(UART_TX, 1, 2)>; }; }; uart1_sleep: uart1_sleep { group1 { psels = <NRF_PSEL(UART_RX, 1, 1)>, <NRF_PSEL(UART_TX, 1, 2)>; low-power-enable; }; }; i2c0_default: i2c0_default { group1 { psels = <NRF_PSEL(TWIM_SDA, 0, 26)>, <NRF_PSEL(TWIM_SCL, 0, 27)>; }; }; i2c0_sleep: i2c0_sleep { group1 { psels = <NRF_PSEL(TWIM_SDA, 0, 26)>, <NRF_PSEL(TWIM_SCL, 0, 27)>; low-power-enable; }; }; i2c1_default: i2c1_default { group1 { psels = <NRF_PSEL(TWIM_SDA, 0, 30)>, <NRF_PSEL(TWIM_SCL, 0, 31)>; }; }; i2c1_sleep: i2c1_sleep { group1 { psels = <NRF_PSEL(TWIM_SDA, 0, 30)>, <NRF_PSEL(TWIM_SCL, 0, 31)>; low-power-enable; }; }; pwm0_default: pwm0_default { group1 { psels = <NRF_PSEL(PWM_OUT0, 0, 13)>; nordic,invert; }; }; pwm0_sleep: pwm0_sleep { group1 { psels = <NRF_PSEL(PWM_OUT0, 0, 13)>; low-power-enable; }; }; spi0_default: spi0_default { group1 { psels = <NRF_PSEL(SPIM_SCK, 0, 27)>, <NRF_PSEL(SPIM_MOSI, 0, 26)>, <NRF_PSEL(SPIM_MISO, 0, 29)>; }; }; spi0_sleep: spi0_sleep { group1 { psels = <NRF_PSEL(SPIM_SCK, 0, 27)>, <NRF_PSEL(SPIM_MOSI, 0, 26)>, <NRF_PSEL(SPIM_MISO, 0, 29)>; low-power-enable; }; }; spi1_default: spi1_default { group1 { psels = <NRF_PSEL(SPIM_SCK, 0, 31)>, <NRF_PSEL(SPIM_MOSI, 0, 30)>, <NRF_PSEL(SPIM_MISO, 1, 8)>; }; }; spi1_sleep: spi1_sleep { group1 { psels = <NRF_PSEL(SPIM_SCK, 0, 31)>, <NRF_PSEL(SPIM_MOSI, 0, 30)>, <NRF_PSEL(SPIM_MISO, 1, 8)>; low-power-enable; }; }; spi2_default: spi2_default { group1 { psels = <NRF_PSEL(SPIM_SCK, 0, 19)>, <NRF_PSEL(SPIM_MOSI, 0, 20)>, <NRF_PSEL(SPIM_MISO, 0, 21)>; }; }; spi2_sleep: spi2_sleep { group1 { psels = <NRF_PSEL(SPIM_SCK, 0, 19)>, <NRF_PSEL(SPIM_MOSI, 0, 20)>, <NRF_PSEL(SPIM_MISO, 0, 21)>; low-power-enable; }; }; qspi_default: qspi_default { group1 { psels = <NRF_PSEL(QSPI_SCK, 0, 19)>, <NRF_PSEL(QSPI_IO0, 0, 20)>, <NRF_PSEL(QSPI_IO1, 0, 21)>, <NRF_PSEL(QSPI_IO2, 0, 22)>, <NRF_PSEL(QSPI_IO3, 0, 23)>, <NRF_PSEL(QSPI_CSN, 0, 17)>; }; }; qspi_sleep: qspi_sleep { group1 { psels = <NRF_PSEL(QSPI_SCK, 0, 19)>, <NRF_PSEL(QSPI_IO0, 0, 20)>, <NRF_PSEL(QSPI_IO1, 0, 21)>, <NRF_PSEL(QSPI_IO2, 0, 22)>, <NRF_PSEL(QSPI_IO3, 0, 23)>; low-power-enable; }; group2 { psels = <NRF_PSEL(QSPI_CSN, 0, 17)>; low-power-enable; bias-pull-up; }; }; spi3_default: spi3_default { group1 { psels = <NRF_PSEL(SPIM_SCK, 1, 15)>, <NRF_PSEL(SPIM_MISO, 1, 14)>, <NRF_PSEL(SPIM_MOSI, 1, 13)>; }; }; spi3_sleep: spi3_sleep { group1 { psels = <NRF_PSEL(SPIM_SCK, 1, 15)>, <NRF_PSEL(SPIM_MISO, 1, 14)>, <NRF_PSEL(SPIM_MOSI, 1, 13)>; low-power-enable; }; }; }; To informacja jak piny mają zachowywać się podczas normalnej pracy, a jak podczas trybu low pawer. Często jest tak, że pracę nad projektem rozpoczynamy z jakąś gotową płytką rozwojową np: NUCLEO STMicroelectronics czy nRF Dev Kit Nordic Semiconductors. Dopiero jak mamy jakiś gotowy koncept tworzymy własną płytkę PCB. Aby taką pracę umożliwić Zephyr pozwala stworzyć narzutę, czyli overlay na Device Tree. Tworząc plik app.overlay możemy zmodyfikować interesujący nas fragment drzewa. W ten sposób oryginalne drzewo opisujące układ scalony czy płytkę nie jest modyfikowane i kompatybilne z innymi projektami, które możemy tworzyć równolegle. Modyfikacja drzewa w praktyce: Załóżmy, że pracujemy z nRF52840 DK i chcielibyśmy ze względu na pobór prądu zamiast ze sprzętowego PWMa bazującego na timerach, użyć softwarowego rozwiania bazującego na Programmable Peripheral Interconnect, GPIO Tasks and Events i RTC, aby sterować piezo buzzerem. Nie będę tłumaczył jak działa PPI oraz GPIOTE, ponieważ sterownik z nRF SDK bardzo ładnie ukrywa przed nami te szczegóły techniczne. Wystarczy, że do prj.conf dodam dwie linijki informujące, że chcę użyć PWMa i że ma być to rozwiązanie softwerowe: CONFIG_PWM=y CONFIG_PWM_NRF5_SW=y Następnie muszę stworzyć plik app.overlay, który będzie zawierał szczegóły działania naszego PWMa, informacje z jakich zasobów należy skorzystać, na jakich pniach ma być wyjście, itd... / { pwmpins { pwm_pin0: pwm_pin_0 { pwms = <&sw_pwm 0 PWM_KHZ(4) PWM_POLARITY_INVERTED>; }; pwm_pin1: pwm_pin_1 { pwms = <&sw_pwm 1 PWM_KHZ(4) PWM_POLARITY_NORMAL>; }; }; aliases { pwm-pin0 = &pwm_pin0; pwm-pin1 = &pwm_pin1; }; }; &sw_pwm { status ="okay"; channel-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>, <&gpio0 14 GPIO_ACTIVE_LOW>; clock-prescaler = <0>; generator = <&rtc0>; }; Zacznijmy analizę od dołu. Tworzymy "device" o nazwie sw_pwm, który będzie miał dwa kanały. Są one powiązane z pinami P0.13 i P0.14. Dzielnik zegara musi być wyłączony, co wynika z ograniczeń sprzętowych, gdy generatorem ma być zegar RTC. Możemy też podać tu jako generator wybrany timmer i wybrać wtedy inną wartość dzielnika zegara. Z zebranych informacji wynika, że w moim przypadku (4kHz), RTC jest lepszym rozwiązaniem, ale musze to jeszcze potwierdzić empirycznie. Następnie tworzę węzły pwmpins opisujące działanie każdego kanału PWM. Przydzielam piny z channel-gpios ustawiam tą samą częstotliwość, ale różną polaryzację. Aliasy zrobiłem "just for fun", aby pokazać, że w ten sposób, będę mógł odwołać się w programie do tego PWMa przez nazwę z podkreślnikiem jak i przez nawę z myślnikiem i wszystko zadziała. Jak już się tak namęczyliśmy to jak to wygląda w programie? No i tu jest całe piękno - wywołuję: static const struct pwm_dt_spec pwm_pin0 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_pin0)); static const struct pwm_dt_spec pwm_pin1 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_pin1)); i mogę korzystać z PWMa i nawet działa: Edytowano Styczeń 3, 2023 przez pmochocki 1 Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Bądź aktywny - zaloguj się lub utwórz konto!
Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony
Utwórz konto w ~20 sekund!
Zarejestruj nowe konto, to proste!
Zarejestruj się »Zaloguj się
Posiadasz własne konto? Użyj go!
Zaloguj się »