U ovom videu prikazana je: ploča Raspberry Pi3, kojoj je putem GPIO priključka spojena FPGA ploča Mars Rover2rpi (Cyclone IV), na koju je spojen HDMI monitor. Drugi monitor je povezan putem standardnog HDMI priključka Raspberry Pi3. Sve zajedno radi kao sustav s dva monitora.
Dalje ću objasniti kako je to realizirano.
Na popularnoj ploči Raspberry Pi3 postoji GPIO priključak putem kojeg se mogu spojiti različite ekspanzijske ploče: senzori, LED diode, upravljači korak-motorima i još mnogo toga. Konkretna funkcija svakog izlaza na priključku ovisi o konfiguraciji portova. GPIO ALT2 konfiguracija omogućava prebacivanje priključka u DPI način rada, Display Parallel Interface. Postoje ekspanzijske ploče za povezivanje VGA monitora putem DPI. Međutim, prvo, VGA monitori nisu više toliko rasprostranjeni kao HDMI, a drugo, digitalni su interfejsi bolji od analognih. Osim toga, DAC na takvim VGA ekspanzijskim pločama obično je izrađen u obliku R-2-R lanaca i često ne više od 6 bita po boji.
U ALT2 režimu GPIO pinovi imaju sljedeće značenje:

Ovdje sam obojio RGB kontakte priključka u crvenu, zelenu i plavu boju. Ostali važni signali su signali sinhronizacije razrješenja V-SYNC i H-SYNC, kao i CLK. Takta frekvencija CLK je frekvencija kojom se vrijednosti piksela šalju na priključak, ovisi o odabranom video režimu.
Za povezivanje digitalnog HDMI monitora potrebno je uhvatiti DPI signale i pretvoriti ih u HDMI signale. To se može učiniti, na primjer, pomoću neke FPGA ploče. Kako se ispostavilo, ploča Mars Rover2rpi odgovara tim potrebama. Iskreno, osnovna opcija povezivanja ove ploče putem specijalnog adaptera izgleda ovako:

Ova ploča služi za povećanje broja GPIO portova i za povezivanje većeg broja perifernih uređaja na Raspberry. Pri tome se 4 GPIO signala koriste za JTAG signale, tako da program iz Raspberry može učitati FPGA firmware u PLIS. Zbog toga mi ta standardna konekcija ne odgovara, ispadaju 4 DPI signala. Na sreću, dodatni konektori na ploči imaju raspored pinova kompatibilan s Raspberry. Tako mogu preokrenuti ploču za 90 stepeni i povezati je na moju malinu:

Naravno, morat ću koristiti vanjski JTAG programator, ali to nije problem.
Ima malo problema. Ne može se svaki FPGA izlaz koristiti kao ulaz takta. Postoji samo nekoliko namjenskih pinova koji se mogu koristiti u te svrhe. Tako je i ovdje ispalo, da signal GPIO_0 CLK ne dolazi na ulaz FPGA, koji se može koristiti kao ulaz takta PLIS-a. Tako da sam ipak morao provući jedan kabel na ploču. Spajam GPIO_0 i signal KEY[1] ploče:

Sad ću malo pričati o projektu u PLIS-u. Glavna otežavajuća okolnost pri formiranju HDMI signala su vrlo visoke frekvencije. Ako pogledamo pin raspored HDMI konektora, vidjet ćemo da su RGB signali sada sekvencijalni diferencijalni signali:

