Skocz do zawartości

The wind of change - z zachodu powiało świeżością i tchnęło nowe życie w NUCLEO-L476RG


pmochocki

Pomocna odpowiedź

@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.

  • Lubię! 2
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

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

(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:

Zephyr_toolchain_0_white.thumb.png.45898a92566045d86aafacc49a519cfd.png

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:

Zephyr_toolchain_1_white.thumb.png.af4b477ed619a928d374fac5a31c72b9.png

 

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 przez pmochocki
  • Lubię! 2
Link do komentarza
Share on other sites

(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.

image.thumb.png.1ad17edc732000538b8550a7166ccaaa.png

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:

image.thumb.png.f2c2a9113454710e4bd8aed155022bac.png

 

 

Edytowano przez pmochocki
  • Lubię! 1
Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • 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.