Második HDMI monitor Raspberry Pi3-hoz DPI interfészen és FPGA lapon keresztül


A videón a Raspberry Pi3 alaplap látható, amelyhez a GPIO csatlakozón keresztül egy Mars Rover2rpi (Cyclone IV) FPGA lap csatlakozik, amelyhez egy HDMI monitor van csatlakoztatva. A második monitor a Raspberry Pi3 beépített HDMI csatlakozóján keresztül csatlakozik. Az egész egy kétmonitoros rendszerként működik.

Most elmondom, hogyan valósítottam ezt meg.

A népszerű Raspberry Pi3 alaplapon van egy GPIO csatlakozó, amelyhez különféle kiterjesztő lapokat lehet csatlakoztatni: érzékelők, LED-ek, léptetőmotor-vezérlők és még sok más. Minden egyes kimenet funkciója a portok konfigurációjától függ. Az GPIO ALT2 konfiguráció lehetővé teszi, hogy a csatlakozó a DPI interfész üzemmódba lépjen, azaz a Display Parallel Interface-be. Léteznek kiterjesztő lapok VGA monitorok csatlakoztatására a DPI-n keresztül. Azonban egyrészt a VGA monitorok már nem annyira elterjedtek, mint az HDMI, másrészt a digitális interfész mindig is jobb volt az analóg interfésznél. Ráadásul a hasonló VGA kiterjesztő lapokon található DAC általában R-2-R láncokból készül, és gyakran nem több mint 6 bit színenként.

ALT2 módban a GPIO csatlakozó lábainak következő értékei vannak:

Második HDMI monitor Raspberry Pi3-hoz DPI interfészen és FPGA lapon keresztül

Itt színeztem az RGB kimeneteket a csatlakozóhoz tartozóan piros, zöld és kék színűre. Más fontos jelek a V-SYNC és H-SYNC szinkronizáló jelek, valamint a CLK. A CLK órajel frekvencia az a frekvencia, amellyel a pixelek értékei megjelennek a csatlakozón, és a kiválasztott videó módjától függ.

Digitális HDMI monitor csatlakoztatásához a DPI interfész jeleit kell elkapni és HDMI jelekké alakítani. Ezt például bármilyen FPGA lappal meg lehet valósítani. Kiderült, hogy a Mars Rover2rpi lap megfelelő erre a célra. Az igazat megvallva, ennek a lapnak a speciális adapterrel való csatlakoztatásának fő verziója így néz ki:

Második HDMI monitor Raspberry Pi3-hoz DPI interfészen és FPGA lapon keresztül

Ez a lap a GPIO portok számának növelésére és több perifériás eszköz csatlakoztatására szolgál a Raspberry-hez. Ugyanakkor 4 GPIO jel a JTAG jeleként van használva, így a Raspberry-ből származó program tudja betölteni az FPGA firmware-t a PLIS-be. Emiatt ez a beépített csatlakozás számomra nem megfelelő, mert 4 DPI jel kiesik. Szerencsére a lapon található további csatlakozók Raspberry-kompatibilisek. Tehát 90 fokkal elforgathatom a lapot, és még mindig csatlakoztathatom a Raspberrymhez:

Második HDMI monitor Raspberry Pi3-hoz DPI interfészen és FPGA lapon keresztül

Természetesen külső JTAG programozót kell használnom, de ez nem probléma.

Van egy kis probléma. Nem minden FPGA kimenet használható órajeleként. Van néhány dedikált láb, amit erre a célra használhatunk. Így alakult, hogy a GPIO_0 CLK jel nem kerül be az FPGA bemenetére, amelyet órajelként lehetne használni. Ezért végül egy vezetéket kellett a deszkára csatlakoztatnom. A GPIO_0-t összekötöm a KEY[1] jelével a tervezőlapján:

Második HDMI monitor Raspberry Pi3-hoz DPI interfészen és FPGA lapon keresztül

Most mesélek egy kicsit a FPGA projektjéről. A HDMI jelek létrehozásának alapvető nehézsége a nagyon magas frekvenciák. Ha megnézzük a HDMI csatlakozó lábkiosztását, láthatjuk, hogy az RGB jelek most már soros differenciális jelek:

Második HDMI monitor Raspberry Pi3-hoz DPI interfészen és FPGA lapon keresztül

A differenciális jelek használata segít a közös impulzusok kiszűrésében a transmissziós vonalakon. Ekkor az eredeti nyolcbites végeredmény minden színjelhez 10-bites TMDS (Transition-minimized differential signaling) jellé alakul. Ez egy különleges kódolási mód, amely eltávolítja a konstans komponenst a jelből, és minimalizálja a jelswitch-ek számát a differenciális vonalon. Mivel egy szín bájtjának átadásához most már 10 bitet kell küldeni a soros vonalon, a soros átalakító órajele 10-szer nagyobb kell legyen, mint a pixel órajele. Például, ha megvizsgáljuk a 1280x720 60Hz videó módot, a pixel frekvenciája 74,25 MHz. A soros átalakítónak 742,5 MHz-nek kell lennie.

A normál FPGA-k sajnos nem képesek erre. De szerencsénkre az FPGA-kban beépített DDIO kimenetek találhatók. Ezek olyan kimenetek, amelyek már mint a 2-ből-1-be soros átalakítók működnek. Tehát a FPGA projektben nem 740 MHz-et, hanem 370 MHz-et használhatunk, de aktiválni kell a DDIO kimeneteket az FPGA-ban. Ez a 370 MHz már egy elérhető frekvencia. Sajnos a 1280×720 a határ. Ennél nagyobb felbontást nem érhetünk el a Marsjáró2 rpi-deszkán telepített Cyclone IV FPGA-n.

Tehát a projektben aCLK pixel órajel belép a PLL-be, ahol 5-szörösére emelik. Ezen a frekvencián az R, G, B bájtokat bitpárokká alakítják. Ezt a TMDS kódoló végzi. A Verilog HDL forráskód így néz ki:

module hdmi(
	input wire pixclk,		// 74MHz
	input wire clk_TMDS2,	// 370MHz
	input wire hsync,
	input wire vsync,
	input wire active,
	input wire [7:0]red,
	input wire [7:0]green,
	input wire [7:0]blue,
	output wire TMDS_bh,
	output wire TMDS_bl,
	output wire TMDS_gh,
	output wire TMDS_gl,
	output wire TMDS_rh,
	output wire TMDS_rl
);

wire [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;  // modulus 5 counter
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;

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

always @(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);
wire [8:0] q_m = {~XNOR, q_m[6:0] ^ VD[7:1] ^ {7{XNOR}}, VD[0]};

reg [3:0] balance_acc = 0;
wire [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;
wire balance_sign_eq = (balance[3] == balance_acc[3]);
wire invert_q_m = (balance==0 || balance_acc==0) ? ~q_m[8] : balance_sign_eq;
wire [3:0] balance_acc_inc = balance - ({q_m[8] ^ ~balance_sign_eq} & ~(balance==0 || balance_acc==0));
wire [3:0] balance_acc_new = invert_q_m ? balance_acc-balance_acc_inc : balance_acc+balance_acc_inc;
wire [9:0] TMDS_data = {invert_q_m, q_m[8], q_m[7:0] ^ {8{invert_q_m}}};
wire [9:0] TMDS_code = CD[1] ? (CD[0] ? 10'b1010101011 : 10'b0101010100) : (CD[0] ? 10'b0010101011 : 10'b1101010100);

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

endmodule

Ezután a kimeneti párok a DDIO kimenetre kerülnek, amely folyamatosan egy bitet ad ki az emelkedő és a süllyedő él mentén.

A DDIO-t a következő Verilog kóddal lehetne leírni:

module ddio(
	input wire d0,
	input wire d1,
	input wire clk,
	output wire out
	);

reg r_d0;
reg r_d1;
always @(posedge clk)
begin
	r_d0 <= d0;
	r_d1 <= d1;
end
assign out = clk ? r_d0 : r_d1;
endmodule

De ha így dolgozunk, valószínűleg nem fog működni. Használni kell az alternatív ALTDDIO_OUT megafunkciót az DDIO kimeneti elemek tényleges aktiválásához. A projektomban pontosan az ALTDDIO_OUT könyvtári komponenst használom.

Talán ez az egész egy kicsit bonyolultnak tűnik, de működik.

A Verilog HDL nyelven írt teljes forráskódot itt lehet megnézni itt, a githubon.

A FPGA-ra írt lefordított firmware az EPCS chipbe kerül, amely a Mars Rover2rpi lapkán található. Így amikor áramot adunk a FPGA lapkának, a FPGA a flash memóriából inicializálódik és elindul.

Most egy kicsit meséljünk a Raspberry konfigurációjáról.

Kísérleteket végzek a Raspberry PI OS (32 bites), a Debian Buster alapjain, 2020 augusztusi verzióval,
Megjelenés dátuma: 2020-08-20, Kernel verzió: 5.4.

Két dolgot kell csinálni:

  • szerkeszteni a config.txt fájlt;
  • létrehozni a X szerver konfigurációját, hogy két monitort használjunk.

A /boot/config.txt fájl szerkesztése során:

  1. ki kell kapcsolni az i2c, i2s, spi használatát;
  2. bekapcsolni DPI módot a dtoverlay=dpi24 overlay segítségével;
  3. beállítani a 1280×720 60Hz, 24 bit pixel video módot a DPI-n;
  4. meg kell adni a szükséges frame buffer-ek számát 2 (max_framebuffers=2, csak így jelenik meg a második eszköz /dev/fb1)

A config.txt fájl teljes szövege így néz ki.

# 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

Ezután létre kell hozni egy konfigurációs fájlt a X szerver számára, hogy a két monitort a két frame buffer-en /dev/fb0 és /dev/fb1 használhassuk:

A konfigurációs fájlom /usr/share/x11/xorg.conf.d/60-dualscreen.conf a következő:

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

Nos, ha még nincs telepítve, akkor telepíteni kell a Xineramát. Így a munkaterület teljes mértékben kiterjed a két monitorra, ahogy az a fenti demó videón látható.

Nos, talán ennyi. Most már a Raspberry Pi3 tulajdonosai is használhatják a két monitort.

A Marsjáró2rpi lap leírását és ábráját itt lehet megnézni.

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster