手搓qspi接口,封装成AXI4‐ip,用zynq驱动 - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki
- gc9b71是格科公司的小尺寸lcd_driver驱动芯片, 支持qspi接口
- 手头的这款屏尺寸是1.89寸,分辨率是320x386,采用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中。

- 图2:发command+addr。这种会应用到qspi-lcd中,其中command是0x02/32,addr则是24'h00xx00,这里xx是真正的cmd

- 图3:1wire模式发command+addr+data

- 图4:4wire模式发command+addr+data,一般用于发图

`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

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]
#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;
}
}
}
- 刷单色图ok
- 刷静态bmp图片ok
- 刷动态movie,QSPI时钟频率40MHz(对应F_CLK0=80MHz) ok,QSPI时钟频率50MHz(对应F_CLK0=100MHz) 不稳定




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