Teine HDMI-monitor Raspberry Pi3-le DPI-liidese ja FPGA-plaadi kaudu


Sellel videol on näha: Raspberry Pi3 plaat, millega GPIO pistiku kaudu on ühendatud FPGA plaat Mars Rover2rpi (Cyclone IV), mille külge on ühendatud HDMI monitor. Teine monitor on ühendatud Raspberry Pi3 standardse HDMI-pistiku kaudu. Kõik töötab koos nagu kahe monitori süsteem.

Järgmisena räägin teile, kuidas seda rakendatakse.

Populaarsel Raspberry Pi3 plaadil on GPIO pistik, mille kaudu saab ühendada erinevaid laienduskaarte: andureid, LED-e, samm-mootori draivereid ja palju muud. Iga pistikupesa täpne funktsioon sõltub pordi konfiguratsioonist. GPIO ALT2 konfiguratsioon võimaldab teil lülitada konnektori DPI-liidese režiimi, Display Parallel Interface. VGA-kuvarite ühendamiseks DPI kaudu on laienduskaardid. Kuid esiteks pole VGA-kuvarid enam nii levinud kui HDMI ja teiseks on digitaalliides järjest parem kui analoog. Lisaks on selliste VGA laiendusplaatide DAC tavaliselt valmistatud R-2-R ahelate kujul ja sageli mitte rohkem kui 6 bitti värvi kohta.

ALT2 režiimis on GPIO pistiku tihvtidel järgmine tähendus:

Teine HDMI-monitor Raspberry Pi3-le DPI-liidese ja FPGA-plaadi kaudu

Siin olen värvinud pistiku RGB-tihvtid vastavalt punaseks, roheliseks ja siniseks. Teised olulised signaalid on V-SYNC ja H-SYNC signaalid, samuti CLK. CLK taktsagedus on sagedus, millega pikslite väärtused pikslisse väljastatakse; see sõltub valitud videorežiimist.

Digitaalse HDMI-monitori ühendamiseks peate jäädvustama liidese DPI-signaalid ja teisendama need HDMI-signaalideks. Seda saab teha näiteks kasutades mingit FPGA-plaati. Nagu selgub, sobib selleks otstarbeks Mars Rover2rpi plaat. Tegelikult näeb selle plaadi ühendamise peamine võimalus spetsiaalse adapteri kaudu välja järgmine:

Teine HDMI-monitor Raspberry Pi3-le DPI-liidese ja FPGA-plaadi kaudu

Seda plaati kasutatakse GPIO-portide arvu suurendamiseks ja rohkemate välisseadmete ühendamiseks vaarikaga. Samal ajal kasutatakse JTAG signaalide jaoks selle ühendusega 4 GPIO signaali, et Raspberry programm saaks laadida FPGA püsivara FPGA-sse. Seetõttu see standardühendus mulle ei sobi, 4 DPI signaali langeb välja. Õnneks on tahvli lisakammidel Raspberryga ühilduv pinout. Nii et saan tahvlit 90 kraadi pöörata ja selle ikkagi oma vaarikaga ühendada:

Teine HDMI-monitor Raspberry Pi3-le DPI-liidese ja FPGA-plaadi kaudu

Loomulikult peate kasutama välist JTAG-i programmeerijat, kuid see pole probleem.

Väike probleem on veel. Mitte iga FPGA kontakti ei saa kasutada kellasisendina. Nendel eesmärkidel saab kasutada vaid mõnda spetsiaalset tihvti. Seega siin selgus, et GPIO_0 CLK signaal ei jõua FPGA sisendisse, mida saab kasutada FPGA kellasisendina. Seega pidin ikka ühe traadi salli külge panema. Ühendan GPIO_0 ja plaadi KEY[1] signaali:

Teine HDMI-monitor Raspberry Pi3-le DPI-liidese ja FPGA-plaadi kaudu

Nüüd räägin teile veidi FPGA projektist. Peamine raskus HDMI-signaalide genereerimisel on väga kõrged sagedused. Kui vaatate HDMI-pistiku väljundit, näete, et RGB-signaalid on nüüd jadadiferentsiaalsignaalid:

Teine HDMI-monitor Raspberry Pi3-le DPI-liidese ja FPGA-plaadi kaudu

Diferentsiaalsignaali kasutamine võimaldab võidelda ühisrežiimi häiretega ülekandeliinil. Sel juhul teisendatakse iga värvisignaali algne kaheksabitine kood 10-bitiseks TMDS-iks (üleminekuga minimeeritud diferentsiaalsignalisatsioon). See on spetsiaalne kodeerimismeetod alalisvoolukomponendi eemaldamiseks signaalist ja signaali ümberlülitumise minimeerimiseks diferentsiaalliinis. Kuna nüüd tuleb ühe värvibaidi jaoks üle jadaliini edastada 10 bitti, siis tuleb välja, et jadajadaja taktsagedus peab olema 10 korda suurem pikslite taktsagedusest. Kui võtame näiteks videorežiimi 1280x720 60Hz, siis selle režiimi pikslisagedus on 74,25 MHz. Serialiseerija peaks olema 742,5 MHz.

Tavalised FPGA-d pole kahjuks selleks võimelised. Kuid meie õnneks on FPGA-l sisseehitatud DDIO kontaktid. Need on järeldused, mis on juba justkui 2-1-serialisaatorid. See tähendab, et nad saavad taktsageduse tõusvatel ja langevatel servadel järjestikku väljastada kaks bitti. See tähendab, et FPGA projektis saate kasutada mitte 740 MHz, vaid 370 MHz, kuid FPGA-s peate kasutama DDIO väljundelemente. Nüüd on 370 MHz juba täiesti saavutatav sagedus. Kahjuks on 1280x720 režiim piir. Meie Mars Rover2rpi plaadile paigaldatud Cyclone IV FPGA-ga ei saa kõrgemat eraldusvõimet saavutada.

Niisiis läheb disainis sisendpikslite sagedus CLK PLL-i, kus see korrutatakse 5-ga. Sellel sagedusel teisendatakse R, G, B baidid bitipaarideks. Seda teeb TMDS-kooder. Verilog HDL lähtekood näeb välja selline:

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

Seejärel suunatakse väljundpaarid DDIO väljundisse, mis toodab järjestikku ühebitise signaali tõusvatel ja langevatel servadel.

DDIO-d saab kirjeldada järgmise Verilogi koodiga:

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

Kuid tõenäoliselt see nii ei tööta. DDIO väljundelementide tegelikuks lubamiseks peate kasutama Alteri megafunktsiooni ALTDDIO_OUT. Minu projekt kasutab teegi komponenti ALTDDIO_OUT.

See kõik võib tunduda pisut keeruline, kuid see toimib.

Saate vaadata kogu Verilog HDL-is kirjutatud lähtekoodi siin githubis.

FPGA jaoks koostatud püsivara välgutatakse Mars Rover2rpi plaadile paigaldatud EPCS-kiibile. Seega, kui FPGA-plaadile toide antakse, lähtestatakse FPGA välkmälust ja käivitub.

Nüüd peame veidi rääkima Raspberry enda konfiguratsioonist.

Teen katseid Debian Busteril põhineva Raspberry PI OS-iga (32-bitine), versioon: august 2020,
Väljalaskekuupäev: 2020-08-20, Kerneli versioon: 5.4.

Peate tegema kahte asja.

  • redigeeri faili config.txt;
  • luua kahe monitoriga töötamiseks X-serveri konfiguratsioon.

Faili /boot/config.txt redigeerimisel vajate:

  1. keelata i2c, i2s, spi kasutamine;
  2. lubage DPI-režiim, kasutades ülekatet dtoverlay=dpi24;
  3. konfigureerige videorežiim 1280 × 720 60 Hz, 24 bitti piksli kohta DPI-s;
  4. määrake vajalik arv kaadripuhvreid 2 (max_framebuffers = 2, alles siis ilmub teine ​​seade /dev/fb1)

Faili config.txt täistekst näeb välja selline.

# 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

Pärast seda peate X-serveri jaoks looma konfiguratsioonifaili, et kasutada kahte monitori kahes kaadripuhvris /dev/fb0 ja /dev/fb1:

Minu konfiguratsioonifail /usr/share/x11/xorg.conf.d/60-dualscreen.conf on selline

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

Noh, kui see pole veel installitud, peate installima Xinerama. Seejärel laiendatakse töölaua ruum täielikult kahele monitorile, nagu on näidatud ülaltoodud demovideos.

See on ilmselt kõik. Nüüd saavad Raspberry Pi3 omanikud kasutada kahte monitori.

Mars Rover2rpi plaadi kirjelduse ja skeemi leiate Vaata siia.

Allikas: www.habr.com