Andra HDMI-skÀrmen till Raspberry Pi3 via DPI-grÀnssnitt och FPGA-kort


I den hÀr videon visas: kortet Raspberry Pi3, till vilket FPGA-kortet Mars Rover 2rpi (Cyclone IV) Àr anslutet via GPIO-kontakten, och som har en HDMI-skÀrm ansluten. Den andra skÀrmen Àr ansluten genom den vanliga HDMI-porten pÄ Raspberry Pi3. Tillsammans fungerar allt som ett system med tvÄ skÀrmar.

Jag kommer att berÀtta hur detta Àr genomfört.

PÄ den populÀra Raspberry Pi3-kortet finns en GPIO-port, genom vilken olika expansionskort kan anslutas: sensorer, lysdioder, stegmotordrivare och mycket mer. Den specifika funktionen för varje utgÄng pÄ porten beror pÄ konfigurationen av portarna. GPIO ALT2-konfigurationen tillÄter en att vÀxla porten till DPI-grÀnssnittets lÀge, Display Parallel Interface. Det finns expansionskort för att ansluta VGA-skÀrmar via DPI. Men för det första Àr VGA-skÀrmar inte lÀngre sÄ vanliga som HDMI, och för det andra Àr det digitala grÀnssnittet alltid bÀttre Àn det analoga. Dessutom Àr DAC:en pÄ sÄdana VGA-expansionskort vanligtvis utförd i form av R-2-R-kedjor och Àr ofta inte mer Àn 6 bitar per fÀrg.

I lÀget ALT2 har GPIO-portens stift följande vÀrde:

Andra HDMI-skÀrmen till Raspberry Pi3 via DPI-grÀnssnitt och FPGA-kort

Jag har hÀr fÀrglagt RGB-utgÄngarna pÄ porten i respektive röd, grön och blÄ fÀrg. Andra viktiga signaler Àr synchroniseringssignalerna V-SYNC och H-SYNC, samt CLK. Klockfrekvensen för CLK Àr den frekvens med vilken pixelvÀrden skickas till porten, och den beror pÄ den valda videolÀget.

För att ansluta en digital HDMI-skĂ€rm mĂ„ste DPI-grĂ€nssnittets signaler fĂ„ngas och omvandlas till HDMI-signaler. Detta kan göras, till exempel, med hjĂ€lp av ett FPGA-kort. Det visade sig att kortet Mars Rover 2rpi passar för dessa Ă€ndamĂ„l. Ärligt talat ser den huvudsakliga anslutningen av detta kort via en speciell adapter ut sĂ„ hĂ€r:

Andra HDMI-skÀrmen till Raspberry Pi3 via DPI-grÀnssnitt och FPGA-kort

Detta kort anvÀnds för att öka antalet GPIO-portar och för att ansluta fler perifera enheter till Raspberry. I detta fall anvÀnds 4 GPIO-signaler för JTAG-signaler, sÄ att programmet frÄn Raspberry kan ladda FPGA-firmware i PLI. Av denna anledning passar inte en sÄdan vanlig anslutning mig, 4 DPI-signaler faller bort. Lyckligtvis har de extra stiftarna pÄ kortet en pin-layout som Àr kompatibel med Raspberry. SÄ, jag kan vrida kortet 90 grader och ÀndÄ ansluta det till min Raspberry:

Andra HDMI-skÀrmen till Raspberry Pi3 via DPI-grÀnssnitt och FPGA-kort

SjÀlvklart, jag mÄste anvÀnda en extern JTAG-programmerare, men det Àr inget problem.

Det finns dock ett litet problem. Inte varje utgÄng frÄn FPGA kan anvÀndas som klocksignal. Det finns bara nÄgra dedikerade pinnar som kan anvÀndas för detta. SÄ det blev sÄ att GPIO_0 signal CLK inte nÄr FPGA-ingÄngen som kan anvÀndas som klocksignal för FPGA. SÄ det blev ÀndÄ nödvÀndigt att dra en kabel till kortet. Jag kopplar GPIO_0 och SIGNAL KEY[1] pÄ kortet:

Andra HDMI-skÀrmen till Raspberry Pi3 via DPI-grÀnssnitt och FPGA-kort

Nu ska jag berÀtta lite om projektet i FPGA. HuvudsvÄrigheten med att generera HDMI-signaler Àr de mycket höga frekvenserna. Om man ser pÄ stiften av HDMI-kontakten, sÄ ser man att RGB-signalerna nu Àr serielle differentialsignaler:

Andra HDMI-skÀrmen till Raspberry Pi3 via DPI-grÀnssnitt och FPGA-kort

AnvÀndningen av differentiella signaler möjliggör att man bekÀmpar gemensamma störningar i överföringslinjen. HÀrigenom omvandlas den ursprungliga 8-bitarskoden för varje fÀrgsignal till 10-bitars TMDS (Transition-minimized differential signaling). Detta Àr en speciell kodningsmetod för att ta bort DC-komponenten frÄn signalen och minimera signalvÀxlingarna i den differentiella linjen. Eftersom det nu för varje fÀrgbyte behöver överföras 10 bitar pÄ den serielle överföringslinjen, betyder det att klockfrekvensen för serialiseraren mÄste vara 10 gÄnger högre Àn pixelfrekvensen. Om vi tar till exempel videolÀget 1280x720 60Hz, sÄ har detta lÀge en pixelfrekvens pÄ 74,25 MHz. PÄ serialiseraren mÄste det vara 742,5 MHz.

Vanliga FPGA:er Ă€r tyvĂ€rr inte kapabla till detta. Men vi har tur, för i FPGA finns det inbyggda DDIO-utgĂ„ngar. Dessa utgĂ„ngar fungerar i praktiken som 2-till-1-serialiserare. Det betyder att de kan ge ut tvĂ„ bitar sekventiellt pĂ„ stigningen och nedgĂ„ngen av klockfrekvensen. SĂ„ i FPGA-projektet kan vi anvĂ€nda inte 740 MHz, utan 370 MHz, men vi behöver aktivera DDIO-utgĂ„ngarna i FPGA. 370 MHz Ă€r redan en fullt uppnĂ„elig frekvens. TyvĂ€rr Ă€r 1280×720 grĂ€nsen. Högre upplösning gĂ„r inte att nĂ„ i vĂ„r FPGA Cyclone IV installerad pĂ„ Mars Rover 2rpi.

SÄ, i projektet kommer ingÄngsfrekvensen för pixlar CLK till PLL, dÀr den multipliceras med 5. Vid denna frekvens omvandlas R-, G-, B-byte till bitpar. Detta gör TMDS-enkodern. Den ursprungliga koden i Verilog HDL ser ut sÄ hÀr:

modul hdmi(
	input wire pixclk,		// 74MHz
	input wire clk_TMDS2,	// 370MHz
	input wire hsync,
	input wire vsync,
	input wire aktiv,
	input wire [7:0]röd,
	input wire [7:0]grön,
	input wire [7:0]blÄ,
	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_röd, TMDS_grön, TMDS_blÄ;
TMDS_encoder encode_R(.clk(pixclk), .VD(röd), .CD({vsync,hsync}), .VDE(aktiv), .TMDS(TMDS_röd));
TMDS_encoder encode_G(.clk(pixclk), .VD(grön), .CD({vsync,hsync}), .VDE(aktiv), .TMDS(TMDS_grön));
TMDS_encoder encode_B(.clk(pixclk), .VD(blÄ), .CD({vsync,hsync}), .VDE(aktiv), .TMDS(TMDS_blÄ));

reg [2:0] TMDS_mod5=0;  // modulus 5 rÀknare
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_blÄ_l = {TMDS_blÄ[9],TMDS_blÄ[7],TMDS_blÄ[5],TMDS_blÄ[3],TMDS_blÄ[1]};
wire [4:0] TMDS_blÄ_h = {TMDS_blÄ[8],TMDS_blÄ[6],TMDS_blÄ[4],TMDS_blÄ[2],TMDS_blÄ[0]};
wire [4:0] TMDS_grön_l = {TMDS_grön[9],TMDS_grön[7],TMDS_grön[5],TMDS_grön[3],TMDS_grön[1]};
wire [4:0] TMDS_grön_h = {TMDS_grön[8],TMDS_grön[6],TMDS_grön[4],TMDS_grön[2],TMDS_grön[0]};
wire [4:0] TMDS_röd_l = {TMDS_röd[9],TMDS_röd[7],TMDS_röd[5],TMDS_röd[3],TMDS_röd[1]};
wire [4:0] TMDS_röd_h = {TMDS_röd[8],TMDS_röd[6],TMDS_röd[4],TMDS_röd[2],TMDS_röd[0]};

always @(posedge clk_TMDS2)
begin
	TMDS_shift_bh <= TMDS_mod5[2] ? TMDS_blÄ_h  : TMDS_shift_bh  [4:1];
	TMDS_shift_bl <= TMDS_mod5[2] ? TMDS_blÄ_l  : TMDS_shift_bl  [4:1];
	TMDS_shift_gh <= TMDS_mod5[2] ? TMDS_grön_h : TMDS_shift_gh  [4:1];
	TMDS_shift_gl <= TMDS_mod5[2] ? TMDS_grön_l : TMDS_shift_gl  [4:1];
	TMDS_shift_rh <= TMDS_mod5[2] ? TMDS_röd_h   : TMDS_shift_rh  [4:1];
	TMDS_shift_rl <= TMDS_mod5[2] ? TMDS_röd_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

Sedan matas utgÄngsparet till DDIO-utgÄngen, som sekventiellt ger ut ett enbitarsignal vid stigande och fallande kant.

SjÀlva DDIO skulle kunna beskrivas med följande Verilog-kod:

modul 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

Men det kommer förmodligen inte att fungera sÄ. Du mÄste anvÀnda alteros megafunktion ALTDDIO_OUT för att faktiskt aktivera utgÄende DDIO-element. I mitt projekt anvÀnds just bibliotekskomponenten ALTDDIO_OUT.

Det kanske ser lite komplicerat ut, men det fungerar.

Du kan se hela kÀllkoden, skriven i Verilog HDL, hÀr, pÄ github..

Den kompilerade firmwaren för FPGA:n lÀses in i EPCS-chippet som Àr installerat pÄ Mars Rover 2rpi-kortet. SÄ vid strömförsörjning till FPGA-kortet kommer FPGA:n att initieras frÄn flashminnet och starta.

Nu Àr det dags att berÀtta lite om konfigurationen av Raspberry sjÀlv.

Jag gör experiment pÄ Raspberry PI OS (32 bit) baserat pÄ Debian Buster, Version: Augusti 2020,
Utgivningsdatum: 2020-08-20, Kernelversion: 5.4.

Det finns tvÄ saker att göra:

  • redigera filen config.txt;
  • skapa en konfiguration för X-servern för att arbeta med tvĂ„ skĂ€rmar.

Vid redigering av filen /boot/config.txt behöver du:

  1. stÀnga av anvÀndning av i2c, i2s, spi;
  2. aktivera DPI-lÀget med hjÀlp av overlay dtoverlay=dpi24;
  3. konfigurera videolĂ€get 1280×720 60Hz, 24 bitar per punkt pĂ„ DPI;
  4. ange det nödvÀndiga antalet framebuffers till 2 (max_framebuffers=2, endast dÄ kommer den andra enheten /dev/fb1 att visas).

Den kompletta texten för filen config.txt ser ut sÄ hÀr.

# 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

Efter detta mÄste du skapa en konfigurationsfil för X-servern för att anvÀnda tvÄ skÀrmar pÄ de tvÄ framebuffers /dev/fb0 och /dev/fb1:

Min konfigurationsfil /usr/share/x11/xorg.conf.d/60-dualscreen.conf ser ut sÄ hÀr

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

Och om det Ànnu inte Àr installerat, behöver du installera Xinerama. DÄ kommer skrivbordets utrymme att fullt ut utvidgas över tvÄ skÀrmar, som visas ovan i demovideon.

Det Àr nog allt. Nu kan Àven Àgare av Raspberry Pi3 anvÀnda tvÄ skÀrmar.

Du kan se beskrivningen och schemat för kortet Mars Rover 2rpi hÀr..

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster