你也許會(huì)有疑問,明明有這么多通信方式和數(shù)據(jù)傳輸(SPI、I2C、UART、以太網(wǎng))為什么偏偏使用USB呢?
原因有很多,如下:
1. 高速數(shù)據(jù)傳輸能力
高帶寬 :USB接口提供了較高的數(shù)據(jù)傳輸速率,尤其是隨著USB版本的升級(如USB 3.0及更高版本),其理論速度可達(dá)5 Gbps甚至更高。這對于需要高速數(shù)據(jù)傳輸?shù)膽?yīng)用(如視頻處理、實(shí)時(shí)數(shù)據(jù)采集等)尤為重要。
低延遲 :相比一些其他接口(如UART),USB的延遲更低,能夠滿足實(shí)時(shí)性要求較高的場景。
2. 通用性和兼容性
廣泛的硬件支持 :幾乎所有現(xiàn)代計(jì)算機(jī)和嵌入式系統(tǒng)都配備了USB接口,這意味著使用USB進(jìn)行通信可以輕松實(shí)現(xiàn)跨平臺支持,無需額外的硬件適配。
標(biāo)準(zhǔn)化接口 :USB是一種標(biāo)準(zhǔn)化的接口,遵循統(tǒng)一的協(xié)議和規(guī)范。這不僅簡化了開發(fā)過程,還確保了不同設(shè)備之間的互操作性。
3. 開發(fā)便利性
豐富的工具支持 :大多數(shù)FPGA開發(fā)工具(如Xilinx Vivado、Altera Quartus等)都提供了對USB接口的支持,簡化了設(shè)計(jì)和驗(yàn)證過程。
成熟的驅(qū)動(dòng)和庫資源 :大量的現(xiàn)成驅(qū)動(dòng)程序和庫資源可以輕松集成到項(xiàng)目中,減少了軟件開發(fā)的工作量。
4. 靈活的通信模式
全雙工通信 :USB支持全雙工通信模式,允許同時(shí)進(jìn)行數(shù)據(jù)的上傳和下載,提高了通信效率。
多種數(shù)據(jù)傳輸類型 :USB支持控制傳輸、批量傳輸、中斷傳輸和同步傳輸?shù)榷喾N數(shù)據(jù)傳輸類型,能夠適應(yīng)不同應(yīng)用場景的需求。
成本效益
低成本解決方案 :相比于一些高端接口(如PCIe),USB的成本較低,適合預(yù)算有限的項(xiàng)目。
減少外部組件需求 :由于USB的標(biāo)準(zhǔn)化和廣泛支持,可以減少對外部組件的需求,從而降低整體硬件成本。
也正是因?yàn)槿绱?,usb廣泛應(yīng)用于數(shù)據(jù)的采集和處理、視頻和音頻傳輸、嵌入式系統(tǒng)開發(fā)等。
而我們今天即將要學(xué)習(xí)的,就是FPGA的USB傳輸,以FX2芯片為例
FX2
USB是一種通用的數(shù)據(jù)傳輸協(xié)議和接口標(biāo)準(zhǔn),定義了設(shè)備與主機(jī)(如電腦)之間的通信規(guī)則(如協(xié)議、電氣特性、數(shù)據(jù)傳輸模式等);FX系列芯片(FX2, FX3)是Cypress(現(xiàn)英飛凌)推出的USB控制芯片,用于實(shí)現(xiàn)高速USB設(shè)備的功能。說的再簡單,直白一點(diǎn):USB是協(xié)議標(biāo)準(zhǔn),F(xiàn)X芯片是實(shí)現(xiàn)這一標(biāo)準(zhǔn)的硬件載體
FX芯片可以
自動(dòng)處理USB復(fù)雜協(xié)議,無需開發(fā)者手動(dòng)實(shí)現(xiàn),
支持高速傳輸(FX2:支持USB2.0高速傳輸, 480Mbps; FX3則為 5Gbps)
提供靈活的接口(GPIF,Slave FIFO)方便直接連接外設(shè),
內(nèi)置微控制器,可以通過固件配置USB功能
FX2控制器內(nèi)部結(jié)構(gòu)圖如下
FX2可以通過兩種方式到FPGA,一個(gè)是(通用可編程接口)GPIF模式和從設(shè)備FIFO模式
GPIF:FX2是總線的主控者,用戶自定義時(shí)序,靈活但開發(fā)復(fù)雜
Slave FIFO: FX2是被動(dòng)的FIFO從設(shè)備,外部主控直接控制,簡單但靈活性受限

