【小梅哥FPGA進階教程】串口發送圖片數據到SRAM在TFT屏上顯示


十五、串口發送圖片數據到SRAM在TFT屏上顯示

之前分享過rom存儲圖片數據在TFT屏上顯示,該方法只能顯示小點的圖片,如果想顯示TFT屏幕大小的圖片上述方法rom內存大小不夠。小梅哥給了個方案,利用串口將圖片數據傳給SRAM,傳完后在從SRAM中讀取圖片數據進行顯示。有了梅哥的提示后就開始動工了,首先是設計SRAM的控制程序。

SRAM(靜態隨機訪問存儲器)是一種半導體存儲器。“靜態”一詞表明只要有電源供電,數據就會保存,而不會“動態”改變。

本實驗平台是基於小梅哥出品的芯航線FPGA開發平台,該平台的SRAM芯片采用的是ISSI的IS61LV25616,它是一個256K*16位字長的高速率靜態隨機存取存儲器。

通過查閱手冊得知,除了地址總線和數據總線外,該芯片還包含五個控制信號(手冊上的符號與這個有差別,手冊是符號上一橫線代表低電平有效)。

ce_n(芯片使能或芯片選擇):禁止或使能芯片。

we_n(寫使能):禁止或使能寫操作。

oe_n(輸出使能):禁止或使能輸出。

lb_n(低字節使能):禁止或使能數據總線的低字節。

ub_n(高字節使能):禁止或使能數據總線的高字節。

所有這些信號都是低電平有效,后綴_n用於強調這一特性。功能表如表1所示:信號ce_n用於存儲器擴展,信號we_n和oe_n用於寫操作和讀操作,lb_n和ub_n用於字節配置。

表1 SRAM控制信號的真值表

 

圖片1圖片2

接下來分析SRAM的讀寫時序圖,兩種類型的讀操作時序如圖1(a)和圖1(b)所示

 

圖片3

(a)地址控制的讀周期時序圖(ce_n=0,we_n=1,oe_n=0)

 

圖片4

(b)oe_n控制的讀周期時序圖

圖片5

(c)部分時序參數的介紹

圖1 讀操作的時序圖和部分參數

本實驗數據用的是16位,所以lb_n和ub_n控制位我們一直給低電平即可。關於ce_n控制位在復位后一直給低電平即可。

芯片手冊上關於寫操作時序有四種類型,這里就簡單介紹其中一種,其他的類似,寫操作時序如圖2所示:

 

圖片6

(a)寫操作時序圖

圖片7

(b)部分時序參數的介紹

圖2 讀操作的時序圖和部分參數

根據上面的讀操作和寫操作時序,結合小梅哥的芯航線開發平台,取讀寫周期為20ns,這樣可以直接采用平台已有的50Mhz的時鍾,根據上面的時間限制,在讀操作時,可以在使能讀操作后,采用在時鍾上升沿時改變地址,這樣在下個時鍾上升沿到來時就可以讀取該地址的數據,也就是數據相對與給的地址是有一個時鍾周期的延時。在寫操作時,同樣也是在時鍾的上升沿給地址和待寫入的數據,這樣可以滿足參數的時間要求。

SRAM控制器的設計如下:

