DPI interfeysi va FPGA platasi orqali Raspberry Pi3 ga ikkinchi HDMI monitor


Ushbu videoda: Raspberry Pi3 platasi, GPIO ulagichi orqali FPGA Mars Rover2rpi (Cyclone IV) platasi ulangan, unga HDMI monitori ulangan. Ikkinchi monitor standart Raspberry Pi3 HDMI ulagichi orqali ulangan. Hammasi birgalikda ikkita monitor tizimi kabi ishlaydi.

Keyinchalik men sizga qanday amalga oshirilishini aytib beraman.

Mashhur Raspberry Pi3 platasida GPIO ulagichi mavjud bo'lib, u orqali siz turli xil kengaytirish platalarini ulashingiz mumkin: sensorlar, LEDlar, step motor drayverlari va boshqalar. Ulagichdagi har bir pinning o'ziga xos funktsiyasi port konfiguratsiyasiga bog'liq. GPIO ALT2 konfiguratsiyasi ulagichni DPI interfeysi rejimiga, Parallel interfeysni ko'rsatishga o'tkazish imkonini beradi. VGA monitorlarini DPI orqali ulash uchun kengaytirish platalari mavjud. Biroq, birinchidan, VGA monitorlari endi HDMI kabi keng tarqalgan emas, ikkinchidan, raqamli interfeys analogdan yaxshiroq bo'ladi. Bundan tashqari, bunday VGA kengaytirish kartalaridagi DAC odatda R-2-R zanjirlari shaklida ishlab chiqariladi va ko'pincha har bir rang uchun 6 bitdan oshmaydi.

ALT2 rejimida GPIO ulagichining pinlari quyidagi ma'noga ega:

DPI interfeysi va FPGA platasi orqali Raspberry Pi3 ga ikkinchi HDMI monitor

Bu erda men ulagichning RGB pinlarini mos ravishda qizil, yashil va ko'k rangga bo'yadim. Boshqa muhim signallar V-SYNC va H-SYNC supurish sinxronlash signallari, shuningdek, CLK. CLK soat chastotasi - bu ulagichga piksel qiymatlari chiqadigan chastota va tanlangan video rejimiga bog'liq.

Raqamli HDMI monitorini ulash uchun siz DPI interfeysi signallarini yozib olishingiz va ularni HDMI signallariga aylantirishingiz kerak. Buni, masalan, har qanday FPGA platasi yordamida amalga oshirish mumkin. Ma'lum bo'lishicha, Mars Rover2rpi platasi shu maqsadda mos keladi. Aslida, ushbu platani maxsus adapter orqali ulashning asosiy varianti quyidagicha ko'rinadi:

DPI interfeysi va FPGA platasi orqali Raspberry Pi3 ga ikkinchi HDMI monitor

Ushbu plata GPIO portlari sonini ko'paytirish va malinaga ko'proq tashqi qurilmalarni ulash uchun ishlatiladi. Shu bilan birga, ushbu ulanishga ega 4 ta GPIO signallari JTAG signallari uchun ishlatiladi, shuning uchun tarqatish dasturi FPGA mikrodasturini FPGA-ga yuklashi mumkin. Shu sababli, bunday muntazam ulanish menga mos kelmaydi, 4 DPI signallari tushib ketadi. Yaxshiyamki, taxtadagi qo'shimcha taroqlarda Raspberry-ga mos keladigan pinout mavjud. Shunday qilib, men taxtani 90 daraja aylantira olaman va uni malinaga ulashim mumkin:

DPI interfeysi va FPGA platasi orqali Raspberry Pi3 ga ikkinchi HDMI monitor

Albatta, siz tashqi JTAG dasturchisidan foydalanishingiz kerak bo'ladi, lekin bu muammo emas.

Hali kichik muammo bor. Har bir FPGA pinini soat kiritish sifatida ishlatib bo'lmaydi. Bu maqsadda ishlatilishi mumkin bo'lgan bir nechta maxsus pinlar mavjud. Shunday qilib, bu erda ma'lum bo'ldiki, GPIO_0 CLK signali FPGA kirishiga tushmaydi, undan FPGA soatiga kirish sifatida foydalanish mumkin. Shunday qilib, men ro'molga bitta postni tashlashim kerak edi. Men plataning GPIO_0 va KEY[1] signalini ulayman:

DPI interfeysi va FPGA platasi orqali Raspberry Pi3 ga ikkinchi HDMI monitor

Endi men sizga FPGAdagi loyiha haqida bir oz gapirib beraman. HDMI signallarini shakllantirishdagi asosiy qiyinchilik juda yuqori chastotalardir. HDMI ulagichining pinoutiga qarab, RGB signallari endi ketma-ket differentsial signallar ekanligini ko'rishingiz mumkin:

DPI interfeysi va FPGA platasi orqali Raspberry Pi3 ga ikkinchi HDMI monitor

