• 任务:基于 STEP-MAX10M08核心板 和 STEP BaseBoard V3.0底板 完成图片显示系统设计并观察调试结果
  • 要求:将小脚丫的Logo转换成单色图片数据,驱动底板上1.8寸彩色液晶屏显示出来
  • 解析:将单色图片的数据存储到rom中,驱动1.8寸将图片刷到液晶屏上。

扩展板卡上集成了1.8寸彩色液晶屏TFT_LCD模块,大家可以驱动LCD显示文字、图片或动态的波形。本实验主要学习1.8寸串行彩色液晶屏的驱动设计,然后将小脚丫Logo处理显示,完成图片显示系统的总体设计。

  • 了解1.8寸串行采样液晶屏的原理及驱动设计
  • 完成图片显示系统设计实现

根据前面的实验解析我们可以得知,该设计可以拆分成两个功能模块实现,

  • LCDRGB:1.8寸串行液晶屏驱动模块。 * LCDRAM:用于存储单色图片数据。

Top-Down层次设计 模块结构设计

液晶屏介绍

液晶屏规格书

查看底板上集成的1.8寸串行彩色液晶屏规格书,屏幕采用ST7735S的驱动芯片,接下来我们主要根据ST7735S的芯片手册来了解其工作原理和驱动方法。

ST7735S为132RGB x 162像素点 262K 控制器/驱动器,芯片可以直接跟外部处理器连接,支持串行SPI通信和8/9/16/18位并行通信(本液晶屏集成ST7735S时没有留并行接口,所以只能使用串行通信),详细参数请参考数据手册。

液晶屏驱动芯片原理示意

ST7735S支持不同位宽的并行通信格式。

ST7735S通信格式

在控制器给屏幕刷屏时,根据MV、MX、MY的配置支持8种不同方向的刷屏模式。

ST7735S部分刷屏模式

支持大量功能指令,部分系统功能指令列表如下

ST7735S部分功能指令

更多的内容这里就不一一介绍了,感兴趣的同学可以详细阅读ST7735S芯片手册。

液晶屏硬件连接

STEP BaseBoard V3.0底板上的1.8寸串行彩色液晶屏模块电路,其电路图如下:

1.8寸串行彩色液晶屏硬件电路

底板上的1.8寸串行彩色液晶屏电路和VGA显示电路复用部分FPGA管脚,两者不能同时使用,当使用1.8寸串行彩色液晶屏时,DISPSEL信号置高,驱动1.8寸串行彩色液晶屏使能同时点亮背光,DISP2~ DISP_5分别对应RESET、D/C、SDA、SCK管脚,最后FPGA驱动1.8寸液晶屏完成屏显示控制即可。

液晶屏驱动设计

要驱动液晶屏需要先了解液晶屏的驱动流程,可以从液晶屏驱动芯片ST7735S的芯片手册上获取,也可以到网上找找有没有别人使用同类液晶屏的案例,或者向卖方问问有没有相关资料提供,这里我们找到了一个用51单片机驱动的程序例程,例程仅供参考,需要根据例程中的配置到芯片手册中查找确认,不可以直接套用。

首先完成液晶屏初始化操作,51程序流程如下:

<code c> void ST7735LAIBAO177INITIAL () { ———–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 ———— LCDWriteCommand(0x11); Sleep out delay(120); Delay 120ms ———ST7735S Frame Rate——————- LCDWriteCommand(0xB1); LCDWriteData(0x05); LCDWriteData(0x3C); LCDWriteData(0x3C); LCDWriteCommand(0xB2); LCDWriteData(0x05); LCDWriteData(0x3C); LCDWriteData(0x3C); LCDWriteCommand(0xB3); LCDWriteData(0x05); LCDWriteData(0x3C); LCDWriteData(0x3C); LCDWriteData(0x05); LCDWriteData(0x3C); LCDWriteData(0x3C); ———–End ST7735S Frame Rate————— LCDWriteCommand(0xB4); Dot inversion LCDWriteData(0x03); ———–ST7735S Power Sequence————— LCDWriteCommand(0xC0); LCDWriteData(0x28); LCDWriteData(0x08); LCDWriteData(0x04); LCDWriteCommand(0xC1); LCDWriteData(0XC0); LCDWriteCommand(0xC2); LCDWriteData(0x0D); LCDWriteData(0x00); LCDWriteCommand(0xC3); LCDWriteData(0x8D); LCDWriteData(0x2A); LCDWriteCommand(0xC4); LCDWriteData(0x8D); LCDWriteData(0xEE); ———-End ST7735S Power Sequence———- LCDWriteCommand(0xC5); VCOM LCDWriteData(0x18); 1a LCDWriteCommand(0x36); MX, MY, RGB mode LCDWriteData(0xC0); ———–ST7735S Gamma Sequence———– LCDWriteCommand(0xE0); LCDWriteData(0x04); LCDWriteData(0x22); LCDWriteData(0x07); LCDWriteData(0x0A); LCDWriteData(0x2E); LCDWriteData(0x30); LCDWriteData(0x25); LCDWriteData(0x2A); LCDWriteData(0x28); LCDWriteData(0x26); LCDWriteData(0x2E); LCDWriteData(0x3A); LCDWriteData(0x00); LCDWriteData(0x01); LCDWriteData(0x03); LCDWriteData(0x13); LCDWriteCommand(0xE1); LCDWriteData(0x04); LCDWriteData(0x16); LCDWriteData(0x06); LCDWriteData(0x0D); LCDWriteData(0x2D); LCDWriteData(0x26); LCDWriteData(0x23); LCDWriteData(0x27); LCDWriteData(0x27); LCDWriteData(0x25); LCDWriteData(0x2D); LCDWriteData(0x3B); LCDWriteData(0x00); LCDWriteData(0x01); LCDWriteData(0x04); LCDWriteData(0x13); ————End ST7735S Gamma Sequence———- LCDWriteCommand(0x3A); 65k mode LCDWriteData(0x05); LCD_WriteCommand(0x29); Display on } <\code> 创建存储器,将初始化过程中写的所有指令和数据存储,同时存储的还有指令或数据标志,例如初始化第1条指令为8'h11,我们增加最高位1‘b0组成9位位宽数据。存储器部分指令和数据如下: <code verilog> initial begin LCD初始化的命令及数据 reginit[ 0] = {1'b0,8'h11}; 最高位为0,表示低8位为指令 reginit[ 1] = {1'b0,8'hb1}; reginit[ 2] = {1'b1,8'h05}; 最高位为1,表示低8位为数据 reginit[ 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) cntwrite ⇐ 1'b0; else cntwrite ⇐ cntwrite + 1'b1; case(cntwrite) 6'd0: begin lcddc ⇐ datareg[8]; end
9位数据最高位为命令数据控制位 6'd1: begin lcd
clk ⇐ LOW; lcddin ⇐ datareg[7]; end
先发高位数据 6'd2: begin lcd
clk ⇐ HIGH; end 6'd3: begin lcdclk ⇐ LOW; lcddin ⇐ datareg[6]; end 6'd4: begin lcdclk ⇐ HIGH; end 6'd5: begin lcdclk ⇐ LOW; lcddin ⇐ datareg[5]; end 6'd6: begin lcdclk ⇐ HIGH; end 6'd7: begin lcdclk ⇐ LOW; lcddin ⇐ datareg[4]; end 6'd8: begin lcdclk ⇐ HIGH; end 6'd9: begin lcdclk ⇐ LOW; lcddin ⇐ datareg[3]; end 6'd10: begin lcdclk ⇐ HIGH; end 6'd11: begin lcdclk ⇐ LOW; lcddin ⇐ datareg[2]; end 6'd12: begin lcdclk ⇐ HIGH; end 6'd13: begin lcdclk ⇐ LOW; lcddin ⇐ datareg[1]; end 6'd14: begin lcdclk ⇐ HIGH; end 6'd15: begin lcdclk ⇐ LOW; lcddin ⇐ datareg[0]; end 后发低位数据 6'd16: begin lcdclk ⇐ HIGH; end 6'd17: begin lcdclk ⇐ LOW; state ⇐ DELAY; end default: state ⇐ IDLE; endcase end <\code> 初始化指令和数据都放到存储器中了,数据写入的SPI串行时序也已经设计成了一个状态,初始化状态只需要在复位后将存储器中的指令或数据通过WRITE状态发送给液晶屏,程序实现如下: <code verilog> INIT:begin 初始化状态 if(cntinit==3'd4) begin if(cnt==INITDEPTH) cntinit ⇐ 1'b0; else cntinit ⇐ cntinit; end else cntinit ⇐ cntinit + 1'b1; case(cntinit) 3'd0: lcdres ⇐ 1'b0; 复位有效 3'd1: begin numdelay⇐16'd3000; state⇐DELAY; stateback⇐INIT; end 3'd2: lcdres ⇐ 1'b1; 复位恢复 3'd3: begin numdelay⇐16'd3000; state⇐DELAY; stateback⇐INIT; end 3'd4: if(cnt>=INITDEPTH) begin 当62条指令及数据发出后,配置完成 cnt ⇐ 16'd0; state ⇐ MAIN; end else begin cnt ⇐ cnt + 16'd1; datareg ⇐ reginit[cnt];
if(cnt==16'd0) numdelay ⇐ 16'd50000; 第一条指令需要较长延时 else numdelay ⇐ 16'd50; state ⇐ WRITE; stateback ⇐ INIT; end default: state ⇐ IDLE; endcase end <\code> 初始化完成,进入刷屏状态,刷屏数据写入前首先进行区域坐标的定位,然后刷写数据,图片采用单色显示,图片ram中每位数表示一个液晶屏一个像素点的亮还是灭,彩色液晶屏本实验采用16bit格式,即需要16bit数据决定像素的颜色,16bit数据分两次发送,最终从ram模块中获取的数据每位数据都要转换成16bit的数据,0转换成背景色对应的数据,1转换成顶层色对应的数据,程序实现如下: <code verilog> SCAN:begin 刷屏状态,从RAM中读取数据刷屏 case(cntscan) 3'd0: if(cnt >= 11) begin
确定刷屏的区域坐标,这里为全屏 cnt ⇐ 16'd0; cnt
scan ⇐ cntscan + 1'b1; end else begin cnt ⇐ cnt + 16'd1; datareg ⇐ regsetxy[cnt]; numdelay ⇐ 16'd50; state ⇐ WRITE; stateback ⇐ SCAN; end 3'd1: begin ramclken⇐HIGH;ramaddr⇐ycnt;cntscan⇐cntscan+1'b1; end
3'd2: begin cnt
scan ⇐ cntscan + 1'b1; end 延时一个时钟 3'd3: begin ramclken⇐LOW;ramdatar⇐ramdata;cntscan⇐cntscan+1'b1; end 3'd4: begin 每个像素点需要16bit的数据,SPI每次传8bit,两次分别传送高8位和低8位 if(xcnt>=LCDW) begin 当一个数据(一行屏幕)写完后, xcnt ⇐ 8'd0;
if(ycnt>=LCDH) begin ycnt ⇐ 8'd0; cntscan ⇐ cntscan + 1'b1; end 如果是最后一行就跳出循环 else begin ycnt ⇐ ycnt + 1'b1; cntscan ⇐ 3'd1; end 否则跳转至RAM时钟使能,循环刷屏 end else begin if(highword) 根据相应bit的状态判定显示顶层色或背景色,根据highword的状态判定写高8位或低8位 datareg ⇐ {1'b1,(ramdatar[xcnt]? colort[15:8]:colorb[15:8])};
else begin data
reg ⇐ {1'b1,(ramdatar[xcnt]? colort[7:0]:colorb[7:0])}; xcnt ⇐ xcnt + 1'b1; end highword ⇐ ~highword; highword的状态翻转 numdelay ⇐ 16'd50; 设定延时时间 state ⇐ WRITE; 跳转至WRITE状态 stateback ⇐ SCAN;
执行完WRITE及DELAY操作后返回SCAN状态 end end 3'd5: begin cnt
scan ⇐ 1'b0; state ⇐ MAIN; end default: state ⇐ IDLE; endcase end <\code> ===系统总体实现=== 液晶屏驱动模块的数据来源于图片数据的ram模块,这些数据由图片取模得到,使用图片取模软件,将图片载入软件,输出数据类型选择C语言数组,根据液晶屏驱动实际情况配置对应的扫描模式,输出灰度选择单色,调整最大宽度和高度符合液晶屏要求,最后点击保存生成需要的文件。 取模软件参数配置 打开生成的文件,数据格式如下,是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核的例化及使用方法,有兴趣的同学可以自己尝试一下。 在顶层模块中,将两个模块例化并连接,最终完成图片显示系统的总体设计。 综合后的设计框图如下: RTL设计框图 ====实验步骤==== - 双击打开Quartus Prime工具软件; - 新建工程:File → New Project Wizard(工程命名,工程目录选择,设备型号选择,EDA工具选择); - 新建文件:File → New → Verilog HDL File,键入设计代码并保存; - 设计综合:双击Tasks窗口页面下的Analysis & Synthesis对代码进行综合; - 管脚约束:Assignments → Assignment Editor,根据项目需求分配管脚; - 设计编译:双击Tasks窗口页面下的Compile Design对设计进行整体编译并生成配置文件; - 程序烧录:点击Tools → Programmer打开配置工具,Program进行下载; - 观察设计运行结果。 ====实验现象====