Dîmena duyemîn HDMI ji Raspberry Pi3 re bi navgîniya DPI û panela FPGA


Vê vîdyoyê destnîşan dike: panelek Raspberry Pi3, ku bi girêdana GPIO ve pê ve girêdayî ye, panelek FPGA Mars Rover2rpi (Cyclone IV) ye, ku çavdêriyek HDMI pê ve girêdayî ye. Dîmendera duyemîn bi girêdana standard HDMI ya Raspberry Pi3 ve girêdayî ye. Her tişt mîna pergala çavdêriya dualî bi hev re dixebite.

Piştre ez ê ji we re vebêjim ka ev çawa tête pêkanîn.

Panela populer Raspberry Pi3 xwedan girêdanek GPIO ye ku bi navgîniya wê hûn dikarin kartên cûrbecûr berfirehbûnê ve girêbidin: sensor, LED, ajokarên motora gavê û hêj bêtir. Fonksiyona rastîn a her pinek li ser girêdanek bi veavakirina portê ve girêdayî ye. Veavakirina GPIO ALT2 destûrê dide we ku hûn girêdanê bi moda pêwendiya DPI-ê, Navbera Parallel Nîşanî veguherînin. Ji bo girêdana çavdêrên VGA-yê bi DPI-ê ve kartên berfirehkirinê hene. Lêbelê, yekem, çavdêrên VGA êdî wekî HDMI ne hevpar in, û ya duyemîn jî, pêwendiya dîjîtal her ku diçe ji ya analog çêtir e. Digel vê yekê, DAC li ser panelên berfirehkirina VGA-ya wusa bi gelemperî di forma zincîreyên R-2-R de têne çêkirin û bi gelemperî ji reng ji 6 bit zêdetir nabe.

Di moda ALT2 de, pêlên girêdana GPIO wateyek jêrîn heye:

Dîmena duyemîn HDMI ji Raspberry Pi3 re bi navgîniya DPI û panela FPGA

Li vir min pinên RGB yên girêdanê bi rêzê sor, kesk û şîn reng kirine. Nîşaneyên din ên girîng nîşaneyên V-SYNC û H-SYNC, û her weha CLK ne. Frekansa demjimêra CLK frekansa ku tê de nirxên pixel ji girêdanê têne derxistin e; ew bi moda vîdyoya hilbijartî ve girêdayî ye.

Ji bo girêdana dîmenderek HDMI-ya dîjîtal, hûn hewce ne ku îşaretên DPI-ê yên navberê bigirin û wan veguherînin nîşaneyên HDMI. Ev dikare were kirin, ji bo nimûne, bi karanîna celebek panelê FPGA. Wekî ku diqewime, panela Mars Rover2rpi ji bo van armancan maqûl e. Bi rastî, vebijarka sereke ji bo girêdana vê panelê bi adapterek taybetî wiha xuya dike:

Dîmena duyemîn HDMI ji Raspberry Pi3 re bi navgîniya DPI û panela FPGA

Ev panel ji bo zêdekirina hejmara portên GPIO û ji bo girêdana bêtir amûrên derdorê bi raspberry re tê bikar anîn. Di heman demê de, 4 îşaretên GPIO yên bi vê pêwendiyê re ji bo îşaretên JTAG têne bikar anîn, da ku bernameya ji Raspberry dikare firmware FPGA di FPGA de bar bike. Ji ber vê yekê, ev pêwendiya standard li min nayê; 4 sînyalên DPI derdikevin. Xweşbextane, komikên pêvek ên li ser panelê xwedan pîneyek ku bi Raspberry-ê ve girêdayî ye heye. Ji ber vê yekê ez dikarim panelê 90 pileyan bizivirim û hîn jî wê bi raspberya xwe ve girêbidim:

Dîmena duyemîn HDMI ji Raspberry Pi3 re bi navgîniya DPI û panela FPGA

Bê guman, hûn ê neçar bibin ku bernamesazek ​​JTAG-ya derveyî bikar bînin, lê ev ne pirsgirêk e.

Dîsa pirsgirêkek piçûk heye. Ne her pin FPGA dikare wekî têketina demjimêrê were bikar anîn. Tenê çend pinên taybetî hene ku ji bo van armancan têne bikar anîn. Ji ber vê yekê li vir derket ku nîşana GPIO_0 CLK nagihîje têketina FPGA, ku dikare wekî têketina demjimêra FPGA were bikar anîn. Ji ber vê yekê ez hîn jî neçar bûm ku têlek li ser şal bikim. Ez GPIO_0 û sînyala KEY[1] ya panelê girêdidim:

Dîmena duyemîn HDMI ji Raspberry Pi3 re bi navgîniya DPI û panela FPGA

Naha ez ê hinekî li ser projeya FPGA ji we re bibêjim. Zehmetiya sereke di hilberîna sînyalên HDMI de frekansên pir bilind in. Ger hûn li pinouta girêdana HDMI-yê mêze bikin, hûn dikarin bibînin ku îşaretên RGB naha îşaretên cihêreng ên serial in:

Dîmena duyemîn HDMI ji Raspberry Pi3 re bi navgîniya DPI û panela FPGA

Bikaranîna îşaretek dîferansê dide we ku hûn li ser xeta veguheztinê bi destwerdana moda hevpar re şer bikin. Di vê rewşê de, koda heşt-bitê ya orîjînal a her îşaretek rengîn vediguhezîne TMDS-ya 10-bit (nîşandana ciyawaziya veguhêz-kêmkirî). Ev rêbazek kodkirinê ya taybetî ye ku pêkhateya DC-ê ji sînyalê derxîne û veguheztina sînyalê di xetek cûda de kêm bike. Ji ber ku 10 bit naha ji bo yek byte reng pêdivî ye ku li ser xeta rêzefîlmê were veguheztin, derket holê ku leza demjimêra serialîzatorê divê 10 carî ji leza demjimêra pixel mezintir be. Ger em moda vîdyoyê 1280x720 60Hz mînak bigirin, wê hingê frekansa pixel a vê modê 74,25 MHz e. Divê rêzker 742,5 MHz be.

FPGA-yên birêkûpêk, mixabin, nekarin vê yekê. Lêbelê, bextewar ji bo me, FPGA pinên DDIO-yê çêkirî ye. Van encamên ku jixwe, wekî ku bû, serializerên 2-bi-1 in. Ango, ew dikarin du bit li dû hev li ser hêlên bilindbûn û daketinê yên frekansa demjimêrê derxînin. Ev tê vê wateyê ku di projeyek FPGA de hûn dikarin ne 740 MHz, lê 370 MHz bikar bînin, lê hûn hewce ne ku di FPGA-yê de hêmanên derketinê DDIO bikar bînin. Naha 370 MHz jixwe frekansek bi tevahî gihîştî ye. Mixabin, moda 1280x720 sînor e. Di Cyclone IV FPGA-ya me ya ku li ser panela Mars Rover2rpi hatî saz kirin de çareseriyek bilindtir nayê bidestxistin.

Ji ber vê yekê, di sêwiranê de, frekansa pixelê ya têketinê CLK diçe PLL-ê, li wir bi 5-ê tê zêdekirin. Di vê frekansê de, baytên R, G, B vediguherin cotên bit. Ya ku şîfrekera TMDS dike ev e. Koda çavkaniyê di Verilog HDL de wiha xuya dike:

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

Dûv re cotên derketinê li derana DDIO-yê têne xwarin, ku bi dû hev re îşaretek yek-bit li ser keviyên bilindbûn û daketinê çêdike.

DDIO bixwe dikare bi koda Verilog-ê ya jêrîn were şirove kirin:

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

Lê bi îhtîmaleke mezin ew ê bi vî rengî nexebite. Pêdivî ye ku hûn megafunctiona Alter ALTDDIO_OUT bikar bînin da ku bi rastî hêmanên derketinê yên DDIO çalak bikin. Projeya min beşa pirtûkxaneya ALTDDIO_OUT bikar tîne.

Dibe ku ev hemî hinekî dijwar xuya bike, lê ew dixebite.

Hûn dikarin hemî koda çavkaniyê ya ku di Verilog HDL de hatî nivîsandin bibînin li vir li ser github.

Firmwareya berhevkirî ya ji bo FPGA-ê di çîpa EPCS ya ku li ser panela Mars Rover2rpi hatî saz kirin de tê rijandin. Bi vî rengî, dema ku hêz li ser panela FPGA tê sepandin, FPGA dê ji bîra flashê were destpêkirin û dest pê bike.

Naha divê em hinekî li ser veavakirina Raspberry bixwe biaxivin.

Ez ceribandinan li ser Raspberry PI OS (32 bit) li ser bingeha Debian Buster, Versiyon: Tebax 2020,
Dîroka berdanê: 2020-08-20, Guhertoya Kernel: 5.4.

Hûn hewce ne ku du tiştan bikin:

  • pelê config.txt biguherîne;
  • veavakirina servera X biafirîne ku bi du çavdêran re bixebite.

Dema ku hûn pelê /boot/config.txt biguherînin hûn hewce ne:

  1. bikaranîna i2c, i2s, spi neçalak bike;
  2. moda DPI-ê bi karanîna pêvekê çalak bike dtoverlay=dpi24;
  3. moda vîdyoyê 1280×720 60Hz, 24 bit per pixel li ser DPI-ê mîheng bikin;
  4. hejmara pêwîst a framebuffers diyar bike 2 (max_framebuffers=2, tenê wê hingê dê cîhaza duyemîn /dev/fb1 xuya bibe)

Tevahiya nivîsa pelê config.txt wiha xuya dike.

# 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

Piştî vê yekê, hûn hewce ne ku ji bo servera X pelê vesazkirinê biafirînin da ku du çavdêran li ser du framebuffer bikar bînin /dev/fb0 û /dev/fb1:

Pelê veavakirina min /usr/share/x11/xorg.conf.d/60-dualscreen.conf bi vî rengî ye

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

Welê, heke ew jixwe nehatiye saz kirin, wê hingê hûn hewce ne ku Xinerama saz bikin. Dûv re cîhê sermaseyê dê bi tevahî li du çavdêran were berfireh kirin, wekî ku di vîdyoya demo ya li jor de tê xuyang kirin.

Ew dibe ku hemî. Naha, xwedan Raspberry Pi3 dê bikaribin du çavdêran bikar bînin.

Danasîn û diyagrama dorpêçê ya panela Mars Rover2rpi dikare were dîtin li vir binêre.

Source: www.habr.com