1   module sram_ctrl(
2       clk50M,
3       rst_n,
4       address,
5       chipselect_n,
6       read_n,
7       write_n,
8       byteenable_n,
9       writedata,
10      readdata,
11      
12      sram_addr,
13      sram_dq,
14      sram_ce_n, 
15      sram_oe_n, 
16      sram_we_n,
17      sram_lb_n,
18      sram_ub_n
19  );
20
21      input  clk50M;           //系統時鍾,默認50M   
22      input  rst_n;            //異步復位,低電平有效
23      
24      input  [17:0] address;   //數據傳輸地址
25      input  chipselect_n;     //SRAM片選信號,低電平有效
26      input  read_n;           //數據讀控制信號,低電平有效
27      input  write_n;          //數據寫控制信號,低電平有效
28      input  [1:0]byteenable_n;//數據高低字節使能,低電平有效
29      input  [15:0]writedata;  //待寫入RAM的數據
30      output [15:0]readdata;   //讀RAM的數據
31      
32      output [17:0]sram_addr;  //操作RAM數據的地址
33      inout  [15:0]sram_dq;    //RAM的數據端口
34      output sram_ce_n;        //SRAM片選信號,低電平有效
35      output sram_oe_n;        //SRAM讀數據控制信號,低電平有效
36      output sram_we_n;        //SRAM寫數據控制信號,低電平有效
37      output sram_lb_n;        //數據低字節有效
38      output sram_ub_n;        //數據高字節有效
39
40      //signal declaration
41      reg [17:0]addr_reg;
42      reg [15:0]rdata_reg, wdata_reg;
43      reg ce_n_reg, lb_n_reg, ub_n_reg, oe_n_reg, we_n_reg;
44      
45      //body
46      //registers
47      always@(posedge clk50M or negedge rst_n)
48      begin
49          if(!rst_n)
50          begin
51              addr_reg <= 18'd0;
52              rdata_reg <= 16'd0;
53              wdata_reg <= 16'd0;
54              ce_n_reg <= 1'b1;
55              oe_n_reg <= 1'b1;
56              we_n_reg <= 1'b1;
57              lb_n_reg <= 1'b1;
58              ub_n_reg <= 1'b1;           
59          end 
60          else
61          begin
62              addr_reg <= address;
63              rdata_reg <= sram_dq;
64              wdata_reg <= writedata;
65              ce_n_reg <= chipselect_n;
66              oe_n_reg <= read_n;
67              we_n_reg <= write_n;
68              lb_n_reg <= byteenable_n[0];
69              ub_n_reg <= byteenable_n[1];        
70          end
71      end
72      
73      //to fpga interface
74      assign readdata = rdata_reg;
75      
76      //to SRAM
77      assign sram_addr = addr_reg;
78      assign sram_ce_n = ce_n_reg;
79      assign sram_oe_n = oe_n_reg;
80      assign sram_we_n = we_n_reg;
81      assign sram_ub_n = ub_n_reg;
82      assign sram_lb_n = lb_n_reg;
83      //SRAM tristate data bus
84      assign sram_dq = (~we_n_reg)?wdata_reg:16'bz;
85      
86  endmodule 

SRAM的數據線是輸出輸入數據共用的,要將其設計成三態門形式,具體如代碼84行所示。接下就是編寫tb文件來驗證驅動程序,代碼如下:

