手搓qspi接口,封装成AXI4‐ip,用zynq驱动 - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki

手搓qspi接口,封装成AXI4‐ip,用zynq驱动

说明

  • gc9b71是格科公司的小尺寸lcd_driver驱动芯片, 支持qspi接口
  • 手头的这款屏尺寸是1.89寸,分辨率是320x386,采用qspi接口驱动
  • 这个模组的引脚定义如下 image image 19f72b9779f9c6840efaab49a8160baf

qspi模块代码

代码的构思过程

  • 1.1整体代码框架参考的是之前写的fmc16结构,也就是全局状态机+局部线性序列机的方式
  • 几个状态?IDLE,SEND_CMD, SEND_DATA, SEND_WAIT
  • 几个控制信号?xfer_start/xfer_end
  • 几个计数器?lsm_cnt/xfer_cnt
  • 线性序列机节拍行为如下
  • 图1:只发command。这种应用在qspi flash读写中,不会用在qspi-lcd中。
image
  • 图2:发command+addr。这种会应用到qspi-lcd中,其中command是0x02/32,addr则是24'h00xx00,这里xx是真正的cmd
image
  • 图3:1wire模式发command+addr+data
image
  • 图4:4wire模式发command+addr+data,一般用于发图
image

代码

`timescale 1ns / 1ps
module qspi(
    input sys_clk,
    input sys_rst_n,
    
    (*mark_debug="true"*)input xfer_start,
    (*mark_debug="true"*)output reg xfer_end,
    
    (*mark_debug="true"*)input [7:0] cmd,
    (*mark_debug="true"*)input [23:0] addr,
    (*mark_debug="true"*)input [7:0] data_in, 
    (*mark_debug="true"*)input [17:0] length, /*0=>cmd, 1=>addr, >=2=>data. real_datalen=length-1*/
    (*mark_debug="true"*)input mode,  /*0: 1wire, 1: 4wire*/
    
    (*mark_debug="true"*)output reg csx,
    (*mark_debug="true"*)output reg sck,
    (*mark_debug="true"*)output reg [3:0] do
    );
    
reg [6:0] lsm_cnt;

parameter IDLE=2'b00;
parameter SEND_CMD=2'b01;
parameter SEND_DATA=2'b10;
parameter SEND_WAIT=2'b11;

reg [1:0] state;
reg [17:0] xfer_cnt;

always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
    state<=IDLE;
else case(state)
    IDLE: if(xfer_start) 
            state<=SEND_CMD;
    SEND_CMD: if(xfer_end && (xfer_cnt==0||xfer_cnt==1))
                state<=IDLE;
              else if(xfer_end)
                state<=SEND_WAIT;
    SEND_DATA: if(xfer_end && xfer_cnt==2)
                state<=IDLE;
               else if(xfer_end)
                state<=SEND_WAIT;
    SEND_WAIT: if(xfer_start)
                state<=SEND_DATA;
    default: state<=IDLE;
endcase

always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
    xfer_cnt<=18'd0;
else if(state==IDLE && xfer_start)
    xfer_cnt<=length;
else if(state==SEND_DATA && xfer_end)
    xfer_cnt<=xfer_cnt-1;

    
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
    lsm_cnt<=0;
else if(state==IDLE || state==SEND_WAIT)
    lsm_cnt<=0;  
else
    lsm_cnt<=lsm_cnt+1;
 
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0) begin
    csx<=1;
    sck<=0;
    do<=4'h0;
end
else case(state)
    IDLE: begin csx<=1; sck=0; end  //begin and final state
    SEND_CMD: begin            
         if(length==0)
             case(lsm_cnt)
                0:  begin csx<=0;end
                1:  begin do[0]<=cmd[7];end
                2:  begin sck<=1;end     
                3:  begin do[0]<=cmd[6]; sck<=0; end
                4:  begin sck<=1;end      
                5:  begin do[0]<=cmd[5]; sck<=0; end
                6:  begin sck<=1;end     
                7:  begin do[0]<=cmd[4]; sck<=0; end
                8:  begin sck<=1;end    
                9:  begin do[0]<=cmd[3]; sck<=0; end
                10: begin sck<=1;end     
                11: begin do[0]<=cmd[2]; sck<=0; end
                12: begin sck<=1;end      
                13: begin do[0]<=cmd[1]; sck<=0; end
                14: begin sck<=1;end     
                15: begin do[0]<=cmd[0]; sck<=0; end
                16: begin sck<=1;end      
//                17: begin csx<=1;end                                                                                
            endcase 
        else
             case(lsm_cnt)
                0:  begin csx<=0;end
                1:  begin do[0]<=cmd[7];end
                2:  begin sck<=1;end     
                3:  begin do[0]<=cmd[6]; sck<=0; end
                4:  begin sck<=1;end      
                5:  begin do[0]<=cmd[5]; sck<=0; end
                6:  begin sck<=1;end     
                7:  begin do[0]<=cmd[4]; sck<=0; end
                8:  begin sck<=1;end    
                9:  begin do[0]<=cmd[3]; sck<=0; end
                10: begin sck<=1;end     
                11: begin do[0]<=cmd[2]; sck<=0; end
                12: begin sck<=1;end      
                13: begin do[0]<=cmd[1]; sck<=0; end
                14: begin sck<=1;end     
                15: begin do[0]<=cmd[0]; sck<=0; end
                16: begin sck<=1;end   
                                  
                17: begin do[0]<=addr[23]; sck<=0; end
                18: begin sck<=1;end      
                19: begin do[0]<=addr[22]; sck<=0; end
                20: begin sck<=1;end     
                21: begin do[0]<=addr[21]; sck<=0; end
                22: begin sck<=1;end    
                23: begin do[0]<=addr[20]; sck<=0; end
                24: begin sck<=1;end     
                25: begin do[0]<=addr[19]; sck<=0; end
                26: begin sck<=1;end      
                27: begin do[0]<=addr[18]; sck<=0; end
                28: begin sck<=1;end     
                29: begin do[0]<=addr[17]; sck<=0; end    
                30: begin sck<=1;end     
                31: begin do[0]<=addr[16]; sck<=0; end
                32: begin sck<=1;end      
                33: begin do[0]<=addr[15]; sck<=0; end
                34: begin sck<=1;end     
                35: begin do[0]<=addr[14]; sck<=0; end
                36: begin sck<=1;end    
                37: begin do[0]<=addr[13]; sck<=0; end
                38: begin sck<=1;end     
                39: begin do[0]<=addr[12]; sck<=0; end
                40: begin sck<=1;end      
                41: begin do[0]<=addr[11]; sck<=0; end
                42: begin sck<=1;end     
                43: begin do[0]<=addr[10]; sck<=0; end    
                44: begin sck<=1;end   
                
                45: begin do[0]<=addr[9]; sck<=0; end
                46: begin sck<=1;end      
                47: begin do[0]<=addr[8]; sck<=0; end
                48: begin sck<=1;end     
                49: begin do[0]<=addr[7]; sck<=0; end
                50: begin sck<=1;end    
                51: begin do[0]<=addr[6]; sck<=0; end
                52: begin sck<=1;end     
                53: begin do[0]<=addr[5]; sck<=0; end
                54: begin sck<=1;end      
                55: begin do[0]<=addr[4]; sck<=0; end
                56: begin sck<=1;end     
                57: begin do[0]<=addr[3]; sck<=0; end    
                58: begin sck<=1;end     
                59: begin do[0]<=addr[2]; sck<=0; end
                60: begin sck<=1;end      
                61: begin do[0]<=addr[1]; sck<=0; end
                62: begin sck<=1;end     
                63: begin do[0]<=addr[0]; sck<=0; end
                64: begin sck<=1;end    
//                65: begin csx<=1;end                                                                                                                           
            endcase  
     end
     
     SEND_DATA: begin
            if(mode==0)
                 case(lsm_cnt)
                    0:  begin do[0]<=data_in[7];end
                    1:  begin sck<=1;end     
                    2:  begin do[0]<=data_in[6]; sck<=0; end
                    3:  begin sck<=1;end      
                    4:  begin do[0]<=data_in[5]; sck<=0; end
                    5:  begin sck<=1;end     
                    6:  begin do[0]<=data_in[4]; sck<=0; end
                    7:  begin sck<=1;end    
                    8:  begin do[0]<=data_in[3]; sck<=0; end
                    9:  begin sck<=1;end     
                    10: begin do[0]<=data_in[2]; sck<=0; end
                    11: begin sck<=1;end      
                    12: begin do[0]<=data_in[1]; sck<=0; end
                    13: begin sck<=1;end     
                    14: begin do[0]<=data_in[0]; sck<=0; end
                    15: begin sck<=1;end  
                endcase    
           else
                 case(lsm_cnt)
                    0:  begin do[3:0]<=data_in[7:4];end
                    1:  begin sck<=1;end     
                    2:  begin do[3:0]<=data_in[3:0]; sck<=0; end
                    3:  begin sck<=1;end       
                endcase                                                      
     end   
     SEND_WAIT: sck<=0;
        
endcase

//xfer_end要单独产生,因为它只占一个aclk脉宽,与其它信号不一样
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0) begin
    xfer_end<=0;
end
else if(state==SEND_CMD && length==0 && lsm_cnt==17)
    xfer_end<=1;
else if(state==SEND_CMD && length>0 && lsm_cnt==65)
    xfer_end<=1;    
else if(state==SEND_DATA && mode==0 && lsm_cnt==16)
    xfer_end<=1;
else if(state==SEND_DATA && mode==1 && lsm_cnt==4)
    xfer_end<=1;    
else
    xfer_end<=0; 
    
endmodule

接口部分

		// Users to add ports here
        output csx,
        output sck,
        output [3:0] dout,

        output xfer_start,
        output xfer_end, 
		// User ports ends

寄存器读写部分

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      slv_reg0 <= 0;
	      slv_reg1 <= 0;
	      slv_reg2 <= 0;
	      slv_reg3 <= 0;
	      slv_reg4 <= 0;
	      slv_reg5 <= 0;
	      slv_reg6 <= 0;
	      slv_reg7 <= 0;
	    end 
	  if(xfer_end) begin
	       slv_reg6<=32'h1;
	  end 	

	// Add user logic here
    reg xfer_start;
    always @( posedge S_AXI_ACLK )
    if ( S_AXI_ARESETN == 1'b0 )
        xfer_start<= 0;
    else
        xfer_start <= (slv_reg_wren && axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]==3'h0)? 1:0;      
        
    qspi qspi_inst (
       // Input Ports - Single Bit
       .sys_clk       (S_AXI_ACLK),    
       .sys_rst_n     (S_AXI_ARESETN),  
       .xfer_start    (xfer_start),       
       // Input Ports - Busses
       .cmd(slv_reg1[7:0]),
       .addr(slv_reg2[23:0]),
       .data_in (slv_reg3[7:0]),
       .length  (slv_reg4[17:0]),
       .mode(slv_reg5[0]),
       // Output Ports - Single Bit
       .csx           (csx),        
       .sck           (sck),            
       .xfer_end      (xfer_end),   
       // Output Ports - Busses
       .do     (do)   
       // InOut Ports - Single Bit
       // InOut Ports - Busses
    ); 
	// User logic ends

5)单独验证(工程中有)

6)封装成AXI4接口后,用VIP验证(工程中有)

7)用zynq驱动,整体框图

image

pin_local.xdc

#create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
#set_property -dict {PACKAGE_PIN N18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
#set_property -dict {PACKAGE_PIN P14 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]

# for qspi lcd, gpio2
set_property -dict {PACKAGE_PIN D18 IOSTANDARD LVCMOS33} [get_ports csx]
set_property -dict {PACKAGE_PIN E18 IOSTANDARD LVCMOS33} [get_ports sck]
set_property -dict {PACKAGE_PIN H17 IOSTANDARD LVCMOS33} [get_ports {dout[0]}]
set_property -dict {PACKAGE_PIN H16 IOSTANDARD LVCMOS33} [get_ports {dout[1]}]
set_property -dict {PACKAGE_PIN G18 IOSTANDARD LVCMOS33} [get_ports {dout[2]}]
set_property -dict {PACKAGE_PIN G17 IOSTANDARD LVCMOS33} [get_ports {dout[3]}]

set_property SLEW FAST [get_ports {dout[0]}]
set_property SLEW FAST [get_ports {dout[1]}]
set_property SLEW FAST [get_ports {dout[2]}]
set_property SLEW FAST [get_ports {dout[3]}]
set_property SLEW FAST [get_ports sck]

# used for qspi-lcd reset
set_property -dict {PACKAGE_PIN E17 IOSTANDARD LVCMOS33} [get_ports rst_o]

8) c代码

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xil_io.h"
#include "sleep.h"
#include "my_qspi_ip.h"
#include "ff.h"
#include "xil_cache.h"
#include "xtime_l.h"

#define QSPI_BASE XPAR_MY_QSPI_IP_0_S00_AXI_BASEADDR

#define  RED        0xF800
#define  ORANGE     0xFC00
#define  YELLOW     0xFFE0
#define  GREEN      0x07E0
#define  CYAN       0x07FF
#define  BLUE       0x001F
#define  PURPPLE    0xF81F
#define  BLACK      0x0000
#define  WHITE      0xFFFF
#define  GRAY       0xD69A

const u16 colors[] = {
	RED,
	GREEN,
	BLUE,
	ORANGE,
	YELLOW,
	CYAN,
	PURPPLE,
	BLACK,
	WHITE,
	GRAY
};

typedef struct{
	u8 txbuf[50];
	u8 rxbuf[50];
	u8 length;
}t_buf;

t_buf WriteBuffer[]={
{ {0xfe} , {0x0} , 1 },
{ {0xef} , {0x0} , 1 },
{ {0x80, 0x11} , {0x0} , 2 },
{ {0x81, 0x70} , {0x0} , 2 },
{ {0x82, 0x9} , {0x0} , 2 },
{ {0x83, 0x3} , {0x0} , 2 },
{ {0x84, 0x62} , {0x0} , 2 },
{ {0x89, 0x18} , {0x0} , 2 },
{ {0x8a, 0x40} , {0x0} , 2 },
{ {0x8b, 0xa} , {0x0} , 2 },
{ {0x3a, 0x5} , {0x0} , 2 },
{ {0x36, 0x40} , {0x0} , 2 },
{ {0xec, 0x7} , {0x0} , 2 },
{ {0x74, 0x1, 0x80, 0x0, 0x0, 0x0, 0x0} , {0x0} , 7 },
{ {0x98, 0x3e} , {0x0} , 2 },
{ {0x99, 0x3e} , {0x0} , 2 },
{ {0xa1, 0x1, 0x4} , {0x0} , 3 },
{ {0xa2, 0x1, 0x4} , {0x0} , 3 },
{ {0xcb, 0x2} , {0x0} , 2 },
{ {0x7c, 0xb6, 0x24} , {0x0} , 3 },
{ {0xac, 0x74} , {0x0} , 2 },
{ {0xf6, 0x80} , {0x0} , 2 },
{ {0xb5, 0x9, 0x9} , {0x0} , 3 },
{ {0xeb, 0x1, 0x81} , {0x0} , 3 },
{ {0x60, 0x38, 0x6, 0x13, 0x56} , {0x0} , 5 },
{ {0x63, 0x38, 0x8, 0x13, 0x56} , {0x0} , 5 },
{ {0x61, 0x3b, 0x1b, 0x58, 0x38} , {0x0} , 5 },
{ {0x62, 0x3b, 0x1b, 0x58, 0x38} , {0x0} , 5 },
{ {0x64, 0x38, 0xa, 0x73, 0x16, 0x13, 0x56} , {0x0} , 7 },
{ {0x66, 0x38, 0xb, 0x73, 0x17, 0x13, 0x56} , {0x0} , 7 },
{ {0x68, 0x0, 0xb, 0x22, 0xb, 0x22, 0x1c, 0x1c} , {0x0} , 8 },
{ {0x69, 0x0, 0xb, 0x26, 0xb, 0x26, 0x1c, 0x1c} , {0x0} , 8 },
{ {0x6a, 0x15, 0x0} , {0x0} , 3 },
{ {0x6e, 0x8, 0x2, 0x1a, 0x0, 0x12, 0x12, 0x11, 0x11, 0x14, 0x14, 0x13, 0x13, 0x4, 0x19, 0x1e, 0x1d, 0x1d, 0x1e, 0x19, 0x4, 0xb, 0xb, 0xc, 0xc, 0x9, 0x9, 0xa, 0xa, 0x0, 0x1a, 0x1, 0x7} , {0x0} , 33 },
{ {0x6c, 0xcc, 0xc, 0xcc, 0x84, 0xcc, 0x4, 0x50} , {0x0} , 8 },
{ {0x7d, 0x72} , {0x0} , 2 },
{ {0x70, 0x2, 0x3, 0x9, 0x7, 0x9, 0x3, 0x9, 0x7, 0x9, 0x3} , {0x0} , 11 },
{ {0x90, 0x6, 0x6, 0x5, 0x6} , {0x0} , 5 },
{ {0x93, 0x45, 0xff, 0x0} , {0x0} , 4 },
{ {0xc3, 0x15} , {0x0} , 2 },
{ {0xc4, 0x36} , {0x0} , 2 },
{ {0xc9, 0x3d} , {0x0} , 2 },
{ {0xf0, 0x47, 0x7, 0xa, 0xa, 0x0, 0x29} , {0x0} , 7 },
{ {0xf2, 0x47, 0x7, 0xa, 0xa, 0x0, 0x29} , {0x0} , 7 },
{ {0xf1, 0x42, 0x91, 0x10, 0x2d, 0x2f, 0x6f} , {0x0} , 7 },
{ {0xf3, 0x42, 0x91, 0x10, 0x2d, 0x2f, 0x6f} , {0x0} , 7 },
{ {0xf9, 0x30} , {0x0} , 2 },
{ {0xbe, 0x11} , {0x0} , 2 },
{ {0xfb, 0x0, 0x0} , {0x0} , 3 },
{ {0x11} , {0x0} , 1 },
{ {0xff} , {0x0} , 0 },
{ {0x29} , {0x0} , 1 },
{ {0xff} , {0x0} , 0 },
{ {0x2c, 0x0, 0x0, 0x0, 0x0} , {0x0} , 5 },
{ {0x2c, 0x0, 0x0, 0x0, 0x0} , {0x0} , 5 },
};

/*************************************************
 * reg rw functions
 ************************************************/
void set_cmd(u8 data){
    Xil_Out32(QSPI_BASE+0x4, data);
}

void set_addr(u32 data){
    Xil_Out32(QSPI_BASE+0x8, data);
}

void set_data(u8 data){
    Xil_Out32(QSPI_BASE+0xc, data);
}

void set_length(u32 data){
    Xil_Out32(QSPI_BASE+0x10, data);
}

void set_line_mode(u8 data){
    Xil_Out32(QSPI_BASE+0x14, data);
}

void set_xfer_start(){
    Xil_Out32(QSPI_BASE+0x00, 0x1);
}

void poll_xfer_end(){
    while(Xil_In32(QSPI_BASE+0x18)==0x0);
    Xil_Out32(QSPI_BASE+0x18, 0x00);
}

/*************************************************
 * basic xfer functions
 ************************************************/
void xfer_data(u8 data){
	set_data(data);
	set_xfer_start();
	poll_xfer_end();
}

void xfer_cmd_pdata8(u8 cmd, u8* pdata, u32 length){
	set_length(length+1);
	set_cmd(0x02);
	set_addr(cmd<<8);
	set_xfer_start();
	poll_xfer_end();

    while(length--){
    	xfer_data(*pdata++);
    }
}

void xfer_cmd_pdata16(u8 cmd, u16* pdata, u32 length){
	set_length(2*length+1);
	set_cmd(0x32);
	set_addr(cmd<<8);
	set_xfer_start();
	poll_xfer_end();

    while(length--){
    	xfer_data(*pdata>>8);
    	xfer_data(*pdata&0xff);
    	pdata++;
    }
}

void xfer_cmd_cdata8(u8 cmd, u8 data, u32 length){
	set_length(length+1);
	set_cmd(0x02);
	set_addr(cmd<<8);
	set_xfer_start();
	poll_xfer_end();

    while(length--){
    	xfer_data(data);
    }
}

void xfer_cmd_cdata16(u8 cmd, u16 data, u32 length){
	set_length(2*length+1);
	set_cmd(0x32);
	set_addr(cmd<<8);
	set_xfer_start();
	poll_xfer_end();

    while(length--){
    	xfer_data(data>>8);
    	xfer_data(data&0xff);
    }
}

void xfer_cmd(u8 cmd){
	set_length(0x1);
	set_cmd(0x02);
	set_addr(cmd<<8);
	set_xfer_start();
}


void seqs_init() {
	set_line_mode(0);

	for (int i = 0; i < sizeof(WriteBuffer) / sizeof(*WriteBuffer); i++) {
		if (WriteBuffer[i].length == 0) {
			usleep(WriteBuffer[i].txbuf[0] * 1000);
			continue;
		}

		u8 cmd=WriteBuffer[i].txbuf[0];
		u8 *pdata=WriteBuffer[i].txbuf+1;
		u16 length=WriteBuffer[i].length-1;

		xfer_cmd_pdata8(cmd, pdata, length);
		usleep(100); //this delay is need
		xil_printf("Exec CMD=%x\r\n\r\n", cmd);
	}
}

void xfer_win(u16 xstart, u16 ystart, u16 width, u16 height)
{
    u8 cmd_list[2][5]={
    		{0x2a, xstart>>8, xstart&0xff, (xstart+width-1)>>8, (xstart+width-1)&0xff},
    		{0x2b, ystart>>8, ystart&0xff, (ystart+height-1)>>8, (ystart+height-1)&0xff},
    };

	set_line_mode(0);
    xfer_cmd_pdata8(cmd_list[0][0], &(cmd_list[0][1]),4);
    xfer_cmd_pdata8(cmd_list[1][0], &(cmd_list[1][1]),4);
}

void xfer_color(u16 color, u16 width, u16 height){
	u32 length=width*height;
	set_line_mode(1);
	xfer_cmd_cdata16(0x2C, color, length);
}

void xfer_pic(u16 *pcolor, u16 width, u16 height){
	u32 length=width*height;
	set_line_mode(1);
	xfer_cmd_pdata16(0x2C, pcolor, length);
}

/*************************************************
 * application functions
 ************************************************/
u8 frame_src[1920*1080*3];
u16 frame_target[320*386];

void load_sd_bmp(u8 *frame, const char *bmp_name);
void convertRGB888toRGB565(
		const uint8_t *input,
		uint16_t *output,
		int inputWidth, int inputHeight,
		int startX, int startY,
		int outputWidth, int outputHeight
		) ;

void dram_eight_colors()
{
	for(int i=0; i<sizeof(colors)/sizeof(*colors); i++){
		xfer_win(0,0,320,386);
		xfer_color(colors[i],320,386);
		sleep(1);
	}
}


void draw_bmp()
{
	load_sd_bmp(frame_src, "aa_320x480.bmp"); //320x386
	convertRGB888toRGB565(
			frame_src,
			frame_target,
			320,480,
			0,0,
			320,386
			);
	xfer_win(0,0,320,386);
	xfer_pic(frame_target,320,386);

	sleep(2);

	load_sd_bmp(frame_src, "bb_320x480.bmp"); //320x386
	convertRGB888toRGB565(
			frame_src,
			frame_target,
			320,480,
			0,0,
			320,386
			);
	xfer_win(0,0,320,386);
	xfer_pic(frame_target,320,386);

	load_sd_bmp(frame_src, "shatan.bmp");	//1920x1080
	convertRGB888toRGB565(
			frame_src,
			frame_target,
			1920,1080,
			0,0,
			320,386
			);
	xfer_win(0,0,320,386);
	xfer_pic(frame_target,320,386);
}

void draw_movie()
{
	load_sd_bmp(frame_src, "shatan.bmp");	//1920x1080
	int x=300,y=400;
	int dir_x=1, dir_y=1;
	while(1){
		if(dir_x) x+=100; else x-=100;
		if(dir_y) y+=50; else y-=50;

		if(x+320>1920) {x=1920-320; dir_x=0;} else if(x<0) {x=0; dir_x=1;}
		if(y+386>1080) {y=1080-386; dir_y=0;} else if(y<0) {y=0; dir_y=1;}
//		xil_printf("x=%d, y=%d\r\n", x,y);

		XTime_SetTime(0);
		XTime t1,t2,t3;
		XTime_GetTime(&t1);
		convertRGB888toRGB565(
				frame_src,
				frame_target,
				1920,1080,
				x,y,
				320,386
				);
		XTime_GetTime(&t2);
		xfer_win(0,0,320,386);
		xfer_pic(frame_target,320,386);
		XTime_GetTime(&t3);

		XTime dt1 = ((t2-t1) * 1000000) / (COUNTS_PER_SECOND);
		XTime dt2 = ((t3-t2) * 1000000) / (COUNTS_PER_SECOND);
		xil_printf("dt1=%d, dt2=%d\r\n", dt1,dt2);
	}
}


int main()
{
    init_platform();

    print("Hello World\n\r");

    //MY_QSPI_IP_Reg_SelfTest((void *)QSPI_BASE);

	seqs_init();

    //dram_eight_colors();
    //draw_bmp();
    draw_movie();

    cleanup_platform();
    return 0;
}

//从SD卡中读取BMP图片
void load_sd_bmp(u8 *frame, const char *bmp_name)
{
	static 	FATFS fatfs;
	FIL 	fil;
	u8		bmp_head[54];
	UINT 	bmp_width,bmp_height,bmp_size;
	UINT 	br;

	f_mount(&fatfs,"",1);//挂载文件系统

	f_open(&fil, bmp_name,FA_READ);	//打开文件, 注意是bmp_24bits格式
	xil_printf("open bmp\n\r");
	f_lseek(&fil,0);//移动文件读写指针到文件开头

	f_read(&fil,bmp_head,54,&br);//读取BMP文件头

	//BMP图片的分辨率和大小
	bmp_width  = *(UINT *)(bmp_head + 0x12);
	bmp_height = *(UINT *)(bmp_head + 0x16);
	bmp_size   = *(UINT *)(bmp_head + 0x22);
	xil_printf("bmp information:\n\r");
	xil_printf(" width  = %d,\n\r height = %d,\n\r size   = %d bytes \n\r",
			bmp_width,bmp_height,bmp_size);

	//读出图片,写入DDR
	f_read(&fil, frame, bmp_width*bmp_height*3,&br);
	xil_printf("br=%d\r\n", br);

	for(int i=0; i<20; i++){
		xil_printf("%x\r\n", frame[i]);
	}

	//关闭文件
	f_close(&fil);

	Xil_DCacheFlush();     //刷新Cache,将数据更新至DDR3中
	xil_printf("display bmp\n\r");
}

void convertRGB888toRGB565(const uint8_t *input, uint16_t *output, int inputWidth, int inputHeight, int startX, int startY, int outputWidth, int outputHeight)
{
    int i, j;

    for (i = 0; i < outputHeight; i++) {
        for (j = 0; j < outputWidth; j++) {
            // Calculate the index in the RGB888 array
            int inputIndex = ((startY + i) * inputWidth + startX + j) * 3;

            // Extract RGB888 components
            uint8_t b = input[inputIndex];
            uint8_t g = input[inputIndex + 1];
            uint8_t r = input[inputIndex + 2];

            // Convert to RGB565 format
            uint16_t rgb565 = ((r>>3) << 11) | ((g>>2) << 5) | (b>>3);

            // Store in the output array
            output[i * outputWidth + j] = rgb565;
        }
    }
}

9)效果

  • 刷单色图ok
  • 刷静态bmp图片ok
  • 刷动态movie,QSPI时钟频率40MHz(对应F_CLK0=80MHz) ok,QSPI时钟频率50MHz(对应F_CLK0=100MHz) 不稳定

10)逻辑分析仪抓到波形

image 帧率4Hz image image image

每发一个数据需要1us左右。因为时钟频率80MHz,而发一个数据读写寄存器需要接近于80个时钟周期,因此需要接近于1us。

⚠️ **GitHub.com Fallback** ⚠️