在實(shí)際項(xiàng)目中,Slave FIFO模式更常用(尤其是FPGA做為主控的場景),而GPIF模式需要更精確控制總線的特殊需求
回環(huán)測試
介紹
我們此處就以簡單的回環(huán)測試為例,實(shí)現(xiàn)FPGA的Usb數(shù)據(jù)傳輸。

所謂回環(huán)測試,就是說由 PC 發(fā)送數(shù)據(jù)到 FX2 芯片的 OUT 端點(diǎn) 2,然后再由主機(jī)將端點(diǎn) 2 中的數(shù)據(jù)讀出,拷貝到IN 端點(diǎn) 6。使用 FPGA 設(shè)計(jì) SlaveFIFO 讀取和寫入接口邏輯,將端點(diǎn) 2 中的數(shù)據(jù)讀出,然后寫入端點(diǎn) 6 中,再由電腦上位機(jī)從端點(diǎn) 6 中將數(shù)據(jù)讀回,從而實(shí)現(xiàn)數(shù)據(jù)的回環(huán)。
代碼
module FiFo #(
Depth = 512,
Width = 16
)
(
input fifo_clk,
input rst_n,
input write_busy,
input read_busy,
input fifo_flush,
input [Width-1:0]din,
output reg fifo_full,
output reg fifo_empty,
output reg [Width-1:0] dout
);
localparam ADDR_Width =$clog2(Depth);
//計(jì)數(shù)多加一位,防止溢出
reg [ADDR_Width:0] write_occupancy;
reg [ADDR_Width:0] read_occupancy;
wire [ADDR_Width:0] next_write_occupancy;
wire [ADDR_Width:0] next_read_occupancy;
//fifo 地址索引
wire [ADDR_Width-1:0] next_write_ptr;
reg [ADDR_Width-1:0] write_ptr;
wire [ADDR_Width-1:0] next_read_ptr;
reg [ADDR_Width-1:0] read_ptr;
reg [Width-1:0] data_array[Depth-1:0];
wire write_enable;
wire read_enable;
// 寫使能和讀使能邏輯
assign write_enable = !write_busy && !fifo_full;
assign read_enable = !read_busy && !fifo_empty;
// 下一個(gè)指針和計(jì)數(shù)器計(jì)算
assign next_write_ptr = (write_enable) ? (write_ptr + 1) : write_ptr;
assign next_read_ptr = (read_enable) ? (read_ptr + 1) : read_ptr;
assign next_write_occupancy = fifo_flush ? 10'd0 : (write_enable ? (write_occupancy + 1) : write_occupancy);
assign next_read_occupancy = fifo_flush ? 10'd0 : (read_enable ? (read_occupancy + 1) : read_occupancy);
// 滿/空狀態(tài)判斷(基于下一個(gè)計(jì)數(shù)器值)
wire [ADDR_Width:0] next_occupancy_diff = next_write_occupancy - next_read_occupancy;
wire next_fifo_full = (next_occupancy_diff >= Depth);
wire next_fifo_empty = (next_occupancy_diff == 0);
//更新指針
always @(posedge fifo_clk or negedge rst_n)begin
if(!rst_n)begin
write_ptr<=0;
read_ptr<=0;
end else if(fifo_flush)begin
write_ptr<=0;
read_ptr<=0;
end else begin
write_ptr<=next_write_ptr;
read_ptr<=next_read_ptr;
end//else
end//always
// 更行空/滿信號
always @(posedge fifo_clk or negedge rst_n)begin
if(!rst_n)begin
fifo_full<=0;
fifo_empty<=1;
end else if (fifo_flush)begin
fifo_full<=0;
fifo_empty<=1;
end else begin
fifo_full<=next_fifo_full;
fifo_empty<=next_fifo_empty;
end
end//always
// 讀/寫計(jì)數(shù)
always @(posedge fifo_clk or negedge rst_n)begin
if(!rst_n)begin
write_occupancy<=0;
read_occupancy<=0;
end else if(fifo_flush)begin
write_occupancy<=0;
read_occupancy<=0;
end else begin
write_occupancy<=next_write_occupancy;
read_occupancy<=next_read_occupancy;
end//else
end//always
//輸出數(shù)據(jù)
always @(posedge fifo_clk or negedge rst_n)begin
if(!rst_n)dout<=0;
else if(fifo_flush) dout<=0;
else dout<=data_array[read_ptr];
end//always
//數(shù)據(jù)寫入存儲(chǔ)陣列
always @(posedge fifo_clk)begin
if(write_enable)
data_array[write_ptr]<=din;?
end
// 溢出警告
always @(posedge fifo_clk) begin
if (fifo_full && write_busy) begin
$display("ERROR: %m: Fifo overflow at time %t", $time);
$finish;
end
end // always
// 下溢警告
always @(posedge fifo_clk) begin
if (fifo_empty && read_busy) begin
$display("ERROR: %m: Fifo underflow at time %t", $time);
$finish;
end
end // always
// synthesis translate_on
endmodule
FX2_SF
module FX2_SF(
input clk,
input reset_n,
inout [15:0] fx2_fdata, // 雙向數(shù)據(jù)總線
output [1:0] fx2_faddr, // FIFO地址選擇
output fx2_slrd, // 讀使能(低有效)
output fx2_slwr, // 寫使能(低有效)
output fx2_sloe, // 輸出使能(低有效)
input ep6_full_flag, // EP6滿標(biāo)志(可寫)
input ep2_empty_flag, // EP2空標(biāo)志(可讀)
input fx2_ifclk, // 接口時(shí)鐘(60MHz)
output fx2_pkt_end, // 包結(jié)束脈沖
output fx2_clear, // 復(fù)位信號
output fx2_slcs // 片選(常低)
);
//------------------------ 參數(shù)優(yōu)化 ------------------------//
localparam [1:0]
LOOPBACK_IDLE = 2'd0,
LOOPBACK_READ = 2'd1,
LOOPBACK_WAIT_ep6_full = 2'd2,
LOOPBACK_WRITE = 2'd3;
localparam [1:0]
FIFO_ADDR_READ = 2'b00, // EP2
FIFO_ADDR_WRITE = 2'b10; // EP6
//------------------------ 信號聲明 ------------------------//
reg [1:0] current_state, next_state;
// FIFO控制信號
wire fifo_wr_en;
wire fifo_rd_en;
reg [15:0] fifo_din;
wire [15:0] fifo_dout;
// FX2接口信號
reg slrd_n;
reg slwr_n;
reg sloe_n;
reg [1:0] faddr_n;
reg pkt_end_n;
//------------------------ 接口分配 ------------------------//
assign fx2_slwr = slwr_n;
assign fx2_slrd = slrd_n;
assign fx2_sloe = sloe_n;
assign fx2_faddr = faddr_n;
assign fx2_pkt_end= pkt_end_n;
assign fx2_slcs = 1'b0; // 常使能
assign fx2_clear = 1'b0; // 未使用
// 三態(tài)總線控制
assign fx2_fdata = (slwr_n == 1'b0) ? fifo_dout : 16'hzzzz;
//------------------------ 狀態(tài)機(jī) ------------------------//
always @(posedge fx2_ifclk or negedge reset_n) begin
if(!reset_n) current_state <= LOOPBACK_IDLE;
else current_state <= next_state;
end
always @(*) begin
next_state = current_state;
case(current_state)
LOOPBACK_IDLE: //ep2為空, 上位機(jī)可傳輸數(shù)據(jù)
if(ep2_empty_flag) next_state = LOOPBACK_READ;
LOOPBACK_READ:
if(!ep2_empty_flag) next_state = LOOPBACK_WAIT_ep6_full;
LOOPBACK_WAIT_ep6_full:
if(ep6_full_flag) next_state = LOOPBACK_WRITE;
LOOPBACK_WRITE: begin
if(!ep6_full_flag || fifo_empty)
next_state = LOOPBACK_IDLE;
end
default: next_state = LOOPBACK_IDLE;
endcase
end
//------------------------ 控制信號生成 ------------------------//
always @(*) begin
// 默認(rèn)值
slrd_n = 1'b1;
sloe_n = 1'b1;
slwr_n = 1'b1;
faddr_n = FIFO_ADDR_READ;
pkt_end_n = 1'b1;
case(current_state)
LOOPBACK_READ: begin
faddr_n = FIFO_ADDR_READ;
slrd_n = !ep2_empty_flag; // 有數(shù)據(jù)時(shí)持續(xù)讀取
sloe_n = !ep2_empty_flag;
end
LOOPBACK_WRITE: begin
faddr_n = FIFO_ADDR_WRITE;
slwr_n = !(ep6_full_flag && !fifo_empty);
// 在最后一次寫入后生成包結(jié)束脈沖
pkt_end_n = (slwr_n == 1'b0) ? 1'b0 : 1'b1;
end
endcase
end
//------------------------ FIFO接口 ------------------------//
assign fifo_wr_en = (current_state == LOOPBACK_READ) && !slrd_n;
assign fifo_rd_en = (current_state == LOOPBACK_WRITE) && !slwr_n;
// 數(shù)據(jù)輸入寄存器
always @(posedge fx2_ifclk) begin
if(fifo_wr_en)
fifo_din <= fx2_fdata;
end
FiFo #(
.Depth(512),
.Width(16)
) u_fifo (
.fifo_clk (fx2_ifclk),
.rst_n (reset_n),
.write_busy (1'b0), // 外部無寫阻塞
.read_busy (1'b0), // 外部無讀阻塞
.fifo_flush (1'b0), // 禁用自動(dòng)flush
.din (fifo_din),
.fifo_full (fifo_full),
.fifo_empty (fifo_empty),
.dout (fifo_dout)
);
wire clk_96m;//生成96M時(shí)鐘用于ILA采樣
pll pll_inst(
.clk_out1(clk_96m),
.clk_in1(clk)
);
//------------------------ 調(diào)試模塊注釋 ------------------------//
ila_0 ila_0_inst(
.clk(clk_96m), // input wire clk
.probe0(fx2_fdata), // input wire [15:0] probe0
.probe1(fx2_faddr), // input wire [1:0] probe1
.probe2(ep2_empty_flag), // input wire [0:0] probe2
.probe3(ep6_full_flag), // input wire [0:0] probe3
.probe4(fx2_sloe), // input wire [0:0] probe4
.probe5(fx2_slwr), // input wire [0:0] probe5
.probe6(fx2_slrd), // input wire [0:0] probe6
.probe7(fifo_empty), // input wire [0:0] probe7
.probe8(fifo_full), // input wire [0:0] probe8
.probe9(fifo_flush) // input wire [0:0] probe9
);
endmodule
-
FPGA
+關(guān)注
關(guān)注
1663文章
22491瀏覽量
638888 -
usb
+關(guān)注
關(guān)注
60文章
8472瀏覽量
285727 -
數(shù)據(jù)傳輸
+關(guān)注
關(guān)注
9文章
2222瀏覽量
67709
原文標(biāo)題:FPGA之Usb數(shù)據(jù)傳輸
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設(shè)計(jì)論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
基于FPGA和USB數(shù)據(jù)傳輸電路的設(shè)計(jì)
基于FPGA的高速LVDS數(shù)據(jù)傳輸
求助 ,關(guān)于STM32的USB數(shù)據(jù)傳輸問題
基于FPGA+USB3.0接口的高速數(shù)據(jù)傳輸系統(tǒng)設(shè)計(jì)
FPGA spartan 3系列,數(shù)據(jù)傳輸接口有可以匹配的藍(lán)牙模塊么?
如何使用FPGA器件和USB通訊實(shí)現(xiàn)高速數(shù)據(jù)傳輸顯示系統(tǒng)的設(shè)計(jì)
基于USB接口的無線數(shù)據(jù)傳輸系統(tǒng)設(shè)計(jì)
基于FPGA和USB的高速數(shù)據(jù)傳輸、記錄及顯示系統(tǒng)
基于DSP的USB數(shù)據(jù)傳輸系統(tǒng)設(shè)計(jì)
基于USB2.0的紅外數(shù)據(jù)傳輸系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)
數(shù)據(jù)傳輸速率是什么意思
USB2.0+FPGA實(shí)現(xiàn)多路數(shù)據(jù)傳輸系統(tǒng)
如何使用FPGA器件和USB通訊實(shí)現(xiàn)高速數(shù)據(jù)傳輸顯示系統(tǒng)的設(shè)計(jì)
基于FPGA的USB數(shù)據(jù)傳輸
評論