差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
后一修订版 两侧同时换到之后的修订记录
图片显示系统设计 [2018/10/31 16:58]
anran [设计框图]
图片显示系统设计 [2018/11/01 10:27]
anran [实验原理]
行 21: 行 21:
 {{:​12-Top-Down层次设计.png?​500|Top-Down层次设计}} {{:​12-模块结构设计.png?​500|模块结构设计}} {{:​12-Top-Down层次设计.png?​500|Top-Down层次设计}} {{:​12-模块结构设计.png?​500|模块结构设计}}
 ====实验原理==== ====实验原理====
 +
 ===液晶屏介绍=== ===液晶屏介绍===
 +
 +{{:​12-液晶屏规格书.png?​800|液晶屏规格书}}
 +
 +查看底板上集成的1.8寸串行彩色液晶屏规格书,屏幕采用ST7735S的驱动芯片,接下来我们主要根据ST7735S的芯片手册来了解其工作原理和驱动方法。
 +
 +ST7735S为132RGB x 162像素点 262K 控制器/​驱动器,芯片可以直接跟外部处理器连接,支持串行SPI通信和8/​9/​16/​18位并行通信(本液晶屏集成ST7735S时没有留并行接口,所以只能使用串行通信),详细参数请参考数据手册。
 +
 +{{:​12-液晶屏驱动芯片原理示意.png?​600|液晶屏驱动芯片原理示意}}
 +
 +ST7735S支持不同位宽的并行通信格式。
 +
 +{{:​12-ST7735S通信格式.png?​800|ST7735S通信格式}}
 +
 +在控制器给屏幕刷屏时,根据MV、MX、MY的配置支持8种不同方向的刷屏模式。
 +
 +{{:​12-ST7735S刷屏模式.png?​600|ST7735S部分刷屏模式}}
 +
 +支持大量功能指令,部分系统功能指令列表如下
 +
 +{{:​12-ST7735S功能指令.png?​800|ST7735S部分功能指令}}
 +
 +更多的内容这里就不一一介绍了,感兴趣的同学可以详细阅读ST7735S芯片手册。
 +
 ===液晶屏硬件连接=== ===液晶屏硬件连接===
 +
 +STEP BaseBoard V3.0底板上的1.8寸串行彩色液晶屏模块电路,其电路图如下:
 +
 +{{:​12-液晶屏硬件电路.png?​600|1.8寸串行彩色液晶屏硬件电路}}
 +
 +底板上的1.8寸串行彩色液晶屏电路和VGA显示电路复用部分FPGA管脚,两者不能同时使用,当使用1.8寸串行彩色液晶屏时,DISP_SEL信号置高,驱动1.8寸串行彩色液晶屏使能同时点亮背光,DISP_2~ DISP_5分别对应RESET、D/​C、SDA、SCK管脚,最后FPGA驱动1.8寸液晶屏完成屏显示控制即可。
 +
 ===液晶屏驱动设计=== ===液晶屏驱动设计===
 +
 +要驱动液晶屏需要先了解液晶屏的驱动流程,可以从液晶屏驱动芯片ST7735S的芯片手册上获取,也可以到网上找找有没有别人使用同类液晶屏的案例,或者向卖方问问有没有相关资料提供,这里我们找到了一个用51单片机驱动的程序例程,例程仅供参考,需要根据例程中的配置到芯片手册中查找确认,不可以直接套用。
 +
 +首先完成液晶屏初始化操作,51程序流程如下:
 +
 +<code c>
 +void  ST7735_LAIBAO177_INITIAL ()
 +
 +//​-----------ST7735R Reset Sequence----------------// ​
 +RES =1; delay (1);        //Delay 1ms 
 +RES =0; delay (1);        //Delay 1ms 
 +RES =1; delay (120); ​     //Delay 120ms 
 +//​----------End ST7735R Reset Sequence ------------// ​
 +LCD_WriteCommand(0x11); ​  //​Sleep out 
 +delay(120); ​              //​Delay 120ms 
 +//​---------ST7735S Frame Rate-------------------// ​
 +LCD_WriteCommand(0xB1); ​
 +LCD_WriteData(0x05);​ LCD_WriteData(0x3C);​ LCD_WriteData(0x3C); ​
 +LCD_WriteCommand(0xB2); ​
 +LCD_WriteData(0x05);​ LCD_WriteData(0x3C);​ LCD_WriteData(0x3C); ​
 +LCD_WriteCommand(0xB3); ​
 +LCD_WriteData(0x05);​ LCD_WriteData(0x3C);​ LCD_WriteData(0x3C); ​
 +LCD_WriteData(0x05);​ LCD_WriteData(0x3C);​ LCD_WriteData(0x3C); ​
 +//​-----------End ST7735S Frame Rate---------------// ​
 +LCD_WriteCommand(0xB4);​ //Dot inversion ​
 +LCD_WriteData(0x03); ​
 +//​-----------ST7735S Power Sequence---------------// ​
 +LCD_WriteCommand(0xC0); ​
 +LCD_WriteData(0x28);​ LCD_WriteData(0x08);​ LCD_WriteData(0x04); ​
 +LCD_WriteCommand(0xC1); ​
 +LCD_WriteData(0XC0); ​
 +LCD_WriteCommand(0xC2); ​
 +LCD_WriteData(0x0D);​ LCD_WriteData(0x00); ​
 +LCD_WriteCommand(0xC3); ​
 +LCD_WriteData(0x8D);​ LCD_WriteData(0x2A); ​
 +LCD_WriteCommand(0xC4); ​
 +LCD_WriteData(0x8D);​ LCD_WriteData(0xEE); ​
 +//​----------End ST7735S Power Sequence----------// ​
 +LCD_WriteCommand(0xC5);​ //​VCOM ​
 +LCD_WriteData(0x18); ​   //1a
 +LCD_WriteCommand(0x36);​ //MX, MY, RGB mode 
 +LCD_WriteData(0xC0); ​
 +//​-----------ST7735S Gamma Sequence-----------// ​
 +LCD_WriteCommand(0xE0); ​
 +LCD_WriteData(0x04);​ LCD_WriteData(0x22);​ LCD_WriteData(0x07); ​
 +LCD_WriteData(0x0A);​ LCD_WriteData(0x2E);​ LCD_WriteData(0x30); ​
 +LCD_WriteData(0x25);​ LCD_WriteData(0x2A);​ LCD_WriteData(0x28); ​
 +LCD_WriteData(0x26);​ LCD_WriteData(0x2E);​ LCD_WriteData(0x3A); ​
 +LCD_WriteData(0x00);​ LCD_WriteData(0x01);​ LCD_WriteData(0x03); ​
 +LCD_WriteData(0x13); ​
 +LCD_WriteCommand(0xE1); ​
 +LCD_WriteData(0x04);​ LCD_WriteData(0x16);​ LCD_WriteData(0x06); ​
 +LCD_WriteData(0x0D);​ LCD_WriteData(0x2D);​ LCD_WriteData(0x26); ​
 +LCD_WriteData(0x23);​ LCD_WriteData(0x27);​ LCD_WriteData(0x27); ​
 +LCD_WriteData(0x25);​ LCD_WriteData(0x2D);​ LCD_WriteData(0x3B); ​
 +LCD_WriteData(0x00);​ LCD_WriteData(0x01);​ LCD_WriteData(0x04); ​
 +LCD_WriteData(0x13); ​
 +//​------------End ST7735S Gamma Sequence----------// ​
 +LCD_WriteCommand(0x3A);​ //65k mode 
 +LCD_WriteData(0x05); ​
 +LCD_WriteCommand(0x29);​ //Display on 
 +}
 +</​code>​
 +
 +创建存储器,将初始化过程中写的所有指令和数据存储,同时存储的还有指令或数据标志,例如初始化第1条指令为8'​h11,我们增加最高位1‘b0组成9位位宽数据。存储器部分指令和数据如下:
 +
 +<code verilog>
 +initial begin //​LCD初始化的命令及数据
 +    reg_init[ 0]    =   ​{1'​b0,​8'​h11};​ //​最高位为0,表示低8位为指令
 +    reg_init[ 1]    =   ​{1'​b0,​8'​hb1}; ​
 +    reg_init[ 2]    =   ​{1'​b1,​8'​h05};​ //​最高位为1,表示低8位为数据
 +    reg_init[ 3]    =   ​{1'​b1,​8'​h3c}; ​
 +    reg_init[ 4]    =   ​{1'​b1,​8'​h3c};  ​
 +</​code>​
 +
 +从51例程中可以看到,整个初始化过程都在给液晶屏写指令或数据,通过查看写指令或写数据的时序发现,唯一不同的就是对A0(对应底板液晶屏模块中的D/​C信号)的控制,程序实现如下:
 +
 +<code c>
 +void  LCD_WriteXXX(uint dat)
 +{  int i;
 +    A0=0; //​写指令,如果写数据 A0=1;
 +    CSB=0; //​液晶屏使能
 +    for(i=0;​i<​8;​i++)
 +    {
 +        if(dat &0x80) SDA=1;
 +        else SDA=0;
 +        SCL=0; SCL=1;
 +        dat <<=1;
 +    }
 +    CSB=1;  ​
 +}
 +</​code>​
 +
 +FPGA驱动液晶屏的设计使用状态机完成,将写数据与写指令的SPI时序整合成一个状态,另加一位指令数据控制位,程序实现如下:
 +
 +<code verilog>
 +WRITE:begin //​WRITE状态,将数据按照SPI时序发送给屏幕
 +        if(cnt_write >= 6'd17) cnt_write <= 1'b0;
 +        else cnt_write <= cnt_write + 1'b1;
 +        case(cnt_write)
 +            6'​d0: ​  begin lcd_dc <= data_reg[8];​ end    //​9位数据最高位为命令数据控制位
 +            6'​d1: ​  begin lcd_clk <= LOW; lcd_din <= data_reg[7];​ end   //​先发高位数据
 +            6'​d2: ​  begin lcd_clk <= HIGH; end
 +            6'​d3: ​  begin lcd_clk <= LOW; lcd_din <= data_reg[6];​ end
 +            6'​d4: ​  begin lcd_clk <= HIGH; end
 +            6'​d5: ​  begin lcd_clk <= LOW; lcd_din <= data_reg[5];​ end
 +            6'​d6: ​  begin lcd_clk <= HIGH; end
 +            6'​d7: ​  begin lcd_clk <= LOW; lcd_din <= data_reg[4];​ end
 +            6'​d8: ​  begin lcd_clk <= HIGH; end
 +            6'​d9: ​  begin lcd_clk <= LOW; lcd_din <= data_reg[3];​ end
 +            6'​d10: ​ begin lcd_clk <= HIGH; end
 +            6'​d11: ​ begin lcd_clk <= LOW; lcd_din <= data_reg[2];​ end
 +            6'​d12: ​ begin lcd_clk <= HIGH; end
 +            6'​d13: ​ begin lcd_clk <= LOW; lcd_din <= data_reg[1];​ end
 +            6'​d14: ​ begin lcd_clk <= HIGH; end
 +            6'​d15: ​ begin lcd_clk <= LOW; lcd_din <= data_reg[0];​ end   //​后发低位数据
 +            6'​d16: ​ begin lcd_clk <= HIGH; end
 +            6'​d17: ​ begin lcd_clk <= LOW; state <= DELAY; end   //
 +            default: state <= IDLE;
 +        endcase
 +    end
 +</​code>​
 +
 +初始化指令和数据都放到存储器中了,数据写入的SPI串行时序也已经设计成了一个状态,初始化状态只需要在复位后将存储器中的指令或数据通过WRITE状态发送给液晶屏,程序实现如下:
 +
 +<code verilog>
 +INIT:​begin ​ //​初始化状态
 +        if(cnt_init==3'​d4) begin
 +            if(cnt==INIT_DEPTH) cnt_init <= 1'b0;
 +            else cnt_init <= cnt_init;
 +        end else cnt_init <= cnt_init + 1'b1;
 +        case(cnt_init)
 +            3'​d0: ​  ​lcd_res <= 1'​b0; ​   //​复位有效
 +            3'​d1: ​  begin num_delay<​=16'​d3000;​ state<​=DELAY;​ state_back<​=INIT;​ end 
 +            3'​d2: ​  ​lcd_res <= 1'​b1; ​   //​复位恢复
 +            3'​d3: ​  begin num_delay<​=16'​d3000;​ state<​=DELAY;​ state_back<​=INIT;​ end 
 +            3'​d4: ​  ​if(cnt>​=INIT_DEPTH) begin //​当62条指令及数据发出后,配置完成
 +                        cnt <= 16'​d0; ​ state <= MAIN;
 +                    end else begin
 +                        cnt <= cnt + 16'​d1; ​ data_reg <= reg_init[cnt];  ​
 +                        if(cnt==16'​d0) num_delay <= 16'​d50000;​ //​第一条指令需要较长延时
 +                        else num_delay <= 16'​d50;​
 +                        state <= WRITE; state_back <= INIT;
 +                    end
 +            default: state <= IDLE;
 +        endcase
 +    end
 +</​code>​
 +
 +初始化完成,进入刷屏状态,刷屏数据写入前首先进行区域坐标的定位,然后刷写数据,图片采用单色显示,图片ram中每位数表示一个液晶屏一个像素点的亮还是灭,彩色液晶屏本实验采用16bit格式,即需要16bit数据决定像素的颜色,16bit数据分两次发送,最终从ram模块中获取的数据每位数据都要转换成16bit的数据,0转换成背景色对应的数据,1转换成顶层色对应的数据,程序实现如下:
 +
 +<code verilog>
 +SCAN:​begin ​ //​刷屏状态,从RAM中读取数据刷屏
 +    case(cnt_scan)
 +        3'd0: if(cnt >= 11) begin //​确定刷屏的区域坐标,这里为全屏
 +                    cnt <= 16'd0;
 +                    cnt_scan <= cnt_scan + 1'b1;
 +                end else begin
 +                    cnt <= cnt + 16'd1;
 +                    data_reg <= reg_setxy[cnt];​
 +                    num_delay <= 16'​d50;​
 +                    state <= WRITE; state_back <= SCAN;
 +                end
 +        3'd1: begin ram_clk_en<​=HIGH;​ram_addr<​=y_cnt;​cnt_scan<​=cnt_scan+1'​b1;​ end   
 +        3'd2: begin cnt_scan <= cnt_scan + 1'b1; end  //​延时一个时钟
 +        3'd3: begin ram_clk_en<​=LOW;​ram_data_r<​=ram_data;​cnt_scan<​=cnt_scan+1'​b1;​ end
 +        3'd4: begin //​每个像素点需要16bit的数据,SPI每次传8bit,两次分别传送高8位和低8位
 +                   ​if(x_cnt>​=LCD_W) begin  //​当一个数据(一行屏幕)写完后,
 +                        x_cnt <= 8'​d0;  ​
 +                        if(y_cnt>​=LCD_H) begin y_cnt <= 8'd0; cnt_scan <= cnt_scan + 1'b1; end  //​如果是最后一行就跳出循环
 +                        else begin y_cnt <= y_cnt + 1'b1; cnt_scan <= 3'd1; end     //​否则跳转至RAM时钟使能,循环刷屏
 +                    end else begin
 +                        if(high_word) ​
 +                            //​根据相应bit的状态判定显示顶层色或背景色,​根据high_word的状态判定写高8位或低8位
 +                            data_reg <= {1'​b1,​(ram_data_r[x_cnt]?​ color_t[15:​8]:​color_b[15:​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 //
 +                        high_word <= ~high_word; ​   //​high_word的状态翻转
 +                        num_delay <= 16'​d50; ​   //​设定延时时间
 +                        state <= WRITE; //​跳转至WRITE状态
 +                        state_back <= SCAN; //​执行完WRITE及DELAY操作后返回SCAN状态
 +                    end
 +                end
 +        3'​d5: ​  begin cnt_scan <= 1'b0; state <= MAIN; end
 +        default: state <= IDLE;
 +endcase ​
 +end
 +</​code>​
 +
 ===系统总体实现=== ===系统总体实现===
  
 +液晶屏驱动模块的数据来源于图片数据的ram模块,这些数据由图片取模得到,使用图片取模软件,将图片载入软件,输出数据类型选择C语言数组,根据液晶屏驱动实际情况配置对应的扫描模式,输出灰度选择单色,调整最大宽度和高度符合液晶屏要求,最后点击保存生成需要的文件。
 +
 +{{:​12-取模软件参数配置.png?​600|取模软件参数配置}}
 +
 +打开生成的文件,数据格式如下,是C语言的格式
 +
 +<code c>
 +const unsigned char gImage_11[1990] = { 0X10,​0X01,​0X00,​0X80,​0X00,​0X7C,​
 +0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​
 +0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​
 +0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​
 +0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0XF8,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​
 +0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X07,​0XFF,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​0X00,​
 +</​code>​
 +
 +使用编辑器的查找替换功能,将数据处理成下图格式
 +
 +<code verilog>
 +132'​h00000000000000000000000000000000,​
 +132'​h00000000000000000000000000000000,​
 +132'​h00000000000000000000000000000000,​
 +132'​h0000000000000000F800000000000000,​
 +132'​h0000000000000007FF00000000000000,​
 +</​code>​
 +
 +创建ram模块,将图片数据初始化到ram中,程序实现图下:
 +
 +<code verilog>
 +module LCD_RAM (input wire [7:0] Address, output reg [131:0] Q);
 +always @ (*)
 +    case(Address)
 +        8'​d0 ​ : Q = 132'​h00000000000000000000000000000000;​
 +        8'​d1 ​ : Q = 132'​h00000000000000000000000000000000;​
 +        8'​d2 ​ : Q = 132'​h00000000000000000000000000000000;​
 +        8'​d3 ​ : Q = 132'​h0000000000000000F800000000000000;​
 +        8'​d4 ​ : Q = 132'​h0000000000000007FF00000000000000;​
 +</​code>​
 +
 +存储图片数据的ram本实验采用分布式ram搭建,前面波形信号发生器实验中讲过ram IP核的例化及使用方法,有兴趣的同学可以自己尝试一下。
 +
 +在顶层模块中,将两个模块例化并连接,最终完成图片显示系统的总体设计。
 +
 +综合后的设计框图如下:
 +
 +{{:​12-RTL设计框图.png?​800|RTL设计框图}}
  
 ====实验步骤==== ====实验步骤====