1   `timescale 1ns/1ns
2   `define PERIOD_CLK 20
3 
4   module sram_tb;
5       reg clk50M;
6       reg rst_n;
7       
8       reg [17:0]address;
9       reg read_n;
10      reg write_n;
11
12      reg [15:0]writedata;
13      wire [15:0]readdata;
14      
15      wire [17:0]sram_addr;
16      wire [15:0]sram_dq;
17      wire sram_ce_n; 
18      wire sram_oe_n; 
19      wire sram_we_n;
20      wire sram_lb_n;
21      wire sram_ub_n;
22      
23      integer i;
24
25      sram_ctrl sram_ctrl_u0( 
26          .clk50M(clk50M),
27          .rst_n(rst_n),
28          .address(address),
29          .chipselect_n(1'b0),
30          .read_n(read_n),
31          .write_n(write_n),
32          .byteenable_n(2'b00),
33          .writedata(writedata),
34          .readdata(readdata),
35          
36          .sram_addr(sram_addr),
37          .sram_dq(sram_dq),
38          .sram_ce_n(sram_ce_n), 
39          .sram_oe_n(sram_oe_n), 
40          .sram_we_n(sram_we_n),
41          .sram_lb_n(sram_lb_n),
42          .sram_ub_n(sram_ub_n)
43      );
44      
45      initial clk50M = 1'b1;
46      always #(`PERIOD_CLK/2) clk50M = ~clk50M;
47      
48      initial 
49      begin
50          rst_n = 1'b0;
51          read_n = 1'b1;
52          address = 0;
53          write_n = 1'b1;
54          writedata = 16'h0;
55          #(`PERIOD_CLK*200 + 1)
56          rst_n = 1'b1;
57          
58          write_n = 1'b0;
59          for(i=0; i<1000; i=i+1)
60          begin
61              #(`PERIOD_CLK);
62              address = address + 1;
63              writedata = writedata + 1;          
64          end
65          write_n = 1'b1;
66          #(`PERIOD_CLK*2000);        
67          
68          #2000;
69          address = 0;
70          read_n = 1'b0;
71          for(i=0; i<1000; i=i+1)
72          begin
73              #(`PERIOD_CLK);
74              address = address + 1;          
75          end
76          read_n = 1'b1;
77          #(`PERIOD_CLK*2000);        
78      
79          #2000;
80          $stop;
81      end 
82      
83  endmodule 

仿真結果如下:

 

圖片10

寫操作控制信號放大后波形如下:

 

圖片11

讀操作控制信號放大后波形如下:

 

圖片12

這里需要說明一下,就是讀操作讀出的數據沒有值,主要是沒有真正的接SRAM,還沒想到怎么去驗證讀數據,但是仿真結果可以看出,讀寫時序與按預期設計的一致。如果想進一步進行板級驗證,也是可以的,這就需要使用SignalTap II Logic Analyzer工具對寫入的數據和讀取的數據進行抓取和比較,從而判斷控制驅動設計的對錯,具體的操作后面會提到。關於SRAM的控制驅動就說這么多,其他的可以參考芯片手冊做更進一步的設計,本人經驗不足,還望前輩們批評指正。

接下來還是進入今天的主題,就是通過串口的傳圖片數據到SRAM,然后通過讀取SRAM的圖片數據在tft上顯示完整的圖片,主要是解決上次通過讀rom數據顯示圖片不能顯示整個tft屏的問題。主要的設計框圖如下:

 

圖片13

框圖中除了UART2SRAM模塊是沒有設計的,其余模塊都已經進行了設計和驗證,串口接收模塊和tft屏的驅動參考的小梅哥教程里的。UART2SRAM模塊主要有兩個功能一個是將串口接收來的8位的數據每兩個合成一個16位的數據傳給writedata,還有一個是向SARM里寫入數據和讀取數據。數據的合成首先對串口接收模塊的輸出數據進行一個計數,然后通過計數器的數將每兩個8位合成一個16位的數據,也就是個數為偶數時進行一次合成。具體代碼如下:

      //串口數據個數計數器
    reg [17:0]data_cnt;
    always@(posedge clk50M or negedge rst_n)
    begin
        if(!rst_n)
            data_cnt <= 18'd0;
        else if(ctrl_state)
            data_cnt <= 18'd0;
        else if(data8bit_en)
            data_cnt <= data_cnt + 18'd1;
        else 
            data_cnt <= data_cnt;   
    end 
    
    //2個8位串口合成一個16位數據
    //step1:將接收的串口數據存儲起來
    reg [7:0]r1_data8bit;
    //reg [7:0]r2_data8bit;
    always@(posedge clk50M or negedge rst_n)
    begin
        if(!rst_n)
        begin
            r1_data8bit <= 8'd0;
        end
        else
        begin
            r1_data8bit <= data8bit;        
        end     
    end
    
    //step2:產生數據合成標志位,即將data8bit_en延時1個周期的信號
    reg r1_data8bit_en;
    always@(posedge clk50M or negedge rst_n)
    begin
        if(!rst_n)
        begin
            r1_data8bit_en <= 1'b0;
        end
        else
        begin
            r1_data8bit_en <= data8bit_en;      
        end
    end
    
    //step3:數據合成
    reg [15:0] data16bit;
    always@(posedge clk50M or negedge rst_n)
    begin
        if(!rst_n)  
            data16bit <= 16'd0;
        else if(r1_data8bit_en && data_cnt[0]==0)
            data16bit <= {r1_data8bit,data8bit};
        else
            data16bit <= data16bit;
    end

這個代碼根據串口接收模塊的不同稍有差別,主要是是看你設計的串口接收模塊接收完成標志位,輸出數據的時序關系,大概有兩種不同的時序,如下圖所示:

圖片15

本實驗串口接收模塊的時序是右邊的圖,如果是左邊的時序圖,上述代碼需要做相應的修改,主要是產生合成數據標志位有所變化,此時標志位就直接為data8bit,不用延時一時鍾周期,具體時序如下圖所示:

圖片16

兩種不同的時序稍有差別,總的思路是一樣的,具體實現可根據實際的情況而定。

接下來就是向SARM寫入數據和讀取數據,本實驗是先將合成的16位的數據寫入SRAM,然后再通過讀取SRAM數據進行圖片的顯示。寫入數據主要是寫控制位ce_n和地址的控制,本實驗沒有加入按鍵等外部的控制,寫控制就直接從接收串口數據開始,圖片數據接收完成截止。具體代碼如下:

//一幀圖片數據傳輸完成標志  
always@(posedge clk50M or negedge rst_n)
begin
    if(!rst_n)
        rx_img_done <= 1'b0;
    else if(r1_data8bit_en && data_cnt == rx_data_cnt_max)
        rx_img_done <= 1'b1;
    else
        rx_img_done <= 1'b0;    
end

//寫數據控制
always@(posedge clk50M or negedge rst_n)
begin
    if(!rst_n)
        write_n <= 1'b1;
    else if(rx_img_done)
        write_n <= 1'b1;
    else if(data_cnt > 0 && r1_data8bit_en)
        write_n <= 1'b0;    
    else
        write_n <= write_n;
end

寫入數據地址在每次合成數據時加1。為了保證寫入的數據是從地址0開始的,在復位狀態下將初始地址設為最大18'h3ffff,這樣在第一次有效16位的數據時,地址正好是從0開始。具體代碼如下:

//SRAM寫入數據地址變化,每接收兩個串口數據加1    
always@(posedge clk50M or negedge rst_n)
begin
    if(!rst_n)  
        wirteaddr <= 18'h3ffff;
    else if(r1_data8bit_en && data_cnt[0]==0)
        wirteaddr <= wirteaddr + 18'd1;
    else 
        wirteaddr <= wirteaddr;
end

上面判斷data_cnt[0]==0是判斷計數器奇偶的。

數據的讀取,和rom讀取數據類似了,這里只多了一個讀取控制,本實驗將該控制信號在數據寫完后就將其變成有效,便可進行數據的讀取,數據讀取的地址主要是依據tft驅動模塊的行掃描和場掃描計數器來計算的。具體代碼如下:

//讀數據控制位
assign read_n = (~ctrl_state)?1'b0:1'b1;
//從SRAM讀取數據地址,依據據TFT行和場掃描計數器變化
always@(posedge clk50M or negedge rst_n)
begin
    if(!rst_n)
        readaddr <= 18'd0;
    else if(tft_de&&(~read_n))
        readaddr <= hcount + vcount * h_pixel;
    else
        readaddr <= 18'd0;  
end

這樣就完成了UART2SRAM模塊的設計,整個設計的代碼如下:

1   module uart2sram(
2       clk50M,
3       rst_n,
4       data8bit,
5       data8bit_en,
6       
7       vcount,
8       hcount,
9       tft_de,
10      
11      address,    
12      write_n,
13      writedata,
14      read_n,
15      rx_img_done
16  );
17 
18      input clk50M;             //系統時鍾
19      input rst_n;              //系統異步復位
20      input [7:0]data8bit;      //串口接收的8位數據
21      input data8bit_en;        //串口接收完成標志位
22      
23      input [9:0]hcount;        //TFT行掃描計數器
24      input [9:0]vcount;        //TFT場掃描計數器   
25      input tft_de;             //TFT數據使能
26      
27      output [17:0]address;     //寫入或讀取數據的SRAM地址 
28      output reg write_n;       //寫數據控制位
29      output [15:0]writedata;   //寫入數據到SRAM數據
30      output read_n;            //讀數據控制位
31      
32      output reg rx_img_done;   //一張圖片數據傳送完成標志位
33      
34      reg [17:0]writeaddr;      //寫入數據到SRAM地址
35      reg [17:0]readaddr;       //從SRAM讀取數據的地址
36      
37      reg ctrl_state;           //讀寫控制狀態,1代表可寫狀態,0代表可讀狀態
38 
39      localparam h_pixel = 480, //屏的行像素點
40                 v_pixel = 272; //屏的場像素點
41                    
42      parameter rx_data_cnt_max = h_pixel*v_pixel;   //最大串口接收數據量,根據屏的大小而定 
43 
44      //串口數據個數計數器
45      reg [17:0]data_cnt;
46      always@(posedge clk50M or negedge rst_n)
47      begin
48          if(!rst_n)
49              data_cnt <= 18'd0;
50          else if(ctrl_state == 1'b0)    //可讀狀態,串口傳數據無效        
51              data_cnt <= 18'd0;
52          else if(data8bit_en)           //可寫狀態,計數串口發送數據
53              data_cnt <= data_cnt + 18'd1;
54          else 
55              data_cnt <= data_cnt;   
56      end 
57      
58      //2個8位串口合成一個16位數據
59      //step1:將接收的串口數據存儲起來
60      reg [7:0]r1_data8bit;
61      always@(posedge clk50M or negedge rst_n)
62      begin
63          if(!rst_n)
64          begin
65              r1_data8bit <= 8'd0;
66          end
67          else
68          begin
69              r1_data8bit <= data8bit;        
70          end     
71      end
72      
73      //step2:產生數據合成標志位,即將data8bit_en延時1個周期的信號
74      reg r1_data8bit_en;
75      always@(posedge clk50M or negedge rst_n)
76      begin
77          if(!rst_n)
78          begin
79              r1_data8bit_en <= 1'b0;
80          end
81          else
82          begin
83              r1_data8bit_en <= data8bit_en;      
84          end
85      end
86      
87      //step3:數據合成
88      reg [15:0] data16bit;
89      always@(posedge clk50M or negedge rst_n)
90      begin
91          if(!rst_n)  
92              data16bit <= 16'd0;
93          else if(r1_data8bit_en && data_cnt[0]==0)
94              data16bit <= {r1_data8bit,data8bit};
95          else
96              data16bit <= data16bit;
97      end
98              
99      //SRAM寫入數據地址變化,每接收兩個串口數據加1    
100     always@(posedge clk50M or negedge rst_n)
101     begin
102         if(!rst_n)  
103             writeaddr <= 18'h3ffff;
104         else if(r1_data8bit_en && data_cnt[0]==0)
105             writeaddr <= writeaddr + 18'd1;
106         else 
107             writeaddr <= writeaddr;
108     end
109     
110     //一幀圖片數據傳輸完成標志  
111     always@(posedge clk50M or negedge rst_n)
112     begin
113         if(!rst_n)
114             rx_img_done <= 1'b0;
115         else if(r1_data8bit_en && data_cnt == rx_data_cnt_max)
116             rx_img_done <= 1'b1;
117         else
118             rx_img_done <= 1'b0;    
119     end
120     
121     //讀寫狀態控制
122     always@(posedge clk50M or negedge rst_n)
123     begin
124         if(!rst_n)
125             ctrl_state <= 1'b1;
126         else if(rx_img_done)
127             ctrl_state <= 1'b0;
128         else 
129             ctrl_state <= ctrl_state;
130     end
131     
132     //寫數據控制位
133     always@(posedge clk50M or negedge rst_n)
134     begin
135         if(!rst_n)
136             write_n <= 1'b1;
137         else if(rx_img_done)
138             write_n <= 1'b1;
139         else if(data_cnt > 0 && r1_data8bit_en)
140             write_n <= 1'b0;    
141         else
142             write_n <= write_n;
143     end
144         
145     //寫數據
146     wire [15:0]writedata = data16bit;
147     
148     //讀數據控制位
149     assign read_n = (~ctrl_state)?1'b0:1'b1;
150     
151     //從SRAM讀取數據地址,依據據TFT行和場掃描計數器變化
152     always@(posedge clk50M or negedge rst_n)
153     begin
154         if(!rst_n)
155             readaddr <= 18'd0;
156         else if(tft_de&&(~read_n))
157             readaddr <= hcount + vcount * h_pixel;
158         else
159             readaddr <= 18'd0;  
160     end
161     
162     //SRAM地址
163     assign address = (~write_n)?writeaddr:(~read_n)?readaddr:18'h0; 
164
165 endmodule 

編寫tb文件進行仿真驗證,這里要借用之前的tft驅動模塊提供vcount、hcount和tft_de信號,具體代碼如下:

1   `timescale 1ns/1ns
2   `define PERIOD_CLK50M 20
3   `define PERIOD_CLK9M 120
4  
5   module uart2sram_tb;
6  
7       reg clk50M;
8       reg clk9M;
9       reg rst_n;  
10      reg [7:0]data8bit;
11      reg data8bit_en;
12      
13      wire [9:0]hcount;
14      wire [9:0]vcount;
15      wire tft_vs;
16      wire tft_de;
17      
18      wire [17:0]address; 
19      wire write_n;
20      wire [15:0]writedata;
21      wire read_n;    
22      wire rx_img_done;
23      
24      reg [7:0]v_cnt = 0; //掃描幀數統計計數器
25 
26      defparam uart2sram.rx_data_cnt_max = 1000;
27      
28      TFT_CTRL u1_TFT_CTRL(
29          .clk9M(clk9M),
30          .rst_n(rst_n),
31          .data_in(),
32          .hcount(hcount),
33          .vcount(vcount),
34          .tft_rgb(),
35          .tft_hs(),
36          .tft_vs(tft_vs),
37          .tft_clk(),
38          .tft_de(tft_de),
39          .tft_pwm()
40      );
41 
42      uart2sram uart2sram(
43          .clk50M(clk50M),
44          .rst_n(rst_n),
45          .data8bit(data8bit),
46          .data8bit_en(data8bit_en),
47          .hcount(hcount),
48          .vcount(vcount),
49          .tft_de(tft_de),
50          
51          .address(address),  
52          .write_n(write_n),
53          .writedata(writedata),
54          .read_n(read_n),
55          .rx_img_done(rx_img_done)
56      );
57 
58      initial clk50M = 1'b1;
59      always #(`PERIOD_CLK50M/2) clk50M = ~clk50M;
60      
61      initial clk9M = 1'b1;
62      always #(`PERIOD_CLK9M/2) clk9M = ~clk9M;
63      
64      initial 
65      begin
66          rst_n = 1'b0;
67          data8bit_en = 1'b0;
68          #(`PERIOD_CLK50M*200 + 1)
69          rst_n = 1'b1;
70          #2000;      
71          forever
72          begin
73              #6000;
74              data8bit_en = 1'b1;
75              #20;
76              data8bit_en = 1'b0;
77          end
78      
79          #2000;
80          $stop;
81      end
82      
83      initial
84      begin
85          data8bit = 8'd0;
86          forever
87          begin
88              @(posedge data8bit_en);
89              #`PERIOD_CLK50M;
90              data8bit = data8bit + 1;
91          end 
92      end
93      
94      initial 
95      begin
96          wait(v_cnt == 3);  //等待掃描2幀后結束仿真
97          $stop;
98      end
99      
100     always@(posedge tft_vs)   //統計總掃描幀數
101         v_cnt = v_cnt + 1'b1;
102 endmodule 

仿真結果如下:

 

圖片22

可以看到數據的合成和寫SRAM數據和地址與設計的是相符的。由於要看到讀數據的地址,需要的時間較長,在編寫tb時,將最大串口接收數據量改小進行仿真得到讀取SRAM數據部分的仿真波形如下:

 

圖片23

從上面的波形可以看出數據讀取的地址波形與預期一致,我們還發現其地址改變的位置與屏的驅動時鍾的上升沿並沒有對齊,這個好像沒有影響,看tft屏的驅動時序圖發現屏的顯示好像是下降沿對應的像素點數據,這樣我們的設計也是符合這個的。或者為了與tft時鍾上升沿同步,可以將tft時鍾延遲相應的時鍾周期。

各模塊設計完成,接下來是頂層文件的設計,設計如下:

1   module uart_tft_img(
2       clk50M,
3       rst_n,
4       Rs232_rx,
5       
6       sram_addr,
7       sram_dq,
8       sram_ce_n, 
9       sram_oe_n, 
10      sram_we_n,
11      sram_lb_n,
12      sram_ub_n,
13      
14      tft_rgb,
15      tft_hs,
16      tft_vs,
17      tft_clk,
18      tft_de,
19      tft_pwm,
20      
21      led
22  );
23 
24      input clk50M;
25      input rst_n;
26      input Rs232_rx;
27      
28      output [17:0]sram_addr;  //操作RAM數據的地址
29      inout  [15:0]sram_dq;    //RAM的數據端口
30      output sram_ce_n;        //SRAM片選信號,低電平有效
31      output sram_oe_n;        //SRAM讀數據控制信號,低電平有效
32      output sram_we_n;        //SRAM寫數據控制信號,低電平有效
33      output sram_lb_n;        //數據低字節有效
34      output sram_ub_n;        //數據高字節有效
35 
36      output [15:0]tft_rgb;
37      output tft_hs;
38      output tft_vs;
39      output tft_clk;
40      output tft_de;
41      output tft_pwm;
42      output led;              //用於指示圖片數據是否已經接收完成
43      
44      wire [7:0]Data_Byte;
45      wire Rx_Done;
46      
47      wire [7:0]data8bit;
48      wire data8bit_en;
49      wire [17:0]address;
50      wire write_n;
51      wire [15:0]writedata;
52      wire read_n;
53      wire rx_img_done;   
54      wire [15:0]readdata;
55      
56      wire clk9M;
57      wire [15:0]data_in;
58      wire [9:0]hcount;
59      wire [9:0]vcount;
60 
61      //串口接收模塊例化  
62      uart_byte_rx u0_uart_byte_rx(
63          .clk50M(clk50M),
64          .rst_n(rst_n),
65          .Rs232_rx(Rs232_rx),
66          .baud_set(3'd4),        //波特率設置為115200
67 
68          .Data_Byte(Data_Byte),
69          .Rx_Done(Rx_Done)  
70      );
71      
72      assign data8bit = Data_Byte;
73      assign data8bit_en = Rx_Done;
74      
75      //串口數據存入SRAM模塊例化    
76      uart2sram u1_uart2sram(
77          .clk50M(clk50M),
78          .rst_n(rst_n),
79          .data8bit(data8bit),
80          .data8bit_en(data8bit_en),
81          .hcount(hcount),
82          .vcount(vcount),
83          .tft_de(tft_de),
84          
85          .address(address),  
86          .write_n(write_n),
87          .writedata(writedata),
88          .read_n(read_n),
89          .rx_img_done(rx_img_done)
90      );
91      
92      assign led = (!rst_n)?1'b1:rx_img_done?1'b0:led; 
93      
94      //SRAM控制模塊例化        
95      sram_ctrl u2_sram_ctrl( 
96          .clk50M(clk50M),
97          .rst_n(rst_n),
98          .address(address),
99          .chipselect_n(1'b0),
100         .read_n(read_n),
101         .write_n(write_n),
102         .byteenable_n(2'b00),
103         .writedata(writedata),
104         .readdata(readdata),
105         
106         .sram_addr(sram_addr),
107         .sram_dq(sram_dq),
108         .sram_ce_n(sram_ce_n), 
109         .sram_oe_n(sram_oe_n), 
110         .sram_we_n(sram_we_n),
111         .sram_lb_n(sram_lb_n),
112         .sram_ub_n(sram_ub_n)
113     );
114     
115     //9Mhz時鍾    
116     pll u3_pll(
117         .areset(!rst_n),
118         .inclk0(clk50M),
119         .c0(clk9M)
120     );  
121     
122     assign data_in = readdata;
123
124     //TFT屏控制模塊例化  
125     TFT_CTRL u4_TFT_CTRL(
126         .clk9M(clk9M),
127         .rst_n(rst_n),
128         .data_in(data_in),
129         .hcount(hcount),
130         .vcount(vcount),
131         
132         .tft_rgb(tft_rgb),
133         .tft_hs(tft_hs),
134         .tft_vs(tft_vs),
135         .tft_clk(tft_clk),
136         .tft_de(tft_de),
137         .tft_pwm(tft_pwm)
138     );
139     
140 endmodule 

以下為仿真頂層模塊的設計

1   `timescale 1ns/1ns
2   `define PERIOD_CLK 20
3 
4   module uart_tft_img_tb;
5 
6       reg clk50M;
7       reg rst_n;
8       reg send_en;
9       reg [7:0]Data_Byte;
10      
11      wire [17:0]sram_addr;
12      wire [15:0]sram_dq;
13      wire sram_ce_n; 
14      wire sram_oe_n; 
15      wire sram_we_n;
16      wire sram_lb_n;
17      wire sram_ub_n;
18      
19      wire [15:0]tft_rgb;
20      wire tft_hs;
21      wire tft_vs;
22      wire tft_clk;
23      wire tft_de;
24      wire tft_pwm;
25      wire led;
26      
27      wire Rs232_Tx;
28      wire Tx_Done;
29      
30      defparam u1_uart_tft_img.u1_uart2sram.rx_data_cnt_max = 10;
31      
32      //例化串口發送模塊
33      uart_byte_tx u0_uart_byte_tx(
34          .Clk(clk50M), 
35          .Rst_n(rst_n), 
36          .send_en(send_en),
37          .baud_set(3'd4),
38          .Data_Byte(Data_Byte),
39
40          .Rs232_Tx(Rs232_Tx),
41          .Tx_Done(Tx_Done),
42          .uart_state()
43      );  
44
45      //例化頂層模塊uart_tft_img
46      uart_tft_img u1_uart_tft_img(
47          .clk50M(clk50M),
48          .rst_n(rst_n),
49          .Rs232_rx(Rs232_Tx),
50          
51          .sram_addr(sram_addr),
52          .sram_dq(sram_dq),
53          .sram_ce_n(sram_ce_n), 
54          .sram_oe_n(sram_oe_n), 
55          .sram_we_n(sram_we_n),
56          .sram_lb_n(sram_lb_n),
57          .sram_ub_n(sram_ub_n),
58          
59          .tft_rgb(tft_rgb),
60          .tft_hs(tft_hs),
61          .tft_vs(tft_vs),
62          .tft_clk(tft_clk),
63          .tft_de(tft_de),
64          .tft_pwm(tft_pwm),
65          .led(led)
66      );
67      
68      initial clk50M <= 1'b1;
69      always #(`PERIOD_CLK/2) clk50M <= ~clk50M;
70      
71      initial begin
72          rst_n <= 1'b0;
73          send_en <= 1'b0;
74          Data_Byte <= 8'b0000_0000;      
75          #(`PERIOD_CLK*20 + 1)
76          rst_n <= 1'b1;      
77          #(`PERIOD_CLK*50)
78          
79          Data_Byte <= 8'h0;      
80          send_en <= 1'b1;
81          #(`PERIOD_CLK)
82          send_en <= 1'b0;
83          
84          repeat(2000)
85          begin
86              @(posedge Tx_Done) //數據傳輸完成
87              #(`PERIOD_CLK*5000);
88              Data_Byte <= Data_Byte + 8'h3;      
89              send_en <= 1'b1;
90              #(`PERIOD_CLK)
91              send_en <= 1'b0;
92          end
93          
94          @(posedge Tx_Done)//數據傳輸完成
95          #(`PERIOD_CLK*500)
96          $stop;      
97      end
98
99  endmodule 

 

 

由於按照實際的數據量來仿真需要的時間太長,為了縮短時間,將數據量更改為小一點的值,主要是更改上面代碼的第30行。

仿真波形如下:

 

圖片26

以上圖片是串口傳數據,然后將數據寫入SRAM的波形,與預期設計效果一致。

有關讀數據的仿真由於仿真過程沒有實際SRAM讀出的數據,只能看讀地址的波形和地址的變化。這個地方沒有想到好的仿真方法。

 

圖片27

板級驗證,引腳分配按照梅哥發的文檔引腳一一對應分配好即可,分配表如下:

 

圖片28

下載后進行板級驗證,在此之前我們先配置一個SignalTap II Logic Analyzer的文件,這樣可以方便我們驗證SRAM寫入和讀取數據的對錯,以及一張圖片數據是否寫完。具體的關於這個配置,小梅哥的視頻上有講,我的配置如下圖所示:

 

圖片29

創建好,保存后重新編譯,下載,然后再打開SignalTap II Logic Analyzer,讓其一直處於循環的抓捕狀態。以下是剛復位后的狀態,此時開發板的led0也處於滅的狀態。

 

圖片30

打開串口軟件,我使用的是友善串口調試助手,這個因人而異,有的串口軟件不好用可以換其他的,總有一款適合你,以下是我用的串口軟件:

 

圖片31

串口設置與我們設計的串口接收模塊的設置保持一致,程序里波特率設置的位115200,圖片數據輸入是將圖片數據復制在下面紅色空中的,最開始是想着直接發送數據文件的,后來發現文件好像默認是把一位16進制數當成了兩個字節,例如一字節的0xFF,在文件里就成了2個字節,如下圖所示,實際261120字節大小的數據,放入文本文檔中就變成了522240字節大小

 

圖片32

這樣將我們要發送的數據量變成了原有的兩倍導致錯誤。我是直接復制數據粘貼在紅框中發送的,反應有點慢,不影響最后的結果。在數據傳輸過程中我們可以在SignalTap II Logic Analyzer工具中看到寫入和讀取SRAM數據的過程,我截取了寫數據和讀數據過程中的兩幅圖如下:

 

圖片33

 

圖片34

板級驗證結果如下:

 

圖片35

在串口數據傳輸完成后LED0變亮,與設計的完全一致。

上述圖片數據是首先在網上找的與tft屏大小一樣的圖片,然后利用軟件Img2Lcd,和rom存儲圖片數據顯示在tft屏的操作差不多,將圖片轉換成 .c的數據文件,該數據文件中數據是0x開頭的,但是有的串口不能識別0x和逗號,我們可以利用Notepad++ 軟件進行簡單的處理,就是用Notepad++ 軟件將數據文件打開,然后利用全部替換功能將0x和逗號之類的無用的字符去掉,這樣剩下的都是有效的數據,然后復制粘貼到串口軟件即可。

如有更多問題,歡迎加入芯航線 FPGA 技術支持群交流學習:472607506

小梅哥

芯航線電子工作室

關於學習資料,小梅哥系列所有能夠開放的資料和更新(包括視頻教程,程序代碼,教程文檔,工具軟件,開發板資料)都會發布在我的雲分享。(記得訂閱)鏈接:http://yun.baidu.com/share/home?uk=402885837&view=share#category/type=0

贈送芯航線AC6102型開發板配套資料預覽版下載鏈接:鏈接:http://pan.baidu.com/s/1slW2Ojj 密碼:9fn3

贈送SOPC公開課鏈接和FPGA進階視頻教程。鏈接:http://pan.baidu.com/s/1bEzaFW 密碼:rsyh


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM