Mfuatiliaji wa pili wa HDMI kwa Raspberry Pi3 kupitia kiolesura cha DPI na bodi ya FPGA


Video hii inaonyesha: bodi ya Raspberry Pi3, ambayo, kupitia kiunganishi cha GPIO, bodi ya FPGA Mars Rover2rpi (Cyclone IV) imeunganishwa, ambayo kufuatilia HDMI imeunganishwa. Mfuatiliaji wa pili umeunganishwa kupitia kiunganishi cha kawaida cha Raspberry Pi3 HDMI. Yote kwa pamoja inafanya kazi kama mfumo wa kufuatilia mbili.

Ifuatayo nitakuambia jinsi inatekelezwa.

Bodi maarufu ya Raspberry Pi3 ina kiunganishi cha GPIO ambacho unaweza kuunganisha bodi mbalimbali za upanuzi: sensorer, LEDs, madereva ya stepper motor na mengi zaidi. Kazi maalum ya kila pini kwenye kontakt inategemea usanidi wa bandari. Usanidi wa GPIO ALT2 hukuruhusu kubadili kiunganishi kwa modi ya kiolesura cha DPI, Kiolesura cha Kuonyesha Sambamba. Kuna bodi za upanuzi za kuunganisha wachunguzi wa VGA kupitia DPI. Walakini, kwanza, wachunguzi wa VGA sio wa kawaida kama HDMI, na pili, kiolesura cha dijiti kinakuwa bora kuliko analog. Kwa kuongezea, DAC kwenye kadi kama hizo za upanuzi za VGA kawaida hufanywa kwa njia ya minyororo ya R-2-R na mara nyingi sio zaidi ya bits 6 kwa kila rangi.

Katika hali ya ALT2, pini za kiunganishi cha GPIO zina maana ifuatayo:

Mfuatiliaji wa pili wa HDMI kwa Raspberry Pi3 kupitia kiolesura cha DPI na bodi ya FPGA

Hapa nilipaka rangi ya pini za RGB za kontakt nyekundu, kijani na bluu kwa mtiririko huo. Ishara nyingine muhimu ni ishara za kusawazisha za V-SYNC na H-SYNC, pamoja na CLK. Masafa ya saa ya CLK ni masafa ambayo maadili ya pixel hutolewa kwa kontakt na inategemea modi ya video iliyochaguliwa.

Ili kuunganisha kifuatiliaji cha dijiti cha HDMI, unahitaji kunasa mawimbi ya kiolesura cha DPI na kuwabadilisha kuwa mawimbi ya HDMI. Hii inaweza kufanyika, kwa mfano, kwa kutumia bodi yoyote ya FPGA. Kama ilivyotokea, bodi ya Mars Rover2rpi inafaa kwa kusudi hili. Kwa kweli, chaguo kuu la kuunganisha bodi hii kupitia adapta maalum inaonekana kama hii:

Mfuatiliaji wa pili wa HDMI kwa Raspberry Pi3 kupitia kiolesura cha DPI na bodi ya FPGA

Bodi hii hutumiwa kuongeza idadi ya bandari za GPIO na kuunganisha vifaa vya pembeni zaidi kwenye raspberry. Wakati huo huo, ishara 4 za GPIO na uunganisho huu hutumiwa kwa ishara za JTAG, ili programu kutoka kwa usambazaji inaweza kupakia firmware ya FPGA kwenye FPGA. Kwa sababu ya hili, uunganisho wa kawaida kama huo haufai kwangu, ishara 4 za DPI huacha. Kwa bahati nzuri, masega ya ziada kwenye ubao yana pinout inayoendana na Raspberry. Ili niweze kuzungusha bodi digrii 90 na bado niunganishe na raspberry yangu:

Mfuatiliaji wa pili wa HDMI kwa Raspberry Pi3 kupitia kiolesura cha DPI na bodi ya FPGA

Kwa kweli, utalazimika kutumia programu ya nje ya JTAG, lakini hii sio shida.

Bado kuna shida ndogo. Sio kila pini ya FPGA inaweza kutumika kama pembejeo ya saa. Kuna pini chache tu zilizojitolea ambazo zinaweza kutumika kwa kusudi hili. Kwa hivyo ikawa hapa kuwa ishara ya GPIO_0 CLK haifikii kwenye pembejeo ya FPGA, ambayo inaweza kutumika kama pembejeo ya saa ya FPGA. Kwa hivyo sawa, ilibidi nitupe chapisho moja kwenye skafu. Ninaunganisha GPIO_0 na ishara KEY[1] ya ubao:

Mfuatiliaji wa pili wa HDMI kwa Raspberry Pi3 kupitia kiolesura cha DPI na bodi ya FPGA

Sasa nitakuambia kidogo kuhusu mradi katika FPGA. Ugumu kuu katika uundaji wa ishara za HDMI ni masafa ya juu sana. Ukiangalia pini ya kiunganishi cha HDMI, unaweza kuona kwamba mawimbi ya RGB sasa ni ishara tofauti za mfululizo:

Mfuatiliaji wa pili wa HDMI kwa Raspberry Pi3 kupitia kiolesura cha DPI na bodi ya FPGA

Matumizi ya ishara tofauti inakuwezesha kukabiliana na kelele ya kawaida ya mode kwenye mstari wa maambukizi. Katika kesi hii, msimbo wa awali wa-bit nane wa kila ishara ya rangi hubadilishwa kuwa TMDS ya 10-bit (ishara ya tofauti ya mpito-minimized). Hii ni njia maalum ya encoding ili kuondoa sehemu ya DC kutoka kwa ishara na kupunguza ubadilishaji wa ishara kwenye mstari tofauti. Kwa kuwa sasa kuna bits 10 za kusambaza kwa byte ya rangi juu ya mstari wa serial, zinageuka kuwa mzunguko wa saa ya serializer lazima iwe mara 10 zaidi kuliko mzunguko wa saa ya saizi. Ikiwa tunachukua kwa mfano hali ya video 1280x720 60Hz, basi mzunguko wa pixel wa hali hii ni 74,25MHz. Serializer inapaswa kuwa 742,5 MHz.

FPGA za kawaida kwa ujumla hazina uwezo wa hii, kwa bahati mbaya. Hata hivyo, kwa bahati yetu, FPGAs wana pini za DDIO zilizojengewa ndani. Hizi ni hitimisho ambazo tayari, kama ilivyokuwa, 2-to-1 serializers. Hiyo ni, wanaweza kutoa bits mbili kwa mlolongo pamoja na masafa ya saa ya kupanda na kushuka. Hii ina maana kwamba katika mradi wa FPGA unaweza kutumia si 740 MHz, lakini 370 MHz, lakini unahitaji kutumia vipengele vya pato vya DDIO katika FPGA. Hapa 370 MHz tayari ni mzunguko unaoweza kufikiwa. Kwa bahati mbaya, hali ya 1280 Γ— 720 ni kikomo. Azimio la juu haliwezi kupatikana katika FPGA Cyclone IV yetu iliyosakinishwa kwenye ubao wa Rover2rpi.

Kwa hiyo, katika mradi huo, mzunguko wa pixel wa pembejeo CLK hutolewa kwa PLL, ambapo huongezeka kwa 5. Kwa mzunguko huu, R, G, B bytes hubadilishwa kuwa jozi kidogo. Hivi ndivyo kisimbaji cha TMDS hufanya. Nambari ya chanzo kwenye Verilog HDL inaonekana kama hii:

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

Kisha jozi za pato hulishwa kwa pato la DDIO, ambalo huzalisha kwa sequentially ishara moja-bit juu ya kupanda na kushuka.

DDIO yenyewe inaweza kuelezewa na nambari ya Verilog kama hii:

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

Lakini labda haitafanya kazi kwa njia hiyo. Unahitaji kutumia megafunction ya Altera ya ALTDDIO_OUT ili kutumia vipengele vya matokeo vya DDIO. Katika mradi wangu, sehemu ya maktaba ALTDDIO_OUT inatumika.

Inaweza kuonekana kuwa gumu kidogo, lakini inafanya kazi.

Unaweza kutazama msimbo mzima wa chanzo ulioandikwa katika Verilog HDL hapa kwenye github.

Firmware iliyokusanywa ya FPGA imeingizwa kwenye chip ya EPCS iliyowekwa kwenye bodi ya Mars Rover2rpi. Kwa hivyo, wakati nguvu inatumiwa kwenye bodi ya FPGA, FPGA itaanzisha kutoka kwa kumbukumbu ya flash na kuanza.

Sasa tunahitaji kuzungumza kidogo juu ya usanidi wa Raspberry yenyewe.

Ninafanya majaribio kwenye Raspberry PI OS (32 bit) kulingana na Debian Buster, Toleo:Agosti 2020,
Tarehe ya kutolewa:2020-08-20, toleo la Kernel:5.4.

Unahitaji kufanya mambo mawili:

  • hariri faili ya config.txt;
  • unda usanidi wa seva ya X kufanya kazi na wachunguzi wawili.

Wakati wa kuhariri faili ya /boot/config.txt, unahitaji:

  1. afya ya matumizi ya i2c, i2s, spi;
  2. wezesha modi ya DPI kwa kuwekea dtoverlay=dpi24;
  3. weka hali ya video 1280 Γ— 720 60Hz, bits 24 kwa pointi kwa DPI;
  4. bainisha nambari inayohitajika ya vihifadhi fremu 2 (max_framebuffers=2, hapo ndipo kifaa cha pili /dev/fb1 kitaonekana)

Maandishi kamili ya faili ya config.txt yanaonekana hivi.

# 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

Baada ya hapo, unahitaji kuunda faili ya usanidi kwa seva ya X kutumia wachunguzi wawili kwenye fremu mbili /dev/fb0 na /dev/fb1:

Faili yangu ya usanidi ni /usr/share/x11/xorg.conf.d/60-dualscreen.conf kama hii

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

Kweli, ikiwa haijasanikishwa tayari, basi unahitaji kusanikisha Xinerama. Kisha nafasi ya eneo-kazi itapanuliwa kikamilifu hadi kwa wachunguzi wawili, kama inavyoonyeshwa kwenye video ya onyesho hapo juu.

Pengine ni hayo tu. Sasa, wamiliki wa Raspberry Pi3 wataweza kutumia vichunguzi viwili.

Maelezo na mchoro wa bodi ya Mars Rover2rpi inaweza kuwa tazama hapa.

Chanzo: mapenzi.com