Przejdź do treści

Lopaka Basics 03: Kolor ​

Moduł TFT 320×240 w kolorze pokazujący pulpit Living room o 07:45 z pomarańczowymi, niebieskimi i zielonymi kartami TEMP 23C, HUMID 48% i AIR OK oraz obrazkiem słońca i chmury obok Outside 18C, sunny

Ten sam pomysł co w pierwszych dwóch odcinkach, teraz w pełnym kolorze: pulpit salonu na kolorowym wyświetlaczu, zbudowany w kilka minut.

W tym odcinku dowiesz się, jak kolorowy ekran tworzy kolory, co oznacza RGB565 i dlaczego jeden obrazek na cały ekran nie mieści się na Arduino UNO. Następnie zbudujesz pulpit w Lopaka z zaokrąglonymi kartami, kolorowym tekstem, przyciemnionymi ikonami i jednym małym kolorowym obrazkiem oraz uruchomisz go na ESP32.

Co zbudujesz: kolorowy pulpit 320×240 z tytułem i godziną, trzema zaokrąglonymi kartami temperatury, wilgotności i jakości powietrza oraz wierszem o pogodzie na zewnątrz. Czego potrzebujesz: przeglądarki i symulatora online z odcinka 1. Prawdziwa płytka jest opcjonalna.

Jak działa kolorowy ekran ​

Jeden piksel to trzy malutkie światła ​

Na OLED z ostatnich dwóch odcinków piksel jest włączony albo wyłączony. Na kolorowym wyświetlaczu każdy piksel składa się z trzech malutkich świateł: czerwonego, zielonego i niebieskiego. Każde z nich może świecić z dowolną jasnością, a z normalnej odległości twoje oko miesza je w jeden kolor.

Zmieszaj je, a otrzymasz dowolny kolor. Czerwień i zieleń razem dają żółty. Wszystkie trzy na pełnej mocy dają biały. Wszystkie trzy wyłączone to czarny.

Jeden piksel to trzy malutkie światła: piksel pulpitu obok nakładających się kół czerwonego, zielonego i niebieskiego; czerwień i zieleń dają żółty, wszystkie trzy dają biały

RGB565: kolor w dwóch bajtach ​

Twój komputer przechowuje kolor w 24 bitach: 8 na czerwień, 8 na zieleń i 8 na niebieski, około 16,7 miliona kolorów. To kolor, który wpisujesz jako #FF8200 na stronie internetowej lub w Lopaka: dwie cyfry szesnastkowe na światło.

Małe chipy i ich wyświetlacze zazwyczaj używają 16 bitów, w formacie zwanym RGB565:

  • 5 bitów na czerwień,
  • 6 bitów na zieleń,
  • 5 bitów na niebieski.

Zieleń otrzymuje dodatkowy bit, ponieważ nasze oczy są na nią najbardziej wrażliwe. Daje to 65 536 kolorów, a każdy piksel zajmuje dokładnie dwa bajty.

Kolor komputerowy: 24 bity na piksel, 8 czerwonego, 8 zielonego i 8 niebieskiego dla 16,7 miliona kolorów; kolor mikrokontrolera: RGB565, 16 bitów podzielonych na 5, 6 i 5, ponieważ nasze oczy są najbardziej wrażliwe na zieleń; 65 536 kolorów, 2 bajty na piksel

Dlaczego obrazek nie mieści się na UNO ​

Teraz matematyka. Ekran 320×240 ma 320 × 240 = 76 800 pikseli. Przy dwóch bajtach na piksel jeden obrazek na cały ekran zajmuje 153 600 bajtów, około 150 KB.

Arduino UNO ma 32 KB flash na cały program. Jeden obrazek na cały ekran to prawie pięć razy tyle. Dlatego na kolorowych ekranach rysujesz kształtami i tekstem, a obrazki utrzymujesz małe.

Matematyka pamięci: 320 × 240 = 76 800 pikseli, × 2 bajty = 153 600 bajtów; jeden obrazek o rozmiarze około 150 KB to prawie pięć razy więcej niż 32 KB flash Arduino UNO

Krok 1: nowy kolorowy projekt ​

Kliknij + Nowy projekt i otwórz listę Platforma. W tym odcinku wybierz AdafruitGFX Color: Adafruit GFX to jedna z najpopularniejszych bibliotek graficznych dla kolorowych wyświetlaczy. Lopaka opisuje ją jako „wspólną warstwę rysowania pod większością sterowników wyświetlaczy Adafruit”: łączysz ją ze sterownikiem dla swojego dokładnego panelu, takiego jak Adafruit_ILI9341, kolory są upakowane jako RGB565, a rysowanie trafia prosto na panel.

Następnie wypełnij resztę okna:

  • Tytuł: „Color Weather”.
  • Rozmiar ekranu: 320×240.
  • Kolor tła: #0F1B2D, ciemny niebieski.

Kliknij Zapisz.

Okno Utwórz nowy projekt z Platformą AdafruitGFX Color, tytułem Color Weather, Rozmiarem ekranu 320×240 i Kolorem tła #0F1B2D

Lopaka konwertuje każdy kolor na RGB565 ​

Panel kodu ma już jedną linię:

cpp
tft.fillScreen(0x8C5);

fillScreen maluje cały wyświetlacz jednym kolorem, a 0x8C5 to ciemny niebieski w RGB565. Lopaka wykonuje konwersję dla każdego wybranego koloru. Ręcznie działa to tak: zachowaj górne 5 bitów czerwieni, górne 6 bitów zieleni i górne 5 bitów błękitu i zapisz je jeden po drugim.

HexGórne bity
red0x0F00001
green0x1B000110
blue0x2D00101

Razem daje to 0000100011000101, czyli 0x08C5. Lopaka zapisuje to bez wiodącego zera.

Krok 2: trzy zaokrąglone karty ​

Wybierz narzędzie prostokąt i przeciągnij kartę na obszarze rysowania. W inspektorze ustaw R na 12, aby zaokrąglić rogi, włącz Wypełniony i wpisz #FF8200 w Kolor wypełnienia dla pomarańczowego. W kodzie pojawia się jedna linia:

cpp
tft.fillRoundRect(10, 56, 96, 140, 12, 0xFC00);

Liczby to x, y, szerokość, wysokość, promień rogu i kolor. Kształt to jedna linia kodu, niezależnie od rozmiaru: ta karta obejmuje 96 × 140 pikseli, a linia, która ją rysuje, zajmuje kilka bajtów flash.

Powtórz to dla dwóch pozostałych kart: niebieskiej #2D7FF9 i zielonej #22B573. Wszystkie trzy mają 96 pikseli szerokości i 140 wysokości, na y 56, i zaczynają się na x 10, 112 i 214. Pozostawia to 10-pikselowy margines po obu stronach ekranu i 6 pikseli między kartami: 10 + 3 × 96 + 2 × 6 + 10 = 320.

Trzy zaokrąglone karty, pomarańczowa, niebieska i zielona, na ciemnoniebieskim ekranie; inspektor pokazuje zieloną kartę na X 214, Y 56, W 96, H 140, R 12, Wypełniony i Kolor wypełnienia #22B573, a kod ma trzy linie fillRoundRect

Krok 3: tekst w kolorze ​

Tekst działa jak w pierwszych dwóch odcinkach, a teraz może przyjmować dowolny kolor. Wybierz narzędzie tekst (Aa): na obszarze rysowania pojawia się nowa warstwa tekstowa. Wpisz tekst do inspektora, wybierz czcionkę i ustaw Kolor wypełnienia. Nowy tekst zaczyna się od koloru użytego ostatnio, więc za każdym razem sprawdzaj kolor.

Tytuł i godzina ​

  • Living room czcionką FreeSansBold12pt7b, biały #FFFFFF, w lewym górnym rogu.
  • 07:45 czcionką FreeSans12pt7b, jasny szaroniebieski #9FB3C8, w prawym górnym rogu.
cpp
tft.setTextColor(0xFFFF);
tft.setTextWrap(false);
tft.setFont(&FreeSansBold12pt7b);
tft.setCursor(12, 35);
tft.print("Living room");

setTextColor ustawia kolor dla każdego tekstu po nim, więc Lopaka pisze go ponownie tylko wtedy, gdy kolor się zmienia. setTextWrap(false) utrzymuje długi tekst w jednej linii zamiast zawijać go na krawędzi ekranu.