Korištenje diferencijalnog signala omogućuje borbu protiv zajedničkih smetnji na prijenosnoj liniji. Pri tome, izvorni osmobitni kod svakog boje signala se pretvara u 10-bitni TMDS (Transition-minimized differential signaling). Ovo je poseban način kodiranja za uklanjanje stalne komponente iz signala i minimiziranje prelazaka signala u diferencijalnoj liniji. S obzirom na to da za jedan bajt boje sada po sekvencijalnoj liniji prijenosa treba prenijeti 10 bitova, ispada da frekvencija takta serializer-a mora biti 10 puta veća od frekvencije piksela. Ako uzmemo za primjer video režim 1280x720 60Hz, tada je frekvencija piksela u tom režimu 74,25MHz. Na serializer-u bi trebalo biti 742,5MHz.
Obični FPGA, nažalost, nisu sposobni za to. No, na našu sreću, u FPGA postoje ugrađeni DDIO izlazi. To su izlazi koji već djeluju kao 2-na-1 serializeri. Odnosno, mogu isporučiti dva bita sekvencijalno po porastu i opadanju takta. To znači da se u FPGA projektu može koristiti ne 740MHz, već 370MHz, ali treba aktivirati izlazne elemente DDIO u PLIS-u. Ovo je 370MHz već sasvim ostvariva frekvencija. Nažalost, rezolucija 1280×720 je limit. Viša razlučivost s našim FPGA Cyclone IV postavljenim na ploči Mars Rovery tidak može se postići.
Dakle, u projektu, ulazna frekvencija piksela CLK dolazi na PLL, gdje se množi s 5. Na ovoj frekvenciji, bajtovi R, G, B, pretvaraju se u parove bita. To radi TMDS enkoder. Izvorni kod na Verilog HDL izgleda ovako:
modul hdmi(
ulazni vod pixclk, // 74MHz
ulazni vod clk_TMDS2, // 370MHz
ulazni vod hsync,
ulazni vod vsync,
ulazni vod aktivni,
ulazni vod [7:0]crvena,
ulazni vod [7:0]zelena,
ulazni vod [7:0]plava,
izlazni vod TMDS_bh,
izlazni vod TMDS_bl,
izlazni vod TMDS_gh,
izlazni vod TMDS_gl,
izlazni vod TMDS_rh,
izlazni vod TMDS_rl
);
vod [9:0] TMDS_crvena, TMDS_zelena, TMDS_plava;
TMDS_encoder encode_R(.clk(pixclk), .VD(crvena), .CD({vsync,hsync}), .VDE(aktivni), .TMDS(TMDS_crvena));
TMDS_encoder encode_G(.clk(pixclk), .VD(zelena), .CD({vsync,hsync}), .VDE(aktivni), .TMDS(TMDS_zelena));
TMDS_encoder encode_B(.clk(pixclk), .VD(plava), .CD({vsync,hsync}), .VDE(aktivni), .TMDS(TMDS_plava));
reg [2:0] TMDS_mod5=0; // brojač modula 5
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;
vod [4:0] TMDS_plava_l = {TMDS_plava[9],TMDS_plava[7],TMDS_plava[5],TMDS_plava[3],TMDS_plava[1]};
vod [4:0] TMDS_plava_h = {TMDS_plava[8],TMDS_plava[6],TMDS_plava[4],TMDS_plava[2],TMDS_plava[0]};
vod [4:0] TMDS_zelena_l = {TMDS_zelena[9],TMDS_zelena[7],TMDS_zelena[5],TMDS_zelena[3],TMDS_zelena[1]};
vod [4:0] TMDS_zelena_h = {TMDS_zelena[8],TMDS_zelena[6],TMDS_zelena[4],TMDS_zelena[2],TMDS_zelena[0]};
vod [4:0] TMDS_crvena_l = {TMDS_crvena[9],TMDS_crvena[7],TMDS_crvena[5],TMDS_crvena[3],TMDS_crvena[1]};
vod [4:0] TMDS_crvena_h = {TMDS_crvena[8],TMDS_crvena[6],TMDS_crvena[4],TMDS_crvena[2],TMDS_crvena[0]};
uvijek @(posedge clk_TMDS2)
begin
TMDS_shift_bh <= TMDS_mod5[2] ? TMDS_plava_h : TMDS_shift_bh [4:1];
TMDS_shift_bl <= TMDS_mod5[2] ? TMDS_plava_l : TMDS_shift_bl [4:1];
TMDS_shift_gh <= TMDS_mod5[2] ? TMDS_zelena_h : TMDS_shift_gh [4:1];
TMDS_shift_gl <= TMDS_mod5[2] ? TMDS_zelena_l : TMDS_shift_gl [4:1];
TMDS_shift_rh <= TMDS_mod5[2] ? TMDS_crvena_h : TMDS_shift_rh [4:1];
TMDS_shift_rl <= TMDS_mod5[2] ? TMDS_crvena_l : TMDS_shift_rl [4:1];
TMDS_mod5 4'd4) || (Nb1s==4'd4 && VD[0]==1'b0);
vod [8:0] q_m = {~XNOR, q_m[6:0] ^ VD[7:1] ^ {7{XNOR}}, VD[0]};
reg [3:0] balance_acc = 0;
vod [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;
vod balance_sign_eq = (balance[3] == balance_acc[3]);
vod invert_q_m = (balance==0 || balance_acc==0) ? ~q_m[8] : balance_sign_eq;
vod [3:0] balance_acc_inc = balance - ({q_m[8] ^ ~balance_sign_eq} & ~(balance==0 || balance_acc==0));
vod [3:0] balance_acc_new = invert_q_m ? balance_acc-balance_acc_inc : balance_acc+balance_acc_inc;
vod [9:0] TMDS_data = {invert_q_m, q_m[8], q_m[7:0] ^ {8{invert_q_m}}};
vod [9:0] TMDS_code = CD[1] ? (CD[0] ? 10'b1010101011 : 10'b0101010100) : (CD[0] ? 10'b0010101011 : 10'b1101010100);
uvijek @(posedge clk) TMDS <= VDE ? TMDS_data : TMDS_code;
uvijek @(posedge clk) balance_acc <= VDE ? balance_acc_new : 4'h0;
kraj
Nakon toga, izlazni parovi se šalju na DDIO izlaz, koji sekvencijalno isporučuje jednosmjerni signal na usponu i padu.
Sam DDIO bi se mogao opisati ovim Verilog kodom:
modul ddio(
ulazni vod d0,
ulazni vod d1,
ulazni vod clk,
izlazni vod out
);
reg r_d0;
reg r_d1;
uvijek @(posedge clk)
begin
r_d0 <= d0;
r_d1 <= d1;
end
dodijeli out = clk ? r_d0 : r_d1;
krajNo, ovako raditi najvjerojatnije neće uspjeti. Potrebno je koristiti alterovu mega funkciju ALTDDIO_OUT kako bi se doista aktivirali izlazni DDIO elementi. U mom projektu koristi se upravo bibliotečki komponent ALTDDIO_OUT.
Možda sve ovo izgleda pomalo komplicirano, ali radi.
Cijeli izvorni kod napisan u Verilog HDL možete pogledati .
Kompilirana firmware za FPGA se prebacuje u EPCS čip, postavljen na Mars rover2rpi. Tako, kada se napajanje uključi na FPGA ploču, FPGA će se inicijalizirati iz flash memorije i započeti.
Sada je potrebno malo reći o konfiguraciji samog Raspberryja.
Eksperimentiram na Raspberry PI OS (32 bit) temeljenom na Debian Buster, Verzija: Kolovoz 2020,
Datum izdanja: 2020-08-20, Verzija kernela: 5.4.
Potrebno je napraviti dvije stvari:
- izmijeniti datoteku config.txt;
- stvoriti konfiguraciju X poslužitelja za rad s dva monitora.
Pri uređivanju datoteke /boot/config.txt potrebno je:
- isključiti korištenje i2c, i2s, spi;
- uključiti DPI mod koristeći overlay dtoverlay=dpi24;
- postaviti video mod 1280×720 60Hz, 24 bita po točki na DPI;
- navesti potrebno broj framebuffers 2 (max_framebuffers=2, samo tada će se pojaviti drugi uređaj /dev/fb1)
Potpuni tekst datoteke config.txt izgleda ovako.
# 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
Nakon toga, potrebno je stvoriti konfiguracijsku datoteku za X poslužitelj za korištenje dvaju monitora na dva framebuffera /dev/fb0 i /dev/fb1:
Moja konfiguracijska datoteka /usr/share/x11/xorg.conf.d/60-dualscreen.conf izgleda ovako
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
Pa, ako još nije instaliran, potrebno je instalirati Xinerama. Tada će prostor radne površine biti potpuno proširen na dva monitora, kao što je prikazano gore u demo videu.
To je to. Sada će i vlasnici Raspberry Pi3 moći koristiti dva monitora.
Opis i shemu ploče Marsovac2rpi možete .
Izvor: habr.com