Differensial signaldan foydalanish sizga uzatish liniyasida umumiy tartibdagi shovqin bilan kurashish imkonini beradi. Bunday holda, har bir rangli signalning asl sakkiz bitli kodi 10 bitli TMDS ga (Transition-minimized differential signaling) aylantiriladi. Bu DC komponentini signaldan olib tashlash va differentsial chiziqdagi signal almashinuvini minimallashtirish uchun maxsus kodlash usuli. Endi ketma-ket chiziq bo'ylab har bir bayt rangni uzatish uchun 10 bit mavjud bo'lganligi sababli, serializatorning takt chastotasi piksellarning soat chastotasidan 10 baravar yuqori bo'lishi kerakligi ma'lum bo'ldi. Masalan, 1280x720 60 Gts video rejimini oladigan bo'lsak, bu rejimning piksel chastotasi 74,25 MGts ni tashkil qiladi. Serializator 742,5 MGts bo'lishi kerak.

An'anaviy FPGAlar, afsuski, odatda bunga qodir emas. Biroq, bizning omadimizga FPGA-larda o'rnatilgan DDIO pinlari mavjud. Bular allaqachon 2 dan 1 gacha seriyali bo'lgan xulosalar. Ya'ni, ular ko'tarilgan va pasaygan soat chastotalari bo'yicha ketma-ket ikkita bitni chiqarishi mumkin. Bu shuni anglatadiki, FPGA loyihasida siz 740 MGts emas, balki 370 MGts dan foydalanishingiz mumkin, lekin siz FPGAda DDIO chiqish elementlaridan foydalanishingiz kerak. Bu erda 370 MGts allaqachon erishish mumkin bo'lgan chastotadir. Afsuski, 1280 Γ— 720 rejimi chegara hisoblanadi. Rover2rpi platasida o'rnatilgan FPGA Cyclone IVda yuqori aniqlikka erishib bo'lmaydi.

Shunday qilib, loyihada kirish pikselli chastotasi CLK PLL ga beriladi, u erda u 5 ga ko'paytiriladi. Ushbu chastotada R, G, B baytlari bit juftlariga aylanadi. TMDS kodlovchisi shunday qiladi. Verilog HDL-dagi manba kodi quyidagicha ko'rinadi:

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

Keyin chiqish juftlari DDIO chiqishiga beriladi, bu ko'tarilish va pasayishda ketma-ket bir bitli signal hosil qiladi.

DDIO ning o'zi Verilog kodi bilan quyidagicha tasvirlanishi mumkin:

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

Lekin, ehtimol, bunday ishlamaydi. DDIO chiqish elementlaridan foydalanish uchun siz Altera-ning ALTDDIO_OUT megafunksiyasidan foydalanishingiz kerak. Mening loyihamda kutubxona komponenti ALTDDIO_OUT ishlatiladi.

Hammasi biroz qiyin ko'rinishi mumkin, lekin u ishlaydi.

Verilog HDL-da yozilgan to'liq manba kodini ko'rishingiz mumkin githubda shu yerda.

FPGA uchun kompilyatsiya qilingan proshivka Mars Rover2rpi platasida o'rnatilgan EPCS chipiga o'rnatilgan. Shunday qilib, FPGA platasiga quvvat berilganda, FPGA flesh xotiradan ishga tushadi va ishga tushadi.

Endi biz Raspberry-ning o'zi konfiguratsiyasi haqida bir oz gapirishimiz kerak.

Men Debian Buster asosida Raspberry PI OS (32 bit) da tajribalar qilyapman, Versiya: Avgust 2020,
Chiqarilgan sana: 2020, yadro versiyasi: 08.

Siz ikkita narsani qilishingiz kerak:

  • config.txt faylini tahrirlash;
  • ikkita monitor bilan ishlash uchun X server konfiguratsiyasini yarating.

/boot/config.txt faylini tahrirlashda sizga quyidagilar kerak:

  1. i2c, i2s, spi-dan foydalanishni o'chirib qo'yish;
  2. dtoverlay=dpi24 bilan DPI rejimini yoqing;
  3. video rejimini o'rnatish 1280 Γ— 720 60Hz, DPI boshiga nuqta uchun 24 bit;
  4. kerakli miqdordagi freymbuferlarni belgilang 2 (max_framebuffers=2, shundan keyingina ikkinchi qurilma /dev/fb1 paydo bo'ladi)

config.txt faylining to'liq matni shunday ko'rinadi.

# 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

Shundan so'ng, ikkita /dev/fb0 va /dev/fb1 freymbuferida ikkita monitordan foydalanish uchun X serveri uchun konfiguratsiya faylini yaratishingiz kerak:

Mening konfiguratsiya faylim /usr/share/x11/xorg.conf.d/60-dualscreen.conf shunga o'xshash

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

Xo'sh, agar o'rnatilmagan bo'lsa, Xinerama-ni o'rnatishingiz kerak. Keyin ish stoli maydoni yuqoridagi demo videoda ko'rsatilganidek, ikkita monitorga to'liq kengaytiriladi.

Balki hammasi shu. Endi Raspberry Pi3 egalari ikkita monitordan foydalanishlari mumkin boβ€˜ladi.

Mars Rover2rpi taxtasining tavsifi va diagrammasi bo'lishi mumkin bu yerga qarang.

Manba: www.habr.com