-
Notifications
You must be signed in to change notification settings - Fork 0
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_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
#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;
//// }
// }
//}
一帧的时间21ms
一行的时间52us,有效宽度49us
可以看到整个刷屏过程,没有单独的填充图像的时间区间(之前有2.6ms)