基于STEP FPGA的SPI RGB液晶屏顯示驅(qū)動(dòng)
硬件說(shuō)明
我們的STEP-BaseBoard底板上集成了1.8寸彩色液晶屏TFT_LCD模塊,大家可以驅(qū)動(dòng)LCD顯示文字、圖片或動(dòng)態(tài)的波形。
首先了解一下液晶屏模塊,相關(guān)資料下載:https://pan.baidu.com/s/1bp6AYsR

框圖如下:

原理圖如下:

原理圖中的器件U1為液晶屏,液晶屏為1.8寸,128RGB160像素,串行總線(SPI),液晶屏集成了ST7735S的驅(qū)動(dòng)器,處理器與ST7735S通信完成液晶屏的顯示控制

ST7735S為132RGBx162像素點(diǎn)262K控制器/驅(qū)動(dòng)器,芯片可以直接跟外部處理器連接,支持串行SPI通信和8/9/16/18位并行通信(本液晶屏集成ST7735S時(shí)沒(méi)有留并行接口,所以只能使用串行通信),詳細(xì)參數(shù)請(qǐng)參考數(shù)據(jù)手冊(cè):st7735s_datasheet.pdf
Verilog代碼
// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
// Module: LCD_RGB
//
// Author: Step
//
// Description: Drive TFT_RGB_LCD_1.8 to display
//
// --------------------------------------------------------------------
// Code Revision History :
// --------------------------------------------------------------------
// Version: |Mod. Date: |Changes Made:
// V1.1 |2016/10/30 |Initial ver
// --------------------------------------------------------------------
module LCD_RGB #(
parameter LCD_W = 8'd132, //液晶屏像素寬度
parameter LCD_H = 8'd162 //液晶屏像素高度)(
input clk_in, //12MHz系統(tǒng)時(shí)鐘
input rst_n_in, //系統(tǒng)復(fù)位,低有效
output reg ram_lcd_clk_en, //RAM時(shí)鐘使能
output reg [7:0] ram_lcd_addr, //RAM地址信號(hào)
input [131:0] ram_lcd_data, //RAM數(shù)據(jù)信號(hào)
output reg lcd_rst_n_out, //LCD液晶屏復(fù)位
output reg lcd_bl_out, //LCD背光控制
output reg lcd_dc_out, //LCD數(shù)據(jù)指令控制
output reg lcd_clk_out, //LCD時(shí)鐘信號(hào)
output reg lcd_data_out //LCD數(shù)據(jù)信號(hào)
);
localparam INIT_DEPTH = 16'd73; //LCD初始化的命令及數(shù)據(jù)的數(shù)量
localparam RED = 16'hf800; //紅色
localparam GREEN = 16'h07e0; //綠色
localparam BLUE = 16'h001f; //藍(lán)色
localparam BLACK = 16'h0000; //黑色
localparam WHITE = 16'hffff; //白色
localparam YELLOW = 16'hffe0; //黃色
localparam IDLE = 3'd0;
localparam MAIN = 3'd1;
localparam INIT = 3'd2;
localparam SCAN = 3'd3;
localparam WRITE = 3'd4;
localparam DELAY = 3'd5;
localparam LOW = 1'b0;
localparam HIGH = 1'b1;
//assign lcd_bl_out = HIGH; // backlight active high level
wire [15:0] color_t = YELLOW; //頂層色為黃色
wire [15:0] color_b = BLACK; //背景色為黑色
reg [7:0] x_cnt;
reg [7:0] y_cnt;
reg [131:0] ram_data_r;
reg [8:0] data_reg; //
reg [8:0] reg_setxy [10:0];
reg [8:0] reg_init [72:0];
reg [2:0] cnt_main;
reg [2:0] cnt_init;
reg [2:0] cnt_scan;
reg [5:0] cnt_write;
reg [15:0] cnt_delay;
reg [15:0] num_delay;
reg [15:0] cnt;
reg high_word;
reg [2:0] state = IDLE;
reg [2:0] state_back = IDLE;
always@(posedge clk_in or negedge rst_n_in) begin
if(!rst_n_in) begin
x_cnt <= 8'd0;
y_cnt <= 8'd0;
ram_lcd_clk_en <= 1'b0;
ram_lcd_addr <= 8'd0;
cnt_main <= 3'd0;
cnt_init <= 3'd0;
cnt_scan <= 3'd0;
cnt_write <= 6'd0;
cnt_delay <= 16'd0;
num_delay <= 16'd50;
cnt <= 16'd0;
high_word <= 1'b1;
lcd_bl_out <= LOW;
state <= IDLE;
state_back <= IDLE;
end else begin
case(state)
IDLE:begin
x_cnt <= 8'd0;
y_cnt <= 8'd0;
ram_lcd_clk_en <= 1'b0;
ram_lcd_addr <= 8'd0;
cnt_main <= 3'd0;
cnt_init <= 3'd0;
cnt_scan <= 3'd0;
cnt_write <= 6'd0;
cnt_delay <= 16'd0;
num_delay <= 16'd50;
cnt <= 16'd0;
high_word <= 1'b1;
state <= MAIN;
state_back <= MAIN;
end
MAIN:begin
case(cnt_main) //MAIN狀態(tài)
3'd0: begin state <= INIT; cnt_main <= cnt_main + 1'b1; end
3'd1: begin state <= SCAN; cnt_main <= cnt_main + 1'b1; end
3'd2: begin cnt_main <= 1'b1;
end
default: state <= IDLE;
endcase
end
INIT:begin //初始化狀態(tài)
case(cnt_init)
3'd0: begin lcd_rst_n_out <= 1'b0;
cnt_init <= cnt_init + 1'b1;
end //復(fù)位有效
3'd1: begin num_delay <= 16'd3000;
state <= DELAY;
state_back <= INIT;
cnt_init <= cnt_init + 1'b1; end //延時(shí)
3'd2: begin lcd_rst_n_out <= 1'b1;
cnt_init <= cnt_init + 1'b1;
end //復(fù)位恢復(fù)
3'd3: begin num_delay <= 16'd3000;
state <= DELAY;
state_back <= INIT;
cnt_init <= cnt_init + 1'b1;
end //延時(shí)
3'd4: begin
if(cnt>=INIT_DEPTH) begin
//當(dāng)73條指令及數(shù)據(jù)發(fā)出后,配置完成
cnt <= 16'd0;
cnt_init <= cnt_init + 1'b1;
end else begin
data_reg <= reg_init[cnt];
if(cnt==16'd0) num_delay <= 16'd50000; //第一條指令需要較長(zhǎng)延時(shí)
else num_delay <= 16'd50;
cnt <= cnt + 16'd1;
state <= WRITE;
state_back <= INIT;
end
end
3'd5: begin cnt_init <= 1'b0;
state <= MAIN; end //初始化完成,返回MAIN狀態(tài)
default: state <= IDLE;
endcase
end
SCAN:begin //刷屏狀態(tài),從RAM中讀取數(shù)據(jù)刷屏
case(cnt_scan)
3'd0: begin //確定刷屏的區(qū)域坐標(biāo),這里為全屏
if(cnt >= 11) begin //
cnt <= 16'd0;
cnt_scan <= cnt_scan + 1'b1;
end else begin
data_reg <= reg_setxy[cnt];
cnt <= cnt + 16'd1;
num_delay <= 16'd50;
state <= WRITE;
state_back <= SCAN;
end
end
3'd1: begin ram_lcd_clk_en <= HIGH;
ram_lcd_addr <= y_cnt;
cnt_scan <= cnt_scan + 1'b1;
end //RAM時(shí)鐘使能
3'd2: begin cnt_scan <= cnt_scan + 1'b1;
end //延時(shí)一個(gè)時(shí)鐘
3'd3: begin ram_lcd_clk_en <= LOW;
ram_data_r <= ram_lcd_data;
cnt_scan <= cnt_scan + 1'b1;
end //讀取RAM數(shù)據(jù),同時(shí)關(guān)閉RAM時(shí)鐘使能
3'd4: begin
//每個(gè)像素點(diǎn)需要16bit的數(shù)據(jù),SPI每次傳8bit,兩次分別傳送高8位和低8位
if(x_cnt>=LCD_W) begin
//當(dāng)一個(gè)數(shù)據(jù)(一行屏幕)寫完后,
x_cnt <= 8'd0;
if(y_cnt>=LCD_H) begin y_cnt <= 8'd0;
cnt_scan <= cnt_scan + 1'b1;
end //如果是最后一行就跳出循環(huán)
else begin y_cnt <= y_cnt + 1'b1;
cnt_scan <= 3'd1;
end //否則跳轉(zhuǎn)至RAM時(shí)鐘使能,循環(huán)刷屏
end else begin
if(high_word) data_reg <= {1'b1,(ram_data_r[x_cnt]? color_t[15:8]:color_b[15:8])};
//根據(jù)相應(yīng)bit的狀態(tài)判定顯示頂層色或背景色,根據(jù)high_word的狀態(tài)判定寫高8位或低8位
else begin data_reg <= {1'b1,(ram_data_r[x_cnt]? color_t[7:0]:color_b[7:0])};
x_cnt <= x_cnt + 1'b1;
end //根據(jù)相應(yīng)bit的狀態(tài)判定顯示頂層色或背景色,根據(jù)high_word的狀態(tài)判定寫高8位或低8位,同時(shí)指向下一個(gè)bit
high_word <= ~high_word;
//high_word的狀態(tài)翻轉(zhuǎn)
num_delay <= 16'd50;
//設(shè)定延時(shí)時(shí)間
state <= WRITE; //跳轉(zhuǎn)至WRITE狀態(tài)
state_back <= SCAN;
//執(zhí)行完WRITE及DELAY操作后返回SCAN狀態(tài)
end
end
3'd5: begin cnt_scan <= 1'b0;
lcd_bl_out <= HIGH;
state <= MAIN;
end
default: state <= IDLE;
endcase
end
WRITE:begin //WRITE狀態(tài),將數(shù)據(jù)按照SPI時(shí)序發(fā)送給屏幕
if(cnt_write >= 6'd17) cnt_write <= 1'b0;
else cnt_write <= cnt_write + 1'b1;
case(cnt_write)
6'd0: begin lcd_dc_out <= data_reg[8];
end //9位數(shù)據(jù)最高位為命令數(shù)據(jù)控制位
6'd1: begin lcd_clk_out <= LOW;
lcd_data_out <= data_reg[7];
end //先發(fā)高位數(shù)據(jù)
6'd2: begin lcd_clk_out <= HIGH;
end
6'd3: begin lcd_clk_out <= LOW;
lcd_data_out <= data_reg[6];
end
6'd4: begin lcd_clk_out <= HIGH;
end
6'd5: begin lcd_clk_out <= LOW;
lcd_data_out <= data_reg[5];
end
6'd6: begin lcd_clk_out <= HIGH;
end
6'd7: begin lcd_clk_out <= LOW;
lcd_data_out <= data_reg[4];
end
6'd8: begin lcd_clk_out <= HIGH; e
nd
6'd9: begin lcd_clk_out <= LOW;
lcd_data_out <= data_reg[3];
end
6'd10: begin lcd_clk_out <= HIGH;
end
6'd11: begin lcd_clk_out <= LOW;
lcd_data_out <= data_reg[2];
end
6'd12: begin lcd_clk_out <= HIGH;
end
6'd13: begin lcd_clk_out <= LOW;
lcd_data_out <= data_reg[1];
end
6'd14: begin lcd_clk_out <= HIGH;
end
6'd15: begin lcd_clk_out <= LOW;
lcd_data_out <= data_reg[0];
end //后發(fā)低位數(shù)據(jù)
6'd16: begin lcd_clk_out <= HIGH;
end
6'd17: begin lcd_clk_out <= LOW;
state <= DELAY;
end //
default: state <= IDLE;
endcase
end
DELAY:begin //延時(shí)狀態(tài)
if(cnt_delay >= num_delay) begin
cnt_delay <= 16'd0;
state <= state_back;
end else cnt_delay <= cnt_delay + 1'b1;
end
default:state <= IDLE;
endcase
end
end // data for setxy
initial //設(shè)定顯示區(qū)域指令及數(shù)據(jù)
begin
reg_setxy[0] = {1'b0,8'h2a};
reg_setxy[1] = {1'b1,8'h00};
reg_setxy[2] = {1'b1,8'h00};
reg_setxy[3] = {1'b1,8'h00};
reg_setxy[4] = {1'b1,LCD_W-1};
reg_setxy[5] = {1'b0,8'h2b};
reg_setxy[6] = {1'b1,8'h00};
reg_setxy[7] = {1'b1,8'h00};
reg_setxy[8] = {1'b1,8'h00};
reg_setxy[9] = {1'b1,LCD_H-1};
reg_setxy[10] = {1'b0,8'h2c};
end // data for init
initial //LCD初始化的命令及數(shù)據(jù)
begin
reg_init[0] = {1'b0,8'h11};
reg_init[1] = {1'b0,8'hb1};
reg_init[2] = {1'b1,8'h05};
reg_init[3] = {1'b1,8'h3c};
reg_init[4] = {1'b1,8'h3c};
reg_init[5] = {1'b0,8'hb2};
reg_init[6] = {1'b1,8'h05};
reg_init[7] = {1'b1,8'h3c};
reg_init[8] = {1'b1,8'h3c};
reg_init[9] = {1'b0,8'hb3};
reg_init[10] = {1'b1,8'h05};
reg_init[11] = {1'b1,8'h3c};
reg_init[12] = {1'b1,8'h3c};
reg_init[13] = {1'b1,8'h05};
reg_init[14] = {1'b1,8'h3c};
reg_init[15] = {1'b1,8'h3c};
reg_init[16] = {1'b0,8'hb4};
reg_init[17] = {1'b1,8'h03};
reg_init[18] = {1'b0,8'hc0};
reg_init[19] = {1'b1,8'h28};
reg_init[20] = {1'b1,8'h08};
reg_init[21] = {1'b1,8'h04};
reg_init[22] = {1'b0,8'hc1};
reg_init[23] = {1'b1,8'hc0};
reg_init[24] = {1'b0,8'hc2};
reg_init[25] = {1'b1,8'h0d};
reg_init[26] = {1'b1,8'h00};
reg_init[27] = {1'b0,8'hc3};
reg_init[28] = {1'b1,8'h8d};
reg_init[29] = {1'b1,8'h2a};
reg_init[30] = {1'b0,8'hc4};
reg_init[31] = {1'b1,8'h8d};
reg_init[32] = {1'b1,8'hee};
reg_init[32] = {1'b0,8'hc5};
reg_init[33] = {1'b1,8'h1a};
reg_init[34] = {1'b0,8'h36};
reg_init[35] = {1'b1,8'hc0};
reg_init[36] = {1'b0,8'he0};
reg_init[37] = {1'b1,8'h04};
reg_init[38] = {1'b1,8'h22};
reg_init[39] = {1'b1,8'h07};
reg_init[40] = {1'b1,8'h0a};
reg_init[41] = {1'b1,8'h2e};
reg_init[42] = {1'b1,8'h30};
reg_init[43] = {1'b1,8'h25};
reg_init[44] = {1'b1,8'h2a};
reg_init[45] = {1'b1,8'h28};
reg_init[46] = {1'b1,8'h26};
reg_init[47] = {1'b1,8'h2e};
reg_init[48] = {1'b1,8'h3a};
reg_init[49] = {1'b1,8'h00};
reg_init[50] = {1'b1,8'h01};
reg_init[51] = {1'b1,8'h03};
reg_init[52] = {1'b1,8'h13};
reg_init[53] = {1'b0,8'he1};
reg_init[54] = {1'b1,8'h04};
reg_init[55] = {1'b1,8'h16};
reg_init[56] = {1'b1,8'h06};
reg_init[57] = {1'b1,8'h0d};
reg_init[58] = {1'b1,8'h2d};
reg_init[59] = {1'b1,8'h26};
reg_init[60] = {1'b1,8'h23};
reg_init[61] = {1'b1,8'h27};
reg_init[62] = {1'b1,8'h27};
reg_init[63] = {1'b1,8'h25};
reg_init[64] = {1'b1,8'h2d};
reg_init[65] = {1'b1,8'h3b};
reg_init[66] = {1'b1,8'h00};
reg_init[67] = {1'b1,8'h01};
reg_init[68] = {1'b1,8'h04};
reg_init[69] = {1'b1,8'h13};
reg_init[70] = {1'b0,8'h3a};
reg_init[71] = {1'b1,8'h05};
reg_init[72] = {1'b0,8'h29};
end
endmodule小結(jié)
本節(jié)主要為大家講解了1.8寸RGB液晶屏圖片顯示的框架,需要大家掌握的同時(shí)自己創(chuàng)建工程,通過(guò)整個(gè)設(shè)計(jì)流程,生成FPGA配置文件加載測(cè)試。






評(píng)論