Druhý HDMI monitor k Raspberry Pi3 přes DPI rozhraní a FPGA desku


V tomto videu jsou zobrazeny: deska Raspberry Pi3, k níž je prostřednictvím konektoru GPIO připojena FPGA deska Mars Rover2rpi (Cyclone IV), k níž je připojen HDMI monitor. Druhý monitor je připojen přes standardní HDMI konektor Raspberry Pi3. Celkově fungují jako systém se dvěma monitory.

Dále vám povím, jak je to realizováno.

Na populární desce Raspberry Pi3 je konektor GPIO, přes který lze připojit různé rozšiřující desky: senzory, LED diody, ovladače krokových motorů a další. Konkrétní funkce každého vývodu na konektoru závisí na konfiguraci portů. Konfigurace GPIO ALT2 umožňuje přepnout konektor do režimu DPI rozhraní, Display Parallel Interface. Existují rozšiřující desky pro připojení VGA monitorů prostřednictvím DPI. Nicméně, za prvé, VGA monitory už nejsou tak rozšířené jako HDMI, a za druhé, digitální rozhraní je stále lepší než analogové. O to víc, že DAC na podobných VGA rozšiřujících deskách je obvykle realizován pomocí R-2-R řetězců a často má maximálně 6 bitů na barvu.

V režimu ALT2 mají piny konektoru GPIO následující hodnotu:

Druhý HDMI monitor k Raspberry Pi3 přes DPI rozhraní a FPGA desku

Zde jsem RGB výstupy konektoru namaloval odpovídajícími barvami: červenou, zelenou a modrou. Další důležité signály jsou signály synchronizace V-SYNC a H-SYNC, stejně jako CLK. Taktní frekvence CLK je frekvence, s níž jsou hodnoty pixelů vydávány na konektor, závisí na vybraném video režimu.

Pro připojení digitálního HDMI monitoru je třeba zachytit signály DPI rozhraní a převést je na signály HDMI. Toho lze dosáhnout například pomocí nějaké FPGA desky. Jak se ukázalo, deska Mars Rover2rpi je pro tyto účely vhodná. Opraveně, základní varianta připojení této desky přes speciální adaptér vypadá takto:

Druhý HDMI monitor k Raspberry Pi3 přes DPI rozhraní a FPGA desku

Tato deska slouží ke zvýšení počtu GPIO portů a pro připojení většího počtu periferních zařízení k raspberry. Přitom se 4 signály GPIO při takovém připojení používají jako signály JTAG, takže program z raspberry může nahrávat FPGA firmware do PLIS. Z tohoto důvodu se mi takové standardní připojení nehodí, vypadávají 4 signály DPI. Naštěstí další konektory na desce mají rozpinování kompatibilní s Raspberry. Takže mohu desku otočit o 90 stupňů a přesto ji připojit k mé malince:

Druhý HDMI monitor k Raspberry Pi3 přes DPI rozhraní a FPGA desku

Samozřejmě bude třeba použít externí JTAG programátor, ale to není problém.

I když existuje malý problém. Ne každý výstup FPGA může sloužit jako vstup pro taktovací signál. Existuje jen několik dedikovaných pinů, které lze pro tento účel použít. Tak se stalo, že signál GPIO_0 CLK nedosahuje na vstup FPGA, který lze použít jako vstup pro taktovací signál CPLD. Takže jsem nakonec musel propojit jeden drát na desku. Propojuji GPIO_0 a signál KEY[1] desky:

Druhý HDMI monitor k Raspberry Pi3 přes DPI rozhraní a FPGA desku

Nyní vám povím něco o projektu v FPGA. Hlavní obtížností při generování HDMI signálů jsou velmi vysoké frekvence. Pokud se podíváte na zapojení HDMI konektoru, uvidíte, že RGB signály jsou nyní sériové diferenciální signály:

Druhý HDMI monitor k Raspberry Pi3 přes DPI rozhraní a FPGA desku

Použití diferenciálního signálu umožňuje boj proti společným rušením na přenosové lince. Při tom je původní osmibitový kód každého barevného signálu převeden na 10-bitový TMDS (Transition-minimized differential signaling). To je speciální způsob kódování pro odstranění stálé složky ze signálu a minimalizaci přepínání signálů v diferenciální lince. Protože na jeden byte barvy je nyní třeba poslat 10 bitů po sériové přenosové lince, znamená to, že taktovací frekvence serializátoru musí být 10krát vyšší než taktovací frekvence pixelů. Pokud vezmeme například video režim 1280x720 60Hz, tak frekvence pixelů u tohoto režimu činí 74,25MHz. Na serializátoru by tedy mělo být 742,5MHz.

Obvyklé FPGA to bohužel nejsou schopny. Nicméně, naštěstí, v FPGA jsou zabudovány výstupy DDIO. To jsou takové výstupy, které se už jakoby chovají jako 2-1 serializátory. Mohou tedy posílat dva bity sekvenčně na vzestupném a sestupném okraji taktovací frekvence. To znamená, že v projektu FPGA lze použít 370MHz místo 740MHz, ale je třeba aktivovat výstupní prvky DDIO v CPLD. 370MHz je už docela dosažitelná frekvence. Bohužel, režim 1280×720 je maximální. Vyšší rozlišení na našem FPGA Cyclone IV umístěném na desce Mars Rover 2rpi nelze dosáhnout.

Takže v projektu je vstupní frekvence pixelů CLK poslána na PLL, kde se násobí 5. Na této frekvenci jsou bajty R, G, B převedeny na páry bitů. To provádí TMDS enkodér. Původní kód v Verilog HDL vypadá takto:

modul hdmi(
	vstupní vodič pixclk,		// 74MHz
	vstupní vodič clk_TMDS2,	// 370MHz
	vstupní vodič hsync,
	vstupní vodič vsync,
	vstupní vodič active,
	vstupní vodič [7:0]red,
	vstupní vodič [7:0]green,
	vstupní vodič [7:0]blue,
	výstupní vodič TMDS_bh,
	výstupní vodič TMDS_bl,
	výstupní vodič TMDS_gh,
	výstupní vodič TMDS_gl,
	výstupní vodič TMDS_rh,
	výstupní vodič TMDS_rl
);

vodič [9:0] TMDS_red, TMDS_green, TMDS_blue;
TMDS_encoder encode_R(.clk(pixclk), .VD(red  ), .CD({vsync,hsync}), .VDE(active), .TMDS(TMDS_red));
TMDS_encoder encode_G(.clk(pixclk), .VD(green), .CD({vsync,hsync}), .VDE(active), .TMDS(TMDS_green));
TMDS_encoder encode_B(.clk(pixclk), .VD(blue ), .CD({vsync,hsync}), .VDE(active), .TMDS(TMDS_blue));

reg [2:0] TMDS_mod5=0;  // přesnost 5
reg [4:0] TMDS_shift_bh=0, TMDS_shift_bl=0;
reg [4:0] TMDS_shift_gh=0, TMDS_shift_gl=0;
reg [4:0] TMDS_shift_rh=0, TMDS_shift_rl=0;

vodič [4:0] TMDS_blue_l  = {TMDS_blue[9],TMDS_blue[7],TMDS_blue[5],TMDS_blue[3],TMDS_blue[1]};
vodič [4:0] TMDS_blue_h  = {TMDS_blue[8],TMDS_blue[6],TMDS_blue[4],TMDS_blue[2],TMDS_blue[0]};
vodič [4:0] TMDS_green_l = {TMDS_green[9],TMDS_green[7],TMDS_green[5],TMDS_green[3],TMDS_green[1]};
vodič [4:0] TMDS_green_h = {TMDS_green[8],TMDS_green[6],TMDS_green[4],TMDS_green[2],TMDS_green[0]};
vodič [4:0] TMDS_red_l   = {TMDS_red[9],TMDS_red[7],TMDS_red[5],TMDS_red[3],TMDS_red[1]};
vodič [4:0] TMDS_red_h   = {TMDS_red[8],TMDS_red[6],TMDS_red[4],TMDS_red[2],TMDS_red[0]};

