Video ini menunjukkan: papan Raspberry Pi3, untuk itu, melalui konektor GPIO, papan Mars Rover2rpi FPGA (Cyclone IV) terhubung ke mana monitor HDMI terhubung. Monitor kedua terhubung melalui konektor HDMI Raspberry Pi3 standar. Semuanya bekerja sama seperti sistem dengan dua monitor.
Saya akan memberi tahu Anda bagaimana ini dilakukan lebih lanjut.
Papan Raspberry Pi3 yang populer memiliki konektor GPIO di mana Anda dapat menghubungkan berbagai papan ekspansi: sensor, LED, driver motor stepper, dan banyak lagi. Fungsi spesifik dari setiap pin pada konektor bergantung pada konfigurasi port. Konfigurasi ALT2 GPIO memungkinkan Anda untuk mengalihkan konektor ke mode antarmuka DPI, Tampilan Antarmuka Paralel. Ada kartu ekspansi untuk menghubungkan monitor VGA melalui DPI. Namun, pertama, monitor VGA tidak lagi umum seperti HDMI, dan kedua, antarmuka digital menjadi lebih baik daripada analog. Selain itu, DAC pada kartu ekspansi VGA serupa biasanya dibuat dalam bentuk rantai R-2-R dan seringkali tidak lebih dari 6 bit per warna.
Dalam mode ALT2, pin GPIO memiliki arti sebagai berikut:
Di sini saya mewarnai pin RGB konektor dengan warna merah, hijau dan biru, masing-masing. Sinyal penting lainnya adalah sinyal sinkronisasi sapuan V-SYNC dan H-SYNC, serta CLK. Jam CLK adalah frekuensi di mana nilai-nilai piksel dikeluarkan ke konektor, itu tergantung pada mode video yang dipilih.
Untuk menyambungkan monitor HDMI digital, Anda perlu menangkap sinyal DPI dan mengubahnya menjadi sinyal HDMI. Ini dapat dilakukan, misalnya, menggunakan papan FPGA apa pun. Ternyata, papan rover2rpi Mars cocok untuk keperluan ini. Sebenarnya, opsi utama untuk menghubungkan papan ini melalui adaptor khusus terlihat seperti ini:
Board ini berfungsi untuk menambah jumlah port GPIO dan untuk menghubungkan lebih banyak peripheral ke raspberry. Pada saat yang sama, 4 sinyal GPIO dengan koneksi ini digunakan untuk sinyal JTAG, sehingga program dari raspberry dapat memuat firmware FPGA ke dalam FPGA. Karena itu, koneksi standar seperti itu tidak cocok untuk saya, sinyal 4 DPI putus. Untungnya, sisir tambahan di papan memiliki pinout yang kompatibel dengan Raspberry. Sehingga saya bisa memutar papan 90 derajat dan masih menghubungkannya ke raspberry saya:
Tentu saja, saya harus menggunakan programmer JTAG eksternal, tapi ini bukan masalah.
Masih ada masalah kecil. Tidak semua pin FPGA dapat digunakan sebagai input clock. Hanya ada beberapa pin khusus yang dapat digunakan untuk tujuan ini. Jadi di sini ternyata sinyal GPIO_0 CLK tidak masuk ke input FPGA, yang dapat digunakan sebagai input frekuensi clock FPGA. Jadi tetap saja saya harus melempar satu kawat ke syal. Saya menghubungkan GPIO_0 dan sinyal KEY [1] dari papan:
Sekarang saya akan memberi tahu Anda sedikit tentang proyek di FPGA. Kesulitan utama dalam membentuk sinyal HDMI adalah frekuensi yang sangat tinggi. Melihat pinout konektor HDMI, Anda dapat melihat bahwa sinyal RGB sekarang menjadi sinyal diferensial serial:
Menggunakan sinyal diferensial memungkinkan Anda memerangi gangguan mode umum pada saluran transmisi. Dalam hal ini, kode delapan-bit asli dari setiap sinyal warna diubah menjadi TMDS 10-bit (pensinyalan diferensial yang diminimalkan transisi). Ini adalah teknik pengkodean khusus untuk menghilangkan komponen DC dari sinyal dan meminimalkan pensaklaran sinyal pada jalur diferensial. Karena satu byte warna sekarang perlu mentransfer 10 bit melalui saluran transmisi serial, ternyata frekuensi clock dari serializer harus 10 kali lebih tinggi dari frekuensi clock piksel. Jika kita ambil, misalnya mode video 1280x720 60Hz, maka frekuensi piksel dari mode ini adalah 74,25 MHz. Serializer harus memiliki 742.5MHz.
Sayangnya, FPGA konvensional tidak mampu melakukan ini. Namun, untungnya bagi kami, FPGA memiliki pin DDIO built-in. Ini adalah kesimpulan yang sudah menjadi jenis serializers 2-ke-1. Artinya, mereka dapat mengeluarkan dua bit berturut-turut pada tepi naik dan turun frekuensi clock. Ini berarti bahwa dalam proyek FPGA, Anda tidak dapat menggunakan 740MHz, tetapi 370MHz, tetapi Anda perlu menggunakan elemen output DDIO di FPGA. Di sini frekuensi 370 MHz sudah cukup terjangkau. Sayangnya, mode 1280x720 adalah batasnya. Resolusi yang lebih tinggi pada FPGA Cyclone IV kami yang dipasang pada papan rover Mars 2rpi tidak dapat dicapai.
Jadi, dalam proyek, frekuensi piksel input CLK pergi ke PLL, di mana ia dikalikan dengan 5. Pada frekuensi ini, byte R, G, B diubah menjadi pasangan bit. Ini dilakukan oleh encoder TMDS. Kode sumber untuk Verilog HDL terlihat seperti ini:
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
Kemudian pasangan keluaran diumpankan ke keluaran DDIO, yang secara berurutan menghasilkan sinyal satu-bit saat naik dan turun.
DDIO sendiri dapat dijelaskan dengan kode Verilog seperti itu:
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
Tapi mungkin tidak akan berhasil seperti itu. Anda harus menggunakan ALTDDIO_OUT perubahan megafungsi untuk benar-benar menggunakan elemen DDIO keluaran. Ini adalah komponen pustaka ALTDDIO_OUT yang digunakan dalam proyek saya.
Semuanya mungkin terlihat sedikit rumit, tetapi berhasil.
Anda dapat melihat semua kode sumber yang ditulis dalam Verilog HDL di sini di github .
Firmware FPGA yang telah dikompilasi digabungkan ke dalam chip EPCS yang dipasang pada papan rover2rpi Mars. Jadi, ketika daya disuplai ke papan FPGA, FPGA akan menginisialisasi dari memori flash dan memulai.
Sekarang kita perlu berbicara sedikit tentang konfigurasi Raspberry itu sendiri.
Saya melakukan percobaan pada Raspberry PI OS (32 bit) berdasarkan Debian Buster, Versi: Agustus 2020,
Tanggal rilis: 2020-08-20, Versi kernel: 5.4.
Ada dua hal yang harus dilakukan:
- edit file config.txt;
- buat konfigurasi server X untuk bekerja dengan dua monitor.
Saat mengedit file /boot/config.txt, Anda perlu:
- nonaktifkan penggunaan i2c, i2s, spi;
- aktifkan mode DPI menggunakan overlay dtoverlay = dpi24;
- setel mode video 1280x720 60Hz, 24 bit per titik per DPI;
- tentukan jumlah framebuffers 2 yang diperlukan (max_framebuffers = 2, baru perangkat kedua / dev / fb1 akan muncul)
Teks lengkap dari file config.txt terlihat seperti ini.
# 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
Setelah itu, Anda perlu membuat file konfigurasi untuk server X untuk menggunakan dua monitor pada dua framebuffers / dev / fb0 dan / dev / fb1:
File konfigurasi saya /usr/share/x11/xorg.conf.d/60-dualscreen.conf seperti ini
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
Nah, jika belum terinstal, maka Anda perlu menginstal Xinerama. Kemudian ruang desktop akan diperluas sepenuhnya menjadi dua monitor, seperti yang ditunjukkan di atas dalam video demo.
Mungkin itu saja. Sekarang, pemilik Raspberry Pi3 akan dapat menggunakan dua monitor.
Deskripsi dan diagram papan rover2rpi Mars dapat dilihat di sini .