Annar HDMI skjár til Raspberry Pi3 í gegnum DPI tengi og FPGA borð


Þetta myndband sýnir: Raspberry Pi3 borð, tengt við það í gegnum GPIO tengið, er FPGA borð Mars Rover2rpi (Cyclone IV), sem HDMI skjár er tengdur við. Annar skjárinn er tengdur með venjulegu HDMI tengi Raspberry Pi3. Allt virkar saman eins og tvöfalt skjákerfi.

Næst skal ég segja þér hvernig þetta er útfært.

Hið vinsæla Raspberry Pi3 borð er með GPIO tengi þar sem hægt er að tengja ýmis stækkunarkort í gegnum: skynjara, LED, stepp mótor drif og margt fleira. Nákvæm virkni hvers pinna á tengi fer eftir tengistillingunni. GPIO ALT2 stillingin gerir þér kleift að skipta um tengið í DPI viðmótsstillingu, Display Parallel Interface. Það eru til stækkunarkort til að tengja VGA skjái í gegnum DPI. Hins vegar í fyrsta lagi eru VGA skjáir ekki lengur eins algengir og HDMI og í öðru lagi er stafræna viðmótið sífellt betra en það hliðræna. Þar að auki er DAC á slíkum VGA stækkunarborðum venjulega framleitt í formi R-2-R keðja og oft ekki meira en 6 bitar á lit.

Í ALT2 ham hafa GPIO tengipinnar eftirfarandi merkingu:

Annar HDMI skjár til Raspberry Pi3 í gegnum DPI tengi og FPGA borð

Hér hef ég litað RGB pinna tengisins rauða, græna og bláa í sömu röð. Önnur mikilvæg merki eru V-SYNC og H-SYNC merki, auk CLK. CLK klukkutíðnin er tíðnin sem pixlagildi eru send út á tengið; það fer eftir völdum myndbandsstillingu.

Til að tengja stafrænan HDMI skjá þarftu að fanga DPI merki viðmótsins og breyta þeim í HDMI merki. Þetta er til dæmis hægt að gera með því að nota einhvers konar FPGA borð. Eins og það kemur í ljós hentar Mars Rover2rpi borðið í þessum tilgangi. Í sannleika sagt lítur aðalvalkosturinn til að tengja þetta borð í gegnum sérstakan millistykki svona út:

Annar HDMI skjár til Raspberry Pi3 í gegnum DPI tengi og FPGA borð

Þetta borð er notað til að fjölga GPIO tengi og til að tengja fleiri jaðartæki við hindberið. Á sama tíma eru notuð 4 GPIO merki með þessari tengingu fyrir JTAG merki, þannig að forritið frá Raspberry getur hlaðið FPGA fastbúnaðinum inn í FPGA. Vegna þessa hentar þessi staðlaða tenging mér ekki; 4 DPI merki falla út. Sem betur fer eru viðbótarkambarnir á borðinu með hindberjasamhæfðum pinout. Svo ég get snúið borðinu 90 gráður og samt tengt það við hindberið mitt:

Annar HDMI skjár til Raspberry Pi3 í gegnum DPI tengi og FPGA borð

Auðvitað verður þú að nota utanaðkomandi JTAG forritara, en þetta er ekki vandamál.

Það er samt lítið vandamál. Ekki er hægt að nota alla FPGA pinna sem klukkuinntak. Það eru aðeins nokkrir sérstakir pinnar sem hægt er að nota í þessum tilgangi. Þannig að hér kom í ljós að GPIO_0 CLK merkið nær ekki FPGA inntakinu, sem hægt er að nota sem FPGA klukkuinntak. Svo ég varð samt að setja einn vír á trefilinn. Ég tengi GPIO_0 og KEY[1] merki borðsins:

Annar HDMI skjár til Raspberry Pi3 í gegnum DPI tengi og FPGA borð

Nú skal ég segja þér aðeins frá FPGA verkefninu. Helsti erfiðleikinn við að búa til HDMI merki er mjög há tíðni. Ef þú horfir á HDMI tengi pinout geturðu séð að RGB merki eru nú raðmismunamerki:

Annar HDMI skjár til Raspberry Pi3 í gegnum DPI tengi og FPGA borð

