FPGA驱动SPI接口的LCD(三)——LCD的初始化

一、跟据参考的STM32代码了解初始化流程 

LCD初始化函数

void LCD_Init(void);

 首先是LCD的复位

void LCD_RESET(void)
{
    LCD_RST_CLR;        //拉低复位引脚
    Delay_Ms(100);        //延时100ms    
    LCD_RST_SET;        //拉高复位引脚
    Delay_Ms(50);        //延时50ms
}

向LCD屏幕写入一个8位命令

void LCD_WR_REG(u8 data)

   LCD_CS_CLR;     //拉低片选引脚
     LCD_RS_CLR;      //拉低dc引脚
   SPI_WriteByte(SPI1,data);        //使用硬件SPI写入一个字节的数据  
   LCD_CS_SET;        //拉高片选引脚
}

将8位数据写入LCD屏幕

void LCD_WR_DATA(u8 data)
{
   LCD_CS_CLR;     //拉低片选引脚
     LCD_RS_SET;     //拉高dc引脚
   SPI_WriteByte(SPI1,data);        //使用硬件SPI写入一个字节的数据
   LCD_CS_SET;        //拉高片选引脚
}

设置液晶显示屏的显示方向

void LCD_direction(u8 direction);

清屏,也就是整个屏幕的点都为白色

void LCD_Clear(u16 Color);

LCD显示窗口设置

void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd);

二、模块框图

 

三、设计状态机

1、初始化命令

初始化命令是厂家已经配置好的,直接调用这部分就行了。 

    LCD_WR_REG(0xCF);  
    LCD_WR_DATA(0x00); 
   (省略~)
    LCD_WR_DATA(0xef);     
    LCD_WR_REG(0x11); //Exit Sleep
    Delay_Ms(120);
    LCD_WR_REG(0x29); //display on

2、设置LCD显示方向

为了方便省事,只考虑参考代码中液晶屏顺时针旋转方向为0的情况。

#define USE_HORIZONTAL       0//定义液晶屏顺时针旋转方向     0-0度旋转,1-90度旋转,2-180度旋转,3-270度旋转

void LCD_direction(u8 direction)函数只需要知道LCD_WriteReg(0x36,(1<<3)|(0<<6)|(0<<7));

传输到lcd_write模块的数据(最高位决定命令/数据,0为命令,1为数据)为9'h036和9'h108。

3、清屏

lcd的清屏步骤是先设置LCD的填充窗口大小(320x240)

9'h02A;        //列地址设置
{1'b1,7'b0000_000,start_x[8]};        //x的开始坐标高8位
{1'b1,start_x[7:0]};                           //x的开始坐标低8位
{1'b1,7'b0000_000,end_x[8]};         //x的结束坐标高8位                
{1'b1,end_x[7:0]};                            //x的结束坐标低8位
9'h02B;        //页面地址设置
{1'b1,7'b0000_000,start_y[8]};        //y的开始坐标高8位
{1'b1,start_y[7:0]};                           //y的开始坐标低8位
{1'b1,7'b0000_000,end_y[8]};         //y的结束坐标高8位
{1'b1,end_y[7:0]};                            //y的结束坐标低8位
9'h02C;        //存储器写入

清屏的时候设置的窗口大小(320x240)和start_x、start_y、end_x、end_y已经确定,可以直接用以下部分。

9'h02A
9'h100
9'h100
9'h100
9'h1ef
9'h02B
9'h100
9'h100
9'h101
9'h13f
9'h02C

然后在需要填充的点进行对应颜色(白色)的填充。颜色填充为16位,先传高8位,再传低8位。

一共传输320*240*2-1=153,599次。

四、波形图绘制

 

 

 五、代码编写

module lcd_init
// #(//仿真时调用
    // parameter   TIME100MS    = 23'd100,  //23'd5000_000  
                // TIME150MS    = 23'd150,  //23'd7500_000  
                // TIME120MS    = 23'd120,  //23'd6000_000  
                // TIMES4MAX    = 18'd51 ,  //320*240*2+13(设置窗口大小)=153_613 
                // DATA_IDLE    = 9'b0_0000_0000
// )
#(//驱动lcd时调用
    parameter   TIME100MS    = 23'd5000_000,  //23'd5000_000  
                TIME150MS    = 23'd7500_000,  //23'd7500_000  
                TIME120MS    = 23'd6000_000,  //23'd6000_000  
                TIMES4MAX    = 18'd153_613 ,  //320*240*2+13(设置窗口大小)=153_613   
                DATA_IDLE    = 9'b0_0000_0000
)
(
    input   wire            sys_clk_50MHz ,
    input   wire            sys_rst_n     ,
    input   wire            wr_done       ,
    
    
    output  reg             lcd_rst       ,
    output  reg     [8:0]   init_data     ,
    output  wire            en_write      ,
    output  wire            init_done
);
//****************** Parameter and Internal Signal *******************//
//画笔颜色
parameter   WHITE   = 16'hFFFF,
            BLACK   = 16'h0000,	  
            BLUE    = 16'h001F,  
            BRED    = 16'hF81F,
            GRED 	= 16'hFFE0,
            GBLUE	= 16'h07FF,
            RED     = 16'hF800,
            MAGENTA = 16'hF81F,
            GREEN   = 16'h07E0,
            CYAN    = 16'h7FFF,
            YELLOW  = 16'hFFE0,
            BROWN 	= 16'hBC40, //棕色
            BRRED 	= 16'hFC07, //棕红色
            GRAY  	= 16'h8430; //灰色

//----------------------------------------------------------------- 
reg [5:0]   state;
parameter   S0_DELAY100MS         = 6'b000_001, 
            S1_DELAY50MS          = 6'b000_010,
            S2_WR_90              = 6'b000_100,
            S3_DELAY120MS         = 6'b001_000,
            S4_WR_DIRECTION_CLEAR = 6'b010_000,
            DONE                  = 6'b100_000;
            
reg [22:0]  cnt_150ms;
reg         lcd_rst_high_flag;
reg [6:0]   cnt_s2_num;
reg         cnt_s2_num_done; 
reg [17:0]  cnt_s4_num;
reg         cnt_s4_num_done;   

//----------------------------------------------------------------- 
//状态跳转            
always@(posedge sys_clk_50MHz or negedge sys_rst_n)
    if(!sys_rst_n)
        state <= S0_DELAY100MS;
    else
        case(state)
            S0_DELAY100MS:
                state <= (cnt_150ms == TIME100MS) ? S1_DELAY50MS : S0_DELAY100MS;
            S1_DELAY50MS:
                state <= (cnt_150ms == TIME150MS) ? S2_WR_90 : S1_DELAY50MS;
            S2_WR_90:
                state <= (cnt_s2_num_done) ? S3_DELAY120MS : S2_WR_90;
            S3_DELAY120MS:
                state <= (cnt_150ms == TIME120MS) ? S4_WR_DIRECTION_CLEAR : S3_DELAY120MS; 
            S4_WR_DIRECTION_CLEAR:
                state <= (cnt_s4_num_done) ? DONE : S4_WR_DIRECTION_CLEAR;
            DONE:
                state <= DONE;
            default:
                state <= S0_DELAY100MS;
        endcase

//cnt_150ms
always@(posedge sys_clk_50MHz or negedge sys_rst_n)
    if(!sys_rst_n)
        cnt_150ms <= 23'd0;
    else if(state == S0_DELAY100MS || state == S1_DELAY50MS || state == S3_DELAY120MS )
        cnt_150ms <= cnt_150ms + 1'b1;
    else
        cnt_150ms <= 23'd0;
        
//lcd_rst_high_flag
always@(posedge sys_clk_50MHz or negedge sys_rst_n)
    if(!sys_rst_n)
        lcd_rst_high_flag <= 1'b0;
    else if(state == S0_DELAY100MS && (cnt_150ms == TIME100MS - 1'b1))
        lcd_rst_high_flag <= 1'b1;
    else
        lcd_rst_high_flag <= 1'b0;

//lcd_rst
always@(posedge sys_clk_50MHz or negedge sys_rst_n)
    if(!sys_rst_n)
        lcd_rst <= 1'b0;
    else if(lcd_rst_high_flag)
        lcd_rst <= 1'b1;
    else
        lcd_rst <= lcd_rst;
//----------------------------------------------------------------- 
//cnt_s2_num决定要传的命令/数据
always@(posedge sys_clk_50MHz or negedge sys_rst_n)
    if(!sys_rst_n)
        cnt_s2_num <= 7'd0;
    else if(state != S2_WR_90)
        cnt_s2_num <= 7'd0;
    else if(wr_done && state == S2_WR_90)
        cnt_s2_num <= cnt_s2_num + 1'b1;
    else
        cnt_s2_num <= cnt_s2_num;

//cnt_s2_num_done == 1'b1则S2_WR_90完成
always@(posedge sys_clk_50MHz or negedge sys_rst_n)
    if(!sys_rst_n)
        cnt_s2_num_done <= 1'b0;
    else if(cnt_s2_num == 7'd89 && wr_done == 1'b1)
        cnt_s2_num_done <= 1'b1;
    else
        cnt_s2_num_done <= 1'b0;
        
//init_data[8:0]
always@(posedge sys_clk_50MHz or negedge sys_rst_n)
    if(!sys_rst_n)
        init_data <= DATA_IDLE;
    else if(state == S2_WR_90)
        //初始化命令/数据,直接借用厂家的
        case(cnt_s2_num)    //init_data[8] == 1'b1写数据; == 1'b0写命令
                                        7'd0 :  init_data <= 9'h0CF ; 
            7'd1 :  init_data <= 9'h100 ;                        
            7'd2 :  init_data <= 9'h1C9 ;                        
            7'd3 :  init_data <= 9'h130 ;                        
                                        7'd4 :  init_data <= 9'h0ED ;  
            7'd5 :  init_data <= 9'h164 ;                        
            7'd6 :  init_data <= 9'h103 ;                        
            7'd7 :  init_data <= 9'h112 ;                        
            7'd8 :  init_data <= 9'h181 ;                        
                                        7'd9 :  init_data <= 9'h0E8 ;  
            7'd10:  init_data <= 9'h185 ;                        
            7'd11:  init_data <= 9'h110 ;                        
            7'd12:  init_data <= 9'h17A ;                        
                                        7'd13:  init_data <= 9'h0CB ;  
            7'd14:  init_data <= 9'h139 ;                        
            7'd15:  init_data <= 9'h12C ;                        
            7'd16:  init_data <= 9'h100 ;                        
            7'd17:  init_data <= 9'h134 ;                        
            7'd18:  init_data <= 9'h102 ;                        
                                        7'd19:  init_data <= 9'h0F7 ;  
            7'd20:  init_data <= 9'h120 ;                        
                                        7'd21:  init_data <= 9'h0EA ;  
            7'd22:  init_data <= 9'h100 ;                        
            7'd23:  init_data <= 9'h100 ;                        
                                        7'd24:  init_data <= 9'h0C0 ;  
            7'd25:  init_data <= 9'h11B ;                        
                                        7'd26:  init_data <= 9'h0C1 ;  
            7'd27:  init_data <= 9'h100 ;                        
                                        7'd28:  init_data <= 9'h0C5 ;  
            7'd29:  init_data <= 9'h130 ;                        
            7'd30:  init_data <= 9'h130 ;                        
                                        7'd31:  init_data <= 9'h0C7 ;  
            7'd32:  init_data <= 9'h1B7 ;                        
                                        7'd33:  init_data <= 9'h036 ;  
            7'd34:  init_data <= 9'h108 ;                        
                                        7'd35:  init_data <= 9'h03A ;  
            7'd36:  init_data <= 9'h155 ;                        
                                        7'd37:  init_data <= 9'h0B1 ;  
            7'd38:  init_data <= 9'h100 ;                        
            7'd39:  init_data <= 9'h11A ;                        
                                        7'd40:  init_data <= 9'h0B6 ;  
            7'd41:  init_data <= 9'h10A ;                        
            7'd42:  init_data <= 9'h1A2 ;                        
                                        7'd43:  init_data <= 9'h0F2 ;  
            7'd44:  init_data <= 9'h100 ;                        
                                        7'd45:  init_data <= 9'h026 ;  
            7'd46:  init_data <= 9'h101 ;                        
                                        7'd47:  init_data <= 9'h0E0 ;  
            7'd48:  init_data <= 9'h10F ;                        
            7'd49:  init_data <= 9'h12A ;                        
            7'd50:  init_data <= 9'h128 ;                        
            7'd51:  init_data <= 9'h108 ;                        
            7'd52:  init_data <= 9'h10E ;                        
            7'd53:  init_data <= 9'h108 ;                        
            7'd54:  init_data <= 9'h154 ;                        
            7'd55:  init_data <= 9'h1A9 ;                        
            7'd56:  init_data <= 9'h143 ;                        
            7'd57:  init_data <= 9'h10A ;                        
            7'd58:  init_data <= 9'h10F ;                        
            7'd59:  init_data <= 9'h100 ;                        
            7'd60:  init_data <= 9'h100 ;                        
            7'd61:  init_data <= 9'h100 ;                        
            7'd62:  init_data <= 9'h100 ;                        
                                        7'd63:  init_data <= 9'h0E1 ;  
            7'd64:  init_data <= 9'h100 ;                        
            7'd65:  init_data <= 9'h115 ;                        
            7'd66:  init_data <= 9'h117 ;                        
            7'd67:  init_data <= 9'h107 ;                        
            7'd68:  init_data <= 9'h111 ;                        
            7'd69:  init_data <= 9'h106 ;                        
            7'd70:  init_data <= 9'h12B ;                        
            7'd71:  init_data <= 9'h156 ;                        
            7'd72:  init_data <= 9'h13C ;                        
            7'd73:  init_data <= 9'h105 ;                        
            7'd74:  init_data <= 9'h110 ;                        
            7'd75:  init_data <= 9'h10F ;                        
            7'd76:  init_data <= 9'h13F ;                        
            7'd77:  init_data <= 9'h13F ;                        
            7'd78:  init_data <= 9'h10F ;                        
                                        7'd79:  init_data <= 9'h02B ;  
            7'd80:  init_data <= 9'h100 ;                        
            7'd81:  init_data <= 9'h100 ;                        
            7'd82:  init_data <= 9'h101 ;                        
            7'd83:  init_data <= 9'h13f ;                        
                                        7'd84:  init_data <= 9'h02A ;  
            7'd85:  init_data <= 9'h100 ;                        
            7'd86:  init_data <= 9'h100 ;                        
            7'd87:  init_data <= 9'h100 ;                        
            7'd88:  init_data <= 9'h1ef ;                        
                                        7'd89:  init_data <= 9'h011 ;  
            default: init_data <= DATA_IDLE;
        endcase
        
    else if(state == S4_WR_DIRECTION_CLEAR)
        case(cnt_s4_num)
            'd0 :  init_data <= 9'h029;
            //设置LCD显示方向
            'd1 :  init_data <= 9'h036;
            'd2 :  init_data <= 9'h108;
            
            //LCD显示窗口设置
            'd3 :  init_data <= 9'h02a;
                             
            'd4 :  init_data <= 9'h100;
            'd5 :  init_data <= 9'h100;
            'd6 :  init_data <= 9'h100;
            'd7 :  init_data <= 9'h1ef;
                             
            'd8 :  init_data <= 9'h02b;
                             
            'd9 :  init_data <= 9'h100;
            'd10:  init_data <= 9'h100;
            'd11:  init_data <= 9'h101;
            'd12:  init_data <= 9'h13f;
                             
            'd13:  init_data <= 9'h02c;
            
            //填充对应点的颜色,可以换用比较明显的红色,便于观察现象
            default : 
                //当cnt_s4_num大于14且为偶数时,传输颜色数据的高8位
                if(cnt_s4_num >= 'd14 && cnt_s4_num[0] == 0)
                    init_data <= {1'b1,WHITE[15:8]};
                //当cnt_s4_num大于14且为奇数时,传输颜色数据的低8位
                else if(cnt_s4_num >= 'd14 && cnt_s4_num[0] == 1)
                    init_data <= {1'b1,WHITE[7:0]};
                else
                    init_data <= DATA_IDLE;
        endcase
    else
        init_data <= DATA_IDLE;

//cnt_s4_num决定要传的命令/数据
always@(posedge sys_clk_50MHz or negedge sys_rst_n)
    if(!sys_rst_n)
        cnt_s4_num <= 18'd0;
    else if(state != S4_WR_DIRECTION_CLEAR)
        cnt_s4_num <= 18'd0;
    else if(wr_done && state == S4_WR_DIRECTION_CLEAR)
        cnt_s4_num <= cnt_s4_num + 1'b1;
    else                   
        cnt_s4_num <= cnt_s4_num;

//cnt_s4_num_done
always@(posedge sys_clk_50MHz or negedge sys_rst_n)
    if(!sys_rst_n)
        cnt_s4_num_done <= 1'b0;
    else if(cnt_s4_num == TIMES4MAX && wr_done == 1'b1)
        cnt_s4_num_done <= 1'b1;
    else
        cnt_s4_num_done <= 1'b0;  
        
assign en_write = (state == S2_WR_90 || state == S4_WR_DIRECTION_CLEAR) ? 1'b1 : 1'b0;      

assign init_done = (state == DONE) ? 1'b1 : 1'b0;        
        
endmodule

六、仿真代码

`timescale 1ns/1ns

module tb_lcd_init();

reg             sys_clk_50MHz;
reg             sys_rst_n    ;
reg             wr_done      ;

wire            lcd_rst      ;
wire    [8:0]   init_data    ;
wire            en_write     ;
wire            init_done    ;

reg     [1:0]   cnt1;

initial
begin
    sys_clk_50MHz <= 1'b1;
    sys_rst_n     <= 1'b0;
    wr_done       <= 1'b0;
    #100
    sys_rst_n     <= 1'b1;
end

always #10 sys_clk_50MHz <= ~sys_clk_50MHz;

always@(posedge sys_clk_50MHz or negedge sys_rst_n)
    if(!sys_rst_n)
        cnt1 <= 'd0;
    else if(en_write)
        cnt1 <= cnt1 + 1'b1;

always@(posedge sys_clk_50MHz or negedge sys_rst_n)
    if(!sys_rst_n)
        wr_done <= 1'b0;
    else if(cnt1 == 'd3)
        wr_done <= 1'b1;
    else
        wr_done <= 1'b0;

lcd_init    lcd_init_inst
(
    .sys_clk_50MHz (sys_clk_50MHz),
    .sys_rst_n     (sys_rst_n    ),
    .wr_done       (wr_done      ),
    
    .lcd_rst       (lcd_rst      ),
    .init_data     (init_data    ),
    .en_write      (en_write     ),
    .init_done     (init_done    )
);

endmodule

七、仿真波形图

 

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FPGA可以通过模拟SPI接口驱动ADC。SPI接口通常用于与Flash、ADC、LCD控制器等设备进行通信。在FPGA中,可以使用FPGA内部的逻辑电路来模拟SPI接口的功能。通过控制FPGA的引脚和时序,可以实现与ADC的通信。 在模拟SPI接口时,需要定义时钟信号、复位信号、使能信号和数据信号等。时钟信号用于同步数据传输,复位信号用于初始化接口,使能信号用于控制数据传输的开始和结束,数据信号用于传输具体的数据。 具体实现时,可以使用FPGA的时钟模块来生成时钟信号,使用寄存器来存储和控制数据,使用逻辑门来实现SPI接口的功能。通过编写Verilog或VHDL代码,可以描述FPGA的逻辑电路,并进行仿真和验证。 在模拟SPI接口驱动ADC的过程中,需要根据ADC的规格和时序要求来配置FPGA的引脚和时钟频率。通过发送控制信号和数据,可以实现与ADC的通信和数据采集。 总结来说,FPGA可以通过模拟SPI接口驱动ADC,通过配置引脚和时序,控制数据传输和采集。这样可以实现FPGA与ADC之间的数据交互和通信。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [D2--FPGA SPI接口通信2022-08-03](https://blog.csdn.net/weixin_40615338/article/details/126145035)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [FPGA_SPI驱动设计](https://blog.csdn.net/qq_43485409/article/details/109138893)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值