Monitori i dytë HDMI për Raspberry Pi3 përmes ndërfaqes DPI dhe bordit FPGA


Në këtë video shikohet: karta Raspberry Pi3, e cila është e lidhur me kartën FPGA Marsrover2rpi (Cyclone IV) përmes portit GPIO, dhe kjo kartë është e lidhur me një monitor HDMI. Monitori i dytë është i lidhur përmes portit HDMI standard të Raspberry Pi3. Të gjitha së bashku funksionojnë si një sistem me dy monitore.

Më pas do të tregoj se si realizohet kjo.

Në kartën popullore Raspberry Pi3 ka një port GPIO, përmes të cilit mund të lidhni karta të ndryshme ndihmëse: sensorë, LED, drejtojës për motorë hapësorë dhe shumë të tjera. Funksioni specifik i çdo daljeje në port varet nga konfigurimi i porteve. Konfigurimi GPIO ALT2 mundëson kalimin e portit në modalitetin DPI të ndërfaqes, Display Parallel Interface. Ekzistojnë karta zgjerimi për lidhjen e monitoreve VGA përmes DPI. Megjithatë, së pari, monitorët VGA nuk janë më aq të përhapur sa HDMI, dhe së dyti, ndërfaqja digjitale gjithmonë është më e mirë se ajo analoge. Aq më tepër, që DAC në këto karta zgjerimi VGA zakonisht realizohet në formë të zinxhirëve R-2-R dhe shpesh nuk kalon 6 bit për ngjyrë.

Në modalitetin ALT2, pinët e portit GPIO kanë këto vlera:

Monitori i dytë HDMI për Raspberry Pi3 përmes ndërfaqes DPI dhe bordit FPGA

Këtu pata kromuar daljet RGB të ndërfaqes në ngjyrat e kuqe, jeshile dhe blu. Sinealet e tjera të rëndësishme janë sinealet e sinkronizimit V-SYNC dhe H-SYNC, si dhe CLK. Frekuenca e satelisë CLK është frekuenca me të cilën vlerat e pikselëve jepen në ndërfaqe, dhe varet nga moda grafike e zgjedhur.

Për të lidhur një monitor digjital HDMI, duhet të kapim sinealet e ndërfaqes DPI dhe t'i konvertojmë ato në sineale HDMI. Kjo mund të bëhet, për shembull, me ndihmën e ndonjë pllake FPGA. Siç rezultoi, plaka Mars Rover2rpi është e përshtatshme për këto qëllime. Sipas të vërtetës, varianti kryesor i lidhjes së kësaj plake përmes një adaptori të veçantë duket kështu:

Monitori i dytë HDMI për Raspberry Pi3 përmes ndërfaqes DPI dhe bordit FPGA

Kyçi i këtij bordi shërben për të rritur numrin e porteve GPIO dhe për të lidhur një numër më të madh pajisjesh periferike me raspberry. Në këtë rast, 4 sinjalet GPIO janë përdorur për sinjalet JTAG, kështu që programi nga raspberry mund të ngarkojë firmware-n e FPGA në PLIS. Prandaj, kjo lidhje e zakonshme nuk është e përshtatshme për mua, pasi humbasin 4 sinjalet DPI. Fatmirësisht, grebenat e shtesë në bord kanë një lidhje të përputhshme me Raspberry. Kështu, mund ta rrotulloj bordin 90 gradë dhe ende ta lidh me malin tim:

Monitori i dytë HDMI për Raspberry Pi3 përmes ndërfaqes DPI dhe bordit FPGA

Sigurisht, do të duhet të përdor një programator të jashtëm JTAG, por kjo nuk është problem.

MegjithatĂ«, ka njĂ« problem tĂ« vogĂ«l. Çdo dalje e FPGA nuk mund tĂ« pĂ«rdoret si hyrje e frekuencĂ«s sĂ« taktimeve. Ka vetĂ«m disa pin tĂ« dedikuar, tĂ« cilat mund tĂ« pĂ«rdoren pĂ«r kĂ«to qĂ«llime. Po ashtu ndodhi qĂ« sinjali GPIO_0 CLK nuk arrin nĂ« hyrjen e FPGA, e cila mund tĂ« pĂ«rdoret si hyrje e frekuencĂ«s sĂ« taktimeve tĂ« PLIS. KĂ«shtu qĂ« mĂ« nĂ« fund mĂ« duhej tĂ« lidh njĂ« kabllo nĂ« bord. Po lidhem GPIO_0 me sinjalin KEY[1] tĂ« bordit:

Monitori i dytë HDMI për Raspberry Pi3 përmes ndërfaqes DPI dhe bordit FPGA

Tani do të flas pak për projektin në FPGA. Vështirësia kryesore në formimin e sinjaleve HDMI është frekuencat shumë të larta. Nëse e shikoni pinout-in e konektorit HDMI, do të vini re se sinjalet RGB tani janë sinjale diferenciale të radhitur:

Monitori i dytë HDMI për Raspberry Pi3 përmes ndërfaqes DPI dhe bordit FPGA

Përdorimi i sinjaleve diferenciale lejon të luftohet me ndërhyrjet në vijën e transmetimit. Kështu, kodi origjinal tetëbitësh i secilit sinjal ngjyrë konvertohet në TMDS 10-bit (Transition-minimized differential signaling). Ky është një mënyrë speciale kodimi për të hequr komponentën e vazhdueshme nga sinjali dhe për të minimizuar kalimet e sinjaleve në vijën diferenciale. Sepse për një byte ngjyre tani në vijën e transmetimit duhet të dërgohen 10 bit, atëherë rezulton se frekuenca e taktimeve të serializatorit duhet të jetë 10 herë më e lartë se frekuenca e pikselëve. Nëse marrim si shembull modin e videos 1280x720 60Hz, frekuenca e pikselëve në këtë mod është 74,25 MHz. Serializatori duhet të ketë 742,5 MHz.

TĂ« zakonshmet FPGA nĂ« fakt nuk janĂ« tĂ« afta pĂ«r kĂ«tĂ«, pĂ«r fat tĂ« keq. MegjithatĂ«, pĂ«r fatin tonĂ«, FPGA kanĂ« daljet e integruara DDIO. KĂ«to janĂ« dalje qĂ« praktikisht veprojnĂ« si serializatorĂ« 2- nĂ«-1. Kjo do tĂ« thotĂ« se ato mund tĂ« lĂ«shojnĂ« dy bita nĂ« mĂ«nyrĂ« tĂ« njĂ«pasnjĂ«shme pĂ«r çdo formĂ« dhe rĂ«nie tĂ« taktimeve. KĂ«shtu qĂ« nĂ« projektin FPGA mund tĂ« pĂ«rdorim jo 740MHz, por 370MHz, por Ă«shtĂ« e nevojshme tĂ« aktivizohen elementĂ«t dalĂ«s DDIO nĂ« FPGA. Epo, 370MHz Ă«shtĂ« njĂ« frekuencĂ« tashmĂ« e arritshme. FatkeqĂ«sisht, rezolucioni 1280×720 Ă«shtĂ« kufiri. NjĂ« rezolucion mĂ« tĂ« lartĂ« nĂ« FPGA-nĂ« tonĂ« Cyclone IV tĂ« instaluar nĂ« bordin Mars Rover 2rpi nuk mund tĂ« arrihet.

Pra, në projekt, frekuenca hyrëse e pikseleve CLK e dërgon në PLL, ku shumëzohet me 5. Në këtë frekuencë, byte-et R, G, B, konvertohen në çifte bitësh. Kjo bën TMDS coder. Kodi burimor në Verilog HDL duket kështu:

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 <= (TMDS_mod5[2]) ? 3'd0 : TMDS_mod5+3'd1;
end

assign TMDS_bh = TMDS_shift_bh[0];
assign TMDS_bl = TMDS_shift_bl[0];
assign TMDS_gh = TMDS_shift_gh[0];
assign TMDS_gl = TMDS_shift_gl[0];
assign TMDS_rh = TMDS_shift_rh[0];
assign TMDS_rl = TMDS_shift_rl[0];

endmodule

module TMDS_encoder(
	input clk,
	input [7:0] VD,	// video data (red, green or blue)
	input [1:0] CD,	// control data
	input VDE,  	// video data enable, to choose between CD (when VDE=0) and VD (when VDE=1)
	output reg [9:0] TMDS = 0
);

wire [3:0] Nb1s = VD[0] + VD[1] + VD[2] + VD[3] + VD[4] + VD[5] + VD[6] + VD[7];
wire XNOR = (Nb1s>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

Më pas, çiftet e fundjavës dorëzohen në dalje DDIO, e cila lëshon një sinjal një bit në rritje dhe rënie.

DDIO mund të përshkruhet me këtë kod Verilog:

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

Por, me siguri nuk do të funksionojë kështu. Duhet të përdoret megafunksioni i Altera ALTDDIO_OUT për të aktivizuar realisht elementet dalje DDIO. Në projektin tim përdoret në të vërtetë komponenti i bibliotekës ALTDDIO_OUT.

Ndoshta, e gjithë kjo duket pak e komplikuar, por funksionon.

Mund të shihni të gjithë kodin burimor të shkruar në Verilog HDL këtu, në github.

Firmware e kompiluar për FPGA ngarkohet në çipin EPCS, i instaluar në bordin Mars Rover2rpi. Kështu, kur energjia i jepet bordit FPGA, PLS do të inicializohet nga memoria flash dhe të niste.

Tani duhet të flas pak për konfigurimin e vetë Raspberry.

Unë bëj eksperimente në Raspberry PI OS (32 bit) bazuar në Debian Buster, Version: Gusht 2020,
Data e shpërndarjes: 2020-08-20, Versioni i kernelit: 5.4.

Duhet të bëhen dy gjëra:

  • tĂ« redaktohet skedari config.txt;
  • tĂ« krijohet konfigurimi i serverit X pĂ«r tĂ« punuar me dy monitorĂ«.

Kur gjatë redaktimit të skedarit /boot/config.txt është nevoja të:

  1. ç'aktivizoni përdorimin e i2c, i2s, spi;
  2. aktivizoni modin DPI me anë të overlay-it dtoverlay=dpi24;
  3. konfiguroni modin e videos 1280×720 60Hz, 24 bit pĂ«r pikĂ« nĂ« DPI;
  4. specifikoni numrin e nevojshëm të framebuffers 2 (max_framebuffers=2, vetëm atëherë do të shfaqet pajisja e dytë /dev/fb1)

Teksti i plotë i skedarit config.txt duket kështu.

# 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

Pas kësaj, duhet të krijoni një skedar konfigurimi për serverin X për të përdorur dy monitorë në dy framebuffers /dev/fb0 dhe /dev/fb1:

Skedari im i konfigurimit /usr/share/x11/xorg.conf.d/60-dualscreen.conf është kështu

Sekcioni "Device"
        Identifikues      "LCD"
        Shofer          "fbturbo"
        Opsion          "fbdev" "/dev/fb0"
        Opsion          "ShadowFB" "off"
        Opsion          "SwapbuffersWait" "true"
MbyllSekcioni

Sekcioni "Device"
        Identifikues      "HDMI"
        Shofer          "fbturbo"
        Opsion          "fbdev" "/dev/fb1"
        Opsion          "ShadowFB" "off"
        Opsion          "SwapbuffersWait" "true"
MbyllSekcioni

Sekcioni "Monitor"
        Identifikues      "LCD-monitor"
        Opsion          "Primary" "true"
MbyllSekcioni

Sekcioni "Monitor"
        Identifikues      "HDMI-monitor"
        Opsion          "RightOf" "LCD-monitor"
MbyllSekcioni

Sekcioni "Screen"
        Identifikues      "screen0"
        Device          "LCD"
        Monitor         "LCD-monitor"
MbyllSekcioni

Sekcioni "Screen"
        Identifikues      "screen1"
        Device          "HDMI" 
	Monitor         "HDMI-monitor"
MbyllSekcioni

Sekcioni "ServerLayout"
        Identifikues      "default"
        Opsion          "Xinerama" "on"
        Opsion          "Clone" "off"
        Screen 0        "screen0"
        Screen 1        "screen1" RightOf "screen0"
MbyllSekcioni

Sido që, nëse akoma nuk është instaluar, duhet të instaloni Xinerama. Atëherë hapësira e Desktop-it do të zgjerohet plotësisht në dy monitorë, siç është treguar më sipër në videon demonstrative.

Kjo ishte gjithçka. Tani, edhe pronarët e Raspberry Pi3 do të mund të përdorin dy monitorë.

Përshkrimi dhe skema e bordit Mars Rover 2rpi mund të shikohet këtu.

Burimi: habr.com

Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« đŸ”„ Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« | ProHoster