Skip to content

qspi_stream_ip驱动st77903的c程序(qspi终稿进一步改进)

minichao9901 edited this page Apr 29, 2024 · 4 revisions

改进点

  • 使用了st77903的0x36指令,修改了RGB/BGR反。这样子避免了软件取反,耗费时间
  • 不使用任何memcpy函数,直接发每行数据时,找到相应的指针地址,这样节省了memcpy大约2.6ms的时间。帧率进一步提高
  • qspi_stream_ip用gpio来控制cmd/stream模式的切换,现在qspi的位宽直接用满512bits
  • 效果:非常的丝滑,帧率50Hz。现在的帧率取决于qspi的时钟频率(100MHz/2=50MHz还是低了一点)。

qspi_ip代码

  • qspi_stream_ip用gpio来控制cmd/stream模式的切换,现在qspi的位宽直接用满512bits
`timescale 1 ns / 1 ps

	module qspi_stream_ip_v1_0_S00_AXIS #
	(
		// Users to add parameters here

		// User parameters ends
		// Do not modify the parameters beyond this line

		// AXI4Stream sink: Data Width
		parameter integer C_S_AXIS_TDATA_WIDTH	= 512
	)
	(
		// Users to add ports here
		input qspi_clk,
		input qspi_clk_rstn,
		input qspi_stream_mode,	
        output csx,
        output sck,
        output [3:0] dout,
        output full,
        output empty,

		// User ports ends
		// Do not modify the ports beyond this line

		// AXI4Stream sink: Clock
		input wire  S_AXIS_ACLK,
		// AXI4Stream sink: Reset
		input wire  S_AXIS_ARESETN,
		// Ready to accept data in
		output wire  S_AXIS_TREADY,
		// Data in
		input wire [C_S_AXIS_TDATA_WIDTH-1 : 0] S_AXIS_TDATA,
		// Byte qualifier
		input wire [(C_S_AXIS_TDATA_WIDTH/8)-1 : 0] S_AXIS_TSTRB,
		// Indicates boundary of last packet
		input wire  S_AXIS_TLAST,
		// Data is in valid
		input wire  S_AXIS_TVALID
	);
	// function called clogb2 that returns an integer which has the 
	// value of the ceiling of the log base 2.
	function integer clogb2 (input integer bit_depth);
	  begin
	    for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
	      bit_depth = bit_depth >> 1;
	  end
	endfunction

	// Total number of input data.
	localparam NUMBER_OF_INPUT_WORDS  = 128;
	// bit_num gives the minimum number of bits needed to address 'NUMBER_OF_INPUT_WORDS' size of FIFO.
	localparam bit_num  = clogb2(NUMBER_OF_INPUT_WORDS-1);
	// Define the states of state machine
	// The control state machine oversees the writing of input streaming data to the FIFO,
	// and outputs the streaming data from the FIFO
	parameter [1:0] IDLE = 1'b0,        // This is the initial/idle state 

	                WRITE_FIFO  = 1'b1; // In this state FIFO is written with the
	                                    // input stream data S_AXIS_TDATA 
	wire  	axis_tready;
	// State variable
	reg mst_exec_state;  
	// FIFO implementation signals
	genvar byte_index;     
	// FIFO write enable
	wire fifo_wren;
	// FIFO full flag
//	reg fifo_full_flag;
	// FIFO write pointer
//	reg [bit_num-1:0] write_pointer;
	// sink has accepted all the streaming data and stored in FIFO
	reg writes_done;
	// I/O Connections assignments
	reg fifo_rden;
    reg xfer_start;

	assign S_AXIS_TREADY	= axis_tready;
	// Control state machine implementation
	always @(posedge S_AXIS_ACLK) 
	begin  
	  if (!S_AXIS_ARESETN) 
	  // Synchronous reset (active low)
	    begin
	      mst_exec_state <= IDLE;
	    end  
	  else
	    case (mst_exec_state)
	      IDLE: 
	        // The sink starts accepting tdata when 
	        // there tvalid is asserted to mark the
	        // presence of valid streaming data 
	          if (S_AXIS_TVALID)
	            begin
	              mst_exec_state <= WRITE_FIFO;
	            end
	          else
	            begin
	              mst_exec_state <= IDLE;
	            end
	      WRITE_FIFO: 
	        // When the sink has accepted all the streaming input data,
	        // the interface swiches functionality to a streaming master
	        if (writes_done)
	          begin
	            mst_exec_state <= IDLE;
	          end
	        else
	          begin
	            // The sink accepts and stores tdata 
	            // into FIFO
	            mst_exec_state <= WRITE_FIFO;
	          end

	    endcase
	end
	// AXI Streaming Sink 
	// 
	// The example design sink is always ready to accept the S_AXIS_TDATA  until
	// the FIFO is not filled with NUMBER_OF_INPUT_WORDS number of input words.
	assign axis_tready = ((mst_exec_state == WRITE_FIFO) && ~full);

	always@(posedge S_AXIS_ACLK)
	begin
	  if(!S_AXIS_ARESETN)
	    begin
	      writes_done <= 1'b0;
	    end  
	  else
	    if (~full)
	      begin
	        if (fifo_wren)
	          begin
	            // write pointer is incremented after every write to the FIFO
	            // when FIFO write signal is enabled.
	            writes_done <= 1'b0;
	          end
	          if (~full|| S_AXIS_TLAST)
	            begin
	              // reads_done is asserted when NUMBER_OF_INPUT_WORDS numbers of streaming data 
	              // has been written to the FIFO which is also marked by S_AXIS_TLAST(kept for optional usage).
	              writes_done <= 1'b1;
	            end
	      end  
	end

	// FIFO write enable generation
	assign fifo_wren = S_AXIS_TVALID && axis_tready;

	// Add user logic here
    wire full;
    wire empty;
    wire  [0:511] fifo_dout;
    wire [6:0] 	rd_data_count;
    wire [6:0] wr_data_count;
	
    fifo_generator_0 your_instance_name (
      .wr_clk(S_AXIS_ACLK),                // input wire wr_clk
      .rd_clk(qspi_clk),                // input wire rd_clk
      .din(S_AXIS_TDATA),                      // input wire [511 : 0] din
      .wr_en(fifo_wren),                  // input wire wr_en
      .rd_en(fifo_rden),                  // input wire rd_en
      .dout(fifo_dout),                    // output wire [511 : 0] dout
      .full(full),                    // output wire full
      .almost_full(almost_full),      // output wire almost_full
      .empty(empty),                  // output wire empty
      .almost_empty(almost_empty),    // output wire almost_empty
      .rd_data_count(rd_data_count),  // output wire [6 : 0] rd_data_count
      .wr_data_count(wr_data_count)  // output wire [6 : 0] wr_data_count
    );
    
    reg empty_d;
    always @(posedge qspi_clk or negedge qspi_clk_rstn)
    if(qspi_clk_rstn==0)
        empty_d<=0;
    else
        empty_d<=empty;
    
    assign first_rd_en=(~empty) & empty_d & xfer_rdy;            //第一次读,由empty信号下降沿自动启动   


    always @(posedge qspi_clk or negedge qspi_clk_rstn)
    if(qspi_clk_rstn==0) begin
        fifo_rden <= 0;
        xfer_start<=0;
    end
    else begin    
        fifo_rden <= (first_rd_en|xfer_final) & (~empty);  //fifo不为空的时候才可以读    
        xfer_start<= fifo_rden;
    end
    
//    parameter CNT_DLY_MAX=10;
//    reg [6:0] dly_cnt;
//    reg dly_cnt_en;
    
//    always @(posedge qspi_clk or negedge qspi_clk_rstn)
//    if(qspi_clk_rstn==0)
//        dly_cnt<=0;
//    else if(add_dly_cnt) begin
//        if(end_dly_cnt)
//            dly_cnt<=0;
//        else
//            dly_cnt<=dly_cnt+1; 
//    end 
    
//    always @(posedge qspi_clk or negedge qspi_clk_rstn)
//    if(qspi_clk_rstn==0) 
//        dly_cnt_en<=0;
//    else if(fifo_rden)
//        dly_cnt_en<=1;
//    else if(end_dly_cnt)
//        dly_cnt_en<=0; 
        
//    assign add_dly_cnt=dly_cnt_en;
//    assign end_dly_cnt=add_dly_cnt && (dly_cnt==CNT_DLY_MAX-1); 
//    assign xfer_start= end_dly_cnt;
    


    reg [7:0] cmd;
    reg [23:0] addr;
    reg [0:512-1] din; 
    reg [10:0]  vadid_length;  /* Max suppport DATA_SIZE=2047*8=16376 */   
    reg [1:0] data_mode;  /*00: cmd, 01: cmd+addr, 10: cmd+addr+1wire data, 11: cmd+addr+4wire data*/ 
    reg  cs_mode;  /*IDLE state, 0: cs=0, 1:cs=1, to support multi data_bursts while keep cs=0*/
    reg byte_mode; /* (0:for init) 0->1->2->3,  (1: for rgb565 color) 2->3->0->1 */ 
    
    qspi2  #(.DATA_SIZE(512)) qspi2_inst(
       // Input Ports - Single Bit
       .clk                (qspi_clk),                  
       .rst_n              (qspi_clk_rstn),   
       .cs_mode            (cs_mode),              
       .xfer_start         (xfer_start),      
       // Input Ports - Busses
       .addr         (addr),      
       .cmd           (cmd),        
       .data_mode     (data_mode),  
       .din        (din),     
       .vadid_length (vadid_length),
       // Output Ports - Single Bit
       .csx                (csx),             
       .sck                (sck),             
       .xfer_final         (xfer_final),      
       .xfer_rdy           (xfer_rdy),        
       // Output Ports - Busses
       .dout          (dout)       
       // InOut Ports - Single Bit
       // InOut Ports - Busses
    );    

    reg  [0:511] fifo_dout_rev; 
    integer i;    
    always @(*)
    begin
        for (i = 0; i < 64; i = i + 1) begin
            fifo_dout_rev[8*i +: 8] = fifo_dout[(504-8*i) +: 8]; 
        end        
    end
    
    wire [31:0] reg0;
    assign reg0=fifo_dout[480:511];  
  

    parameter CNT_MAX=19;
    reg [4:0] div_cnt;
    
    always @(posedge qspi_clk or negedge qspi_clk_rstn)
    if(qspi_clk_rstn==0)
        div_cnt<=0;
    else if(add_div_cnt) begin
        if(end_div_cnt)
            div_cnt<=0;
        else
            div_cnt<=div_cnt+1; 
    end 
    
    assign add_div_cnt=(qspi_stream_mode==1) & xfer_final;
    assign end_div_cnt=add_div_cnt && (div_cnt==CNT_MAX-1);       
    
    always @(*)
    if(qspi_stream_mode==0) begin 
        cmd=reg0[7:0];
        addr={8'h00, reg0[15:8], 8'h00};
        {byte_mode,cs_mode,data_mode}=reg0[19:16];
        vadid_length={3'b000,reg0[27:20]};
        
        din={fifo_dout_rev[32:511],32'h0};                                                                   
    end
    else begin
        if(div_cnt==CNT_MAX-1) begin /*final burst of a line */
            cmd=8'hde;
            addr={8'h00, 8'h60, 8'h00};
            {byte_mode,cs_mode,data_mode}=4'b0111;
            vadid_length=48;                        
        end                                                    
        else begin
            cmd=8'hde;
            addr={8'h00, 8'h60, 8'h00};
            {byte_mode,cs_mode,data_mode}=4'b0011;
            vadid_length=64;                        
        end
        
        din=fifo_dout_rev[0:511]; 
    end    

   //仅供波形分析使用
    reg [31:0] regx1 [0:15];    
    always @(*)
    begin
        for (i = 0; i < 16; i = i + 1) begin
            regx1[i]=fifo_dout[32*i+:32];   
        end         
    end 
    
    reg [31:0] regx2 [0:15];    
    always @(*)
    begin
        for (i = 0; i < 16; i = i + 1) begin
            regx2[i]=fifo_dout_rev[32*i+:32];   
        end         
    end 

    reg [31:0] regx3 [0:15];    
    always @(*)
    begin
        for (i = 0; i < 16; i = i + 1) begin
            regx3[i]=din[32*i+:32];   
        end         
    end               


	// User logic ends

	endmodule

bd设计

image image

刷单色图程序

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


#define PS_KEY  (54 + 0)  //EMIO0,对应的GPIO编号为54+0=54
#define FULL_PIN  (54 + 1)    //EMIO1,对应的GPIO编号为54+1=55
#define EMPTY_PIN  (54 + 2)    //EMIO2,对应的GPIO编号为54+2=56

#define INPUT		1
#define OUTPUT		0
XGpioPs GpioPs;	//GPIO实例对象
void PS_GPIO_Init()
{
	XGpioPs_Config *ConfigPtr;
	ConfigPtr = XGpioPs_LookupConfig(XPAR_XGPIOPS_0_DEVICE_ID);
	XGpioPs_CfgInitialize(&GpioPs, ConfigPtr, ConfigPtr->BaseAddr);
}

/**
  *****************************************************
  * @brief	初始化特定GPIO口的模式与状态
  * @param	GPIO_Num	GPIO编号(MIO为0~53,EMIO从54开始)
  * @param	Dir			输入/输出:OUTPUT为输出,INPUT为输入
  * @param	Data		输出电平高低:0为低,1为高(若设置为输入则此处数据无影响)
  * @usage	//设置GPIO46为输出模式,输出为高电平
  * 		GPIO_SetMode(46, OUTPUT, 1);
  *****************************************************
**/
void PS_GPIO_SetMode(uint8_t GPIO_Num, uint8_t Dir, uint8_t Data)
{
	if(Dir == INPUT) {
		XGpioPs_SetDirectionPin(&GpioPs, GPIO_Num, 0);
	} else if(Dir == OUTPUT){
		XGpioPs_SetDirectionPin(&GpioPs, GPIO_Num, 1);
		XGpioPs_SetOutputEnablePin(&GpioPs, GPIO_Num, 1);
		XGpioPs_WritePin(&GpioPs, GPIO_Num, Data);
	}
}

/**
  *****************************************************
  * @brief	控制特定GPIO口的输出高/低电平,使用前必须先用GPIO_SetMode设置该GPIO口为"输出"模式
  * @param	GPIO_Num	GPIO编号(MIO为0~53,EMIO从54开始)
  * @param	Data		输出电平高低:0为低,1为高
  * @usage	//控制GPIO46输出高电平
  * 		GPIO_SetPort(46, 1);
  *****************************************************
**/
void PS_GPIO_SetPort(uint8_t GPIO_Num, uint8_t Data)
{
	XGpioPs_WritePin(&GpioPs, GPIO_Num, Data);	//输出高/低电平,0为低,1为高
}

/**
  *****************************************************
  * @brief	读取特定GPIO口的输入电平,使用前必须先用GPIO_SetMode设置该GPIO口为"输入"模式
  * @param	GPIO_Num	GPIO编号(MIO为0~53,EMIO从54开始)
  * @return	返回采集的GPIO电平值,0为低,1为高
  * @usage	//采集GPIO46的电平,存放在state
  * 		state = GPIO_GetPort(46, state);
  *****************************************************
**/
uint8_t PS_GPIO_GetPort(uint8_t GPIO_Num)
{
	return XGpioPs_ReadPin(&GpioPs, GPIO_Num);		//输出高/低电平,0为低,1为高
}


void gpio_init()
{
    PS_GPIO_Init(); //初始化PS端MIO和EMIO
    PS_GPIO_SetMode(PS_KEY, OUTPUT, 0);
    PS_GPIO_SetMode(FULL_PIN, INPUT, 0);
    PS_GPIO_SetMode(EMPTY_PIN, INPUT, 1);
}

void qspi_mode_set(u8 mode)
{
	PS_GPIO_SetPort(PS_KEY, mode);
}

u32 read_full()
{
	return PS_GPIO_GetPort(FULL_PIN);
}

u32 read_empty()
{
	return PS_GPIO_GetPort(EMPTY_PIN);
}

/***********************************************************************************/

#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;

//#define BIST_MODE
t_buf WriteBuffer[]={
		{{0xf0, 0xc3}, {0x00},2},
		{{0xf0, 0x96}, {0x00},2},
		{{0xf0, 0xa5}, {0x00},2},
		{{0xe9, 0x20}, {0x00},2},
		{{0xe7,  0x80, 0x77, 0x1f, 0xcc}, {0x00}, 5},
		{{0xc1,  0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Normal:VGHS/VGLS/VSP/VSN电压
		{{0xc2,  0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Idle: VGHS/VGLS/VSP/VSN电压
		{{0xc3,  0x22, 0x02, 0x22, 0x04}, {0x00},5},//Normal: VGH/VGL/VSP/VSN Clk
		{{0xc4,  0x22, 0x02, 0x22, 0x04}, {0x00},5},//Idle: VGH/VGL/VSP/VSN Clk
		{{0xc5,  0xed},{0x00},2}, //VCOM
		{{0xe0, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
		{{0xe1, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
		{{0xe5, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
		{{0xe6, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
		{{0xec, 0x40, 0x03},{0x00},3},
		{{0x36,  0x0c},{0x00},2},
		{{0x3a,  0x07},{0x00},2},
		{{0xb2,  0x00},{0x00},2},//GIP pattern
		{{0xb3,  0x01},{0x00},2},//video mode dot-inversion
		{{0xb4,  0x01},{0x00},2},
		{{0xb5,  0x00, 0x08, 0x00, 0x08},{0x00},5}, //vfp
		{{0xb6,  0xc7, 0x31},{0x00},3},
		{{0xa5,  0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
		{{0xa6,  0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
		{{0xba,  0x0a, 0x5a, 0x23, 0x10, 0x25, 0x02, 0x00},{0x00},8},
		{{0xbb,  0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
		{{0xbc,  0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
		{{0xbd, 0xa1, 0xb2, 0x2b, 0x1a, 0x56, 0x43, 0x34, 0x65, 0xff, 0xff, 0x0f},{0x00},12},
		{{0x35,  0x00},{0x00},2},
		{{0x21},{0x00},1},
		{{0x11},{0x00},1},
		{{0xff},{0x00},0},
		{{0x29},{0x00},1},
		{{0xff},{0x00},0},
		#ifdef BIST_MODE
		{{0xb0,  0xa5},{0x00},2},
		{{0xcc,  0x40, 0x00, 0x3f, 0x00, 0x14, 0x14, 0x20, 0x20, 0x03},{0x00},10},
		#endif
};

/*************************************************
 * reg rw functions
 ************************************************/
#define DMA_BASE_ADRR  0x40000000
#define DDR_BASE_ADDR  0x10000000

void wait_for_idle()
{
	u32 tmp;
	while(1){
		tmp=Xil_In32(DMA_BASE_ADRR+04);
		if(((tmp>>1)&0x1) == 1){
			break;
		}
	}
}

void dma_xfer(u32 mem_addr, u32 length)
{/*用于初始化阶段,需要刷新Cache,确保初始化成功*/
	//Xil_DCacheFlush();
	Xil_DCacheFlushRange(mem_addr, length);


	Xil_Out32(DMA_BASE_ADRR,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x30,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x18,  mem_addr);
	Xil_Out32(DMA_BASE_ADRR+0x28,  length);	 /*length is bytes*/
	wait_for_idle();
	//usleep(10);
}

void dma_xfer2(u32 mem_addr, u32 length)
{/*发送数据阶段,可以不用Cache*/
	//Xil_DCacheFlush();
	Xil_DCacheFlushRange(mem_addr, length);

	Xil_Out32(DMA_BASE_ADRR,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x30,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x18,  mem_addr);
	Xil_Out32(DMA_BASE_ADRR+0x28,  length);	 /*length is bytes*/
	wait_for_idle();
	//usleep(10);
}

void set_cfg(u32 mem_addr, u8 cmd, u8 addr, u8 mode, u8 work_mode, u16 length)
{
	u32 tmp=cmd;
	tmp|=addr<<8;
	tmp|=mode<<16;
	tmp|=length<<20;
//	tmp|=work_mode<<30;
	Xil_Out32(mem_addr,  tmp);
}

u32 get_cfg(u8 cmd, u8 addr, u8 mode, u8 work_mode, u16 length)
{
	u32 tmp=cmd;
	tmp|=addr<<8;
	tmp|=mode<<16;
	tmp|=length<<20;
//	tmp|=work_mode<<30;
	return tmp;
}

void set_npdata8(u32 mem_addr, u8 *pdata, u32 length){
	u32 addr=mem_addr;
	while(length--){
		Xil_Out8(addr++, *pdata++);
	}
}

/*************************************************
 * basic xfer functions
 ************************************************/
void xfer_cmd_pdata8(u32 mem_addr, u8 cmd, u8* pdata, u32 length){
	if(length==0)
		set_cfg(mem_addr, 0xde, cmd, 0b0101, 0b00, length);  /*byte_mode=0, cs_mode=1, data_mode=0b01*/
	else
		set_cfg(mem_addr, 0xde, cmd, 0b0110, 0b00, length); /*byte_mode=0, cs_mode=1, data_mode=0b10*/

	set_npdata8(mem_addr+4, pdata,length);
	dma_xfer(mem_addr, 4+length);
}


void xfer_cmd_pdata16_dma(u32 mem_addr, u32 length){
	dma_xfer2(mem_addr, length);
}


void seqs_init() {

	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(DDR_BASE_ADDR, cmd, pdata, length);
		usleep(10); //this delay is need
		//xil_printf("Exec CMD=%x\r\n\r\n", cmd);
	}
}

/*************************************************
 * application functions
 ************************************************/

#define LCD_BPP             (24)

#define LCD_X_SIZE          (400U)                      /* available x pixel size */
#define LCD_Y_SIZE          (400U)		                /* available y pixle size */
#define LCD_PBYTE           ((LCD_BPP + 0) / 8)         /* bytes in pixel unit */
#define LCD_HBYTE           (LCD_X_SIZE * LCD_PBYTE)    /* bytes in horizontal line */

#define LCD_VSW             (1U)
#define LCD_HFP             (8U)
#define LCD_HBP             (8U)

#define LCD_TE_OFT          (25U)


typedef u8 (*ArrayPtr)[400][20 * 60];
ArrayPtr ptr1=(ArrayPtr)0x10100000;
ArrayPtr ptr2=(ArrayPtr)0x10200000;
ArrayPtr ptr3=(ArrayPtr)0x10300000;
ArrayPtr ptr4=(ArrayPtr)0x10400000;
ArrayPtr ptr5=(ArrayPtr)0x10500000;
ArrayPtr ptr6=(ArrayPtr)0x10600000;
ArrayPtr ptr7=(ArrayPtr)0x10700000;
ArrayPtr ptr8=(ArrayPtr)0x10800000;

void fill_data(ArrayPtr ptr, u8 r, u8 g, u8 b) {
//    u32 cmd1 = get_cfg(0xde, 0x60, 0b0011, 0b00, 60);
//    u32 cmd2 = get_cfg(0xde, 0x60, 0b0111, 0b00, 60);

    // 遍历数组的每一行
    for (int i = 0; i < 400; ++i) {
        // 遍历每一行的每一个packet
        for (int j = 0; j < 20; ++j) {
            // 计算当前packet的起始地址
        	u8 *packet = (u8 *)(ptr + i) + j * 60; // ptr指向整个二维数组,ptr+i指向第i行,然后加上j个packet的偏移

            // 填充cmd部分
//            u32 *cmd_ptr = (u32 *)packet;
//            *cmd_ptr = (j == 19) ? cmd2 : cmd1; // 第20个packet使用cmd2,其余使用cmd1

            // 移动到color数据部分的起始地址
            //packet += sizeof(cmd1);

            // 填充color数据部分
            for (int k = 0; k < 60; k += 3) {
                packet[k] = b;
                packet[k + 1] = g;
                packet[k + 2] = r;
            }
        }
    }
}

void fill_color()
{
	for(int i=0; i<8; i++){
			switch(i){
			case 0: fill_data(ptr1, 0xff,0x00,0x00); break;
			case 1: fill_data(ptr2, 0x00,0xff,0x00); break;
			case 2: fill_data(ptr3, 0x00,0x00,0xff); break;
			case 3: fill_data(ptr4, 0xff,0xff,0x00); break;
			case 4: fill_data(ptr5, 0xff,0x00,0xff); break;
			case 5: fill_data(ptr6, 0x00,0xff,0xff); break;
			case 6: fill_data(ptr7, 0xff,0xff,0xff); break;
			case 7: fill_data(ptr8, 0x00,0x00,0x00); break;
			}
	}
}

ArrayPtr get_color_ptr(u32 index)
{
	ArrayPtr tmp;
	switch(index){
		case 0: tmp=ptr1; break;
		case 1: tmp=ptr2; break;
		case 2: tmp=ptr3; break;
		case 3: tmp=ptr4; break;
		case 4: tmp=ptr5; break;
		case 5: tmp=ptr6; break;
		case 6: tmp=ptr7; break;
		case 7: tmp=ptr8; break;
	}

	return tmp;
}

void dram_eight_colors()
{
	u32 loop_cnt=0;
	u8 color_index=0;
	fill_color();

    while (1)
    {
    	ArrayPtr my_ptr=get_color_ptr(color_index);

        /* vs(0x61) packet */
        for (int i = 0; i < LCD_VSW; i++)
        {
        	xfer_cmd_pdata8(DDR_BASE_ADDR, 0x61,NULL,0);
            usleep(40);
        }

        /* hbp(0x60) packet */
        for (int i = 0; i < LCD_HBP; i++)
        {
        	xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
            usleep(40);
        }

        qspi_mode_set(1);
        /* transmit display cache data to lcd line by line */
        for (int i = 0; i < LCD_Y_SIZE; i+=1)
        {
            //xfer_cmd_enter_stream(DDR_BASE_ADDR);
        	xfer_cmd_pdata16_dma((u32)my_ptr[i],20*60);
           usleep(48);
//        	if(read_full()){
//        		//usleep(2000);
//        		while(read_full());
//        	}
        }
        //usleep(2*000);
        //while(read_empty()==0);
        qspi_mode_set(0);


        /* hfp(0x60) packet */
        for (int i = 0; i < LCD_HFP; i++)
        {
        	xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
            usleep(40);
        }

        loop_cnt++;
        if(loop_cnt%60==0){
			color_index++;
			if(color_index==8)
				color_index=0;
        }
    }

}




int main()
{
    init_platform();
    //Xil_DCacheDisable();

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

    gpio_init();
    qspi_mode_set(0);
	seqs_init();
	dram_eight_colors();

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

    cleanup_platform();
    return 0;
}

刷图片程序

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


#define PS_KEY  (54 + 0)    //PS_KEY为EMIO0,对应的GPIO编号为54+0=54
#define INPUT		1
#define OUTPUT		0
XGpioPs GpioPs;	//GPIO实例对象
void PS_GPIO_Init()
{
	XGpioPs_Config *ConfigPtr;
	ConfigPtr = XGpioPs_LookupConfig(XPAR_XGPIOPS_0_DEVICE_ID);
	XGpioPs_CfgInitialize(&GpioPs, ConfigPtr, ConfigPtr->BaseAddr);
}

/**
  *****************************************************
  * @brief	初始化特定GPIO口的模式与状态
  * @param	GPIO_Num	GPIO编号(MIO为0~53,EMIO从54开始)
  * @param	Dir			输入/输出:OUTPUT为输出,INPUT为输入
  * @param	Data		输出电平高低:0为低,1为高(若设置为输入则此处数据无影响)
  * @usage	//设置GPIO46为输出模式,输出为高电平
  * 		GPIO_SetMode(46, OUTPUT, 1);
  *****************************************************
**/
void PS_GPIO_SetMode(uint8_t GPIO_Num, uint8_t Dir, uint8_t Data)
{
	if(Dir == INPUT) {
		XGpioPs_SetDirectionPin(&GpioPs, GPIO_Num, 0);
	} else if(Dir == OUTPUT){
		XGpioPs_SetDirectionPin(&GpioPs, GPIO_Num, 1);
		XGpioPs_SetOutputEnablePin(&GpioPs, GPIO_Num, 1);
		XGpioPs_WritePin(&GpioPs, GPIO_Num, Data);
	}
}


void qspi_mode_set(u8 mode)
{
    PS_GPIO_Init(); //初始化PS端MIO和EMIO
    PS_GPIO_SetMode(PS_KEY, OUTPUT, mode);
}

/***********************************************************************************/



#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;

//#define BIST_MODE
t_buf WriteBuffer[]={
		{{0xf0, 0xc3}, {0x00},2},
		{{0xf0, 0x96}, {0x00},2},
		{{0xf0, 0xa5}, {0x00},2},
		{{0xe9, 0x20}, {0x00},2},
		{{0xe7,  0x80, 0x77, 0x1f, 0xcc}, {0x00}, 5},
		{{0xc1,  0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Normal:VGHS/VGLS/VSP/VSN电压
		{{0xc2,  0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Idle: VGHS/VGLS/VSP/VSN电压
		{{0xc3,  0x22, 0x02, 0x22, 0x04}, {0x00},5},//Normal: VGH/VGL/VSP/VSN Clk
		{{0xc4,  0x22, 0x02, 0x22, 0x04}, {0x00},5},//Idle: VGH/VGL/VSP/VSN Clk
		{{0xc5,  0xed},{0x00},2}, //VCOM
		{{0xe0, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
		{{0xe1, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
		{{0xe5, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
		{{0xe6, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
		{{0xec, 0x40, 0x03},{0x00},3},
		//{{0x36,  0x0c},{0x00},2},
		{{0x36,  0x04},{0x00},2},
		{{0x3a,  0x07},{0x00},2},
		{{0xb2,  0x00},{0x00},2},//GIP pattern
		{{0xb3,  0x01},{0x00},2},//video mode dot-inversion
		{{0xb4,  0x01},{0x00},2},
		{{0xb5,  0x00, 0x08, 0x00, 0x08},{0x00},5}, //vfp
		{{0xb6,  0xc7, 0x31},{0x00},3},
		{{0xa5,  0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
		{{0xa6,  0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
		{{0xba,  0x0a, 0x5a, 0x23, 0x10, 0x25, 0x02, 0x00},{0x00},8},
		{{0xbb,  0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
		{{0xbc,  0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
		{{0xbd, 0xa1, 0xb2, 0x2b, 0x1a, 0x56, 0x43, 0x34, 0x65, 0xff, 0xff, 0x0f},{0x00},12},
		{{0x35,  0x00},{0x00},2},
		{{0x21},{0x00},1},
		{{0x11},{0x00},1},
		{{0xff},{0x00},0},
		{{0x29},{0x00},1},
		{{0xff},{0x00},0},
		#ifdef BIST_MODE
		{{0xb0,  0xa5},{0x00},2},
		{{0xcc,  0x40, 0x00, 0x3f, 0x00, 0x14, 0x14, 0x20, 0x20, 0x03},{0x00},10},
		#endif
};

/*************************************************
 * reg rw functions
 ************************************************/
#define DMA_BASE_ADRR  0x40000000
#define DDR_BASE_ADDR  0x10000000

void wait_for_idle()
{
	u32 tmp;
	while(1){
		tmp=Xil_In32(DMA_BASE_ADRR+04);
		if(((tmp>>1)&0x1) == 1){
			break;
		}
	}
}

void dma_xfer(u32 mem_addr, u32 length)
{/*用于初始化阶段,需要刷新Cache,确保初始化成功*/
	//Xil_DCacheFlush();
	Xil_DCacheFlushRange(mem_addr, length);

	Xil_Out32(DMA_BASE_ADRR,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x30,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x18,  mem_addr);
	Xil_Out32(DMA_BASE_ADRR+0x28,  length);	 /*length is bytes*/
	//wait_for_idle();
	//usleep(10);
}

void dma_xfer2(u32 mem_addr, u32 length)
{/*发送数据阶段,可以不用Cache*/
	//Xil_DCacheFlush();
	Xil_DCacheFlushRange(mem_addr, length);

	Xil_Out32(DMA_BASE_ADRR,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x30,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x18,  mem_addr);
	Xil_Out32(DMA_BASE_ADRR+0x28,  length);	 /*length is bytes*/
	wait_for_idle();
	//usleep(10);
}

void set_cfg(u32 mem_addr, u8 cmd, u8 addr, u8 mode, u8 work_mode, u16 length)
{
	u32 tmp=cmd;
	tmp|=addr<<8;
	tmp|=mode<<16;
	tmp|=length<<20;
	tmp|=work_mode<<30;
	Xil_Out32(mem_addr,  tmp);
}

u32 get_cfg(u8 cmd, u8 addr, u8 mode, u8 work_mode, u16 length)
{
	u32 tmp=cmd;
	tmp|=addr<<8;
	tmp|=mode<<16;
	tmp|=length<<20;
	tmp|=work_mode<<30;
	return tmp;
}


void set_npdata8(u32 mem_addr, u8 *pdata, u32 length){
	u32 addr=mem_addr;
	while(length--){
		Xil_Out8(addr++, *pdata++);
	}
}


/*************************************************
 * basic xfer functions
 ************************************************/
void xfer_cmd_pdata8(u32 mem_addr, u8 cmd, u8* pdata, u32 length){
	if(length==0)
		set_cfg(mem_addr, 0xde, cmd, 0b0101,  0b00, length);  /*byte_mode=0, cs_mode=1, data_mode=0b01*/
	else
		set_cfg(mem_addr, 0xde, cmd, 0b0110,  0b00, length); /*byte_mode=0, cs_mode=1, data_mode=0b10*/

	set_npdata8(mem_addr+4, pdata,length);
	dma_xfer(mem_addr, 4+length);
}


void xfer_cmd_pdata16_dma(u32 mem_addr, u32 length){
	//dma_xfer(mem_addr, 20*64);
	dma_xfer2(mem_addr, length);
}


void seqs_init() {

	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(DDR_BASE_ADDR, cmd, pdata, length);
		usleep(10); //this delay is need
		//xil_printf("Exec CMD=%x\r\n\r\n", cmd);
	}
}

/*************************************************
 * application functions
 ************************************************/
u8 frame_src[1920*1080*3];
u8 frame_target[400*400*3];

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


#define LCD_BPP             (24)

#define LCD_X_SIZE          (400U)                      /* available x pixel size */
#define LCD_Y_SIZE          (400U)		                /* available y pixle size */
#define LCD_PBYTE           ((LCD_BPP + 0) / 8)         /* bytes in pixel unit */
#define LCD_HBYTE           (LCD_X_SIZE * LCD_PBYTE)    /* bytes in horizontal line */

#define LCD_VSW             (1U)
#define LCD_HFP             (8U)
#define LCD_HBP             (8U)

#define LCD_TE_OFT          (25U)


typedef u8 (*ArrayPtr)[400][20 * 60];
ArrayPtr ptr1=(ArrayPtr)0x11000000;



void fill_pic(ArrayPtr ptr, u8 *pcolor) {
//    u32 cmd1 = get_cfg(0xde, 0x60, 0b0011,  0b00, 60);
//    u32 cmd2 = get_cfg(0xde, 0x60, 0b0111,  0b00, 60);

    // 遍历数组的每一行
    for (int i = 0; i < 400; ++i) {
        // 遍历每一行的每一个packet
        for (int j = 0; j < 20; ++j) {
            // 计算当前packet的起始地址
        	u8 *packet = (u8 *)(ptr + i) + j * 60; // ptr指向整个二维数组,ptr+i指向第i行,然后加上j个packet的偏移

//            // 填充cmd部分
//            u32 *cmd_ptr = (u32 *)packet;
//            *cmd_ptr = (j == 19) ? cmd2 : cmd1; // 第20个packet使用cmd2,其余使用cmd1
//
//            // 移动到color数据部分的起始地址
//            packet += sizeof(cmd1);

            // 填充color数据部分
			memcpy(packet, pcolor, 60);
			pcolor+=60;
//            for (int k = 0; k < 60; k += 3) {
//            	u8 r=*pcolor++;
//            	u8 g=*pcolor++;
//            	u8 b=*pcolor++;
//
//                packet[k] = r;
//                packet[k + 1] = g;
//                packet[k + 2] = b;
//            }
        }
    }
}

XTime t1,t2,t3,t4;
void draw_bmp()
{
	XTime_GetTime(&t1);
	load_sd_bmp(frame_src, "shatan.bmp");	//1920x1080
//	XTime_GetTime(&t2);
	convertRGB888toRGB888(
			frame_src,
			frame_target,
			1920,1080,
			300,500,
			400,400
			);
//	XTime_GetTime(&t3);
	fill_pic(ptr1,frame_target);
//	XTime_GetTime(&t4);
//
//	XTime dt21 = ((t2-t1) * 1000000) / (COUNTS_PER_SECOND);
//	XTime dt32 = ((t3-t2) * 1000000) / (COUNTS_PER_SECOND);
//	XTime dt43 = ((t4-t3) * 1000000) / (COUNTS_PER_SECOND);
//	xil_printf("dt21=%d, dt32=%d, dt43=%d\r\n", dt21,dt32,dt43);

    while (1)
    {
        /* vs(0x61) packet */
        for (int i = 0; i < LCD_VSW; i++)
        {
        	xfer_cmd_pdata8(DDR_BASE_ADDR, 0x61,NULL,0);
            usleep(40);
        }

        /* hbp(0x60) packet */
        for (int i = 0; i < LCD_HBP; i++)
        {
        	xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
            usleep(40);
        }

        /* transmit display cache data to lcd line by line */
        qspi_mode_set(1);
        for (int i = 0; i < LCD_Y_SIZE; i+=1)
        {
        	//xfer_cmd_enter_stream(DDR_BASE_ADDR);
        	xfer_cmd_pdata16_dma((u32)ptr1[i],20*60);
            usleep(47);
        }
        qspi_mode_set(0);

        /* hfp(0x60) packet */
        for (int i = 0; i < LCD_HFP; i++)
        {
        	xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
            usleep(40);
        }

    }
}

int main()
{
    //init_platform();
    //Xil_DCacheDisable();

    qspi_mode_set(0);
	seqs_init();
    draw_bmp();
    //draw_movie();
    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 convertRGB888toRGB888(const uint8_t *input, uint8_t *output, int inputWidth, int inputHeight, int startX, int startY, int outputWidth, int outputHeight)
{
    int i, j;

    for (i = 0; i < outputHeight; i++) {
        int inputIndex = ((startY + i) * inputWidth + startX + 0) * 3;
        int outputIndex=(i * outputWidth + 0)*3;
    	memcpy(&output[outputIndex], &input[inputIndex],outputWidth*3);

//        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];
//
//            int outputIndex=(i * outputWidth + j)*3;
//            // Store in the output array
//            output[outputIndex] = r;
//            output[outputIndex+1] = g;
//            output[outputIndex+2] = b;
//        }
    }
}

利用私有定时器刷新的程序

/**
  *****************************************************************************
  * 					存放用户中断处理函数,方便统一处理
  *****************************************************************************
  *
  * @File   : ISR.c
  * @By     : Sun
  * @Version: V0.5
  * @Date   : 2022 / 06 / 01
  * @Shop	: https://xiaomeige.taobao.com/
  *
  *****************************************************************************
**/

#include "ISR.h"


u32 Refresh_flag=0;

/**
  *****************************************************
  * @brief	私有定时器中断处理程序
  * @tag	本函数用来处理私有定时器中断,在内部加入用户程序即可
  *****************************************************
**/
void ScuTimer_IRQ_Handler(void *CallBackRef)
{
	/* ↓↓↓用户处理↓↓↓ */
	Refresh_flag=1;

	/* ↑↑↑结束处理↑↑↑ */
    XScuTimer_ClearInterruptStatus(&ScuTimer);
}
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xil_io.h"
#include "sleep.h"
#include "ff.h"
#include "xil_cache.h"
#include "xtime_l.h"
#include <string.h>
#include "xgpiops.h"
#include "xiao_mei_ge/COMMON.h"


#define PS_KEY  (54 + 0)    //PS_KEY为EMIO0,对应的GPIO编号为54+0=54
#define EMPTY_PIN  (54 + 2)    //EMIO2,对应的GPIO编号为54+2=56
#define INPUT		1
#define OUTPUT		0
XGpioPs GpioPs;	//GPIO实例对象
//void PS_GPIO_Init()
//{
//	XGpioPs_Config *ConfigPtr;
//	ConfigPtr = XGpioPs_LookupConfig(XPAR_XGPIOPS_0_DEVICE_ID);
//	XGpioPs_CfgInitialize(&GpioPs, ConfigPtr, ConfigPtr->BaseAddr);
//}

/**
  *****************************************************
  * @brief	初始化特定GPIO口的模式与状态
  * @param	GPIO_Num	GPIO编号(MIO为0~53,EMIO从54开始)
  * @param	Dir			输入/输出:OUTPUT为输出,INPUT为输入
  * @param	Data		输出电平高低:0为低,1为高(若设置为输入则此处数据无影响)
  * @usage	//设置GPIO46为输出模式,输出为高电平
  * 		GPIO_SetMode(46, OUTPUT, 1);
  *****************************************************
**/
//void PS_GPIO_SetMode(uint8_t GPIO_Num, uint8_t Dir, uint8_t Data)
//{
//	if(Dir == INPUT) {
//		XGpioPs_SetDirectionPin(&GpioPs, GPIO_Num, 0);
//	} else if(Dir == OUTPUT){
//		XGpioPs_SetDirectionPin(&GpioPs, GPIO_Num, 1);
//		XGpioPs_SetOutputEnablePin(&GpioPs, GPIO_Num, 1);
//		XGpioPs_WritePin(&GpioPs, GPIO_Num, Data);
//	}
//}

/**
  *****************************************************
  * @brief	读取特定GPIO口的输入电平,使用前必须先用GPIO_SetMode设置该GPIO口为"输入"模式
  * @param	GPIO_Num	GPIO编号(MIO为0~53,EMIO从54开始)
  * @return	返回采集的GPIO电平值,0为低,1为高
  * @usage	//采集GPIO46的电平,存放在state
  * 		state = GPIO_GetPort(46, state);
  *****************************************************
**/
//uint8_t PS_GPIO_GetPort(uint8_t GPIO_Num)
//{
//	return XGpioPs_ReadPin(&GpioPs, GPIO_Num);		//输出高/低电平,0为低,1为高
//}


void qspi_mode_set(u8 mode)
{
    PS_GPIO_Init(); //初始化PS端MIO和EMIO
    PS_GPIO_SetMode(PS_KEY, OUTPUT, mode);
    PS_GPIO_SetMode(EMPTY_PIN, INPUT, 1);
}

u32 read_empty()
{
	return PS_GPIO_GetPort(EMPTY_PIN);
}



int main_scutimer_irq(void)
{
	//GIC通用中断控制器初始化
	ScuGic_Init();

	ScuTimer_Int_Init(55);

	return 0;
}

/***********************************************************************************/



#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;

//#define BIST_MODE
t_buf WriteBuffer[]={
		{{0xf0, 0xc3}, {0x00},2},
		{{0xf0, 0x96}, {0x00},2},
		{{0xf0, 0xa5}, {0x00},2},
		{{0xe9, 0x20}, {0x00},2},
		{{0xe7,  0x80, 0x77, 0x1f, 0xcc}, {0x00}, 5},
		{{0xc1,  0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Normal:VGHS/VGLS/VSP/VSN电压
		{{0xc2,  0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Idle: VGHS/VGLS/VSP/VSN电压
		{{0xc3,  0x22, 0x02, 0x22, 0x04}, {0x00},5},//Normal: VGH/VGL/VSP/VSN Clk
		{{0xc4,  0x22, 0x02, 0x22, 0x04}, {0x00},5},//Idle: VGH/VGL/VSP/VSN Clk
		{{0xc5,  0xed},{0x00},2}, //VCOM
		{{0xe0, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
		{{0xe1, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
		{{0xe5, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
		{{0xe6, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
		{{0xec, 0x40, 0x03},{0x00},3},
		//{{0x36,  0x0c},{0x00},2},
		{{0x36,  0x04},{0x00},2},
		{{0x3a,  0x07},{0x00},2},
		{{0xb2,  0x00},{0x00},2},//GIP pattern
		{{0xb3,  0x01},{0x00},2},//video mode dot-inversion
		{{0xb4,  0x01},{0x00},2},
		{{0xb5,  0x00, 0x08, 0x00, 0x08},{0x00},5}, //vfp
		{{0xb6,  0xc7, 0x31},{0x00},3},
		{{0xa5,  0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
		{{0xa6,  0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
		{{0xba,  0x0a, 0x5a, 0x23, 0x10, 0x25, 0x02, 0x00},{0x00},8},
		{{0xbb,  0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
		{{0xbc,  0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
		{{0xbd, 0xa1, 0xb2, 0x2b, 0x1a, 0x56, 0x43, 0x34, 0x65, 0xff, 0xff, 0x0f},{0x00},12},
		{{0x35,  0x00},{0x00},2},
		{{0x21},{0x00},1},
		{{0x11},{0x00},1},
		{{0xff},{0x00},0},
		{{0x29},{0x00},1},
		{{0xff},{0x00},0},
		#ifdef BIST_MODE
		{{0xb0,  0xa5},{0x00},2},
		{{0xcc,  0x40, 0x00, 0x3f, 0x00, 0x14, 0x14, 0x20, 0x20, 0x03},{0x00},10},
		#endif
};

/*************************************************
 * reg rw functions
 ************************************************/
#define DMA_BASE_ADRR  0x40000000
#define DDR_BASE_ADDR  0x10000000

void wait_for_idle()
{
	u32 tmp;
	while(1){
		tmp=Xil_In32(DMA_BASE_ADRR+04);
		if(((tmp>>1)&0x1) == 1){
			break;
		}
	}
}

void dma_xfer(u32 mem_addr, u32 length)
{/*用于初始化阶段,需要刷新Cache,确保初始化成功*/
	//Xil_DCacheFlush();
	Xil_DCacheFlushRange(mem_addr, length);

	Xil_Out32(DMA_BASE_ADRR,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x30,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x18,  mem_addr);
	Xil_Out32(DMA_BASE_ADRR+0x28,  length);	 /*length is bytes*/
	//wait_for_idle();
	//usleep(10);
}

void dma_xfer2(u32 mem_addr, u32 length)
{/*发送数据阶段,可以不用Cache*/
	//Xil_DCacheFlush();
	Xil_DCacheFlushRange(mem_addr, length);

	Xil_Out32(DMA_BASE_ADRR,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x30,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x18,  mem_addr);
	Xil_Out32(DMA_BASE_ADRR+0x28,  length);	 /*length is bytes*/
	wait_for_idle();
	//usleep(10);
}

void set_cfg(u32 mem_addr, u8 cmd, u8 addr, u8 mode, u8 work_mode, u16 length)
{
	u32 tmp=cmd;
	tmp|=addr<<8;
	tmp|=mode<<16;
	tmp|=length<<20;
	tmp|=work_mode<<30;
	Xil_Out32(mem_addr,  tmp);
}

u32 get_cfg(u8 cmd, u8 addr, u8 mode, u8 work_mode, u16 length)
{
	u32 tmp=cmd;
	tmp|=addr<<8;
	tmp|=mode<<16;
	tmp|=length<<20;
	tmp|=work_mode<<30;
	return tmp;
}


void set_npdata8(u32 mem_addr, u8 *pdata, u32 length){
	u32 addr=mem_addr;
	while(length--){
		Xil_Out8(addr++, *pdata++);
	}
}


/*************************************************
 * basic xfer functions
 ************************************************/
void xfer_cmd_pdata8(u32 mem_addr, u8 cmd, u8* pdata, u32 length){
	if(length==0)
		set_cfg(mem_addr, 0xde, cmd, 0b0101,  0b00, length);  /*byte_mode=0, cs_mode=1, data_mode=0b01*/
	else
		set_cfg(mem_addr, 0xde, cmd, 0b0110,  0b00, length); /*byte_mode=0, cs_mode=1, data_mode=0b10*/

	set_npdata8(mem_addr+4, pdata,length);
	dma_xfer(mem_addr, 4+length);
}


void xfer_cmd_pdata16_dma(u32 mem_addr, u32 length){
	//dma_xfer(mem_addr, 20*64);
	dma_xfer2(mem_addr, length);
}


void seqs_init() {

	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(DDR_BASE_ADDR, cmd, pdata, length);
		usleep(10); //this delay is need
		//xil_printf("Exec CMD=%x\r\n\r\n", cmd);
	}
}

/*************************************************
 * application functions
 ************************************************/
u8 frame_src[1920*1080*3];
u8 frame_target[400*400*3];

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


#define LCD_BPP             (24)

#define LCD_X_SIZE          (400U)                      /* available x pixel size */
#define LCD_Y_SIZE          (400U)		                /* available y pixle size */
#define LCD_PBYTE           ((LCD_BPP + 0) / 8)         /* bytes in pixel unit */
#define LCD_HBYTE           (LCD_X_SIZE * LCD_PBYTE)    /* bytes in horizontal line */

#define LCD_VSW             (1U)
#define LCD_HFP             (8U)
#define LCD_HBP             (8U)

#define LCD_TE_OFT          (25U)


typedef u8 (*ArrayPtr)[400][20 * 60];
ArrayPtr ptr1=(ArrayPtr)0x11000000;



void fill_pic(ArrayPtr ptr, u8 *pcolor) {
//    u32 cmd1 = get_cfg(0xde, 0x60, 0b0011,  0b00, 60);
//    u32 cmd2 = get_cfg(0xde, 0x60, 0b0111,  0b00, 60);

    // 遍历数组的每一行
    for (int i = 0; i < 400; ++i) {
        // 遍历每一行的每一个packet
        for (int j = 0; j < 20; ++j) {
            // 计算当前packet的起始地址
        	u8 *packet = (u8 *)(ptr + i) + j * 60; // ptr指向整个二维数组,ptr+i指向第i行,然后加上j个packet的偏移

//            // 填充cmd部分
//            u32 *cmd_ptr = (u32 *)packet;
//            *cmd_ptr = (j == 19) ? cmd2 : cmd1; // 第20个packet使用cmd2,其余使用cmd1
//
//            // 移动到color数据部分的起始地址
//            packet += sizeof(cmd1);

            // 填充color数据部分
			memcpy(packet, pcolor, 60);
			pcolor+=60;
//            for (int k = 0; k < 60; k += 3) {
//            	u8 r=*pcolor++;
//            	u8 g=*pcolor++;
//            	u8 b=*pcolor++;
//
//                packet[k] = r;
//                packet[k + 1] = g;
//                packet[k + 2] = b;
//            }
        }
    }
}


XTime t1,t2,t3,t4;
void draw_bmp()
{
	XTime_GetTime(&t1);
	load_sd_bmp(frame_src, "shatan.bmp");	//1920x1080
//	XTime_GetTime(&t2);
	convertRGB888toRGB888(
			frame_src,
			frame_target,
			1920,1080,
			300,500,
			400,400
			);
//	XTime_GetTime(&t3);
	fill_pic(ptr1,frame_target);
//	XTime_GetTime(&t4);
//
//	XTime dt21 = ((t2-t1) * 1000000) / (COUNTS_PER_SECOND);
//	XTime dt32 = ((t3-t2) * 1000000) / (COUNTS_PER_SECOND);
//	XTime dt43 = ((t4-t3) * 1000000) / (COUNTS_PER_SECOND);
//	xil_printf("dt21=%d, dt32=%d, dt43=%d\r\n", dt21,dt32,dt43);

	main_scutimer_irq();
	u32 lines=0;

    while (1)
    {
    	if(Refresh_flag){
    		if(lines==LCD_VSW+LCD_HBP){
    			qspi_mode_set(1);
    		}
    		else if(lines==LCD_VSW+LCD_HBP+LCD_Y_SIZE){
    			qspi_mode_set(0);
    		}

			if(lines<LCD_VSW){
				xfer_cmd_pdata8(DDR_BASE_ADDR, 0x61,NULL,0);
			}
			else if(lines<LCD_VSW+LCD_HBP){
				xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
			}
			else if(lines<LCD_VSW+LCD_HBP+LCD_Y_SIZE){
				xfer_cmd_pdata16_dma((u32)ptr1[lines-(LCD_VSW+LCD_HBP)],20*60);
			}
			else if(lines<LCD_VSW+LCD_HBP+LCD_Y_SIZE+LCD_HFP){
				xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
			}

			lines++;
			if(lines==LCD_VSW+LCD_HBP+LCD_Y_SIZE+LCD_HFP){
				lines=0;
			}

			Refresh_flag=0;
    	}

    }
}

int main()
{
    //init_platform();
    //Xil_DCacheDisable();

    qspi_mode_set(0);
	seqs_init();

    draw_bmp();
    //draw_movie();
    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 convertRGB888toRGB888(const uint8_t *input, uint8_t *output, int inputWidth, int inputHeight, int startX, int startY, int outputWidth, int outputHeight)
{
    int i, j;

    for (i = 0; i < outputHeight; i++) {
        int inputIndex = ((startY + i) * inputWidth + startX + 0) * 3;
        int outputIndex=(i * outputWidth + 0)*3;
    	memcpy(&output[outputIndex], &input[inputIndex],outputWidth*3);

//        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];
//
//            int outputIndex=(i * outputWidth + j)*3;
//            // Store in the output array
//            output[outputIndex] = r;
//            output[outputIndex+1] = g;
//            output[outputIndex+2] = b;
//        }
    }
}

刷动画程序

#include <stdio.h>
#include "xil_printf.h"
#include "xparameters.h"
#include "xil_io.h"
#include "sleep.h"
#include "ff.h"
#include "xil_cache.h"
#include "xtime_l.h"
#include <string.h>
#include "xgpiops.h"


#define PS_KEY  (54 + 0)  //EMIO0,对应的GPIO编号为54+0=54
#define FULL_PIN  (54 + 1)    //EMIO1,对应的GPIO编号为54+1=55
#define EMPTY_PIN  (54 + 2)    //EMIO2,对应的GPIO编号为54+2=56

#define INPUT		1
#define OUTPUT		0
XGpioPs GpioPs;	//GPIO实例对象
void PS_GPIO_Init()
{
	XGpioPs_Config *ConfigPtr;
	ConfigPtr = XGpioPs_LookupConfig(XPAR_XGPIOPS_0_DEVICE_ID);
	XGpioPs_CfgInitialize(&GpioPs, ConfigPtr, ConfigPtr->BaseAddr);
}

/**
  *****************************************************
  * @brief	初始化特定GPIO口的模式与状态
  * @param	GPIO_Num	GPIO编号(MIO为0~53,EMIO从54开始)
  * @param	Dir			输入/输出:OUTPUT为输出,INPUT为输入
  * @param	Data		输出电平高低:0为低,1为高(若设置为输入则此处数据无影响)
  * @usage	//设置GPIO46为输出模式,输出为高电平
  * 		GPIO_SetMode(46, OUTPUT, 1);
  *****************************************************
**/
void PS_GPIO_SetMode(uint8_t GPIO_Num, uint8_t Dir, uint8_t Data)
{
	if(Dir == INPUT) {
		XGpioPs_SetDirectionPin(&GpioPs, GPIO_Num, 0);
	} else if(Dir == OUTPUT){
		XGpioPs_SetDirectionPin(&GpioPs, GPIO_Num, 1);
		XGpioPs_SetOutputEnablePin(&GpioPs, GPIO_Num, 1);
		XGpioPs_WritePin(&GpioPs, GPIO_Num, Data);
	}
}

/**
  *****************************************************
  * @brief	控制特定GPIO口的输出高/低电平,使用前必须先用GPIO_SetMode设置该GPIO口为"输出"模式
  * @param	GPIO_Num	GPIO编号(MIO为0~53,EMIO从54开始)
  * @param	Data		输出电平高低:0为低,1为高
  * @usage	//控制GPIO46输出高电平
  * 		GPIO_SetPort(46, 1);
  *****************************************************
**/
void PS_GPIO_SetPort(uint8_t GPIO_Num, uint8_t Data)
{
	XGpioPs_WritePin(&GpioPs, GPIO_Num, Data);	//输出高/低电平,0为低,1为高
}

/**
  *****************************************************
  * @brief	读取特定GPIO口的输入电平,使用前必须先用GPIO_SetMode设置该GPIO口为"输入"模式
  * @param	GPIO_Num	GPIO编号(MIO为0~53,EMIO从54开始)
  * @return	返回采集的GPIO电平值,0为低,1为高
  * @usage	//采集GPIO46的电平,存放在state
  * 		state = GPIO_GetPort(46, state);
  *****************************************************
**/
uint8_t PS_GPIO_GetPort(uint8_t GPIO_Num)
{
	return XGpioPs_ReadPin(&GpioPs, GPIO_Num);		//输出高/低电平,0为低,1为高
}


void gpio_init()
{
    PS_GPIO_Init(); //初始化PS端MIO和EMIO
    PS_GPIO_SetMode(PS_KEY, OUTPUT, 0);
    PS_GPIO_SetMode(FULL_PIN, INPUT, 0);
    PS_GPIO_SetMode(EMPTY_PIN, INPUT, 1);
}

void qspi_mode_set(u8 mode)
{
	PS_GPIO_SetPort(PS_KEY, mode);
}

u32 read_full()
{
	return PS_GPIO_GetPort(FULL_PIN);
}

u32 read_empty()
{
	return PS_GPIO_GetPort(EMPTY_PIN);
}

/***********************************************************************************/


#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;

//#define BIST_MODE
t_buf WriteBuffer[]={
		{{0xf0, 0xc3}, {0x00},2},
		{{0xf0, 0x96}, {0x00},2},
		{{0xf0, 0xa5}, {0x00},2},
		{{0xe9, 0x20}, {0x00},2},
		{{0xe7,  0x80, 0x77, 0x1f, 0xcc}, {0x00}, 5},
		{{0xc1,  0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Normal:VGHS/VGLS/VSP/VSN电压
		{{0xc2,  0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Idle: VGHS/VGLS/VSP/VSN电压
		{{0xc3,  0x22, 0x02, 0x22, 0x04}, {0x00},5},//Normal: VGH/VGL/VSP/VSN Clk
		{{0xc4,  0x22, 0x02, 0x22, 0x04}, {0x00},5},//Idle: VGH/VGL/VSP/VSN Clk
		{{0xc5,  0xed},{0x00},2}, //VCOM
		{{0xe0, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
		{{0xe1, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
		{{0xe5, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
		{{0xe6, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
		{{0xec, 0x40, 0x03},{0x00},3},
		//{{0x36,  0x0c},{0x00},2},
		{{0x36,  0x04},{0x00},2},
		{{0x3a,  0x07},{0x00},2},
		{{0xb2,  0x00},{0x00},2},//GIP pattern
		{{0xb3,  0x01},{0x00},2},//video mode dot-inversion
		{{0xb4,  0x01},{0x00},2},
		{{0xb5,  0x00, 0x08, 0x00, 0x08},{0x00},5}, //vfp
		{{0xb6,  0xc7, 0x31},{0x00},3},
		{{0xa5,  0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
		{{0xa6,  0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
		{{0xba,  0x0a, 0x5a, 0x23, 0x10, 0x25, 0x02, 0x00},{0x00},8},
		{{0xbb,  0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
		{{0xbc,  0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
		{{0xbd, 0xa1, 0xb2, 0x2b, 0x1a, 0x56, 0x43, 0x34, 0x65, 0xff, 0xff, 0x0f},{0x00},12},
		{{0x35,  0x00},{0x00},2},
		{{0x21},{0x00},1},
		{{0x11},{0x00},1},
		{{0xff},{0x00},0},
		{{0x29},{0x00},1},
		{{0xff},{0x00},0},
		#ifdef BIST_MODE
		{{0xb0,  0xa5},{0x00},2},
		{{0xcc,  0x40, 0x00, 0x3f, 0x00, 0x14, 0x14, 0x20, 0x20, 0x03},{0x00},10},
		#endif
};

/*************************************************
 * reg rw functions
 ************************************************/
#define DMA_BASE_ADRR  0x40000000
#define DDR_BASE_ADDR  0x10000000

void wait_for_idle()
{
	u32 tmp;
	while(1){
		tmp=Xil_In32(DMA_BASE_ADRR+04);
		if(((tmp>>1)&0x1) == 1){
			break;
		}
	}
}

void dma_xfer(u32 mem_addr, u32 length)
{/*用于初始化阶段,需要刷新Cache,确保初始化成功*/
	//Xil_DCacheFlush();
	Xil_DCacheFlushRange(mem_addr, length);

	Xil_Out32(DMA_BASE_ADRR,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x30,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x18,  mem_addr);
	Xil_Out32(DMA_BASE_ADRR+0x28,  length);	 /*length is bytes*/
	//wait_for_idle();
	//usleep(10);
}

void dma_xfer2(u32 mem_addr, u32 length)
{/*发送数据阶段,可以不用Cache*/
	//Xil_DCacheFlush();
	//Xil_DCacheFlushRange(mem_addr, length);

	Xil_Out32(DMA_BASE_ADRR,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x30,  0x00010003);
	Xil_Out32(DMA_BASE_ADRR+0x18,  mem_addr);
	Xil_Out32(DMA_BASE_ADRR+0x28,  length);	 /*length is bytes*/
	wait_for_idle();
	//usleep(10);
}
void set_cfg(u32 mem_addr, u8 cmd, u8 addr, u8 mode, u16 length)
{
	u32 tmp=cmd;
	tmp|=addr<<8;
	tmp|=mode<<16;
	tmp|=length<<20;
	Xil_Out32(mem_addr,  tmp);
}

u32 get_cfg(u8 cmd, u8 addr, u8 mode, u16 length)
{
	u32 tmp=cmd;
	tmp|=addr<<8;
	tmp|=mode<<16;
	tmp|=length<<20;
	return tmp;
}

void set_npdata8(u32 mem_addr, u8 *pdata, u32 length){
	u32 addr=mem_addr;
	while(length--){
		Xil_Out8(addr++, *pdata++);
	}
}


/*************************************************
 * basic xfer functions
 ************************************************/
void xfer_cmd_pdata8(u32 mem_addr, u8 cmd, u8* pdata, u32 length){
	if(length==0)
		set_cfg(mem_addr, 0xde, cmd, 0b0101, length);  /*byte_mode=0, cs_mode=1, data_mode=0b01*/
	else
		set_cfg(mem_addr, 0xde, cmd, 0b0110, length); /*byte_mode=0, cs_mode=1, data_mode=0b10*/

	set_npdata8(mem_addr+4, pdata,length);
	dma_xfer(mem_addr, 4+length);
}


void xfer_cmd_pdata16_dma(u32 mem_addr, u32 length){
	//dma_xfer(mem_addr, 20*64);
	dma_xfer2(mem_addr, length);
}


void seqs_init() {

	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(DDR_BASE_ADDR, cmd, pdata, length);
		usleep(10); //this delay is need
		//xil_printf("Exec CMD=%x\r\n\r\n", cmd);
	}
}

/*************************************************
 * application functions
 ************************************************/
void load_sd_bmp(u8 *frame, const char *bmp_name);
//void convertRGB888toRGB888(
//		const uint8_t *input,
//		uint8_t *output,
//		int inputWidth, int inputHeight,
//		int startX, int startY,
//		int outputWidth, int outputHeight
//		) ;


#define LCD_BPP             (24)

#define LCD_X_SIZE          (400U)                      /* available x pixel size */
#define LCD_Y_SIZE          (400U)		                /* available y pixle size */
#define LCD_PBYTE           ((LCD_BPP + 0) / 8)         /* bytes in pixel unit */
#define LCD_HBYTE           (LCD_X_SIZE * LCD_PBYTE)    /* bytes in horizontal line */

#define LCD_VSW             (1U)
#define LCD_HFP             (8U)
#define LCD_HBP             (8U)

#define LCD_TE_OFT          (25U)

//#define NFRAMES   1
//typedef u8 (*ArrayPtr)[400][20 * 60];
//ArrayPtr ptr[NFRAMES];

//u8 frame_src[1920*1080*3];
//u8 frame_target[400*400*3];

typedef u8 (*frame_src_type)[1920*1080*3];
frame_src_type frame_src=(frame_src_type)0x11000000;

typedef u8 (*frame_target_type)[400*400*3];
frame_target_type frame_target=(frame_target_type)0x12000000;

//void init_Arrayptr()
//{
//	for(int i=0; i<NFRAMES; i++){
//		ptr[i]=(ArrayPtr)(0x13000000+400*20*60*i);
//	}
//}


//void fill_pic(ArrayPtr ptr, u8 *pcolor) {
//    u32 cmd1 = get_cfg(0xde, 0x60, 0b0011, 60);
//    u32 cmd2 = get_cfg(0xde, 0x60, 0b0111, 60);
//
//    // 遍历数组的每一行
//    for (int i = 0; i < 400; ++i) {
//        // 遍历每一行的每一个packet
//        for (int j = 0; j < 20; ++j) {
//            // 计算当前packet的起始地址
//        	u8 *packet = (u8 *)(ptr + i) + j * 64; // ptr指向整个二维数组,ptr+i指向第i行,然后加上j个packet的偏移
//
//            // 填充cmd部分
//            u32 *cmd_ptr = (u32 *)packet;
//            *cmd_ptr = (j == 19) ? cmd2 : cmd1; // 第20个packet使用cmd2,其余使用cmd1
//
//            // 移动到color数据部分的起始地址
//            packet += sizeof(cmd1);
//
//            // 填充color数据部分
//			memcpy(packet, pcolor, 60); /*库函数速度更快*/
//			pcolor+=60;
////            for (int k = 0; k < 60; k += 3) {
////            	u8 r=*pcolor++;
////            	u8 g=*pcolor++;
////            	u8 b=*pcolor++;
////
////                packet[k] = r;
////                packet[k + 1] = g;
////                packet[k + 2] = b;
////            }
//        }
//    }
//}

//void fill_pic_new(
//		const uint8_t *input, ArrayPtr ptr,
//		int inputWidth, int inputHeight,
//		int startX, int startY,
//		int outputWidth, int outputHeight
//)
//{
////    u32 cmd1 = get_cfg(0xde, 0x60, 0b0011, 60);
////    u32 cmd2 = get_cfg(0xde, 0x60, 0b0111, 60);
//
//    // 遍历数组的每一行
//    for (int i = 0; i < outputHeight; ++i) {
//        // 遍历每一行的每一个packet
//        for (int j = 0; j < outputWidth*3/60; ++j) {
//            // 计算当前packet的起始地址
//        	u8 *packet = (u8 *)(ptr + i) + j * 60; // ptr指向整个二维数组,ptr+i指向第i行,然后加上j个packet的偏移
//
////            // 填充cmd部分
////            u32 *cmd_ptr = (u32 *)packet;
////            *cmd_ptr = (j == 19) ? cmd2 : cmd1; // 第20个packet使用cmd2,其余使用cmd1
////
////            // 移动到color数据部分的起始地址
////            packet += sizeof(cmd1);
//
//            // 填充color数据部分
//            int inputIndex = ((startY + i) * inputWidth + startX + j*20) * 3;
//			memcpy(packet,  &input[inputIndex], 60); /*库函数速度更快*/
//        }
//    }
//}

u32 get_line_ptr(
	const uint8_t *input,
	int inputWidth,
	int startX, int startY,
	int line_num
)
{
	int inputIndex = ((startY + line_num) * inputWidth + startX + 0*20) * 3;
	return (u32)&input[inputIndex];
}

void show_bmp();

XTime t1,t2,t3,t4;
void draw_bmp()
{
	int x=500,y=500;
	int dir_x=1, dir_y=1;

	XTime_GetTime(&t1);
	load_sd_bmp(*frame_src, "shatan.bmp");	//1920x1080

	int i=0;
	while(1){
		if(dir_x) x+=1; else x-=1;
		if(dir_y) y+=1; else y-=1;

		if(x+400>1920) {x=1920-400; dir_x=0;} else if(x<0) {x=0; dir_x=1;}
		if(y+400>1080) {y=1080-400; dir_y=0;} else if(y<0) {y=0; dir_y=1;}

//		XTime_GetTime(&t2);
//		convertRGB888toRGB888(
//				frame_src,
//				(u8 *)frame_target,
//				1920,1080,
//				x,y,
//				400,400
//				);
//		XTime_GetTime(&t3);
//		fill_pic(ptr[i],(u8 *)frame_target);
//		fill_pic_new(
//				frame_src,
//				ptr[i],
//				1920,1080,
//				x,y,
//				400,400
//				);
//		XTime_GetTime(&t4);
//
//		int dt32 = (u32)(t3-t2)*(1000000.0/COUNTS_PER_SECOND);
//		int dt43 = (u32)(t4-t3)*(1000000.0/COUNTS_PER_SECOND);
//		printf("ptr=%p, x=%d, y=%d, dt32=%d, dt43=%d\r\n", ptr[i], x,y,dt32,dt43);

		show_bmp(x,y);
	}
}

void show_bmp(int x, int y)
{
	u32 frame_index=0;
    do
    {
        /* vs(0x61) packet */
        for (int i = 0; i < LCD_VSW; i++)
        {
        	xfer_cmd_pdata8(DDR_BASE_ADDR, 0x61,NULL,0);
            usleep(40);
        }

        /* hbp(0x60) packet */
        for (int i = 0; i < LCD_HBP; i++)
        {
        	xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
            usleep(40);
        }

        /* transmit display cache data to lcd line by line */
        qspi_mode_set(1);
        for (int i = 0; i < LCD_Y_SIZE; i+=1)
        {
        	/*每次获得一行图像数据的指针,直接在原始图像frame_buffer中取到,避免了复制*/
    		u32 addr=get_line_ptr(
    				*frame_src,
    				1920,
    				x,y,
					i
    				);
    		//printf("%x\r\n", addr);
        	//xfer_cmd_pdata16_dma((u32)ptr[frame_index][i],20*60);
    		xfer_cmd_pdata16_dma(addr,20*60);
            usleep(45);  //这个时间设置非常关键,如果小了就不行
        }
        qspi_mode_set(0);


        /* hfp(0x60) packet */
        for (int i = 0; i < LCD_HFP; i++)
        {
        	xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
            usleep(40);
        }

//        frame_index++;
//        if(frame_index==NFRAMES){
//        	frame_index=0;
//        }

    }
    while(0);
}

int main()
{
/*
 *	在 void dma_xfer(u32 mem_addr, u32 length)中
 *  Xil_DCacheFlush();  //加上这一句话,就可以运行!这里最关键!
 *  其它地方都不需要加
 */

   Xil_DCacheDisable();
	gpio_init();
	//init_Arrayptr();
	qspi_mode_set(0);
    seqs_init();
    draw_bmp();

    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 convertRGB888toRGB888(const uint8_t *input, uint8_t *output, int inputWidth, int inputHeight, int startX, int startY, int outputWidth, int outputHeight)
//{
//    int i, j;
//
//    for (i = 0; i < outputHeight; i++) {
//        int inputIndex = ((startY + i) * inputWidth + startX + 0) * 3;
//        int outputIndex=(i * outputWidth + 0)*3;
//    	memcpy(&output[outputIndex], &input[inputIndex],outputWidth*3); /*库函数速度更快*/
//
////        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];
////
////            int outputIndex=(i * outputWidth + j)*3;
////            // Store in the output array
////            output[outputIndex] = r;
////            output[outputIndex+1] = g;
////            output[outputIndex+2] = b;
////        }
//    }
//}

逻辑分析仪抓波形

image image

一帧的时间21ms
一行的时间52us,有效宽度49us
可以看到整个刷屏过程,没有单独的填充图像的时间区间(之前有2.6ms)

Clone this wiki locally