Monitor HDMI kapindho menyang Raspberry Pi3 liwat antarmuka DPI lan papan FPGA


Video iki nuduhake: Papan Raspberry Pi3, sing disambungake liwat konektor GPIO yaiku papan FPGA Mars Rover2rpi (Siklon IV), sing disambungake karo monitor HDMI. Monitor kapindho disambungake liwat konektor HDMI standar Raspberry Pi3. Kabeh bisa bebarengan kaya sistem monitor dual.

Sabanjure aku bakal pitutur marang kowe carane iki dileksanakake.

Papan Raspberry Pi3 sing populer nduweni konektor GPIO sing bisa nyambungake macem-macem kertu ekspansi: sensor, LED, driver motor stepper lan liya-liyane. Fungsi pas saben pin ing konektor gumantung konfigurasi port. Konfigurasi GPIO ALT2 ngidini sampeyan ngalih konektor menyang mode antarmuka DPI, Tampilan Paralel Antarmuka. Ana kertu expansion kanggo nyambungake monitor VGA liwat DPI. Nanging, sepisanan, monitor VGA ora umum kaya HDMI, lan nomer loro, antarmuka digital luwih apik tinimbang analog. Menapa malih, DAC ing Papan expansion VGA kuwi biasane digawe ing wangun R-2-R chain lan asring ora luwih saka 6 bit saben werna.

Ing mode ALT2, pin konektor GPIO nduweni teges ing ngisor iki:

Monitor HDMI kapindho menyang Raspberry Pi3 liwat antarmuka DPI lan papan FPGA

Kene aku wis colored pin RGB saka konektor abang, ijo lan biru mungguh. Sinyal penting liyane yaiku sinyal V-SYNC lan H-SYNC, uga CLK. Frekuensi jam CLK yaiku frekuensi ing ngendi nilai piksel dikirim menyang konektor; gumantung saka mode video sing dipilih.

Kanggo nyambungake monitor HDMI digital, sampeyan kudu njupuk sinyal DPI antarmuka lan ngowahi menyang sinyal HDMI. Iki bisa ditindakake, contone, nggunakake sawetara papan FPGA. Ternyata, papan Mars Rover2rpi cocok kanggo tujuan kasebut. Bener, pilihan utama kanggo nyambungake papan iki liwat adaptor khusus katon kaya iki:

Monitor HDMI kapindho menyang Raspberry Pi3 liwat antarmuka DPI lan papan FPGA

Papan iki digunakake kanggo nambah nomer bandar GPIO lan kanggo nyambungake piranti peripheral liyane kanggo raspberry. Ing wektu sing padha, 4 sinyal GPIO karo sambungan iki digunakake kanggo sinyal JTAG, supaya program saka Raspberry bisa mbukak perangkat kukuh FPGA menyang FPGA. Amarga iki, sambungan standar iki ora cocog karo aku; 4 sinyal DPI ilang. Untunge, sisir tambahan ing papan duwe pinout sing kompatibel karo Raspberry. Dadi aku bisa muter papan 90 derajat lan isih nyambung menyang raspberry:

Monitor HDMI kapindho menyang Raspberry Pi3 liwat antarmuka DPI lan papan FPGA

Mesthi, sampeyan kudu nggunakake programmer JTAG external, nanging iki ora masalah.

Isih ana masalah cilik. Ora saben pin FPGA bisa digunakake minangka input jam. Mung sawetara pin khusus sing bisa digunakake kanggo tujuan kasebut. Dadi ing kene sinyal GPIO_0 CLK ora tekan input FPGA, sing bisa digunakake minangka input jam FPGA. Dadi aku isih kudu nyelehake kabel siji ing selendang. Aku nyambungake GPIO_0 lan sinyal KEY [1] papan:

Monitor HDMI kapindho menyang Raspberry Pi3 liwat antarmuka DPI lan papan FPGA

Saiki aku bakal ngandhani sampeyan babagan proyek FPGA. Kesulitan utama kanggo ngasilake sinyal HDMI yaiku frekuensi sing dhuwur banget. Yen sampeyan ndeleng pinout konektor HDMI, sampeyan bisa ndeleng manawa sinyal RGB saiki dadi sinyal diferensial serial:

Monitor HDMI kapindho menyang Raspberry Pi3 liwat antarmuka DPI lan papan FPGA

Panggunaan sinyal diferensial ngidini sampeyan nglawan gangguan mode umum ing saluran transmisi. Ing kasus iki, kode wolung bit asli saben sinyal werna diowahi dadi 10-bit TMDS (Transition-minimized differential signaling). Iki minangka cara coding khusus kanggo mbusak komponen DC saka sinyal lan nyilikake sinyal switching ing baris diferensial. Wiwit 10 bit saiki kudu ditularakΓ© liwat baris serial kanggo siji bait saka werna, dadi metu sing kacepetan jam serializer kudu 10 kaping luwih saka kacepetan jam piksel. Yen kita njupuk contone mode video 1280x720 60Hz, frekuensi piksel mode iki 74,25 MHz. Serializer kudu 742,5 MHz.

FPGA biasa, sayangΓ©, ora bisa nindakake iki. Nanging, untunge kanggo kita, FPGA duwe pin DDIO sing dibangun. Iki minangka kesimpulan sing wis, kaya, serializer 2-kanggo-1. Sing, padha bisa output loro bit sequentially ing Rising lan Mudhun sudhut frekuensi jam. Iki tegese ing project FPGA sampeyan bisa nggunakake ora 740 MHz, nanging 370 MHz, nanging sampeyan kudu nggunakake unsur output DDIO ing FPGA. Saiki 370 MHz wis dadi frekuensi sing bisa ditindakake. Sayange, mode 1280x720 minangka watesan. RΓ©solusi sing luwih dhuwur ora bisa digayuh ing FPGA Siklon IV sing dipasang ing papan Mars Rover2rpi.

Dadi, ing desain, frekuensi piksel input CLK menyang PLL, sing dikalikan karo 5. Ing frekuensi iki, R, G, B bita diowahi dadi pasangan bit. Iki sing ditindakake encoder TMDS. Kode sumber ing Verilog HDL katon kaya iki:

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

Banjur pasangan output sing dipakani kanggo output DDIO, kang sequentially mrodhuksi sinyal siji-dicokot ing munggah lan Mudhun sudhut.

DDIO dhewe bisa diterangake nganggo kode Verilog ing ngisor iki:

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

Nanging paling kamungkinan ora bakal bisa. Sampeyan kudu nggunakake megafunction Alter kang ALTDDIO_OUT kanggo bener ngaktifake unsur output DDIO. Proyekku nggunakake komponen perpustakaan ALTDIO_OUT.

Iki kabeh bisa uga katon rada angel, nanging bisa digunakake.

Sampeyan bisa ndeleng kabeh kode sumber sing ditulis ing Verilog HDL kene ing github.

Firmware kompilasi kanggo FPGA flashed menyang chip EPCS diinstal ing Papan Mars Rover2rpi. Mangkono, nalika daya Applied kanggo Papan FPGA, FPGA bakal initialized saka memori lampu kilat lan miwiti.

Saiki kita kudu ngomong sethithik babagan konfigurasi Raspberry dhewe.

Aku nindakake eksperimen ing Raspberry PI OS (32 bit) adhedhasar Debian Buster, Versi:Agustus 2020,
Tanggal rilis:2020-08-20, versi Kernel:5.4.

Sampeyan kudu nindakake rong perkara:

  • ngowahi file config.txt;
  • nggawe konfigurasi server X kanggo karya karo loro monitor.

Nalika nyunting file /boot/config.txt sampeyan kudu:

  1. mateni panggunaan i2c, i2s, spi;
  2. ngaktifake mode DPI nggunakake overlay dtoverlay=dpi24;
  3. ngatur mode video 1280 Γ— 720 60Hz, 24 bit saben piksel ing DPI;
  4. nemtokake jumlah framebuffers 2 sing dibutuhake (max_framebuffers = 2, mung banjur piranti kapindho / dev / fb1 bakal katon)

Teks lengkap file config.txt katon kaya iki.

# 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

Sawise iki, sampeyan kudu nggawe file konfigurasi kanggo server X nggunakake rong monitor ing rong framebuffers / dev / fb0 lan / dev / fb1:

File konfigurasiku /usr/share/x11/xorg.conf.d/60-dualscreen.conf kaya iki

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

Inggih, yen durung diinstal, sampeyan kudu nginstal Xinerama. Banjur papan desktop bakal ditambahi kanthi lengkap dadi rong monitor, kaya sing ditampilake ing video demo ing ndhuwur.

Sing mbok menawa kabeh. Saiki, pamilik Raspberry Pi3 bakal bisa nggunakake rong monitor.

Katrangan lan diagram sirkuit papan Mars Rover2rpi bisa ditemokake deleng kene.

Source: www.habr.com