DPI интерфейсі және FPGA тақтасы арқылы Raspberry Pi3 үшін екінші HDMI мониторы


Бұл бейнеде: GPIO қосқышы арқылы қосылған Raspberry Pi3 тақтасы - HDMI мониторы қосылған FPGA Mars Rover2rpi (Циклон IV) тақтасы. Екінші монитор Raspberry Pi3 стандартты HDMI қосқышы арқылы қосылған. Барлығы қос монитор жүйесі сияқты бірге жұмыс істейді.

Әрі қарай бұл қалай жүзеге асырылатынын айтып беремін.

Танымал Raspberry Pi3 тақтасында GPIO қосқышы бар, ол арқылы әртүрлі кеңейту карталарын қосуға болады: сенсорлар, жарықдиодты шамдар, қадамдық қозғалтқыш драйверлері және т.б. Коннектордағы әрбір түйреуіштің нақты қызметі порт конфигурациясына байланысты. GPIO ALT2 конфигурациясы қосқышты DPI интерфейс режиміне, Параллель интерфейсті көрсетуге ауыстыруға мүмкіндік береді. DPI арқылы VGA мониторларын қосуға арналған кеңейту карталары бар. Дегенмен, біріншіден, VGA мониторлары HDMI сияқты кең таралған емес, екіншіден, сандық интерфейс аналогтыға қарағанда жақсырақ. Сонымен қатар, мұндай VGA кеңейту тақталарындағы DAC әдетте R-2-R тізбектері түрінде жасалады және көбінесе түс үшін 6 биттен аспайды.

ALT2 режимінде GPIO қосқышының түйреуіштері келесі мағынаға ие:

DPI интерфейсі және FPGA тақтасы арқылы Raspberry Pi3 үшін екінші HDMI мониторы

Мұнда мен қосқыштың RGB түйреуіштерін сәйкесінше қызыл, жасыл және көк түске боядым. Басқа маңызды сигналдар V-SYNC және H-SYNC сигналдары, сондай-ақ CLK болып табылады. CLK тактілік жиілігі - пиксель мәндерінің қосқышқа шығарылатын жиілігі; ол таңдалған бейне режиміне байланысты.

Сандық HDMI мониторын қосу үшін интерфейстің DPI сигналдарын түсіріп, оларды HDMI сигналдарына түрлендіру керек. Мұны, мысалы, FPGA тақтасының қандай да бір түрін пайдалану арқылы жасауға болады. Белгілі болғандай, Mars Rover2rpi тақтасы осы мақсаттарға жарамды. Шындығында, бұл тақтаны арнайы адаптер арқылы қосудың негізгі нұсқасы келесідей:

DPI интерфейсі және FPGA тақтасы арқылы Raspberry Pi3 үшін екінші HDMI мониторы

Бұл тақта GPIO порттарының санын көбейту және таңқурайға қосымша перифериялық құрылғыларды қосу үшін қолданылады. Сонымен бірге JTAG сигналдары үшін осы қосылымы бар 4 GPIO сигналы пайдаланылады, осылайша Raspberry бағдарламасы FPGA микробағдарламасын FPGA-ға жүктей алады. Осыған байланысты бұл стандартты қосылым маған сәйкес келмейді; 4 DPI сигналдары жоғалады. Бақытымызға орай, тақтадағы қосымша тарақтарда таңқураймен үйлесімді түйреуіш бар. Сондықтан мен тақтаны 90 градусқа айналдыра аламын және оны таңқурайыма қоса аламын:

DPI интерфейсі және FPGA тақтасы арқылы Raspberry Pi3 үшін екінші HDMI мониторы

Әрине, сізге сыртқы JTAG бағдарламашысын қолдануға тура келеді, бірақ бұл мәселе емес.

Кішкентай мәселе әлі де бар. Әрбір FPGA істікшесін сағат кірісі ретінде пайдалануға болмайды. Осы мақсаттар үшін пайдалануға болатын бірнеше арнайы түйреуіштер бар. Сонымен, бұл жерде GPIO_0 CLK сигналы FPGA сағатының кірісі ретінде пайдаланылуы мүмкін FPGA кірісіне жетпейтіндігі анықталды. Сондықтан мен әлі де орамалға бір сым салуға тура келді. Мен GPIO_0 және тақтаның KEY[1] сигналын қосамын:

DPI интерфейсі және FPGA тақтасы арқылы Raspberry Pi3 үшін екінші HDMI мониторы

Енді мен сізге FPGA жобасы туралы аздап айтып беремін. HDMI сигналдарын жасаудағы негізгі қиындық өте жоғары жиіліктер болып табылады. HDMI коннекторының түйіспе шығысына қарасаңыз, RGB сигналдары енді сериялық дифференциалдық сигналдар екенін көре аласыз:

DPI интерфейсі және FPGA тақтасы арқылы Raspberry Pi3 үшін екінші HDMI мониторы

Дифференциалды сигналды пайдалану тарату желісіндегі жалпы режимдік кедергілермен күресуге мүмкіндік береді. Бұл жағдайда әрбір түсті сигналдың бастапқы сегіз разрядты коды 10 биттік TMDS (Transition-minimized дифференциалды сигнализация) түрлендіріледі. Бұл тұрақты ток құрамдас бөлігін сигналдан алып тастау және дифференциалдық желідегі сигналды ауыстыруды азайту үшін арнайы кодтау әдісі. Енді бір байт түс үшін сериялық желі арқылы 10 бит берілуі керек болғандықтан, сериялайзердің тактілік жылдамдығы пиксельдік тактілік жылдамдығынан 10 есе жоғары болуы керек екен. Мысалы, 1280x720 60 Гц бейне режимін алсақ, онда бұл режимнің пиксель жиілігі 74,25 МГц құрайды. Сериализатор 742,5 МГц болуы керек.

Кәдімгі FPGA, өкінішке орай, бұған қабілетті емес. Дегенмен, бақытымызға орай, FPGA-да кірістірілген DDIO түйреуіштері бар. Бұл қазірдің өзінде 2-ден 1-ге дейін сериялаушы болып табылатын тұжырымдар. Яғни, олар тактілік жиіліктің көтерілу және төмендеу жиектерінде екі битті дәйекті түрде шығара алады. Бұл FPGA жобасында 740 МГц емес, 370 МГц жиілігін пайдалануға болатынын білдіреді, бірақ FPGA-да DDIO шығыс элементтерін пайдалану қажет. Қазір 370 МГц - бұл толығымен қол жетімді жиілік. Өкінішке орай, 1280x720 режимі шектеу болып табылады. Mars Rover2rpi тақтасында орнатылған IV Cyclone FPGA құрылғысында жоғары ажыратымдылыққа қол жеткізу мүмкін емес.

Сонымен, дизайнда CLK кіріс пикселінің жиілігі PLL-ге барады, онда ол 5-ке көбейтіледі. Бұл жиілікте R, G, B байттары бит жұптарына түрленеді. TMDS кодтары осылай жасайды. Verilog HDL ішіндегі бастапқы код келесідей:

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

Содан кейін шығыс жұптары DDIO шығысына беріледі, ол көтерілу және төмендеу жиектерінде бір разрядтық сигналды дәйекті түрде шығарады.

DDIO өзін келесі Verilog кодымен сипаттауға болады:

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

Бірақ ол осылай жұмыс істемеуі әбден мүмкін. DDIO шығыс элементтерін нақты қосу үшін Alter мегафункциясы ALTDDIO_OUT пайдалану керек. Менің жобам ALTDDIO_OUT кітапхана компонентін пайдаланады.

Мұның бәрі аздап күрделі көрінуі мүмкін, бірақ ол жұмыс істейді.

Verilog HDL жүйесінде жазылған барлық бастапқы кодты көруге болады мұнда github сайтында.

FPGA үшін құрастырылған микробағдарлама Mars Rover2rpi тақтасында орнатылған EPCS чипіне жыпылықтайды. Осылайша, FPGA тақтасына қуат берілгенде, FPGA флэш-жадтан инициализацияланады және іске қосылады.

Енді таңқурайдың конфигурациясы туралы аздап айту керек.

Мен Debian Buster негізіндегі Raspberry PI операциялық жүйесінде (32 бит) эксперименттер жасап жатырмын, Нұсқа: тамыз 2020 ж.,
Шығарылған күні: 2020, ядро ​​нұсқасы: 08.

Сізге екі нәрсені істеу керек:

  • config.txt файлын өңдеу;
  • екі монитормен жұмыс істеу үшін X сервер конфигурациясын жасаңыз.

/boot/config.txt файлын өңдеу кезінде сізге қажет:

  1. i2c, i2s, spi пайдалануды өшіру;
  2. DPI режимін overlay dtoverlay=dpi24 арқылы қосу;
  3. бейне режимін конфигурациялау 1280×720 60Гц, DPI бойынша бір пиксельге 24 бит;
  4. кадр буферлерінің қажетті санын 2 көрсетіңіз (max_framebuffers=2, сонда ғана екінші құрылғы /dev/fb1 пайда болады)

config.txt файлының толық мәтіні осылай көрінеді.

# 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

Осыдан кейін, /dev/fb0 және /dev/fb1 екі фреймбуферінде екі мониторды пайдалану үшін X сервері үшін конфигурация файлын жасау керек:

Менің /usr/share/x11/xorg.conf.d/60-dualscreen.conf конфигурация файлым осындай

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

Егер ол әлі орнатылмаған болса, Xinerama орнату керек. Содан кейін жұмыс үстелінің кеңістігі жоғарыдағы демонстрациялық бейнеде көрсетілгендей екі мониторға дейін толығымен кеңейтіледі.

Бәрі осы шығар. Енді Raspberry Pi3 иелері екі мониторды пайдалана алады.

Mars Rover2rpi тақтасының сипаттамасы мен схемасын табуға болады мұнда қараңыз.

Ақпарат көзі: www.habr.com