poniedziałek, 14 listopada 2022

Colibri IMx6 - Wyświetlacz Waveshare

W tym poście opiszę sposób implementacji wyświetlacza firmy Waveshare.


Wyświetlacz został skonfigurowany do wersji BSP 2.8b6. 

Podłączenie sprzętowe:


Opis pinów wyświetlacza wygląda następująco:


Z wyświetlacza zostały wyprowadzone dwie tasiemki jedna dla panelu dotykowego, druga dla linii wyświetlacza.

Opis linii wyświetlacza:

VLED- - katoda podświetlenia ekranu
VLED+ - anoda podświetlenia ekranu
GND - masa wyświetlacza
VCC - zasilanie wyświetlacza
R0 - R7 - 8 linii wyświetlacza odpowiedzialnych za kolor czerwony.
G0 - G7 - 8 linii wyświetlacza odpowiedzialnych za kolor zielony.
B0 - B7 - 8 linii wyświetlacza odpowiedzialnych za kolor niebieski.
CLK - sygnał służący do wysterowania komunikacji.
R/L - Right/Left Selection. 
HSync - odświeżanie poziome. Informacja o przejściu do następnej linii.
VSync - odświeżanie pionowe. Informacja o zakończeniu rysowania wszystkich linii.
U/D - Up/Down Selection.
Reset - reset wyświetlacza.

Konfiguracja skanowania w zależności od ustawienia stanu na pinach R/L oraz U/D:


Wyświetlacz domyślnie wykorzystuje paletę kolorów RGB565. Po delikatnych modyfikacjach na wyświetlaczu (zmianie podciągania pinu sterującego z VCC do GND, zmiana wykonana na taśmie wyświetlacza) pozwala na działanie z paletą kolorów RGB888.

Sposób podłączenia wyświetlacza można znaleźć w dokumentacji modułu [link]:


Ustawienia czasowe wyświetlacza można znaleźć w datasheet:


Wyświetlacz wykorzystuje interfejs równoległy. Jego zaletą jest duża prędkość działania oraz bezpośredni zapis danych do wyświetlacza z linii RGB. Pewnym problemem w tego rodzaju rozwiązaniach jest brak standardowego złącza (co dosyć mocno ogranicza zmianę modelu wyświetlacza w już gotowym projekcie), duża ilość linii do podłączenia oraz występujące zakłócenia, które ograniczają możliwość stosowania długiego połączenia. Dodatkowym problemem jest brak możliwości wykrycia wyświetlacza przez programistę. Co oznacza konieczność wprowadzania jego definicji w kodzie sterownika oraz Device Tree systemu. 

Modyfikacja sterownika:


Na samym początku należy zmodyfikować plik linux-toradex/drivers/video/fbdev/mxc/mxc_lcdif.c, przez dodanie do niego parametrów wyświetlacza:

  1. {
  2.     /* RCP-KD LCD WinStar WF70YTIAGDNG0# (7" 800x480 24bit) */
  3.     "WINSTAR-WVGA",
  4.     60,   /* refresh */
  5.     800, 480,   /* xres, yres */
  6.     30000,   /* pixclock */
  7.     .left_margin = 26,
  8.     .right_margin = 210,
  9.     .hsync_len = 20,
  10.     .upper_margin = 13,
  11.     .lower_margin = 22,
  12.     .vsync_len = 10,
  13.     .sync = FB_SYNC_CLK_LAT_FALL,
  14.     .vmode = FB_VMODE_NONINTERLACED,
  15.     .flag = 0,
  16. },

Powyższa deklaracja zawiera tryby działania wyświetlacza 

Definicja struktury do której dane są wprowadzone znajduje się w pliku linux-kernel/include/linux/fb.h:

  1. struct fb_videomode {
  2.     const char *name;   /* optional */
  3.     u32 refresh;        /* optional */
  4.     u32 xres;
  5.     u32 yres;
  6.     u32 pixclock;
  7.     u32 left_margin;
  8.     u32 right_margin;
  9.     u32 upper_margin;
  10.     u32 lower_margin;
  11.     u32 hsync_len;
  12.     u32 vsync_len;
  13.     u32 sync;
  14.     u32 vmode;
  15.     u32 flag;
  16. };

Opis zmiennych jest następujący:

refresh - 60 - Częstotliwość odświeżania. 
xres, yres - 800, 480 - widoczny obszar wyświetlacza 
left_margin, right_margin - margines lewy i prawy w pixelach.
upper_margin, lower_margin - margines dolny i górny w liniach.
hsync_len, vsync_len - długość sygnałów synchronizacji. 
sync - FB_SYNC_CLK_LAT_FALL - negatywna polaryzacja sygnału zegarowego (ang. Negative polarity of pixel clock)
vmode - FB_VMODE_NONINTERLACED - skanowanie linii wyświetlacza od góry do dołu (ang. Non interlaced video) (link)

Modyfikacja Device Tree:


Teraz należy zmodyfikować Device Tree w celu obsługi wszystkich linii wyświetlacza. Przykładowy plik device tree od producenta modułów Colibri można znaleźć pod tym linkiem, dodatkowo można posłużyć się definicjami zapisanymi w plikach imx6dl-colibri-cam-eval-v3.dts czy imx6dl-colibri-aster.dts

  1. &pinctrl_ipu1_lcd {
  2.     fsl,pins = <
  3.         MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0xa1
  4.         MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15       0xa1
  5.         MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02        0xa1
  6.         MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03        0xa1
  7.         MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00   0xa1
  8.         MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01   0xa1
  9.         MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02   0xa1
  10.         MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03   0xa1
  11.         MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04   0xa1
  12.         MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05   0xa1
  13.         MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06   0xa1
  14.         MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07   0xa1
  15.         MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08   0xa1
  16.         MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09   0xa1
  17.         MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10  0xa1
  18.         MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11  0xa1
  19.         MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12  0xa1
  20.         MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13  0xa1
  21.         MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14  0xa1
  22.         MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15  0xa1
  23.         MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16  0xa1
  24.         MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17  0xa1
  25.         MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18  0xa1
  26.         MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19  0xa1
  27.         MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20  0xa1
  28.         MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21  0xa1
  29.         MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22  0xa1
  30.         MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23  0xa1
  31.     >;
  32. };

Podświetlenie realizowane jest z PWM3:

  1. &backlight {
  2.     pwms = <&pwm3 0 5000000 1>;
  3.     brightness-levels = <0 4 8 16 32 64 128 255>;
  4.     default-brightness-level = <6>;
  5.     status = "okay";
  6. };

Należy tutaj pamiętać, że przy takiej konfiguracji nie można osiągnąć czasu wypełnienia impulsu z przedziału od 0 do 50%. Wybranie wartości 1, do której jest przypisana wartość 4, da wypełnienie prawie 100%. Wartość 7 do której przypisana będzie wartość 255 spowoduje wygenerowanie PWM o współczynniku wypełnienia 0.

CTP FT5426 wykorzystujący interfej I2C również musi zostać opisany w DT [link]:

  1. aliases {
  2.     i2c0 = &i2cddc;
  3.     i2c1 = &i2c2;
  4.     i2c2 = &i2c3;
  5. };
  1. &i2c3 {
  2.     status = "okay";
  3.   //...
  4. //...
  5.     edt_ft5x06: edt-ft5x06@38 {
  6.         compatible = "edt,edt-ft5406", "edt,edt-ft5x06";
  7.         reg = <0x38>;
  8.         interrupt-parent = <&gpio2>;
  9.         interrupts = <24 IRQ_TYPE_EDGE_FALLING>;
  10.         reset-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>;
  11.         pinctrl-names = "default";
  12.         pinctrl-0 = <&pinctrl_edt_ft5x06>;
  13.     };
  14. };

  1. pinctrl_edt_ft5x06: edt-ft5x06 {
  2.     fsl,pins = <
  3.         MX6QDL_PAD_EIM_CS1__GPIO2_IO24  0x130b0     /* Interrupt */
  4.         MX6QDL_PAD_SD2_DAT1__GPIO1_IO14 0x80000000 /* Reset */
  5.     >;
  6. };

Sterownik jest standardowo umieszczony w projekcie jądra systemu.

Dodatkowo w opcji nconfig (make menuconfig) należy uruchomić sterownik:

Device Drivers->Input device support->Touchscreens->EDT FocalTech FT5x06 I2C Touchscreen support"->y

Następnie zostało skompilowanie jądra systemu z przygotowanych plików.

  1. //Kompilacja wszystkich plików dts
  2. make dtbs
  3.  
  4. make colibri_imx6_defconfig
  5. make nconfig
  6. make -j$(nproc) zImage LOADADDR=10008000 2>&1 | tee build.log
  7. make imx6dl-colibri-custom-board.dtb

Modyfikacja uEnv.txt:


Urządzenie wyjściowe, czyli ekran, jest kontrolowany przez U-Boot. Oznacza to konieczność modyfikacji wartości zapisanych także w pliku uEnv.txt. Przykładowe konfiguracje można pobrać z tego [link] [link] [link]:

panel=wvga-rgb
vidargs=video=mxcfb0:dev=lcd,WINSTAR-WVGA,if=RGB24,bpp=32 video=mxcfb1:off fbmem=8M

Komenda przyjmuje następujący format:

  1. video=mxcfb<number>:dev=<Output>,<Mode Specifier>,if=<Output Format>,[bpp=<Framebuffer Depth>]

Poniżej opis wprowadzonych modyfikacji [link1] [link2]:

panel=wvga-rgb - rodzaj wyświetlacza (u-boot-toradex/board/toradex/colibri_imx6/colibri_imx6.c)
vidargs=video=mxcfb0 - Domyślny stosowany ekran
dev=lcd, WINDSTAR-WVGA - wybranie interfejsu wyjściowego, lcd oznacza RGB. Po przecinku podana jest nazwa wyświetlacza.
if=RGB24 - definicja danych wyjściowych.
bpp=32 - wielkość bufora ramki danych.
video=mxcfb1:off - wyłączony inny drugi wyświetlacz
fbmem=8M - rozmiar pamięci przygotowanej dla każdej ramki danych wysyłanej do wyświetlacza.

Uruchomiony ekran z załadowanym domyślnym pulpitem wygląda następująco:


Przed uruchomieniem pulpitu na ekranie powinny wyświetlić się pingwiny. Ich ilość odpowiada ilości rdzeni procesora. 

Ustawione parametry wyświetlacza można sprawdzić przez komendę fbset [link];


Panel dotykowy można sprawdzić następującymi komendami:


Wyświetlenie błędów i szczegółów dotyczących I2C:


Sprawdzenie podłączonych urządzeń do interfejsów I2C można sprawdzić przez komendę:

  1. i2cdetect -y -r <numer_interfejsu>

Panel dotykowy jest podłączony do interfejsu I2C-2. Adres podłączonego układu można sprawdzić odpowiednią komendą:


Odczytywane dane przypisane będą do jednego z zdarzeń (event) w systemie:


Następnie sprawdzenie odbieranych danych wejściowych:

  1. evtest /dev/input/event0

Wynik działania komendy jest następujący:


Zakres dotyku dla osi X wynosi od 0 do 1790, dla oxi Y od 0 do 1040. 

W celu uzyskania punktu kliknięcia na ekranie należy przetworzyć otrzymane wartości (ABS_X, ABS_Y):

  1. posX = (click/max_x_value) * x_max_screen_resolution
  2. posY = (click/max_y_value) * y_max_screen_resolution