图形化LCD的显示

显示屏的介绍

视频产生

parameter ScreenWidth = 480;
parameter ScreenHeight = 320;
 
reg [6:0] CounterX;  // counts from 0 to 119
reg [8:0] CounterY;  // counts from 0 to 319
wire CounterXmaxed = (CounterX==ScreenWidth/4-1);
wire CounterYmaxed = (CounterY==ScreenHeight-1);
 
always @(posedge clk)
begin
  if(CounterXmaxed)
    CounterX <= 0;
  else
    CounterX <= CounterX + 1;
end
 
always @(posedge clk)
if(CounterXmaxed)
begin
  if(CounterYmaxed)
    CounterY <= 0;
  else
    CounterY <= CounterY + 1;
end
 
reg HSync, VSync;
always @(posedge clk)
begin
  HSync <= CounterXmaxed;
  VSync <= CounterYmaxed;
end

在屏幕上画图

// Use a blockram to hold the graphical data
wire [7:0] BitmapData;
blockram_8x512 RAM_bitmap(.clk(clk), .rd_adr({CounterY[4:0],CounterX[4:1]}), .data_out(BitmapData));
 
// Let's say we need 4 bits at a time
wire [3:0] LCD_Bitmap4 = CounterX[0] ? BitmapData[3:0] : BitmapData[7:4];
 
// Display the data into a chessboard pattern
wire [3:0] LCD_BitmapChessboard = (CounterY[5] ^ CounterX[5]) ? 4'b000 : LCD_Bitmap4 ^ {4{CounterY[5]}};
// We assume CounterX and CounterY are available:
//  CounterX is the pixel number of the current line
//  CounterY is the line number
 
// We use a RAM to hold the "Y" values
// Y=F(CounterX)
wire [7:0] RAM_Y_value;
blockram_8x512 RAM_FXY(.clk(clk), .rd_adr(CounterX), .data_out(RAM_Y_value));
 
// check for equality between the "Y" values and "CounterY"
reg grcpeq1;  always @(posedge clk) grcpeq1 <= (RAM_Y_value==CounterY);
reg grcpeq2;  always @(posedge clk) grcpeq2 <= grcpeq1;
 
// check for "greater-than" between the "Y" values and "CounterY"
reg grcp1;  always @(posedge clk) grcp1 <= (RAM_Y_value>CounterY);
reg grcp2;  always @(posedge clk) grcp2 <= grcp1;
 
// display a pixel if equality, or if "CounterY" is between 2 successive "Y" values
wire FXpix= grcpeq2 | (grcp1 ^ grcp2);
reg [15:0] X0, Y0, X1, Y1;
 
always @(posedge clk)
if(Vsync) 
begin
    X0 <= 0;
    Y0 <= 0;
    X1 <= 0;
    Y1 <= 0;
end
else
if(Hsync) 
begin
    X0 <= X1 - 100;
    Y0 <= Y1 + 400;
    X1 <= X1 - 100;
    Y1 <= Y1 + 400;
end
else
begin
    X0 <= X0 + 400;
    Y0 <= Y0 + 100;
end
 
// Display a chessboard pattern by XOR'ing the MSB of X and Y counters
// You could also display a rotozoomed bitmap by feeding X and Y to a bitmap in a RAM
 
wire rotozoom_pix = X0[15] ^ Y0[15];

在图形LCD上显示文本信息

wire [7:0] CharacterRAM_dout;
 
ram8x2048 CharacterRAM(
	.clk(clk),
	.rd_adr({CounterY[7:3],CounterX[6:1]}),
	.data_out(CharacterRAM_dout)
);
 
wire [7:0] raster8;
rom8x2048 FontROM(
	.clk(clk),
	.rd_adr({CharacterRAM_dout, CounterY[2:0]}),
	.data_out(raster8)
);
 
wire [3:0] LCDdata = CounterX[0] ? raster8[7:4] : raster8[3:0];
wire [3:0] charfont0, charfont1;
 
always @(posedge clk)
begin
  case(cnt_mod3)
    2'b00: LCDdata <=  charfont0;
    2'b01: LCDdata <= {charfont0[3:2], charfont1[3:2]};
    2'b10: LCDdata <= {charfont1[3:2], charfont0[1:0]};
  endcase
end
reg [1:0] cnt_mod3;
always @(posedge clk) if(cnt_mod3==2) cnt_mod3 <= 0; else cnt_mod3 <= cnt_mod3 + 1;
// character-counter (increments only twice every 3 clocks)
reg [6:0] cnt_charbuf;
always @(posedge clk) if(cnt_mod3!=1) cnt_charbuf <= cnt_charbuf + 1;
 
wire [11:0] CharacterRAM_rdaddr = CounterY[8:3]*80 + cnt_charbuf;
wire [7:0] CharacterRAM_dout;
 
ram8x2048 CharacterRAM(
	.clk(clk),
	.rd_adr(CharacterRAM_rdaddr),
	.data_out(CharacterRAM_dout)
);
// remember the previous character displayed
reg [7:0] RAM_charbuf_dout_last;
always @(posedge clk) CharacterRAM_dout_last <= CharacterRAM_dout;
 
// because we need it when we display 2 half characters at once
wire [10:0] readaddr0 = {CharacterRAM_dout, CounterY[2:0]};
wire [10:0] readaddr1 = {CharacterRAM_dout_last, CounterY[2:0]};
 
// The font ROMs are split in two blockrams, holding 4 pixels each
// (half of the second ROM is not used, since we need only 6 pixels)
rom4x2048 FontROM0(.clk(clk), .rd_adr(readaddr0), .data_out(charfont0));
rom4x2048 FontROM1(.clk(clk), .rd_adr(readaddr1), .data_out(charfont1));
reg [5:0] cnt_cursorblink;
always @(posedge clk) if(vsync & hsync) cnt_cursorblink <= cnt_cursorblink + 1;
wire cursorblinkstate = cnt_cursorblink[5];  // cursor on for 32 frames, off for 32 frames
// Do we have a cursor-character match?
wire cursorblink_adrmatch = cursorblinkstate & (CharacterRAM_rdaddr==CursorAddress);
 
// When we have a match, "invert" the character
// First for charfont0
wire [3:0] charfont0_cursor = charfont0 ^ {4{cursorblink_adrmatch}};
 
// Next for charfont1
reg cursorblink_adrmatch_last;
always @(posedge clk) cursorblink_adrmatch_last <= cursorblink_adrmatch;
wire [3:0] charfont1_cursor = charfont1 ^ {4{cursorblink_adrmatch_last}};