Abstract
在上一篇blog,我們學會了將wav檔放在SD卡上,實做出一個SD卡wav player,第一次體會出軟硬體設計的威力。由於FAT16格式的讀取,必須牽涉到軟體的動作,所以必須引入Nios II與Avalon Bus,不能再靠純硬體的方式設計。這次我們將圖片放在SD卡上,在DE2-70實做出一個簡易的數位相框。
Introduction使用環境:Quartus II 8.1 + Nios II EDS 8.1 + DE2-70 (Cyclone II EP2C70F896C6N) + TRDB-LTM
這4篇原本是設計在一起的lab,適合初學者從0開始慢慢熟悉Quartus II、SOPC Builder、Nios II EDS、Avalon Bus Slave、Avalon Bus Master。
面臨的挑戰要在DE2-70設計一個數位相框,面臨了幾個挑戰:
1.因為圖片是放在SD卡上,用的是FAT16格式,無論開檔、讀檔都必須透過軟體,關於SD卡使用與FAT16的讀取,在Lab 3我們已經學會,所以已經解決。
2.Altera並沒有提供LTM (touch panel) controller,所以我們必須依照Homework 2的model自己開發一個LTM Controller,這也是本次Lab的重點。
『在DE2-70 CD的範例中,是否有接近的範例可以參考而加以修改呢?』
在DE2_70_D5M_LTM範例中,提供了一個相近的範例,不過他的input是500萬像素cmos,而不是SD卡。
SDRAM之前屬於CMOS部分,與本Lab無關,可以不用理他,SDRAM為frame buffer,影像就是存在這裡。LTM Controller與Data Request就是LCD Timing Generator部分,這部分可以保留下來繼續用。
由於讀取SD卡與FAT16需要軟體配合,勢必加上Nios II CPU與Avalon Bus,此時這裡的Multi-Port SDRAM Controller與LTM Controller就很尷尬了,因為他是個純硬體的Controller,無法掛在Avalon Bus上,所以必須將這2個controller換掉。
Altera有提供SDRAM Controller可用,在Lab1 ~ Lab3我們都使用過Altera SOPC版的SDRAM Controller,但Altera並沒有提供SOPC版的LTM Controller,這就得靠我們自己寫了。最後架構如下圖所示:
系統架構圖
Master Interface
Slave Interface已經在Lab 2 七段顯示器Controller練習過,主要提供C語言透過Nios II對controller設定register。LTM Controller雖也提供slave interface,
但主要的影像傳輸使用的是Master Interface。
為什麼要使用Master Interface呢?由於影像放在SDRAM,當成LTM的frame buffer,LTM必須以33MHz不斷對SDRAM要資料,對LTM做掃描顯示,在Lab 2我曾經說過,master與slave的差別在於Master能夠自己發起傳輸,Slave則必須由Master控制而被動的發起傳輸,若影像傳輸使用Slave Interface,則Nios II CPU必須不斷的介入,命令資料從SDRAM搬到LTM,如此Nios II CPU將非常忙碌,所以比較理想的方式是LTM Controller支援Master Interface,這樣LTM Controller就能主動地讀取SDRAM,不需Nios II CPU介入。
硬體部分Step 1:
使用內的DE2_70_LTM_NIOS project 將DE2_70_LTM_NIOS複製到c:\DE2-70下
Step 2:使用Lab4_files.zip內的LTM_Controller
將LTM_Controller複製到c:\DE2-70\DE2_70_LTM_NIOS\ip目錄下
Step 3:開發LTM_Controller.v
57行,請補上FIFODEPTH參數,這是設定FIFO的大小
59行,請補上FIFOFULLVALUE參數,這是設定當FIFO還剩下多少時,就認為FIFO已經滿了。
109行,請補上fifo_full的條件。
最後完整程式如下所示:
LTM_Controller.v / Verlilog 1 /* 2 (C) OOMusou 2008 http://oomusou.cnblogs.com 3 4 Filename : LTM_Controller.v 5 Compiler : Quartus II 8.1 6 Description : LTM Controller for Avalon Bus 7 Release : 12/18/2008 1.0 8 */ 9 10 `default_nettype none 11 12 module LTM_Controller ( 13 // Avalon clock interface siganals 14 input csi_clockreset_clk, // avalon-mm clk 15 input csi_clockreset_reset_n, // avalon-mm reset_n 16 // Signals for Avalon-MM master port 17 output [DATAWIDTH - 1 : 0 ] avm_m1_address, // avalon-mm master address 18 output [BYTEENABLEWIDTH - 1 : 0 ] avm_m1_byteenable, // avalon-mm master byteenable 19 output avm_m1_read, // avalon-mm master read 20 input avm_m1_readdatavalid, // avalon-mm master readdatavalid 21 input [DATAWIDTH - 1 : 0 ] avm_m1_readdata, // avalon-mm master readdata 22 input avm_m1_waitrequest, // avalon-mm master waitrequest 23 // Signals for Avalon-MM slave port 24 input [ADDRWIDTH - 1 : 0 ] avs_s1_address, // avalon-mm slave address 25 input [BYTEENABLEWIDTH - 1 : 0 ] avs_s1_byteenable, // avalon-mm slave byteenable 26 input avs_s1_write, // avalon-mm slave write 27 input [DATAWIDTH - 1 : 0 ] avs_s1_writedata, // avalon-mm slave writedata 28 input avs_s1_read, // avalon-mm slave read 29 output [DATAWIDTH - 1 : 0 ] avs_s1_readdata, // avalon-mm slave readdata 30 // LTM couduit input 31 input coe_ltm_export_iCLK_50, // 50MHz 32 input coe_ltm_export_iRST0, // reset delay 0 33 input coe_ltm_export_iRST2, // reset delay 2 34 // LTM couduit output 35 output coe_ltm_export_oLTM_CLK, // ltm clk 36 output [LTM_DATAWIDTH - 1 : 0 ] coe_ltm_export_oR, // ltm R 37 output [LTM_DATAWIDTH - 1 : 0 ] coe_ltm_export_oG, // ltm G 38 output [LTM_DATAWIDTH - 1 : 0 ] coe_ltm_export_oB, // ltm B 39 output coe_ltm_export_oHD, // ltm h.sync 40 output coe_ltm_export_oVD, // ltm v.sync 41 output coe_ltm_export_oDEN, // ltm data enable 42 output coe_ltm_export_oSCLK, // ltm I2S clk 43 inout coe_ltm_export_ioSDAT, // ltm I2S data 44 output coe_ltm_export_oSCEN, // ltm I2S clk enable 45 output coe_ltm_export_oFIFO_FULL, // for debug use only (ltm fifo empty) 46 output coe_ltm_export_oFIFO_EMPTY // for debug use only (ltm fifo full) 47 ); 48 49 // LTM parameter 50 parameter LTM_DATAWIDTH = 8 ; // ltm data width 51 52 // Avalon-MM parameter 53 parameter DATAWIDTH = 32 ; // width of databus 54 parameter BYTEENABLEWIDTH = 4 ; // width of byteenable 55 parameter ADDRESSBASE = 32 ' h0080_0000; // SDRAM frame buffer start address 56 parameter LENGTH = 800 * 480 ; // LTM width : 800, height = 480 57 parameter FIFODEPTH = 8192 ; // FIFO depth : number of words 58 parameter FIFODEPTHLOG2 = 13 ; // FIFO width of depth 59 parameter FIFOFULLVALUE = 195 ; // FIFO full value 60 parameter ADDRWIDTH = 3 ; // avalon-mm slave address width 61 62 localparam FIFOALMOSTFULL = FIFODEPTH - FIFOFULLVALUE; // FIFO almost full value 63 64 // LTM wire 65 wire ltm_nclk; // ltm clk 66 wire read; // sdram read request 67 68 // fifo wire 69 wire fifo_wrclk; // fifo write clk 70 wire fifo_aclr; // fifo asynchronous clk 71 wire fifo_wrreq; // fifo write request 72 wire [DATAWIDTH - 1 : 0 ] fifo_wrdata; // fifo write data 73 wire fifo_rdclk; // fifo read clk 74 wire fifo_rereq; // fifo read request 75 wire [DATAWIDTH - 1 : 0 ] fifo_rddata; // fifo read data 76 wire fifo_empty; // fifo empty 77 wire fifo_full; // fifo full 78 wire [FIFODEPTHLOG2 - 1 : 0 ] fifo_used; // fifo data used 79 80 // internal logic 81 reg [DATAWIDTH - 1 : 0 ] address; // address of sdram 82 wire [DATAWIDTH - 1 : 0 ] end_address; // end address of sdram 83 wire is_address_sload; // is sload of address? 84 wire is_increment_address; // is increment address of sdram? 85 reg [FIFODEPTHLOG2 - 1 : 0 ] reads_pending; // pending reads 86 87 // master output 88 assign avm_m1_address = address; // avalon-mm master address 89 assign avm_m1_byteenable = 4 ' b1111; // avalon-mm master byteenable (32 bit) 90 assign avm_m1_read = ( ! fifo_full); // avalon-mm master read 91 92 // export output 93 assign coe_ltm_export_oLTM_CLK = ltm_nclk; // ltm clk 94 assign coe_ltm_export_oFIFO_FULL = fifo_full; // for debug use only (ltm fifo full) 95 assign coe_ltm_export_oFIFO_EMPTY = fifo_empty; // for debug use only (ltm fifo empty) 96 97 // fifo input 98 assign fifo_wrclk = csi_clockreset_clk; // fifo write clk 99 assign fifo_aclr = ~ csi_clockreset_reset_n; // fifo asynchronous clear 100 assign fifo_wrreq = avm_m1_readdatavalid; // fifo write request 101 assign fifo_wrdata = avm_m1_readdata; // fifo write data 102 assign fifo_rdclk = ltm_nclk; // fifo read clk 103 assign fifo_rereq = read; // fifo read request 104 105 // internal logic 106 assign end_address = ADDRESSBASE + LENGTH * BYTEENABLEWIDTH; // end address of sdram 107 assign is_address_sload = (address == end_address); // is sload of address? 108 assign is_increment_address = ( ! fifo_full) && ( ! avm_m1_waitrequest); // is increment address of sdram? 109 assign fifo_full = (fifo_used + reads_pending) > (FIFOALMOSTFULL); // is fifo full 110 111 // address register 112 always @( posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin 113 if ( ! csi_clockreset_reset_n) 114 address <= 0 ; 115 else begin 116 if (address == 0 ) // initailize 117 address <= ADDRESSBASE; 118 else if (is_address_sload) // is sload of address? 119 address <= ADDRESSBASE; 120 else if (is_increment_address) // is increment address of sdram? 121 address <= address + BYTEENABLEWIDTH; 122 end 123 end 124 125 // reads_pending register 126 always @( posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin 127 if ( ! csi_clockreset_reset_n) 128 reads_pending <= 0 ; 129 else begin 130 if (is_increment_address) begin // is increment address of sdram? 131 if ( ! avm_m1_readdatavalid) 132 reads_pending <= reads_pending + 1 ' b1; 133 end 134 else begin 135 if (avm_m1_readdatavalid) // read data valid 136 reads_pending <= reads_pending - 1 ' b1; 137 end 138 end 139 end 140 141 // ltm clk 142 ltm_pll ltm_pll0 ( 143 .inclk0(coe_ltm_export_iCLK_50), // 50MHz 144 .c0(ltm_nclk) // 33MHz 145 ); 146 147 // I2S ltm config 148 lcd_3wire_config wire0 ( 149 // Host Side 150 .iCLK(coe_ltm_export_iCLK_50), // 50MHz 151 .iRST_n(coe_ltm_export_iRST0), // reset delay 0 152 // 3 wire Side 153 .o3WIRE_SCLK(coe_ltm_export_oSCLK), // I2S clk 154 .io3WIRE_SDAT(coe_ltm_export_ioSDAT), // I2S data 155 .o3WIRE_SCEN(coe_ltm_export_oSCEN) // I2s clk enable 156 ); 157 158 // tcon controller 159 touch_tcon tcon0 ( 160 .iCLK(ltm_nclk), // 33MHz 161 .iRST_n(coe_ltm_export_iRST2), // reset delay 2 162 // sdram side 163 .iREAD_DATA1({ 1 ' b0, fifo_rddata[15:11], fifo_rddata[7:0], 2 ' b00}), // G[9:5]B[9:0] 164 .iREAD_DATA2({ 1 ' b0, fifo_rddata[10:8], 2 ' b00, fifo_rddata[ 23 : 16 ], 2 ' b00}), // G[4:0]R[9:0] 165 .oREAD_SDRAM_EN(read), // sdram read request 166 // lcd side 167 .oLCD_R(coe_ltm_export_oR), // ltm R 168 .oLCD_G(coe_ltm_export_oG), // ltm G 169 .oLCD_B(coe_ltm_export_oB), // ltm B 170 .oHD(coe_ltm_export_oHD), // ltm h. sync 171 .oVD(coe_ltm_export_oVD), // ltm v.sync 172 .oDEN(coe_ltm_export_oDEN) // ltm data enable 173 ); 174 175 // ltm fifo 176 dcfifo # ( 177 .lpm_width(DATAWIDTH), // data width of fifo 178 .lpm_numwords(FIFODEPTH), // fifo depth of fifo 179 .lpm_widthu(FIFODEPTHLOG2) // fifo width of depth 180 ) fifo0 ( 181 .wrclk(fifo_wrclk), // fifo write clk 182 .aclr(fifo_aclr), // fifo asynchronous clear 183 .wrreq(fifo_wrreq), // fifo write request 184 .data(fifo_wrdata), // fifo write data 185 .rdclk(fifo_rdclk), // fifo read clk 186 .rdreq(fifo_rereq), // fifo read request 187 .q(fifo_rddata), // fifo read data 188 .rdempty(fifo_empty), // fifo empty 189 .wrusedw(fifo_used) // fifo used data 190 ); 191 192 endmodule LTM Controller使用的是效率較佳的Master Pipelined Read Transfer
(1) 此時Master開始對Avalon Bus做讀取的動作,因此對Avalon Bus發出addr1信號並將read信號拉high。此時剛好碰到Avalon Bus給master的waitrequest為high,表示Avalon Bus正在忙碌中,因此addr1與read必須再多delay 1個clk。
(2) 此時Avalon Bus已經不忙碌,所以Avalon Bus將waitrequest拉low,此時Avalon Bus接受了Master所發出的addr1與read信號。
(3) 此時Avalon Bus不忙碌,所以Avalon Bus的waitrequest為low,此時Avalon Bus接受了下一筆Master所發出的addr2與read信號。在此同時,Avalon Bus發給Master的readdatavalid為high信號,表示Master可從Avalon Bus去讀取addr1的數據data1。
(4) 此時Avalon Bus不忙碌,所以Avalon Bus的waitrequest為low,此時Avalon Bus接受了下一筆Master所發出的addr3與read信號,所以在此時已經有兩筆數據pending在Avalon Bus還未傳輸。
(5) 此時Avalon Bus發給Master的readdatavalid為high,所以Master從Avalon Bus讀取數據 (data2)。
(6) 此時Avalon Bus發給Master的readdatavalid為low,所以Master無法從Avalon Bus讀取數據。在此同時,Master對Avalon Bus發出addr1、read與flush為high信號,告訴Avalon Bus讀取第4筆數據並且放棄讀取pending在Avalon Bus而尚未讀取的數據(data3)
(7) 此時Avalon Bus發給Master的readdatavalid為high,所以Master從Avalon Bus讀取數據(data4),因為data3以前被放棄。
Step 4:開發HAL
目前僅提供一個HAL,就是讓C語言可以指定x, y座標與該pixel的RGB,如此才能將讀取的bmp圖片寫進SDRAM frame buffer。
第9行,請補上offset的計算方式。
最後完整程式如下所示:
ltm.c / C
1 /* 2 (C) OOMusou 2008 http://oomusou.cnblogs.com 3 4 Filename : ltm.c 5 Compiler : Nios II 8.1 6 Description : LTM API for LTM Controller 7 Release : 12/18/2008 1.0 8 */ 9 10 #include " alt_types.h " // alt_u32, alt_u8 11 #include " ltm.h " 12 #include " ltm_regs.h " // register map of ltm controller 13 14 void ltm_write_rgb_to_pixel(alt_u32 base , alt_u16 x, alt_u16 y, alt_u8 r, alt_u8 g, alt_u8 b) { 15 alt_u32 offset; 16 alt_u32 data; 17 18 offset = ((y * LTM_WIDTH) + (x)) * BYTEENABLEWIDTH; 19 20 data = g & 0xff ; 21 data = (data << 8 ) | (r & 0xff ); 22 data = (data << 8 ) | (g & 0xff ); 23 data = (data << 8 ) | (b & 0xff ); 24 25 IOWR_LTM_RGB( base + offset, data); 26 } Step 5:使用SOPC Builder將IP打包
在Lab 2我們已經學會將七段顯示器controller打包的方法,使用相同的方式將LTM controller打包成ip。唯一不同的是,因為LTM Controller的module較多,所以必須加入多個.v檔。注意Top Level Module是LTM_Controller.v
建立成功會在左上角顯示LTM_Controller。
Step 6:加入Pipeline Bridge
在Quartus II 7.1之後,提出了Pipeline Bridge架構,可以將master與slave間的address、write、writedata、read、readdata等信號加上pipeline stage,可以提高整個系統的Fmax,更詳細的解釋請參考:
Step 7:加入LTM Controller
接受原來LTM_Controller.v預設的parameter即可。
由於ltm是透過pipeline_bridge與sdram相連。所以將
ltm的master與pipeline_bridge_ltm的slave相連。
pipeline_bridge_ltm的master與sdram的slave相連。
ltm的slave與CPU的data_master相連。
設定LTM Controller的Bus Arbitration Rule由於LTM的更新速度很快(33MHz),所以LTM Contoller需要不斷的對SDRAM要資料顯示,因此預設的bus佔有率已經無法滿足LTM的正常顯示,必須加以調整。
顯示Arbitration
將Arbitration Rule調85,表示LTM將占據SDRAM 85%的頻寬。
事實上,LTM要能穩定的顯示,有三個重要的參數彼此影響:
1.LTM Controller的Bus Arbitration Rule
2.LTM Controller的FIFO depth
3.LTM Controller的FIFO almost full value
我花了很多時間去tune這三個參數讓LTM的顯示穩定,若3個參數沒調好,可能會造成LTM影像上下shift或者上下左右不斷的輪播..等等問題。
最後Auto-Assign Base Address,解決address overlap的錯誤。
按Generate開始產生SOPC System。
Step 8:Quartus II編譯與Programmer燒入
top module部分我就沒要求同學修改,Quartus II直接編譯即可,不過還是建議同學花時間看看top module是怎麼寫的。
軟體部分Step 9:Import hello_world_0與hello_world_0_syslibRun As Hardware
整個程式的架構與Lab 3的DE2_70_SD_Card_Audio_Player類似,只是Wav Lib改成Bmp Lib,Audio HAL改成LTM HAL。程式難度不高,同學可自行研究。
唯一要同學加上的是140行,使用我們自己寫的ltm_write_rgb_to_pixel()將RGB載入SDRAM。
最後完整程式如下所示:
hello_world.c / C
1 /* 2 (C) OOMusou 2008 http://oomusou.cnblogs.com 3 4 Filename : hello_world.c 5 Compiler : Nios II 8.1 6 Description : main() for photo frame 7 Release : 12/18/2008 1.0 8 */ 9 #include < stdio.h > 10 #include " my_includes.h " 11 #include " my_types.h " 12 #include " FatFileSystem.h " // FAT16 lib 13 #include " SDCardDriver.h " // SDCard HAL 14 #include " bitmap.h " // bmp lib 15 #include " ltm.h " // LTM HAL 16 17 #define WAITING_SEC 5 18 // bmp file list parameter 19 #define MAX_FILE_NUM 128 // maximum file number in file list 20 #define FILENAME_LEN 32 // length of file name 21 22 typedef struct { 23 int filenum; 24 char filename[MAX_FILE_NUM][FILENAME_LEN]; 25 } BmpFileList; 26 27 static BmpFileList bmp_file_list; 28 29 // bmp parameter 30 #define BMP_WIDTH 800 31 #define BMP_HEIGHT 480 32 #define BMP_RGB_OFST 54 33 #define NUM_RGB 3 34 #define OFST_R 2 35 #define OFST_G 1 36 #define OFST_B 0 37 38 // wait sdcard insert into socket 39 void wait_sdcard_insert( void ) { 40 bool bFirstTime2Detect = TRUE; 41 42 while ( ! SD_card_init()) { 43 if (bFirstTime2Detect){ 44 printf( " Please insert SD card.\n " ); 45 bFirstTime2Detect = FALSE; 46 } 47 } 48 49 printf( " Find SD card.\n " ); 50 } 51 52 // build bmp file list 53 int build_bmp_play_list( void ) { 54 int filecnt = 0 ; // number of bmp 55 FAT_BROWSE_HANDLE hFileBrowse; // FAT browse handle 56 FAT_DIRECTORY Directory; // FAT directory 57 FAT_FILE_HANDLE hFile; // FAT file handle 58 alt_u8 header[BMP_RGB_OFST]; 59 char filename[FILENAME_LEN]; 60 61 bmp_file_list.filenum = 0 ; 62 63 // FatFileSystem.h 64 if ( ! Fat_FileBrowseBegin( & hFileBrowse)){ 65 printf( " browse file fail.\n " ); 66 return 0 ; 67 } 68 69 // FatFileSystem.h 70 while (Fat_FileBrowseNext( & hFileBrowse, & Directory)) { 71 // only bmp in file list 72 if (strncmpi(Directory.Extension, " BMP " , 3 )) 73 continue ; 74 75 // compose filename 76 Fat_ComposeFilename( & Directory, filename); 77 78 // fopen() (FatFileSystem.h) 79 if ( ! Fat_FileOpen( & hFile, filename)) { 80 printf( " bmp file open fail.\n " ); 81 continue ; 82 } 83 84 // fread() (FatFileSystem.h) 85 if ( ! Fat_FileRead( & hFile, header, sizeof (header))) { 86 printf( " bmp file read fail.\n " ); 87 continue ; 88 } 89 90 // fclose() (FatFileSystem.h) 91 Fat_FileClose( & hFile); 92 93 // convert to bmp header (bitmap.h) 94 BitmapHeader * bmp_header = get_bmp_header(header); 95 96 // check valid bmp format 97 if ( ! chk_valid_bmp(bmp_header)) { 98 printf( " %s is invalid bmp for DE2-70 LTM.\n " , filename); 99 continue ; 100 } 101 102 // copy filename into file list 103 strcpy(bmp_file_list.filename[filecnt], filename); 104 filecnt ++ ; 105 } // while 106 107 bmp_file_list.filenum = filecnt; 108 109 return filecnt; 110 } 111 112 // play bmp by filename 113 bool play_bmp( char * filename){ 114 FAT_FILE_HANDLE hFile; // FAT file handle 115 116 // fopen() (FatFileSystem.h) 117 if ( ! Fat_FileOpen( & hFile, filename)){ 118 printf( " Fat_FileOpen fail.\n " ); 119 return FALSE; 120 } 121 122 printf( " BMP file name is %s.\n " , filename); 123 124 if ( ! Fat_FileSeek( & hFile, FILE_SEEK_BEGIN, BMP_RGB_OFST)) { 125 printf( " Fat_FileSeek fail.\n " ); 126 return FALSE; 127 } 128 129 // bmp RGB array 130 alt_u8 buff_bmp[BMP_WIDTH * BMP_HEIGHT * NUM_RGB]; 131 132 // fread() (FatFileSystem.h) 133 if ( ! Fat_FileRead( & hFile, buff_bmp, sizeof (buff_bmp))) { 134 printf( " Fat_FileRead fail.\n " ); 135 return FALSE; 136 } 137 138 // move to sdram frame buffer 139 int x, y; 140 for (x = 0 ; x < BMP_WIDTH; x ++ ) { 141 for (y = 0 ; y < BMP_HEIGHT; y ++ ) { 142 // get RGB 143 int r = buff_bmp[(y * BMP_WIDTH + x) * NUM_RGB + OFST_R]; 144 int g = buff_bmp[(y * BMP_WIDTH + x) * NUM_RGB + OFST_G]; 145 int b = buff_bmp[(y * BMP_WIDTH + x) * NUM_RGB + OFST_B]; 146 147 // write rgb to sdram frame buffer (ltm.h) 148 ltm_write_rgb_to_pixel(SDRAM_BASE, x,y, r, g, b); 149 } 150 } 151 152 printf( " Finsh reading FAT16\n " ); 153 // file close 154 Fat_FileClose( & hFile); 155 156 return TRUE; 157 } 158 159 int main() { 160 int play_index = 0 ; 161 alt_u8 filename[FILENAME_LEN]; 162 163 // check SD card 164 wait_sdcard_insert(); 165 166 // Mount SD-CARD 167 if ( ! Fat_Mount(FAT_SD_CARD)) { 168 printf( " SD card mount fail.\n " ); 169 return - 1 ; 170 } 171 172 // build wave list in gWavePlayList 173 if (build_bmp_play_list() == 0 ) { 174 printf( " There is no bmp file in the root directory of SD card.\n " ); 175 return - 1 ; 176 } 177 178 while ( 1 ) { 179 strcpy(filename, bmp_file_list.filename[play_index]); 180 181 if ( ! play_bmp(filename)){ 182 printf( " bmp play fail.\n " ); 183 return - 1 ; 184 } 185 186 play_index ++ ; 187 188 // repeat 189 if (play_index >= bmp_file_list.filenum) 190 play_index = 0 ; 191 192 // waiting 5 sec between bmp 193 printf( " Waiting %d sec...\n " , WAITING_SEC); 194 usleep(WAITING_SEC * 1000 * 1000 ); 195 } 196 197 return 0 ; 198 } 199 提供了3張bmp圖檔,請將這3個圖檔copy到SD卡根目錄,即可開始測試。每張照片中顯示會間格5秒鐘。
希志あいの
七海なな
瑤瑤
若你要使用自己的bmp也可以,但有幾個限制:
1.使用24位元的bmp且不能壓縮。
2.大小為800 * 480,配合LTM的解析度。
Step 10:將軟體與硬體存到Flash
從Lab 1到Lab 3,每次要執行Nios II程式,一定得經過Quartus II燒入sof,並且用Nios II EDS Run As Hardware,才能將elf載入到SDRAM或SRAM執行,對於一個完整的嵌入式產品,不可能每次都要靠PC將軟體與硬體傳入DE2-70。所幸DE2-70提供了兩個Flash,儘管斷電之後,資料仍然存在,如此只要一Power on後,就可以執行Nios II程式,不需要PC的介入。
接下來我們會將硬體sof放到epcs_flash,將軟體elf放到cfi_flash,這樣以後只要電源按鈕按下,就可以執行我們的數位相框,不再需要PC了。
Step 11:使用Nios II EDS的Flash Programmer
Nios II EDSTools -> Flash Programmer
新增一個configuration
若成功會出現以下訊息:
# !/ bin / sh## This file was automatically generated by the Nios II IDE Flash Programmer.## It will be overwritten when the flash programmer options change.#cd C: / DE2 - 70 / DE2_70_LTM_NIOS / software / hello_world_0 / Debug# Creating .flash file for the FPGA configuration " $SOPC_KIT_NIOS2/bin/sof2flash " -- epcs -- input = " C:/DE2-70/DE2_70_LTM_NIOS/DE2_70 .sof " --output= " DE2_70.flash " Info: ******************************************************************* Info: Running Quartus II Convert_programming_fileInfo: Command: quartus_cpf -- no_banner -- convert -- device = EPCS128 -- option = DE2_7 0 .opt C: / DE2 - 70 / DE2_70_LTM_NIOS / DE2_70.sof DE2_70.pofInfo: Quartus II Convert_programming_file was successful. 0 errors, 0 warnings Info: Peak virtual memory: 72 megabytes Info: Processing ended: Wed Dec 24 01 : 00 : 31 2008 Info: Elapsed time: 00 : 00 : 03 Info: Total CPU time (on all processors): 00 : 00 : 03 Info: ******************************************************************* Info: Running Quartus II Convert_programming_fileInfo: Command: quartus_cpf -- no_banner -- convert DE2_70.pof DE2_70.rpdInfo: Quartus II Convert_programming_file was successful. 0 errors, 0 warnings Info: Peak virtual memory: 66 megabytes Info: Processing ended: Wed Dec 24 01 : 00 : 33 2008 Info: Elapsed time: 00 : 00 : 02 Info: Total CPU time (on all processors): 00 : 00 : 02 # Programming flash with the FPGA configuration " $SOPC_KIT_NIOS2/bin/nios2-flash-programmer " -- epcs -- base = 0x09000000 -- sidp = 0x0 90008a8 -- id = 1669606734 -- timestamp = 1230048124 " DE2_70.flash " Using cable " USB-Blaster [USB-0] " , device 1 , instance 0x00 Resetting and pausing target processor: OKReading System ID at address 0x090008A8 : verified : Checksumming existing contents 00000000 : Verifying existing contents 00010000 : Verifying existing contents 00020000 : Verifying existing contents 00030000 : Verifying existing contents 00040000 : Verifying existing contents 00050000 : Verifying existing contents 00060000 : Verifying existing contents 00070000 : Verifying existing contents 00080000 : Verifying existing contents 00090000 : Verifying existing contents 00000000 : Reading existing contents 00010000 : Reading existing contents 00020000 : Reading existing contents 00030000 : Reading existing contents 00040000 : Reading existing contents 00050000 : Reading existing contents 00060000 : Reading existing contents 00070000 : Reading existing contents 00080000 : Reading existing contents 00090000 : Reading existing contents Checksummed / read 640kB in 15 .4s 00000000 ( 0 % ): Erasing 00010000 ( 10 % ): Erasing 00020000 ( 20 % ): Erasing 00030000 ( 30 % ): Erasing 00040000 ( 40 % ): Erasing 00050000 ( 50 % ): Erasing 00060000 ( 60 % ): Erasing 00070000 ( 70 % ): Erasing 00080000 ( 80 % ): Erasing 00090000 ( 90 % ): Erasing Erased 640kB in 5 .9s ( 108 .4kB / s) 00000000 ( 0 % ): Programming 00010000 ( 10 % ): Programming 00020000 ( 20 % ): Programming 00030000 ( 30 % ): Programming 00040000 ( 40 % ): Programming 00050000 ( 50 % ): Programming 00060000 ( 60 % ): Programming 00070000 ( 70 % ): Programming 00080000 ( 80 % ): Programming 00090000 ( 90 % ): Programming Programmed 589KB + 51KB in 13 .5s ( 47 .4KB / s) Did not attempt to verify device contentsLeaving target processor paused# Creating .flash file for the project " $SOPC_KIT_NIOS2/bin/elf2flash " -- base = 0x0a800000 -- end = 0xaffffff -- reset = 0xa800 000 -- input = " hello_world_0.elf " -- output = " cfi_flash.flash " -- boot = " C:/altera/81/ ip / altera / nios2_ip / altera_nios2 / boot_loader_cfi.srec " # Programming flash with the project " $SOPC_KIT_NIOS2/bin/nios2-flash-programmer " -- base = 0x0a800000 -- sidp = 0x090008a8 -- id = 1669606734 -- timestamp = 1230048124 " cfi_flash.flash " Using cable " USB-Blaster [USB-0] " , device 1 , instance 0x00 Resetting and pausing target processor: OKReading System ID at address 0x090008A8 : verified : Checksumming existing contents 00000000 : Verifying existing contents 00002000 : Verifying existing contents 00004000 : Verifying existing contents 00006000 : Verifying existing contents 00008000 : Verifying existing contents 0000A000 : Verifying existing contents 0000C000 : Verifying existing contents 0000E000 : Verifying existing contents 00010000 : Verifying existing contents Checksummed / read 81kB in 1 .9s Erase not required 00000000 ( 0 % ): Programming 00002000 ( 0 % ): Programming 00004000 ( 0 % ): Programming 00006000 ( 0 % ): Programming 00008000 ( 0 % ): Programming 0000A000 ( 0 % ): Programming 0000C000 ( 0 % ): Programming 0000E000 ( 0 % ): Programming 00010000 ( 0 % ): Programming Programmed 81KB in 0 .0s No change to device contentsLeaving target processor paused 重新Power On,就可以看到數位相框自動執行了!!
完整程式碼下載
(一個未完成的半成品,可以根著本文一步一步完成)
(最後完整的結果)
Question(這是我當時給學生的homework,各位有興趣可以自己自做做看)
1.雖然設定每張圖片間隔5秒鐘顯示,為什麼實際執行時,圖片與圖片的間格時間超過5秒鐘呢?
2.目前LTM controller的SDRAM base address使用的是Verilog的parameter,請改用register的方式,讓Nios II的C語言可以透過Slave Interface動態更改SDRAM的base address。
3.目前每一次都要重新從SD卡讀取像片至SDRAM,很花時間,請試著改成只有第一次需從SD卡讀取相片至SDRAM,以後就不必從SD卡讀取。(有很多種方法,請發揮你的創意)。
4.請結合並搭配μC/OS-II,讓DE2-70可以同時當數位像框,並可播放音樂。
Conclusion
很多人想將原本純硬體的設計加上Nios II CPU變成HW/SW Co-Design,最常見的問題就是SDRAM要怎麼讓軟硬體共用,若你還是一直執著於使用SDRAM_Control_4Port那條路,將永遠無法解決,既然打算掛上Nios II CPU,就要引進Avalon Bus概念,我歸納出3點:
1.使用Avalon Bus將整個系統的Nios II CPU與SDRAM切開。
2.使用Altera為Avalon Bus所提供的SDRAM Controller,而不要使用SDRAM_Control_4Port。
3.為你自己的硬體週邊寫Master或Slave Controller。
如此整個系統才會變成SOPC Enable,整體的效能才會好,當然所付出的代價就是你要去了解 ,並且慢慢的調試。
See Also
Reference