DPI интерфэйс болон FPGA самбараар дамжуулан Raspberry Pi3 хүртэлх хоёр дахь HDMI дэлгэц


Энэ видеог харуулж байна: GPIO холбогчоор холбогдсон Raspberry Pi3 хавтан нь FPGA хавтан Mars Rover2rpi (Циклон IV) бөгөөд түүнд HDMI дэлгэц холбогдсон байна. Хоёр дахь дэлгэц нь стандарт Raspberry Pi3 HDMI холбогчоор холбогдсон. Бүгдээрээ хос хяналтын систем шиг ажилладаг.

Дараа нь би үүнийг хэрхэн хэрэгжүүлж байгааг танд хэлэх болно.

Алдартай Raspberry Pi3 хавтан нь GPIO холбогчтой бөгөөд үүгээр дамжуулан та янз бүрийн өргөтгөлийн картуудыг холбох боломжтой: мэдрэгч, LED, stepper мотор драйверууд болон бусад. Холбогч дээрх зүү тус бүрийн тодорхой функц нь портын тохиргооноос хамаарна. GPIO ALT2 тохиргоо нь холбогчийг DPI интерфэйсийн горим, Дэлгэц Зэрэгцээ интерфэйс рүү шилжүүлэх боломжийг олгодог. VGA дэлгэцийг DPI-ээр холбох өргөтгөлийн самбарууд байдаг. Гэсэн хэдий ч, нэгдүгээрт, 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 портуудын тоог нэмэгдүүлэх, нэмэлт төхөөрөмжүүдийг бөөрөлзгөнөтэй холбоход ашиглагддаг. Үүний зэрэгцээ энэ холболттой 4 GPIO дохиог JTAG дохионд ашигладаг бөгөөд ингэснээр 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 самбар дээр суурилуулсан манай Cyclone IV 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 нь флаш санах ойноос эхэлж, эхлэх болно.

Одоо бид Raspberry-ийн тохиргооны талаар бага зэрэг ярих хэрэгтэй.

Би Debian Buster дээр суурилсан Raspberry PI үйлдлийн систем (32 бит) дээр туршилт хийж байна, Хувилбар: 2020 оны XNUMX-р сар,
Гарсан огноо: 2020-08-20, Цөмийн хувилбар: 5.4.

Та хоёр зүйлийг хийх хэрэгтэй:

  • config.txt файлыг засварлах;
  • хоёр дэлгэцтэй ажиллахын тулд X серверийн тохиргоог үүсгэ.

/boot/config.txt файлыг засварлахдаа та:

  1. i2c, i2s, spi ашиглахыг идэвхгүй болгох;
  2. DPI горимыг давхцуулж идэвхжүүлэх 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