W przypadku czcionek FreeSans współrzędna y w setCursor to linia bazowa: ostatni wiersz pikseli liter. W inspektorze tytuł ma Y 36, linię pod tym wierszem (linijki liczą linie między pikselami, jak w odcinku 1), więc kod mówi 35.

Mała etykieta i duża liczba na każdej karcie ​

Każda karta otrzymuje małą etykietę klasyczną czcionką i dużą liczbę pogrubioną czcionką FreeSans:

  • Etykiety TEMP, HUMID i AIR używają Adafruit 5x7, klasycznej czcionki wbudowanej w bibliotekę, z Rozmiarem ustawionym na 2.
  • Liczby 23C, 48% i OK używają FreeSansBold18pt7b.

Wszystkie sześć tekstów jest białych. Umieść każdą etykietę blisko góry jej karty, a każdą liczbę blisko dołu.

Zaznaczona etykieta TEMP: czcionka Adafruit 5x7, Rozmiar 2, X 20 i Y 86 w inspektorze; kod brzmi setTextSize(2), setFont() i setCursor(20, 72)

Klasyczną czcionkę można powiększyć dwa lub trzy razy: Rozmiar 2 zamienia każdy piksel litery w blok 2×2. Kod etykiety i liczby:

cpp
tft.setTextSize(2);
tft.setFont();
tft.setCursor(20, 72);
tft.print("TEMP");
tft.setTextSize(1);
tft.setFont(&FreeSansBold18pt7b);
tft.setCursor(20, 179);
tft.print("23C");

setFont() bez niczego w nawiasach przełącza z powrotem na klasyczną czcionkę. Klasyczna czcionka liczy y inaczej: setCursor przyjmuje lewy górny róg tekstu, a nie linię bazową. Litery etykiety mają 7 pikseli wysokości, 14 przy rozmiarze 2, więc 86 z inspektora staje się w kodzie 86 − 14 = 72. Lopaka wykonuje tę konwersję za ciebie. Przed liczbą FreeSans resetuje rozmiar do 1, aby duża czcionka nie została podwojona.

Tekst mówi 23C, a nie 23°C: czcionki FreeSans zawierają tylko podstawowe znaki ASCII, a znak stopnia do nich nie należy.

Czcionki kosztują flash tylko wtedy, gdy ich używasz ​

FreeSans i pozostałe czcionki z listy są dostarczane z biblioteką Adafruit GFX. Każda czcionka to osobny plik i zajmuje flash tylko wtedy, gdy twój ekran jej używa. Lopaka dodaje #include dla każdej czcionki używanej przez projekt, tutaj trzy:

cpp
#include "FreeSans12pt7b.h"
#include "FreeSansBold12pt7b.h"
#include "FreeSansBold18pt7b.h"

Krok 4: przyciemnione ikony ​

Otwórz kartę Obrazy, wyszukaj ikonę i kliknij ją, aby dodać ją do ekranu. Dodaj trzy:

  • termometr (szukaj „thermo”),
  • kroplę (szukaj „drop”),
  • liść (szukaj „leaf”).

Wróć do Warstw i umieść każdą ikonę pod etykietą jej karty.

Ikony z biblioteki są jednokolorowe, więc możesz je przyciemnić, aby pasowały do projektu. Zaznacz każdą ikonę i ustaw jej Kolor wypełnienia na ciemny niebieski tła, #0F1B2D. Na jasnych kartach ikony wyglądają teraz jak wycięte.

Ikona termometru zaznaczona pod etykietą TEMP, z Kolorem wypełnienia #0F1B2D w inspektorze; wszystkie trzy ikony są ciemnoniebieskie na pomarańczowej, niebieskiej i zielonej karcie

W kodzie jednokolorowa ikona staje się drawBitmap:

cpp
tft.drawBitmap(20, 104, image_streamline_weather_temperature_thermometer__weather_png_bits, 21, 21, 0x8C5);

Argumenty to pozycja, tablica bitów, szerokość i wysokość oraz kolor. Tablica przechowuje jeden bit na piksel, jak na OLED w odcinku 2: termometr ma 21×21 pikseli, 3 bajty na wiersz, łącznie 63 bajty. Kolor jest osobną liczbą w kodzie, więc tę samą ikonę można narysować w dowolnym kolorze bez drugiej kopii.

Krok 5: kolorowy obrazek ​

Prześlij go jako RGB ​

Obrazki też mogą być w pełnym kolorze. W karcie Obrazy kliknij Prześlij i wybierz obrazek, tak samo jak w odcinku 2. Tym razem przełącz konwerter z Monochromatyczny na RGB.

Okno importu obrazu z obrazkiem słońca i chmury, konwerterem przełączonym na RGB i 115 200 bajtów u góry, dla Wymiarów 240 na 240

Licznik u góry pokazuje koszt. W pełnym rozmiarze 240×240 pikseli ten obrazek zająłby 240 × 240 × 2 = 115 200 bajtów, ponad trzy razy więcej niż cały flash UNO.

Zmniejsz go do 40×40 ​

Ustaw Wymiary na W 40 i H 40. Teraz obrazek zajmuje 40 × 40 × 2 = 3200 bajtów, około trzech kilobajtów. To idealny rozmiar dla kolorowych obrazków: małe, kolorowe szczegóły. Kliknij Importuj.

W kodzie obrazek staje się tablicą 1600 liczb RGB565, po jednej na piksel, oraz jedną linią, która go rysuje:

cpp
tft.drawRGBBitmap(12, 197, image_sun_cloud_pixels, 40, 40);

W przeciwieństwie do drawBitmap to wywołanie nie przyjmuje koloru: każdy piksel przynosi własny.

Podsumowanie ​

Umieść obrazek w lewym dolnym rogu i dodaj obok niego jeszcze jedną linię tekstu: Outside: 18C, sunny czcionką Adafruit 5x7, Rozmiar 2, w tym samym szaroniebieskim #9FB3C8 co godzina.

Krok 6: pobierz kod ​

Jedna karta, trzy dialekty ​

Każda biblioteka mówi własnym dialektem. Ta sama zaokrąglona karta to drawRBox w U8g2, fillRoundRect w Adafruit GFX i stylizowany obiekt w LVGL. Lopaka pisze ten dla platformy, którą wybrałeś.

Jedna karta, trzy dialekty: u8g2.drawRBox(10, 56, 96, 140, 12) w U8g2, tft.fillRoundRect(10, 56, 96, 140, 12, 0xFC00) w Adafruit GFX oraz obiekt LVGL z pozycją, rozmiarem i promieniem rogu 12

Kopiuj i pobierz pliki czcionek ​

Kliknij Kopiuj nad kodem. Pod przełącznikami obok kodu Lopaka wymienia Czcionki i Obrazy używane przez projekt. Kliknij każdy z trzech plików czcionek, aby go pobrać, i umieść pliki obok swojego szkicu. Obrazek i ikony znajdują się już w skopiowanym kodzie jako tablice, więc nie potrzebujesz plików obrazów.

Gotowy pulpit w edytorze; pod przełącznikami kodu lista Czcionek pokazuje FreeSansBold12pt7b.h, FreeSans12pt7b.h i FreeSansBold18pt7b.h, a lista Obrazów pokazuje cztery pliki obrazów

Jeśli zapomnisz plików czcionek, kompilator zatrzyma się z błędem fatal error: FreeSans12pt7b.h: No such file or directory.

Lopaka generuje ten kod dla pulpitu. Tablica obrazka ma 1600 liczb, więc jest tutaj skrócona; Kopiuj daje ci całość.

cpp
// [BEGIN lopaka generated]
#include "FreeSans12pt7b.h"
#include "FreeSansBold12pt7b.h"
#include "FreeSansBold18pt7b.h"

static const unsigned char PROGMEM image_drop_bits[] = {0x10,0x38,0x7c,0x7c,0xfe,0xde,0xde,0xde,0x7c,0x38};

static const unsigned char PROGMEM image_streamline_ecology_leaf_bug__ecology_png_bits[] = {0x00,0x00,0x30,0x00,0x00,0x48,0x00,0x00,0x88,0x00,0x3f,0x08,0x03,0xe0,0x08,0x0c,0x00,0x08,0x10,0xe0,0x08,0x21,0x70,0x88,0x41,0xd1,0x08,0x40,0xe2,0x08,0x80,0x0c,0x08,0x80,0x30,0x10,0x80,0xc0,0x10,0x83,0x00,0x20,0x84,0x00,0x20,0x88,0x00,0x40,0x50,0x00,0x80,0x20,0x03,0x00,0x50,0x0c,0x00,0x8f,0xf0,0x00,0x80,0x00,0x00};

static const unsigned char PROGMEM image_streamline_weather_temperature_thermometer__weather_png_bits[] = {0x03,0x00,0x00,0x04,0x80,0x00,0x04,0x80,0x00,0x04,0x87,0x80,0x04,0x80,0x00,0x04,0x80,0x00,0x04,0x80,0x00,0x04,0x87,0x80,0x04,0x80,0x00,0x04,0x80,0x00,0x07,0x80,0x00,0x07,0x87,0x80,0x07,0x80,0x00,0x07,0x80,0x00,0x0f,0xc0,0x00,0x1f,0x60,0x00,0x1f,0xa0,0x00,0x1f,0xa0,0x00,0x1f,0xe0,0x00,0x0f,0xc0,0x00,0x07,0x80,0x00};

static const uint16_t PROGMEM image_sun_cloud_pixels[] = {/* 1,600 RGB565 values, shortened here */};

void drawScreen_1(void) {
    tft.fillScreen(0x8C5);
    // rect 1
    tft.fillRoundRect(10, 56, 96, 140, 12, 0xFC00);
    // rect 2
    tft.fillRoundRect(112, 56, 96, 140, 12, 0x2BFF);
    // rect 3
    tft.fillRoundRect(214, 56, 96, 140, 12, 0x25AE);
    // string 4
    tft.setTextColor(0xFFFF);
    tft.setTextWrap(false);
    tft.setFont(&FreeSansBold12pt7b);
    tft.setCursor(12, 35);
    tft.print("Living room");
    // string 5
    tft.setTextColor(0x9D99);
    tft.setFont(&FreeSans12pt7b);
    tft.setCursor(246, 35);
    tft.print("07:45");
    // string 6
    tft.setTextColor(0xFFFF);
    tft.setTextSize(2);
    tft.setFont();
    tft.setCursor(20, 72);
    tft.print("TEMP");
    // string 7
    tft.setTextSize(1);
    tft.setFont(&FreeSansBold18pt7b);
    tft.setCursor(20, 179);
    tft.print("23C");
    // string 8
    tft.setTextSize(2);
    tft.setFont();
    tft.setCursor(122, 72);
    tft.print("HUMID");
    // string 9
    tft.setTextSize(1);
    tft.setFont(&FreeSansBold18pt7b);
    tft.setCursor(122, 179);
    tft.print("48%");
    // string 10
    tft.setTextSize(2);
    tft.setFont();
    tft.setCursor(224, 72);
    tft.print("AIR");
    // string 11
    tft.setTextSize(1);
    tft.setFont(&FreeSansBold18pt7b);
    tft.setCursor(224, 179);
    tft.print("OK");
    // streamline-weather-temperature-thermometer--weather.png
    tft.drawBitmap(20, 104, image_streamline_weather_temperature_thermometer__weather_png_bits, 21, 21, 0x8C5);
    // drop
    tft.drawBitmap(122, 104, image_drop_bits, 7, 10, 0x8C5);
    // streamline-ecology-leaf-bug--ecology.png
    tft.drawBitmap(224, 104, image_streamline_ecology_leaf_bug__ecology_png_bits, 21, 21, 0x8C5);
    // sun_cloud
    tft.drawRGBBitmap(12, 197, image_sun_cloud_pixels, 40, 40);
    // string 16
    tft.setTextColor(0x9D99);
    tft.setTextSize(2);
    tft.setFont();
    tft.setCursor(62, 211);
    tft.print("Outside: 18C, sunny");
}
// [END lopaka generated]

Krok 7: uruchom to na ESP32 ​

Szkic ​

Tym razem uruchamiamy ekran na ESP32, szybszej płytce, popularnej przy kolorowych ekranach; typowa płytka ESP32 ma 4 MB flash. Wyświetlacz to moduł ILI9341 320×240 na SPI.

cpp
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

// ILI9341 320x240 color display on SPI
#define TFT_CS  15
#define TFT_DC  2
#define TFT_RST 4
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);

// Paste the Lopaka code here

void setup() {
  tft.begin();
  tft.setRotation(1);   // landscape, 320x240
  drawScreen_1();
}

void loop() {
}
  • Adafruit_ILI9341 tft(...) tworzy obiekt wyświetlacza i mówi mu, których pinów używa do wyboru chipa, danych/poleceń i resetu. Kod Lopaka nazywa go tft, więc zachowaj tę nazwę.
  • ILI9341 to panel portretowy, szeroki na 240 i wysoki na 320. setRotation(1) obraca go do orientacji poziomej, 320×240, rozmiaru, który zaprojektowaliśmy.
  • drawScreen_1() uruchamia się raz, w setup(). Ekran zachowuje swój obraz, dopóki coś nie narysuje na nim, więc loop() na razie pozostaje puste.

Podłącz to w symulatorze ​

W symulatorze z odcinka 1 rozpocznij nowy projekt ESP32 i dodaj wyświetlacz ILI9341. Podłącz go:

  • VCC i LED (podświetlenie) do 3V3, GND do GND,
  • CS do 15, D/C do 2, RST do 4,
  • MOSI do 23, SCK do 18, MISO do 19.

Dodaj biblioteki Adafruit ILI9341 i Adafruit GFX Library, wklej szkic z kodem Lopaka w jego miejsce, dodaj trzy pliki czcionek do projektu i naciśnij Play. Pulpit pojawia się na symulowanym wyświetlaczu.

Symulowane ESP32 podłączone do wyświetlacza ILI9341 pokazującego pulpit Living room z kartami TEMP, HUMID i AIR

Brak bufora ramki ​

Pamiętasz bufor ramki z odcinka 1, cały obraz przechowywany w RAM przed wysłaniem na wyświetlacz? Przy 320×240 w kolorze potrzebowałby tych samych 150 KB co obrazek na cały ekran. Kolorowe biblioteki takie jak Adafruit GFX pomijają go i rysują prosto na wyświetlacz: każde fillRoundRect i print wysyła swoje piksele przez SPI od razu.

Dzięki temu zużycie RAM jest malutkie. Po skompilowaniu dla ESP32 szkic używa 23% przestrzeni programu i 7% RAM. Ten sam szkic skompilowany dla Arduino UNO:

Sketch uses 25946 bytes (80%) of program storage space. Maximum is 32256 bytes.
Global variables use 317 bytes (15%) of dynamic memory, leaving 1731 bytes for local variables. Maximum is 2048 bytes.

Tylko 15% RAM UNO. Ciasna jest pamięć flash: 80%, a trzy czcionki i obrazek o rozmiarze 3200 bajtów stanowią jej dużą część.

Na prawdziwej płytce ​

  • Napięcie sygnału. ESP32 działa na 3,3 V, podobnie jak ILI9341. Wiele niedrogich modułów ILI9341 nie ma przesuwnika poziomu, więc na płytce 5 V, takiej jak UNO, przed podłączeniem sprawdź opis swojego modułu.
  • Kontroler. Niektóre moduły 320×240 używają zamiast tego kontrolera ST7789. W ich przypadku dołącz Adafruit_ST7789.h, utwórz wyświetlacz jako Adafruit_ST7789 tft(TFT_CS, TFT_DC, TFT_RST); i uruchom go za pomocą tft.init(240, 320); zamiast tft.begin();. Kod Lopaka pozostaje ten sam.

Potrzebujesz pomysłów? Galeria projektów zawiera tysiące publicznych projektów. Otwórz jeden, naciśnij Kopia, a Lopaka skopiuje go do twoich projektów, gotowego do zmiany.

Podsumowanie ​

  1. Trzy światła tworzą każdy kolor: każdy piksel miesza czerwień, zieleń i niebieski.
  2. RGB565 upycha je w dwóch bajtach: 5 bitów czerwieni, 6 zieleni, 5 błękitu, a Lopaka konwertuje każdy kolor za ciebie.
  3. Kształty wygrywają z dużymi obrazkami: karta to jedna linia kodu, obrazek na cały ekran to 150 KB, więc utrzymuj obrazki małe.

Następny odcinek: Dane na żywo. Sprawiamy, że te liczby zmieniają się same. Przeczytaj odcinek 4 lub obejrzyj go na YouTube.

Gotowy, aby spróbować? Otwórz Lopaka i zbuduj własny kolorowy pulpit.