Prawdziwe „Hello, World!”
Aktualnie nasz program początkowy komunikuje się z użytkownikiem jedynie przez miganie diodą. Pora to zmienić, wprowadzając do pliku main.c funkcję printk, która w prosty sposób pozwoli nam wysyłać wiadomości przez port szeregowy UART. Oczywiście edytując naszą aplikację hello_world, wciąż korzystamy z przygotowanego wcześniej środowiska, w tym rozszerzenia nRF Connect w VS Code. Zmodyfikowany kod, widoczny na listingu 1, wysyła początkową wiadomość tuż po wejściu do funkcji main oraz cyklicznie wewnątrz pętli.
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_NODELABEL(led0), gpios);
int main(void) {
printk("Hello, World!\n");
if (!gpio_is_ready_dt(&led))
return 0;
gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
int counter = 0;
while (1) {
printk("Hello, World %d!\n", counter);
++counter;
gpio_pin_toggle_dt(&led);
k_msleep(1000);
}
return 0;
}
Listing 1. Plik main.c z dodanymi funkcjami printk
Jeżeli również korzystasz z płytki nRF5340 DK, podłączonej do komputera przewodem USB, w tym momencie projekt wystarczy ponownie przebudować i wgrać plik wynikowy z użyciem wbudowanego J-Linka. Domyślna konfiguracja UART w devicetree (nrf5340dk_nrf5340_cpuapp.dts) zawiera wszystko, co potrzebne do przesyłania wiadomości. Ponadto sam hardware jest tak przygotowany, że linie sygnałowe komunikacji szeregowej, wychodzące z SoC-a, są podłączone do debuggera i udostępnione jako wirtualne porty COM. Oznacza to, że jedyne, co musimy zrobić, to w sekcji CONNECTED DEVICES rozwinąć pole z widocznym numerem seryjnym i kliknąć na ikonę wtyczki po prawej stronie pola VCOM1 (rysunek 1).
Spowoduje to otwarcie okna wyboru portu szeregowego, w którym klikamy na jedyny dostępny element na liście (rysunek 2).
U dołu okna VS Code, w zakładce TERMINAL, powinniśmy teraz zobaczyć konsolę z wiadomościami odbieranymi od procesora (rysunek 3).
Warto zaznaczyć, że do komunikacji szeregowej możemy również użyć wielu innych, popularnych programów, takich jak Putty (Windows) czy GTKTerm (Linux).
Zwróciłeś też być może uwagę na port VCOM0, który obecnie nie odbiera żadnych wiadomości. Jak już wspominaliśmy na początku kursu, SoC nRF5340 ma dwa osobne procesory – application oraz network. Domyślna konfiguracja sprzętowa naszej płytki przewiduje możliwość przesyłania przez każdy z nich wiadomości na osobnych peryferiach UART, stąd też dwa niezależne wirtualne porty widoczne w interfejsie. Jest to rozwiązanie bardzo praktyczne i nie wymaga od nas stosowania żadnych przejściówek. A co, jeśli jednak chcielibyśmy podłączyć się bezpośrednio do pinów TX i RX wychodzących z układu? W tym celu wystarczy przestawić dostępne na płytce przełączniki FLOW CONTROL na pozycję OFF (rysunek 4).
Próba skompilowania i uruchomienia kodu z funkcjami printk na własną płytkę z układem nRF5340 może zakończyć się niepowodzeniem. W takim przypadku należy upewnić się, że UART wraz z konsolą jest prawidłowo skonfigurowany w devicetree (listing 2) oraz odpowiednie ustawienia KConfig są włączone dla projektu (listing 3).
/ {
chosen {
zephyr,console = &uart0;
};
};
&pinctrl {
uart0_default: uart0_default {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 20)>,
<NRF_PSEL(UART_RTS, 0, 19)>;
};
group2 {
psels = <NRF_PSEL(UART_RX, 0, 22)>,
<NRF_PSEL(UART_CTS, 0, 21)>;
bias-pull-up;
};
};
uart0_sleep: uart0_sleep {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 20)>,
<NRF_PSEL(UART_RX, 0, 22)>,
<NRF_PSEL(UART_RTS, 0, 19)>,
<NRF_PSEL(UART_CTS, 0, 21)>;
low-power-enable;
};
};
};
&uart0 {
status = "okay";
current-speed = <115200>;
pinctrl-0 = <&uart0_default>;
pinctrl-1 = <&uart0_sleep>;
pinctrl-names = "default", "sleep";
};
Listing 2. Przykładowa konfiguracja konsoli UART w devicetree