Notkun mismunadrifsmerkis gerir þér kleift að berjast gegn truflunum í algengri stillingu á flutningslínunni. Í þessu tilviki er upprunalega átta bita kóða hvers litamerkis breytt í 10 bita TMDS (Transition-minimized differential signaling). Þetta er sérstök kóðunaraðferð til að fjarlægja DC íhlutinn úr merkinu og lágmarka merkjaskipti í mismunalínu. Þar sem nú þarf að senda 10 bita yfir raðlínuna fyrir eitt litabæti, kemur í ljós að klukkuhraði serializer verður að vera 10 sinnum hærri en pixel klukkuhraði. Ef við tökum sem dæmi myndbandsstillinguna 1280x720 60Hz, þá er pixlatíðnin í þessari stillingu 74,25 MHz. Serializer ætti að vera 742,5 MHz.

Venjulegir FPGA eru því miður ekki færir um þetta. Hins vegar, sem betur fer fyrir okkur, hefur FPGA innbyggða DDIO pinna. Þetta eru ályktanir sem eru nú þegar, eins og það var, 2-til-1 serializers. Það er, þeir geta gefið út tvo bita í röð á hækkandi og lækkandi brúnum klukkutíðnarinnar. Þetta þýðir að í FPGA verkefni er ekki hægt að nota 740 MHz, heldur 370 MHz, en þú þarft að nota DDIO úttakseiningar í FPGA. Nú er 370 MHz nú þegar algjörlega hægt að ná tíðni. Því miður er 1280x720 stillingin takmörk. Ekki er hægt að ná hærri upplausn í Cyclone IV FPGA okkar sem er uppsett á Mars Rover2rpi borðinu.

Svo, í hönnuninni, fer inntakspixlatíðnin CLK í PLL, þar sem hún er margfölduð með 5. Á þessari tíðni er R, G, B bætum breytt í bitapör. Þetta er það sem TMDS kóðarinn gerir. Kóðinn í Verilog HDL lítur svona út:

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

Síðan eru úttakspörin færð til DDIO úttaksins, sem framleiðir í röð eins bita merki á hækkandi og lækkandi brúnum.

DDIO sjálfu gæti verið lýst með eftirfarandi Verilog kóða:

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

En það mun líklegast ekki virka þannig. Þú þarft að nota megavirkni Alter ALTDDIO_OUT til að virkja DDIO úttakseiningarnar. Verkefnið mitt notar ALTDDIO_OUT bókasafnshlutann.

Þetta kann allt að líta svolítið flókið út, en það virkar.

Þú getur skoðað allan frumkóðann sem er skrifaður í Verilog HDL hér á github.

Samanlagður fastbúnaður fyrir FPGA er flassaður í EPCS flísinn sem er uppsettur á Mars Rover2rpi borðinu. Þannig, þegar afl er sett á FPGA borðið, verður FPGA frumstillt úr flassminni og byrjað.

Nú þurfum við að tala aðeins um uppsetninguna á Raspberry sjálfum.

Ég er að gera tilraunir á Raspberry PI OS (32 bita) byggt á Debian Buster, útgáfa: ágúst 2020,
Útgáfudagur: 2020-08-20, Kjarnaútgáfa: 5.4.

Þú þarft að gera tvennt:

  • breyta config.txt skránni;
  • búa til X netþjónsstillingu til að vinna með tveimur skjáum.

Þegar þú breytir /boot/config.txt skránni þarftu:

  1. slökkva á notkun i2c, i2s, spi;
  2. virkjaðu DPI ham með því að nota yfirlag dtoverlay=dpi24;
  3. stilla myndbandsstillingu 1280×720 60Hz, 24 bita á pixla á DPI;
  4. tilgreindu nauðsynlegan fjölda framebuffers 2 (max_framebuffers=2, aðeins þá mun annað tækið /dev/fb1 birtast)

Fullur texti config.txt skrárinnar lítur svona út.

# 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

Eftir þetta þarftu að búa til stillingarskrá fyrir X þjóninn til að nota tvo skjái á tveimur rammabuffum /dev/fb0 og /dev/fb1:

Stillingarskráin mín /usr/share/x11/xorg.conf.d/60-dualscreen.conf er svona

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

Jæja, ef það er ekki þegar uppsett, þá þarftu að setja upp Xinerama. Þá verður skjáborðsrýmið stækkað að fullu í tvo skjái, eins og sést í kynningarmyndbandinu hér að ofan.

Það er líklega allt. Nú munu eigendur Raspberry Pi3 geta notað tvo skjái.

Lýsing og hringrásarmynd af Mars Rover2rpi borðinu má finna Sjáðu hér.

Heimild: www.habr.com