vždy @(posedge clk_TMDS2)
begin
	TMDS_shift_bh <= TMDS_mod5[2] ? TMDS_blue_h  : TMDS_shift_bh  [4:1];
	TMDS_shift_bl <= TMDS_mod5[2] ? TMDS_blue_l  : TMDS_shift_bl  [4:1];
	TMDS_shift_gh <= TMDS_mod5[2] ? TMDS_green_h : TMDS_shift_gh  [4:1];
	TMDS_shift_gl <= TMDS_mod5[2] ? TMDS_green_l : TMDS_shift_gl  [4:1];
	TMDS_shift_rh <= TMDS_mod5[2] ? TMDS_red_h   : TMDS_shift_rh  [4:1];
	TMDS_shift_rl <= TMDS_mod5[2] ? TMDS_red_l   : TMDS_shift_rl  [4:1];
	TMDS_mod5 4'd4) || (Nb1s==4'd4 && VD[0]==1'b0);
vodič [8:0] q_m = {~XNOR, q_m[6:0] ^ VD[7:1] ^ {7{XNOR}}, VD[0]};

reg [3:0] balance_acc = 0;
vodič [3:0] balance = q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7] - 4'd4;
vodič balance_sign_eq = (balance[3] == balance_acc[3]);
vodič invert_q_m = (balance==0 || balance_acc==0) ? ~q_m[8] : balance_sign_eq;
vodič [3:0] balance_acc_inc = balance - ({q_m[8] ^ ~balance_sign_eq} & ~(balance==0 || balance_acc==0));
vodič [3:0] balance_acc_new = invert_q_m ? balance_acc-balance_acc_inc : balance_acc+balance_acc_inc;
vodič [9:0] TMDS_data = {invert_q_m, q_m[8], q_m[7:0] ^ {8{invert_q_m}}};
vodič [9:0] TMDS_code = CD[1] ? (CD[0] ? 10'b1010101011 : 10'b0101010100) : (CD[0] ? 10'b0010101011 : 10'b1101010100);

vždy @(posedge clk) TMDS <= VDE ? TMDS_data : TMDS_code;
vždy @(posedge clk) balance_acc <= VDE ? balance_acc_new : 4'h0;

konačit

Poté jsou výstupní páry přivedeny na DDIO výstup, který postupně vydává jednorázový signál na vzestupné a sestupné hraně.

Samotný DDIO by mohl být popsán tímto Verilog kódem:

modul ddio(
	vstupní vodič d0,
	vstupní vodič d1,
	vstupní vodič clk,
	výstupní vodič out
	);

reg r_d0;
reg r_d1;
vždy @(posedge clk)
begin
	r_d0 <= d0;
	r_d1 <= d1;
end
přiřazení out = clk ? r_d0 : r_d1;
končit

Ale takto to pravděpodobně nebude fungovat. Je nutné použít alternativní mega funkci ALTDDIO_OUT pro skutečné aktivování výstupních DDIO prvků. V mém projektu se používá právě knihovní komponent ALTDDIO_OUT.

Možná to všechno vypadá poněkud komplikovaně, ale skutečně to funguje.

Celý zdrojový kód napsaný ve Verilog HDL si můžete prohlédnout zde, na githubu.

Sestavený firmware pro FPGA se nahrává do čipu EPCS umístěného na desce Mars Rover2rpi. Takže při zapnutí napájení desky FPGA se FPGA inicializuje z flash paměti a spustí se.

Teď je potřeba trochu povědět o konfiguraci samotného Raspberry.

Experimentuji na Raspberry PI OS (32 bit) založeném na Debian Buster, verze: srpen 2020,
Datum vydání: 2020-08-20, verze jádra: 5.4.

Je třeba udělat dvě věci:

  • upravit soubor config.txt;
  • vytvořit konfiguraci X serveru pro práci se dvěma monitory.

Při úpravě souboru /boot/config.txt je třeba:

  1. vypnout používání i2c, i2s, spi;
  2. zapnout režim DPI pomocí overlay dtoverlay=dpi24;
  3. nastavit video režim 1280×720 60Hz, 24 bity na pixel pro DPI;
  4. uvést požadovaný počet frame bufferů na 2 (max_framebuffers=2, pouze tehdy se objeví druhé zařízení /dev/fb1)

Celý text souboru config.txt vypadá takto.

# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details

# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1

# uncomment this if your display has a black border of unused pixels visible
# and your display can output without overscan
disable_overscan=1

# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16

# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720

# uncomment if hdmi display is not detected and composite is being output
hdmi_force_hotplug=1

# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1

# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2

# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4

# uncomment for composite PAL
#sdtv_mode=2

#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800

# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on

dtparam=i2c_arm=off
dtparam=spi=off
dtparam=i2s=off

dtoverlay=dpi24
overscan_left=0
overscan_right=0
overscan_top=0
overscan_bottom=0
framebuffer_width=1280
framebuffer_height=720
display_default_lcd=0
enable_dpi_lcd=1
dpi_group=2
dpi_mode=87
#dpi_group=1
#dpi_mode=4
dpi_output_format=0x6f027
dpi_timings=1280 1 110 40 220 720 1 5 5 20 0 0 0 60 0 74000000 3

# Uncomment this to enable infrared communication.
#dtoverlay=gpio-ir,gpio_pin=17
#dtoverlay=gpio-ir-tx,gpio_pin=18

# Additional overlays and parameters are documented /boot/overlays/README

# Enable audio (loads snd_bcm2835)
dtparam=audio=on

[pi4]
# Enable DRM VC4 V3D driver on top of the dispmanx display stack
#dtoverlay=vc4-fkms-v3d
max_framebuffers=2

[all]
#dtoverlay=vc4-fkms-v3d
max_framebuffers=2

Poté je třeba vytvořit konfigurační soubor pro X server pro použití dvou monitorů na dvou frame bufferech /dev/fb0 a /dev/fb1:

Můj konfigurační soubor /usr/share/x11/xorg.conf.d/60-dualscreen.conf je následující

Section "Device"
        Identifier      "LCD"
        Driver          "fbturbo"
        Option          "fbdev" "\/dev\/fb0"
        Option          "ShadowFB" "off"
        Option          "SwapbuffersWait" "true"
EndSection

Section "Device"
        Identifier      "HDMI"
        Driver          "fbturbo"
        Option          "fbdev" "\/dev\/fb1"
        Option          "ShadowFB" "off"
        Option          "SwapbuffersWait" "true"
EndSection

Section "Monitor"
        Identifier      "LCD-monitor"
        Option          "Primary" "true"
EndSection

Section "Monitor"
        Identifier      "HDMI-monitor"
        Option          "RightOf" "LCD-monitor"
EndSection

Section "Screen"
        Identifier      "screen0"
        Device          "LCD"
        Monitor         "LCD-monitor"
EndSection

Section "Screen"
        Identifier      "screen1"
        Device          "HDMI" 
	Monitor         "HDMI-monitor"
EndSection

Section "ServerLayout"
        Identifier      "default"
        Option          "Xinerama" "on"
        Option          "Clone" "off"
        Screen 0        "screen0"
        Screen 1        "screen1" RightOf "screen0"
EndSection

A pokud ještě není nainstalován, je nutné nainstalovat Xineramu. Pak bude pracovní plocha plně rozšířena na dva monitory, jak je ukázáno výše v demonstračním videu.

A to je všechno. Nyní budou moci vlastníci Raspberry Pi3 používat dva monitory.

Popis a schéma desky Mars Rover2rpi lze shlédnout zde.

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster