差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
图片显示系统设计 [2018/10/31 16:57] anran [实验目的] |
图片显示系统设计 [2021/01/06 22:57] gongyu |
||
---|---|---|---|
行 1: | 行 1: | ||
- | =====图片显示系统设计===== | + | ### 图片显示系统设计 |
- | ----- | + | |
- | ====实验任务==== | + | |
+ | #### 实验任务 | ||
* 任务:基于 STEP-MAX10M08核心板 和 STEP BaseBoard V3.0底板 完成图片显示系统设计并观察调试结果 | * 任务:基于 STEP-MAX10M08核心板 和 STEP BaseBoard V3.0底板 完成图片显示系统设计并观察调试结果 | ||
* 要求:将小脚丫的Logo转换成单色图片数据,驱动底板上1.8寸彩色液晶屏显示出来 | * 要求:将小脚丫的Logo转换成单色图片数据,驱动底板上1.8寸彩色液晶屏显示出来 | ||
* 解析:将单色图片的数据存储到rom中,驱动1.8寸将图片刷到液晶屏上。 | * 解析:将单色图片的数据存储到rom中,驱动1.8寸将图片刷到液晶屏上。 | ||
- | ====实验目的==== | + | #### 实验目的 |
扩展板卡上集成了1.8寸彩色液晶屏TFT_LCD模块,大家可以驱动LCD显示文字、图片或动态的波形。本实验主要学习1.8寸串行彩色液晶屏的驱动设计,然后将小脚丫Logo处理显示,完成图片显示系统的总体设计。 | 扩展板卡上集成了1.8寸彩色液晶屏TFT_LCD模块,大家可以驱动LCD显示文字、图片或动态的波形。本实验主要学习1.8寸串行彩色液晶屏的驱动设计,然后将小脚丫Logo处理显示,完成图片显示系统的总体设计。 | ||
行 13: | 行 12: | ||
* 完成图片显示系统设计实现 | * 完成图片显示系统设计实现 | ||
- | ====设计框图==== | + | #### 设计框图 |
+ | 根据前面的实验解析我们可以得知,该设计可以拆分成两个功能模块实现, | ||
+ | * LCD_RGB:1.8寸串行液晶屏驱动模块 | ||
- | ====实验原理==== | + | * LCD_RAM:用于存储单色图片数据。 |
- | ===液晶屏介绍=== | + | |
- | ===液晶屏硬件连接=== | + | |
- | ===液晶屏驱动设计=== | + | |
- | ===系统总体实现=== | + | |
+ | {{: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设计框图}} | ||
+ | |||
+ | ##### 实验步骤 | ||
- 双击打开Quartus Prime工具软件; | - 双击打开Quartus Prime工具软件; | ||
- 新建工程:File → New Project Wizard(工程命名,工程目录选择,设备型号选择,EDA工具选择); | - 新建工程:File → New Project Wizard(工程命名,工程目录选择,设备型号选择,EDA工具选择); | ||
行 34: | 行 306: | ||
- | ====实验现象==== | + | ##### 实验现象 |
+ | |||
+ | 将设计加载到FPGA中,观察底板液晶屏显示,小脚丫的Logo被显示出来了,前面说了1.8寸串行液晶屏支持不同的刷新方向,大家可以调整图片显示的方向 | ||
+ | |||
+ | {{:12-实验现象.png?400|实验现象}} |