文章目錄
0、前言
1、目標
2、圖片的預處理
3、SD NAND的預處理
4、FPGA實現
4.1、詳細設計
4.2、仿真
4.3、實驗結果
·前言
在上一篇文章《基于FPGA的SD卡的數據讀寫實現(SD NAND FLASH)》中,我們了解到了SD NAND Flash的相關知識,并在FPGA平臺上實現了對SD NAND的讀寫測試。SD NAND的讀寫測試可能會有點簡單和枯燥,所以本文我們來搞點有樂趣性的----將存儲在SD NAND內的兩張圖片通過FPGA讀取,并通過VGA的方式在顯示器上輪回顯示。
1、目標
使用 SD NAND數據讀寫控制器讀取事先存儲在 SD NAND的圖片數據,將讀取的圖片數據通過SDRAM 數據讀寫控制器暫存在 SDRAM 芯片中,通過 VGA 顯示器將暫存在 SDRAM 的圖片顯示出來。 SD 卡內存儲兩張圖片,其交替顯示在 VGA 顯示器上,分辨率為 640*480。
SD NAND在SD2.0版本協議下,SPI模式的理論最大傳輸速率為50Mbps,加上命令號以及等待返回響應信號的時間,實際上的傳輸速率還會下降。對于采用分辨率為640*480@60Hz 的顯示器來說,一幅圖像的數據量達到640*480*16bit = 4915200bit = 4800Kbit(1Kbit=1024bit), 每秒鐘刷新60次,那么每秒鐘需要傳輸的數據量達到4800Kbit*60 = 288000Kbit =281.25Mbit (1Mbit=1024Kbit)。由此可以看出,SD卡的讀寫速度完全跟不上VGA的數據發送速度,因此必須先緩存一幅圖像到內部或外部存儲器,再通過VGA接口顯示。FPGA的片內存儲資源較少,對于緩存如此大量的數據,只能使用SDRAM或DDR3緩存數據。
2、圖片的預處理
首先選取要顯示的圖片兩張,使用 Window 系統自帶的畫圖工具對圖片進行處理,將圖片處理為分辨率 640*480。
VGA的顯示格式為16位RGB565格式,為了使SD NAND讀出的數據可以直接在VGA上顯示,需要將圖片通過 “ IMG2LCD ” 上位機軟件轉成16位的RGB565格式的bin文件,再將bin文件導入SD NAND中。
使用 “ IMG2LCD ” 上位機軟件打開兩張圖片,按如下設置相關參數,然后點擊保存,就生成了兩個圖片的二進制文件(像素值)。
3、SD NAND的預處理
SD NAND在經過多次存放數據與刪除數據之后,存入的文件有可能不是按照連續的扇區地址存儲的,為了避免圖片顯示錯誤,我們將bin文件導入SD NAND之前,需要對SD NAND進行一個格式化處理。
首先得找個讀卡器,再把所用到的SD NAND開發板插到讀卡器上邊,通過USB接口與PC建立鏈接。
本次實驗我依然選用的是深圳雷龍公司的一款SD NAND產品----CSNP32GCR01-AOW。 可以看到這款SD NAND開發板設計得很巧妙,把對外接口設計成了通用的micro接口,兼容性非常強,不管是插讀卡器還是直接插FPGA開發板,都是即插即用,十分方便。
接著說回來對SD NAND的初始化處理。插上讀卡器后,選擇對應的磁盤,點擊“格式化”,并點擊“開始”
格式化完成后,將前面生成的兩張圖片對應的bin文件存入對應的SD NAND磁盤中:
SD NAND內部的存儲資源是以扇區的形式進行劃分的,為了將圖片的bin數據從SD NAND中讀取出來,我們需要找到圖片存儲對應的扇區地址。扇區地址可以用“WinHex 軟件”來查看。
以管理員身份運行軟件 WinHex 軟件,點擊“工具 ”,然后點擊“打開磁盤”:
雙擊打開對應的SD NAND,記錄下兩個 bin文件的第一扇區地址:
此時查詢到的扇區地址就是bin文件存放的起始扇區地址,我們只需要按照這個起始扇區地址,按順序讀出SD NAND中的數據即可,直到讀完一張圖片中的所有數據。SD NAND中一個扇區存放512個字節,也就是256個16位數據,對于分辨率為640*480的圖片來說,共需要讀出1200(640*480/256)個扇區數據。
4、FPGA實現
先說下總體思路:
· SD NAND中存有兩幅圖片,一副為雷龍公司的官網截圖,另一幅則是本博客的頭像
· FPGA從SD NAND中讀取這兩幅圖片的像素信息,并緩存到SDRAM中
· 將SDRAM中的數據(兩幅圖片的像素信息)通過VGA接口顯示在顯示器上
根據這個思路,可以對應的畫對應的系統框圖:
FPGA頂層模塊例化了以下五個模塊:PLL時鐘模塊、SD NAND讀取圖片控制模塊、SD NAND控制器模塊、SDRAM控制器模塊和VGA驅動模塊。
4.1、詳細設計
(1) 頂層模塊
頂層模塊:頂層模塊主要完成對其余各模塊的例化,實現各模塊之間的數據交互。需要注意的是,系統初始化完成是在SD NAND以及SDRAM都初始化完成后才開始拉高的,該信號控制著SD NAND讀取圖片控制模塊的復位信號,因此SD NAND讀取圖片控制模塊是在系統初始化完成后才工作的,防止因SD NAND或者SDRAM初始化未完成導致數據錯誤。
此部分代碼如下:
module top_sd_photo_vga(
input sys_clk , //系統時鐘
input sys_rst_n , //系統復位,低電平有效
//SD NAND接口
input sd_miso , //SD NANDSPI串行輸入數據信號
output sd_clk , //SD NANDSPI時鐘信號
output sd_cs , //SD NANDSPI片選信號
output sd_mosi , //SD NANDSPI串行輸出數據信號
//SDRAM接口
output sdram_clk , //SDRAM 時鐘
output sdram_cke , //SDRAM 時鐘有效
output sdram_cs_n , //SDRAM 片選
output sdram_ras_n , //SDRAM 行有效
output sdram_cas_n , //SDRAM 列有效
output sdram_we_n , //SDRAM 寫有效
output [1:0] sdram_ba , //SDRAM Bank地址
output [1:0] sdram_dqm , //SDRAM 數據掩碼
output [12:0] sdram_addr , //SDRAM 地址
inout [15:0] sdram_data , //SDRAM 數據
//VGA接口
output vga_hs , //行同步信號
output vga_vs , //場同步信號
output [15:0] vga_rgb //紅綠藍三原色輸出
);
//parameter define
parameter PHOTO_H_PIXEL = 24'd640 ; //設置SDRAM緩存大小
parameter PHOTO_V_PIXEL = 24'd480 ; //設置SDRAM緩存大小
//wire define
wire clk_100m ; //100mhz時鐘,SDRAM操作時鐘
wire clk_100m_shift ; //100mhz時鐘,SDRAM相位偏移時鐘
wire clk_50m ;
wire clk_50m_180deg ;
wire clk_25m ;
wire rst_n ;
wire locked ;
wire sys_init_done ; //系統初始化完成
wire sd_rd_start_en ; //開始寫SD NAND數據信號
wire [31:0] sd_rd_sec_addr ; //讀數據扇區地址
wire sd_rd_busy ; //讀忙信號
wire sd_rd_val_en ; //數據讀取有效使能信號
wire [15:0] sd_rd_val_data ; //讀數據
wire sd_init_done ; //SD NAND初始化完成信號
wire wr_en ; //sdram_ctrl模塊寫使能
wire [15:0] wr_data ; //sdram_ctrl模塊寫數據
wire rd_en ; //sdram_ctrl模塊讀使能
wire [15:0] rd_data ; //sdram_ctrl模塊讀數據
wire sdram_init_done ; //SDRAM初始化完成
//*****************************************************
//** main code
//*****************************************************
assign rst_n = sys_rst_n & locked;
assign sys_init_done = sd_init_done & sdram_init_done; //SD NAND和SDRAM都初始化完成
assign wr_en = sd_rd_val_en;
assign wr_data = sd_rd_val_data;
//鎖相環
pll_clk u_pll_clk(
.areset (1'b0 ),
.inclk0 (sys_clk ),
.c0 (clk_100m ),
.c1 (clk_100m_shift ),
.c2 (clk_50m ),
.c3 (clk_50m_180deg ),
.c4 (clk_25m ),
.locked (locked )
);
//讀取SD NAND圖片
sd_read_photo u_sd_read_photo(
.clk (clk_50m),
//系統初始化完成之后,再開始從SD NAND中讀取圖片
.rst_n (rst_n & sys_init_done),
.rd_busy (sd_rd_busy),
.rd_start_en (sd_rd_start_en),
.rd_sec_addr (sd_rd_sec_addr)
);
//SD NAND頂層控制模塊
sd_ctrl_top u_sd_ctrl_top(
.clk_ref (clk_50m),
.clk_ref_180deg (clk_50m_180deg),
.rst_n (rst_n),
//SD NAND接口
.sd_miso (sd_miso),
.sd_clk (sd_clk),
.sd_cs (sd_cs),
.sd_mosi (sd_mosi),
//用戶寫SD NAND接口
.wr_start_en (1'b0), //不需要寫入數據,寫入接口賦值為0
.wr_sec_addr (32'b0),
.wr_data (16'b0),
.wr_busy (),
.wr_req (),
//用戶讀SD NAND接口
.rd_start_en (sd_rd_start_en),
.rd_sec_addr (sd_rd_sec_addr),
.rd_busy (sd_rd_busy),
.rd_val_en (sd_rd_val_en),
.rd_val_data (sd_rd_val_data),
.sd_init_done (sd_init_done)
);
//SDRAM 控制器頂層模塊,封裝成FIFO接口
//SDRAM 控制器地址組成: {bank_addr[1:0],row_addr[12:0],col_addr[8:0]}
sdram_top u_sdram_top(
.ref_clk (clk_100m), //sdram 控制器參考時鐘
.out_clk (clk_100m_shift), //用于輸出的相位偏移時鐘
.rst_n (rst_n), //系統復位
//用戶寫端口
.wr_clk (clk_50m), //寫端口FIFO: 寫時鐘
.wr_en (wr_en), //寫端口FIFO: 寫使能
.wr_data (wr_data), //寫端口FIFO: 寫數據
.wr_min_addr (24'd0), //寫SDRAM的起始地址
.wr_max_addr (PHOTO_H_PIXEL*PHOTO_V_PIXEL),//寫SDRAM的結束地址
.wr_len (10'd512), //寫SDRAM時的數據突發長度
.wr_load (~rst_n), //寫端口復位: 復位寫地址,清空寫FIFO
//用戶讀端口
.rd_clk (clk_25m), //讀端口FIFO: 讀時鐘
.rd_en (rd_en), //讀端口FIFO: 讀使能
.rd_data (rd_data), //讀端口FIFO: 讀數據
.rd_min_addr (24'd0), //讀SDRAM的起始地址
.rd_max_addr (PHOTO_H_PIXEL*PHOTO_V_PIXEL),//讀SDRAM的結束地址
.rd_len (10'd512), //從SDRAM中讀數據時的突發長度
.rd_load (~rst_n), //讀端口復位: 復位讀地址,清空讀FIFO
//用戶控制端口
.sdram_read_valid (1'b1), //SDRAM 讀使能
.sdram_pingpang_en (1'b0), //SDRAM 乒乓操作使能
.sdram_init_done (sdram_init_done), //SDRAM 初始化完成標志
//SDRAM 芯片接口
.sdram_clk (sdram_clk), //SDRAM 芯片時鐘
.sdram_cke (sdram_cke), //SDRAM 時鐘有效
.sdram_cs_n (sdram_cs_n), //SDRAM 片選
.sdram_ras_n (sdram_ras_n), //SDRAM 行有效
.sdram_cas_n (sdram_cas_n), //SDRAM 列有效
.sdram_we_n (sdram_we_n), //SDRAM 寫有效
.sdram_ba (sdram_ba), //SDRAM Bank地址
.sdram_addr (sdram_addr), //SDRAM 行/列地址
.sdram_data (sdram_data), //SDRAM 數據
.sdram_dqm (sdram_dqm) //SDRAM 數據掩碼
);
//VGA驅動模塊
vga_driver u_vga_driver(
.vga_clk (clk_25m),
.sys_rst_n (rst_n),
.vga_hs (vga_hs),
.vga_vs (vga_vs),
.vga_rgb (vga_rgb),
.pixel_data (rd_data),
.data_req (rd_en), //請求像素點顏色數據輸入
.pixel_xpos (),
.pixel_ypos ()
);
endmodule
(2) PLL時鐘模塊
PLL時鐘模塊:PLL時鐘模塊通過調用鎖相環(PLL)IP核實現,總共輸出五個時鐘,頻率分別為100Mhz、100Mhz(相位偏移-180度)、50Mhz、50Mhz(相位偏移180度)和25Mhz。 兩個100Mhz的時鐘用于為SDRAM控制器模塊提供驅動時鐘;兩個50Mhz的時鐘用于為SD NAND控制器模塊提供驅動時鐘;25Mhz用于為VGA驅動模塊提供驅動時鐘。
(3) SD NAND讀取圖片控制模塊
SD NAND讀取圖片控制模塊:SD NAND讀取圖片控制模塊通過控制SD NAND控制器的讀接口,從SD NAND中讀取圖像數據,并在讀完一張圖片后延時一段時間,再去讀取另一張圖片數據,實現兩張圖片的循環切換讀取。
此部分代碼:
module sd_read_photo(
input clk , //時鐘信號
input rst_n , //復位信號,低電平有效
input rd_busy , //SD NAND讀忙信號
output reg rd_start_en , //開始寫SD NAND數據信號
output reg [31:0] rd_sec_addr //讀數據扇區地址
);
//parameter define
parameter PHOTO_SECCTION_ADDR0 = 32'd16640; //第一張圖片扇區起始地址
parameter PHOTO_SECTION_ADDR1 = 32'd17856; //第二張圖片扇區起始地址
//640*480/256 = 1200
parameter RD_SECTION_NUM = 11'd1200 ; //單張圖片總共讀出的次數
//reg define
reg [1:0] rd_flow_cnt ; //讀數據流程控制計數器
reg [10:0] rd_sec_cnt ; //讀扇區次數計數器
reg rd_addr_sw ; //讀兩張圖片切換
reg [25:0] delay_cnt ; //延時切換圖片計數器
reg rd_busy_d0 ; //讀忙信號打拍,用來采下降沿
reg rd_busy_d1 ;
//wire define
wire neg_rd_busy ; //SD NAND讀忙信號下降沿
//*****************************************************
//** main code
//*****************************************************
assign neg_rd_busy = rd_busy_d1 & (~rd_busy_d0);
//對rd_busy信號進行延時打拍,用于采rd_busy信號的下降沿
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) begin
rd_busy_d0 <= 1'b0;
rd_busy_d1 <= 1'b0;
end
else begin
rd_busy_d0 <= rd_busy;
rd_busy_d1 <= rd_busy_d0;
end
end
//循環讀取SD NAND中的兩張圖片(讀完之后延時1s再讀下一個)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
rd_flow_cnt <= 2'd0;
rd_addr_sw <= 1'b0;
rd_sec_cnt <= 11'd0;
rd_start_en <= 1'b0;
rd_sec_addr <= 32'd0;
end
else begin
rd_start_en <= 1'b0;
case(rd_flow_cnt)
2'd0 : begin
//開始讀取SD NAND數據
rd_flow_cnt <= rd_flow_cnt + 2'd1;
rd_start_en <= 1'b1;
rd_addr_sw <= ~rd_addr_sw;? ? ? ? ? ? ? ? ? ? ?//讀數據地址切換
if(rd_addr_sw == 1'b0)
rd_sec_addr <= PHOTO_SECCTION_ADDR0;
else
rd_sec_addr <= PHOTO_SECTION_ADDR1;? ??
end
2'd1 : begin
//讀忙信號的下降沿代表讀完一個扇區,開始讀取下一扇區地址數據
if(neg_rd_busy) begin
rd_sec_cnt <= rd_sec_cnt + 11'd1;
rd_sec_addr <= rd_sec_addr + 32'd1;
//單張圖片讀完
if(rd_sec_cnt == RD_SECTION_NUM - 11'b1) begin
rd_sec_cnt <= 11'd0;
rd_flow_cnt <= rd_flow_cnt + 2'd1;
end
else
rd_start_en <= 1'b1;? ? ? ? ? ? ? ? ? ?
end
end
2'd2 : begin
delay_cnt <= delay_cnt + 26'd1;? ? ? ? ? ? ? ? //讀取完成后延時1秒
if(delay_cnt == 26'd50_000_000 - 26'd1) begin //50_000_000*20ns = 1s
delay_cnt <= 26'd0;
rd_flow_cnt <= 2'd0;
end
end
default : ;
endcase
end
end
endmodule
(4)SD NAND控制器模塊
SD NAND控制器模塊:SD NAND控制器模塊負責驅動SD NAND,該模塊將SD NAND的讀寫操作封裝成方便用戶使用的接口。關于SD NAND讀寫控制器模塊在上一篇文章中已經詳細說明了,可參考: 基于FPGA的SD卡的數據讀寫實現(SD NAND FLASH)
(5)SDRAM讀寫控制模塊
SDRAM讀寫控制模塊:SDRAM讀寫控制器模塊負責驅動SDRAM存儲器,緩存圖像數據。該模塊將SDRAM復雜的讀寫操作封裝成類似FIFO的用戶接口, 非常方便用戶的使用。關于此部分,有詳盡的系列文章供參考:相信我,SDRAM真的不難----匯總篇
(6)VGA驅動模塊
VGA驅動模塊根據VGA時序參數輸出行、場同步信號;同時它還要輸出數據請求信號用于讀取SDRAM中的圖片數據,并將圖片通過VGA接口在顯示器上顯示。關于此部分,有詳盡的文章供參考:如何用VGA接口乳法?
4.2、仿真
一般的測試中,我們都需要先進行仿真來觀察時序等測試行為。此次實驗由于找不到好的SD NAND的Verilog模型,所以仿真測試略。
4.3、實驗結果
編譯工程,把程序下載到FPGA開發板,通過VGA接口連接VGA線到顯示器,如下:
接著觀察顯示器是否會交替顯示我們事先保存的兩幅圖片。實驗現象果然與預期一致。
好啦,本次實驗就做完啦。
如果屏幕前的你也有存儲產品方面的需求的話,你都可以試試雷龍公司的SD NAND產品哦。
這是一家專業做存儲產品的公司,NAND Flash是其主要產品。 該公司專注NAND Flash設計研發13年,在這一塊可以說是相當專業。如果你對NAND Flash仍有疑惑的問題,或者你想在你的設計中使用NAND Flash產品,都可以直接聯系:深圳市雷龍發展有限公司
術業有專攻,聞道有先后,專業的事就交給專業的人處理。如果你有這方面的設計需求都可以直接找他們要免費樣品哦。
————————————————
【本文轉載自CSDN,作者:孤獨的單刀】
-
SD卡
+關注
關注
2文章
559瀏覽量
63798 -
TF卡
+關注
關注
2文章
77瀏覽量
12136
發布評論請先 登錄
相關推薦
評論