进阶项目(6)LCD12864液晶屏幕设计讲解

 写在前面的话

液晶(LCD)显示具有功耗低、体积小、重量轻、超薄等许多其他显示器无法比拟的优点,近几年被广泛应用于FPGA控制的智能仪器、仪表和低功耗的电子产品中。LCD可分为段位式LCD、字符式LCD和点阵式LCD。其中段位式LCD和字符式LCD只能用于字符和数字的简单显示,不能满足图像曲线和汉字显示的要求;而点阵式LCD不仅可以显示字符、数字,还可以显示各种图形、曲线及汉字,并且可以实现屏幕上下左右滚动、动画功能、分区开窗口、反转、闪烁等功能,用途十分广泛。

基本概念

LCD12864 是一种具有4 位/8 位并行、2 线或3 线串行多种接口方式,内部含有国标一级、二级简体 、中文字库的点阵图形液晶显示模块;其显示分辨率为128×64, 内置8192 个16*16 点汉字,和128 个16*8 点ASCII 字符集,利用该模块灵活的接口方式和简单方便的操作指令,可构成全中文人机交互图形界面。可以显示8×4 行16×16 点 阵的汉字,也可完成图形显示。低电压低功耗是其又一显著特点。由该模块构成的液晶显示方案与同类型的图形点阵液晶显示模块相比,不论硬件电路结构或显示程序都要简洁得多,且该模块的价格也略低于相同点阵的图形液晶模块。

硬件电路结构

我们开发板上所使用的液晶为晶联讯生产的JLX12864G-13903型液晶显示器,可以显示128列*64行点阵单色图片,或显示8个/行*4行16*16点阵的汉字,或显示16个/行*8行8*8点阵的英文、数字、符号。

该液晶的通信方式可以通过不同的硬件设置,配置成串行和并行两种不同的通信方式。在这里,为了节约IO资源,我们将其配置成串行通信方式,具体电路结构如下:

由电路图可以看出,我们需要关心的其实也就只有五条线,那么接下来就让我们梳理一下这几条信号线的具体意义。


管脚接口


说明


12864_cs


片选信号,低电平有效


12864_res


复位信号,低电平有效


12864_rs


寄存器选择信号,高为数据寄存器,低为指令寄存器


12864_sck


串行时钟线


12864_sda


串行数据线

官方代码解析

我们明白了定义的信号之后,接下来看一下它的时序:

注:此处的A0即为我们的12864_rs信号,SI是我们定义的数据线12864_sda,只是表示方式不同而已。

由时序图可以得出,只要我们的代码逻辑能符合上述时序要求,就可以顺利地发送数据,可是点亮这个LCD,需要哪些步骤呢?官方的资料提供了一些例程,可惜这些例程都是C语言版本的,接下来,就让我们这些“门外汉”慢慢品读吧。

C程序如下:


/* Test program for JLX12864G-139,串行接口

Driver IC is:ST7565R(or competible)

Programmed by Ken,Dec.24,2010

JLX electronic Co.,ltd, http://www.jlxlcd.cn;http://www.jlxlcd.com.cn

*/

#include <reg51.H>

sbit cs1=P1^1;

sbit reset=P1^0;

sbit rs=P3^0;

sbit sclk=P3^1;

sbit sid=P3^2;

void transfer_data(int data1);

void transfer_command(int data1);

char code graphic1[];

char code graphic2[];

char code graphic3[];

char code graphic4[];

char code graphic5[];

void Delay(int i);

void Delay1(int i);

void disp_grap(char *dp);

void initial_lcd();

void clear_screen();

void waitkey();

//===============main program===================

void main(void)

{ int i,j,k;

initial_lcd();

while(1)

{

clear_screen(); //clear all dots

disp_grap(graphic1); //display a picture of 128*64 dots

waitkey();

disp_grap(graphic2); //display a picture of 128*64 dots

waitkey();

disp_grap(graphic3); //display a picture of 128*64 dots

waitkey();

disp_grap(graphic4); //display a picture of 128*64 dots

waitkey();

disp_grap(graphic5); //display a picture of 128*64 dots

waitkey();

}

}

/*LCD 初始化*/

void initial_lcd()

{

reset=0; /*低电平复位*/

Delay(20);

reset=1; /*复位完毕*/

Delay(20);

transfer_command(0xe2); /*软复位*/

Delay(5);

transfer_command(0x2c); /*升压步聚 1*/

Delay(5);

transfer_command(0x2e); /*升压步聚 2*/

Delay(5);

transfer_command(0x2f); /*升压步聚 3*/

Delay(5);

transfer_command(0x23); /*粗调对比度,可设置范围 0x20~0x27*/

transfer_command(0x81); /*微调对比度*/

transfer_command(0x1a); /*微调对比度的值,可设置范围 0x00~0x3f*/

transfer_command(0xa2); /*1/9 偏压比(bias)*/

transfer_command(0xc0); /*行扫描顺序:从上到下*/

transfer_command(0xa1); /*列扫描顺序:从左到右*/

transfer_command(0xaf); /*开显示*/

}

//===============clear all dot martrics=============

void clear_screen()

{

unsigned char i,j;

for(i=0;i<9;i++)

{

cs1=0;

transfer_command(0xb0+i);

transfer_command(0x10);

transfer_command(0x00);

for(j=0;j<132;j++)

{

transfer_data(0x00);

}

}

}

//==================display a piture of 128*64 dots================

void disp_grap(char *dp)

{

int i,j;

for(i=0;i<8;i++)

{

cs1=0;

transfer_command(0xb0+i); //set page address,

transfer_command(0x10);

transfer_command(0x00);

for(j=0;j<128;j++)

{

transfer_data(*dp);

dp++;

}

}

}

//=============transfer command to LCM===============

void transfer_command(int data1)

{

char i;

cs1=0;

rs=0;

for(i=0;i<8;i++)

{

sclk=0;

if(data1&0x80) sid=1;

else sid=0;

Delay1(5);

sclk=1;

Delay1(5);

data1=data1<<=1;

}

}

//-----------transfer data to LCM---------------

void transfer_data(int data1)

{

char i;

cs1=0;

rs=1;

for(i=0;i<8;i++)

{

sclk=0;

if(data1&0x80) sid=1;

else sid=0;

sclk=1;

data1=data1<<=1;

}

}

//=============delay time=====================

void Delay(int i)

{

int j,k;

for(j=0;j<i;j++)

for(k=0;k<990;k++);

}

//=============delay time=====================

void Delay1(int i)

{

int j,k;

for(j=0;j<i;j++)

for(k=0;k<10;k++);

}

//--------------wait a switch,jump out if P2.0 get a signal"0"------------------

void waitkey()

{

repeat:

if (P2&0x01) goto repeat;

else Delay(1);

if (P2&0x01) goto repeat;

else;

}

char code graphic1[]={

/*-- 调入了一幅图像:D:\Backup\我的文档\My Pictures\12864-139 英文.bmp --*/

/*-- 宽度 x 高度=128x64 --*/

0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0xF1,0x01,0xF1,0x01,0x01,0x01,0x01,0x01,0x11,0x61,0x81,0x61,0x11,0x01,0x01,0x21,0xF1,0x01,0x01,0x01,0x61,0x11,0x11,0x11,0xE1,0x01,0x61,0x91,0x91,0x91,0x61,0x01,0xC1,0xA1,0x91,0x91,0x21,0x01,0x01,0xC1,0x21,0xF1,0x01,0x01,0xE1,0x11,0x11,0x11,0x61,0x01,0x81,0x81,0x81,0x81,0x81,0x01,0x01,0x21};

对于我们学硬件的人来说,这样的C代码或许有些挑战,但是不要紧,接下来就让我们慢慢来分析它的main函数,定能找到关键线索。Main函数如下:


void main(void)

{ int i,j,k;

initial_lcd();

while(1)

{

clear_screen(); //clear all dots

disp_grap(graphic1); //display a picture of 128*64 dots

waitkey();

disp_grap(graphic2); //display a picture of 128*64 dots

waitkey();

disp_grap(graphic3); //display a picture of 128*64 dots

waitkey();

disp_grap(graphic4); //display a picture of 128*64 dots

waitkey();

disp_grap(graphic5); //display a picture of 128*64 dots

waitkey();

}

}

由此可以看出,main函数开始执行的时候,首先要经过一个叫做initial_lcd的函数,然后进入循环主体,每隔一段时间打印一幅图像。那么接下来,就让我们细细品读这个initial_lcd函数的构成。代码如下:


/*LCD 初始化*/

void initial_lcd()

{

reset=0; /*低电平复位*/

Delay(20);

reset=1; /*复位完毕*/

Delay(20);

transfer_command(0xe2); /*软复位*/

Delay(5);

transfer_command(0x2c); /*升压步聚 1*/

Delay(5);

transfer_command(0x2e); /*升压步聚 2*/

Delay(5);

transfer_command(0x2f); /*升压步聚 3*/

Delay(5);

transfer_command(0x23); /*粗调对比度,可设置范围 0x20~0x27*/

transfer_command(0x81); /*微调对比度*/

transfer_command(0x1a); /*微调对比度的值,可设置范围 0x00~0x3f*/

transfer_command(0xa2); /*1/9 偏压比(bias)*/

transfer_command(0xc0); /*行扫描顺序:从上到下*/

transfer_command(0xa1); /*列扫描顺序:从左到右*/

transfer_command(0xaf); /*开显示*/

}

由此可以看出,所谓LCD初始化就是首先拉低硬件复位信号12864_res一段时间再放开,等待复位结束以后,开始执行下面的一条条指令:

  1. 软件复位
  2. 升压步骤一
  3. 升压步骤二
  4. 升压步骤三
  5. 粗调对比度,可设置范围0x20~0x27
  6. 微调对比度
  7. 微调对比度的值,可设置范围0x00~0x3f
  8. 1/9偏压比(bias)
  9. 行扫描顺序:从上到下

10.列扫描顺序:从左到右

11.起始行位置:从第一行开始

12.开显示

现在的问题主要就是指令是如何被执行的呢?接下来再让我们解析transfer_command这个函数。代码如下:


void transfer_command(int data1)

{

char i;

cs1=0;

rs=0;

for(i=0;i<8;i++)

{

sclk=0;

if(data1&0x80) sid=1;

else sid=0;

Delay1(5);

sclk=1;

Delay1(5);

data1=data1<<=1;

}

}

仔细分析代码,不难发现这个函数的作用就是拉低片选信号,并拉低12864_rs信号(选择指令寄存器),然后通过移位操作,将参数data1发送到12864_sda串行数据总线。

总结:分析上文可知,我们如果要用FPGA实现对12864的控制,首先需要做的就是先硬件复位12864一段时间,然后按照C例程的顺序,发送上述指令集,等12864初始化完毕就可以开始发送图像数据。

接下来,我们开始分析如何发送有效数据,例程代码如下:


//=============display a piture of 128*64 dots=====

void disp_grap(char *dp)

{

int i,j;

for(i=0;i<8;i++)

{

cs1=0;

transfer_command(0xb0+i); //set page address,

transfer_command(0x10);

transfer_command(0x00);

for(j=0;j<128;j++)

{

transfer_data(*dp);

dp++;

}

}}

这里面涉及到了另外一个函数—transfer_data,那么就让我们继续一探究竟,其代码如下:


//-----------transfer data to LCM---------------

void transfer_data(int data1)

{

char i;

cs1=0;

rs=1;

for(i=0;i<8;i++)

{

sclk=0;

if(data1&0x80) sid=1;

else sid=0;

sclk=1;

data1=data1<<=1;

}

}

可以看出,函数transfer_data也只是通过并串转换将数据发送到串行数据总线,在发送数据的过程中必须将12864_rs置为高电平(选择数据寄存器),返回到前一级函数disp_grap,可以看出在发送数据之前,还必须首先发送三组指令:

transfer_command(0xb0+i);   //set page address,

transfer_command(0x10);

transfer_command(0x00);

梳理上文,可以总结如下:

如果想要点亮液晶,必须先发送一系列的指令集,对LCD进行初始化。初始化完毕以后,可以开始发送数据,但每发一组数据之前还必须先发送三组指令,设置页面显示地址,最后才是发送数据。

项目需求

我们本次的设计任务是写入 1024 个相同的八位数,在屏幕上显示黑白条纹,系统架构设计如下

系统架构

1.时钟分频模块(PLL)

我们发送数据的时序要求需要满足一些特定的参数,将频率降低到一定值,就可以满足这些时序参数。具体参数值请查阅官方手册,我们这里分频时钟输出为5MHz。

2.指令控制模块(LCD_control)

如前文所述,想要点亮LCD,首先需要进行LCD初始化,发送特定的指令集,等初始化结束以后,再开始发送具体的显示数据,这些过程都需要紧密配合,所以我们把这部分的控制作为一个单独的模块来实现,端口及其意义如下:


端口名


端口说明


clk


系统时钟5Mhz


rst_n


系统低电平复位信号


cnt_shift


控制状态跳转信号


shift_en


控制数据发送模块开始进行并串转换


data


输出待发送的指令或数据


rs_control


输出LCD寄存器选择信号


res_control


输出LCD复位信号

3.数据发送模块(send_data)

这个模块主要是输出LCD12864的各种控制信号,端口及其意义如下:


端口名


端口说明


clk


系统时钟5Mhz


rst_n


系统低电平复位信号


cnt_shift


输出给指令控制模块的状态跳转信号


shift_en


输入的并串转换的标志信号


data


输入的待发送的指令或数据


rs_control


输入LCD寄存器选择信号


res_control


输入LCD复位信号


cs_12864


输出LCD片选信号


rs_12864


输出LCD寄存器选择信号


res_12864


输出LCD复位信号


sck_12864


输出LCD时钟信号


sda_12864


输出LCD串行数据线

代码解释

LCD_control模块代码如下


/****************************************************

*   Engineer      :   梦翼师兄

*   QQ             :   761664056

*   The module function : LCD控制模块 *****************************************************/

001 module LCD_control(

002                     //系统输入

003                     clk,         //系统5Mhz时钟输入

004                     rst_n,       //低电平复位信号

005                     cnt_shift,   //发送数据模块反馈的移位计数器信号

006                     //系统输出

007                     data,        //待发送数据

008                     rse_contral, //LCD复位信号

009                     rs_contral,  //LCD寄存器选择

010                     shift_en     //发送数据控制信号

011                   );

012 //--------------------系统输入--------------------

013 input clk;      //系统5Mhz时钟输入

014 input rst_n;    //低电平复位信号

015 input [3:0]cnt_shift;   //发送数据模块反馈的移位计数器信号

016 //----------------------系统输出----------------------

017 output reg [7:0]data;   //待发送数据

018 output reg rse_contral; //LCD复位信号

019 output reg rs_contral;  //LCD寄存器选择

020 output reg shift_en;    //发送数据控制信号

021 //-------------------寄存器定义--------------------

022 reg [9:0]state; //状态机状态寄存器

023 reg [3:0]cnt;   //延时计数器

024 reg [3:0]i;     //行计数

025 reg [9:0]j;     //列计数

026

027 always @(posedge clk or negedge rst_n)

028     begin

029         if(!rst_n)

030             begin

031                 data<=8‘h00; //待发送数据或指令初始化

032                 state<=0;

033                 cnt<=0;

034                 i<=0;

035                 j<=0;

036                 rs_contral<=0; //选择指令寄存器

037                 shift_en<=0;

038                 rse_contral<=0;

039             end

040         else

041             begin case(state)

042 //---------------LCD初始化---------------------------------

043             0:begin//上电延时至少1us

044                 if(cnt<8)

045                     begin

046                         cnt<=cnt+1‘b1;

047                         rse_contral<=0;

048                     end

049                 else

050                     begin

051                         cnt<=0;

052                         state<=1;

053                         rse_contral<=1;

054    /*软件复位指令准备*/ data<=8‘he2;

055                     end

056             end

057             1:begin/*软复位*/

058                 rs_contral<=0; //选择指令寄存器

059                 if(cnt_shift<8)

060                     shift_en<=1;

061                 else

062                     begin

063                         if(cnt_shift==8)//发送完毕

064                             begin

065                                 state<=2;

066                                 shift_en<=0;

067           /*升压步聚1指令准备*/ data<=8‘h2c;

068                             end

069                     end

070              end

071             2:begin/*升压步聚1*/

072                 if(cnt_shift<8)

073                     shift_en<=1;

074                 else

075                     begin

076                         if(cnt_shift==8)//发送完毕

077                             begin

078                                 state<=3;

079                                 shift_en<=0;

080           /*升压步聚2指令准备*/ data<=8‘h2e;

081                             end

082                     end

083             end

084             3:begin/*升压步聚2*/

085                 if(cnt_shift<8)

086                     shift_en<=1;

087                 else

088                     begin

089                         if(cnt_shift==8)//发送完毕

090                             begin

091                                 state<=4;

092           /*升压步聚3指令准备*/ data<=8‘h2f;

093                                 shift_en<=0;

094                             end

095                     end

096             end

097             4:begin /*升压步聚3*/

098                 if(cnt_shift<8)

099                     shift_en<=1;

100                 else

101                     begin

102                         if(cnt_shift==8)//发送完毕

103                             begin

104                                 state<=5;

105                                 shift_en<=0;

106             /*粗调对比度指令准备*/ data<=8‘h23;

107                             end

108                     end

109             end

110             5:begin/*粗调对比度,可设置范围0x20~0x27*/

111                 if(cnt_shift<8)

112                     shift_en<=1;

113                 else

114                     begin

115                         if(cnt_shift==8)//发送完毕

116                             begin

117                                 state<=6;

118                                 shift_en<=0;

119          /*微调对比度指令准备*/ data<=8‘h81;

120                             end

121                     end

122             end

123             6:begin/*微调对比度*/

124                 if(cnt_shift<8)

125                     shift_en<=1;

126                 else

127                     begin

128                         if(cnt_shift==8)//发送完毕

129                             begin

130                                 state<=7;

131                                 shift_en<=0;

132      /*微调对比度的值指令准备*/ data<=8‘h1a;

133                             end

134                     end

135             end

136             7:begin/*微调对比度的值,可设置范围0x00~0x3f*/

137                 if(cnt_shift<8)

138                     shift_en<=1;

139                 else

140                     begin

141                         if(cnt_shift==8)//发送完毕

142                             begin

143                                 state<=8;

144                                 shift_en<=0;

145   /*1/9偏压比(bias)指令准备*/ data<=8‘ha2;

146                             end

147                     end

148             end

149             8:begin/*1/9偏压比(bias)*/

150                 if(cnt_shift<8)

151                     shift_en<=1;

152                 else

153                     begin

154                         if(cnt_shift==8)//发送完毕

155                             begin

156                                 state<=9;

157                                 shift_en<=0;

158         /*行扫描顺序指令准备*/ data<=8‘hc0;

159                             end

160                     end

161             end

162             9:begin/*行扫描顺序:从上到下*/

163                 if(cnt_shift<8)

164                     shift_en<=1;

165                 else

166                     begin

167                         if(cnt_shift==8)//发送完毕

168                             begin

169                                 state<=10;

170                                 shift_en<=0;

171          /*列扫描顺序指令准备*/ data<=8‘ha1;

172                             end

173                     end

174             end

175             10:begin/*列扫描顺序:从左到右*/

176                 if(cnt_shift<8)

177                     shift_en<=1;

178                 else

179                     begin

180                         if(cnt_shift==8)//发送完毕

181                             begin

182                                 state<=11;

183                                 shift_en<=0;

184          /*起始行位置指令准备*/ data<=8‘h40;

185                             end

186                     end

187             end

188             11:begin /*起始行位置:从第一行开始*/

189                 if(cnt_shift<8)

190                     shift_en<=1;

191                 else

192                     begin

193                         if(cnt_shift==8)//发送完毕

194                             begin

195                                 state<=12;

196                                 shift_en<=0;

197              /*开显示指令准备*/ data<=8‘haf;

198                             end

199                     end

200             end

201             12:begin /*开显示*/

202                 if(cnt_shift<8)

203                     shift_en<=1;

204                 else

205                     begin

206                         if(cnt_shift==8)//发送完毕

207                             begin

208                                 state<=13;

209                                 shift_en<=0;

210  /*显示页地址位第一页指令准备*/ data<=8‘hb0;

211                             end

212                     end

213             end

214 //--------------------写图像数据到LCD------------------

215             13:begin

216                 if(i<8)  //行计数器小于8,继续写数据

217                     begin

218                         if(cnt_shift<8)

219                             shift_en<=1;

220                         else

221                         begin

222                             if(cnt_shift==8)//发送完毕

223                                 begin

224                                     state<=14;

225                                     shift_en<=0;

226                                     data<=8‘h10; //设置列高地址

227                                 end

228                         end

229                     end

230                 else //行计数器大于8,跳到状态17

231                     begin

232                         state<=17;

233                     end

234             end

235             14:begin

236                 if(cnt_shift<8)

237                     shift_en<=1;

238                 else

239                     begin

240                         if(cnt_shift==8)//发送完毕

241                             begin

242                                 state<=15;

243                                 shift_en<=0;

244                                 data<=8‘h00; //设置列低地址

245                             end

246                     end

247             end

248             15:begin

249                 if(cnt_shift<8)

250                     shift_en<=1;

251                 else

252                     begin

253                         if(cnt_shift==8)//发送完毕

254                             begin

255                                 state<=16;

256                                 shift_en<=0;

257                /*待发送的数据*/ data<=8‘b00001111;

258                             end

259                     end

260             end

261             16:begin

262                 if(j<128)

263                     begin

264                         rs_contral<=1; //选择数据寄存器

265                         if(cnt_shift<8)

266                             shift_en<=1;

267                         else begin

268                             if(cnt_shift==8)//发送完毕

269                                 begin

270                                     j<=j+1‘b1;

271        /*写完128列,页计数器加1*/   if(j==127)

272                                         i<=i+1‘b1;

273                                     shift_en<=0;

274                                 end

275                         end

276                     end

277                 else

278                     begin

279                          j<=0;

280      /*选择指令寄存器*/ rs_contral<=0;

281                          state<=13;

282      /* 页地址加1*/     data<=8‘hb0+i;

283                     end

284             end

285             17:begin

286                 shift_en<=0;

287             end

288             default:;

289             endcase

290         end

291     end

292

293 endmodule

这个模块是LCD的控制模块,对LCD进行初始化和发送待显示的数据。

从第215行开始到结束,作用就是把图像数据写到12864进行显示,首先设置页地址为8’hb0(第一页),然后是设置行地址的高位和低位(第一行第一列),最后发送数据,发送数据的时候一定要把指令寄存器拉高,我们有一个列计数器j和页计数器i,当一行数据写满128列的时候,在开始写下一页,写完8页之后,跳到状态17即可。

发送数据模块


/****************************************************

*   Engineer      :   梦翼师兄

*   QQ             :   761664056

*   The module function : LCD输出数据模块 *****************************************************/

01  module send_data(

02                      //系统输入

03                      clk,//系统5Mhz时钟输入

04                      rst_n,//低电平复位信号

05                      rse_contral,//LCD复位控制信号

06                      rs_contral,//LCD寄存器选择控制信号

07                      shift_en,//移位控制信号

08                      data,//待发送数据

09                      //系统输出

10                      cs_12864,//LCD片选信号

11                      rse_12864,//LCD复位信号

12                      rs_12864,//LCD寄存器选择信号

13                      sck_12864,//LCD串行时钟

14                      sda_12864,//LCD串行数据总线

15                      cnt_shift//LCD移位计数器

16                  );

17  //------------------------系统输入--------------------

18  input clk;//系统5Mhz时钟输入

19  input rst_n;//低电平复位信号

20  input rse_contral;//LCD复位控制信号

21  input rs_contral;//LCD寄存器选择控制信号

22  input shift_en;//移位控制信号

23  input [7:0]data;//待发送数据

24  //------------------------系统输出-------------------

25  output  rs_12864;//LCD寄存器选择信号

26  output  rse_12864;//LCD复位信号

27  output reg cs_12864;//LCD片选信号

28  output reg sck_12864;//LCD串行时钟

29  output reg sda_12864;//LCD串行数据总线

30  output reg [3:0]cnt_shift;//LCD移位计数器

31  //------------------------寄存器定义------------------

32  reg [7:0]data_reg;//待发送数据寄存器

33  //------------------------输出量赋值------------------

34  assign rse_12864=rse_contral;

35  assign rs_12864=rs_contral;

36  //---------------产生LCD时钟sck_12864----------------

37  always@(negedge clk or negedge rst_n)

38      begin

39          if(!rst_n)

40              begin

41                  sck_12864<=0;//sck_12864赋初值

42              end

43          else

44              begin

45                  sck_12864<=~sck_12864;//sck_12864自取反

46              end

47      end

48  //------------------------发送数据逻辑---------------

49  always@(posedge clk or negedge rst_n)

50      begin

51          if(!rst_n)

52              begin

53                  cs_12864<=1;   //复位期间关闭片选信号

54                  sda_12864<=0;  //串行数据总线sda_12864赋初值

55                  cnt_shift<=0;  //移位计数器赋初值

56                  data_reg<=data;//将数据存储到数据寄存器

57              end

58          else

59              begin

60                  if(cnt_shift==0)//前一组数据发送完毕

61                      data_reg<=data;//寄存器数据更新,准备下一次发送

62

63                  if(shift_en)//发送数据使能信号打开

64                      cs_12864<=0;//打开片选信号

65          //在sck_12864低电平期间,如果接收到发送

66          //请求信号shift_en,则开始进行并串转换

67                  if((cnt_shift<8)&&(!sck_12864)&&(shift_en))

68                      begin

69                      //将data_reg每一位赋值给sda_12864

70                          sda_12864<=data_reg[7];

71                          cnt_shift<=cnt_shift+1‘b1;//移位寄存器计数

72                      //控制数据循环移位

73                          data_reg<={data_reg[6:0],data_reg[7]};

74                      end

75                  else

76                      if(cnt_shift==8)//移位结束,即并串转换结束

77                          begin

78                              cs_12864<=1;//片选信号关闭

79                              cnt_shift<=0;//计数器清零

80                          end

81              end

82      end

83  endmodule

第37~47行输出12864时钟,第49行到结束是发送数据的控制逻辑,第60~61行是当移位计数器cnt_shift计数到0也就是发送完一组数据之后数据寄存器data_reg接收新数据,第63~64行当接收到shift_en信号是高电平的时候,使能12864的片选信号,第67~74行在sck_12864低电平期间,如果接收到发送请求信号shift_en,则开始进行并串转换输出数据,第76~80行当每一次数据发送完毕之后,计数器清零,并关闭片选信号。

顶层模块代码


/****************************************************

*   Engineer      :   梦翼师兄

*   QQ             :   761664056

*   E_mail        :   [email protected]

*   The module function : LCD顶层模块 *****************************************************/

01  module LCD_12864    (

02                          //系统输入

03                          clk_sys,//系统50M时钟

04                          rst_n,//系统复位

05                          //系统输出

06                          cs_12864,//LCD片选信号

07                          rse_12864,//LCD复位信号

08                          rs_12864,//LCD寄存器选择信号

09                          sck_12864,//LCD串行时钟

10                          sda_12864//LCD串行数据总线

11                      );

12  //------------------------系统输入------------------------

13  input clk_sys;//系统50M时钟

14  input rst_n;//系统复位

15  //------------------------系统输出------------------------

16  output cs_12864; //LCD片选信号

17  output rse_12864;//LCD复位信号

18  output rs_12864; //LCD寄存器选择信号

19  output sck_12864;//LCD串行时钟

20  output sda_12864;//LCD串行数据总线

21  //------------------------内部连线------------------------

22  wire [7:0]data;

23  wire clk;

24  wire [3:0]cnt_shift;

25  wire rse_contral;

26  wire shift_en;

27  //-----------------------模块实例化-----------------------

28  PLL PLL(

29              .inclk0(clk_sys),

30              .c0(clk)

31          );

32

33  LCD_control LCD_control(

34                                  .clk(clk),

35                                  .rst_n(rst_n),

36                                  .data(data),

37                                  .cnt_shift(cnt_shift),

38                                  .rse_contral(rse_contral),

39                                  .shift_en(shift_en),

40                                  .rs_contral(rs_contral)

41                                  );

42

43  send_data send_data(        .clk(clk),

44                                  .rst_n(rst_n),

45                                  .rse_contral(rse_contral),

46                                  .data(data),

47                                  .rs_contral(rs_contral),

48                                  .shift_en(shift_en),

49                                  .cnt_shift(cnt_shift),

50                                  .cs_12864(cs_12864),

51                                  .rse_12864(rse_12864),

52                                  .rs_12864(rs_12864),

53                                  .sck_12864(sck_12864),

54                                  .sda_12864(sda_12864)

55                              );

56

57  endmodule

综合编译以后,我们可以查看RTL视图,查看电路综合结果和预想是否一致,调用RTL视图如下:

由此可以看到电路综合出的结果和我们预先设计的框架相同。接下来我们编写测试代码,用来验证我们设计的正确性

测试模块代码


/****************************************************

*   Engineer      :   梦翼师兄

*   QQ             :   761664056

*   E_mail        :   [email protected]

*   The module function : LCD顶层模块 *****************************************************/

01  `timescale 1ns/1ps

02  module tb;

03  //------------------------系统输入------------------------

04  reg clk_sys;//系统50M时钟

05  reg rst_n;//系统复位

06  //------------------------系统输出------------------------

07  wire cs_12864; //LCD片选信号

08  wire rse_12864;//LCD复位信号

09  wire rs_12864; //LCD寄存器选择信号

10  wire sck_12864;//LCD串行时钟

11  wire sda_12864;//LCD串行数据总线

12  //------------------------产生测试激励------------------------

13  initial

14      begin

15          clk_sys=0;

16          rst_n=0;

17          #1000.1 rst_n=1;

18      end

19

20  always #10 clk_sys=~clk_sys;//50MHZ时钟

21

22  //------------------------实例化被测试模块------------------------

23  LCD_12864  LCD_12864(

24                          .clk_sys(clk_sys),

25                          .rst_n(rst_n),

26

27                          .cs_12864(cs_12864),

28                          .rse_12864(rse_12864),

29                          .rs_12864(rs_12864),

30                          .sck_12864(sck_12864),

31                          .sda_12864(sda_12864)

32                      );

33  endmodule

仿真分析

查看上述仿真波形可知,各条指令按照顺序通过send_data模块被有序输出。将代码下载到开发板既可以看到对应的正确显示结果。

原文地址:https://www.cnblogs.com/mengyi1989/p/11518264.html

时间: 2024-08-30 08:59:20

进阶项目(6)LCD12864液晶屏幕设计讲解的相关文章

【思库教育】2017PHP项目实战基础+进阶+项目之基础篇

下载链接: [思库教育]2017PHP项目实战基础+进阶+项目之基础篇 小白变大牛,您的专属资源库! 小白变大牛,您的专属资源库! 内容非常充实,可以看目录,设计的面多,项目多,技能多,如果掌握好,找一份PHP的工作,易如反掌!学完后可以到PHP小白变大牛精华区查找更加符合你的资源或者项目! [思库教育]2017PHP项目实战基础+进阶+项目之基础篇[思库教育]2017PHP项目实战基础+进阶+项目之进阶篇[思库教育]2017PHP项目实战基础+进阶+项目之项目篇小白变大牛!Python小白,J

C#进阶系列——DDD领域驱动设计初探(六):领域服务

前言:之前一直在搭建项目架构的代码,有点偏离我们的主题(DDD)了,这篇我们继续来聊聊DDD里面另一个比较重要的知识点:领域服务.关于领域服务的使用,书中也介绍得比较晦涩,在此就根据博主自己的理解谈谈这个知识点的使用. DDD领域驱动设计初探系列文章: C#进阶系列——DDD领域驱动设计初探(一):聚合 C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上) C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下) C#进阶系列——DDD领域驱动设计初探

C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)

前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原因无非以下两点:一是Repository的真实意图没有理解清楚,导致设计的紊乱,随着项目的横向和纵向扩展,到最后越来越难维护:二是赶时髦的为了“模式”而“模式”,仓储并非适用于所有项目,这就像没有任何一种架构能解决所有的设计难题一样.本篇通过这个设计的Demo来谈谈博主对仓储的理解,有不对的地方还望

重磅回归-SSM整合进阶项目实战之个人博客系统

历经一个多月的重新设计,需求分析以及前后端开发,终于有了一定的输出:我自己实现的spring4+springmvc+mybatis3整合的进阶项目实战-个人博客系统 已然完成了,系统采用mvc三层模式进行整体的开发,涉及到技术一下子很难全部列出,其中不得不提的有:整合shiro实现登录安全认证,整合lucene实现全文信息检索,基于Spring的事件驱动模型实现业务服务模块之间的异步解耦(在RabbitMQ视频教程中我也会重提这个技术点!),爬虫框架Jsoup解析html文本中的图片,整合ued

C#进阶系列——DDD领域驱动设计初探(一)

前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的东西.前面介绍了下MEF和AOP的相关知识,后面打算分享Automapper.仓储模式.WCF等东西的,可是每次准备动手写点什么的时候,就被要写的Demo难住了,比如仓储模式,使用过它的朋友应该知道,如果你的项目不是按照DDD的架构而引入仓储的设计,那么会让它变得很“鸡肋”,用不好就会十分痛苦,之前

项目开发中db设计

项目开发中db设计 0.根据原型分析出数据的由来和数据间的关系(实体关系); 1.提取字段,通过powerDesigner设计表; 2.先不加约束,先只建立数据上的单向关联,有需要时在建立双向关联或中间表;3.也可以先建立外键关系,最后删除外键关系;(方便查看表的关系);4.对实体对象通常会补充的字段:     id         主键    entity        关联实体/自己    isDelete varchar(1)  Null    #是否删除    CreateEmpId v

大项目微服务架构设计

大项目微服务架构设计 李万鸿 根据目前产品存在的问题,针对快速开发.海量用户.大量数据.低延迟等互联网应用的实际需要,通过对业务架构.系统架构.基础架构.技术架构进行分析,采用先进实用的微服务SOA架构重构智慧校园.数字化校园等产品,彻底解决系统解耦.性能低下等问题,而且支持云计算部署,可以满足高并发.高可用.高稳定和高安全等性能要求,提供强大的saas和互联网访问服务.由于采用微服务架构,各个服务模块化编写,具有高内聚低耦合的优势,便于灵活更新升级,而不会影响其他业务.一套代码,同时支持移动应

地铁规划项目需求理解和设计思路

地铁规划项目需求理解和设计思路 需求理解 将地铁线路保存成一个可读入,简洁明了的文本 程序能正确读入这个文件,并获取地铁线路信息 程序能正确处理输入的命令行 地铁能正确输出指定地铁线经过的站点 程序能正确输出两个站点间的最短路径 程序要有健壮性,能通过各类性能测试 按要求编写博客,详细说明花费时间,代码,各个模块和测试用例 设计思路 文本格式 1号线 刘园 西横堤 果酒厂 本溪路 勤俭道..... 2号线 曹庄 卞兴 荠园西道 咸阳路...... 3号线 小淀 丰产河 华北集团...... 按照

Python Django框架实现商城项目源码加设计文档和注释

Python Django框架实现商城项目源码加设计文档和注释 链接:https://pan.baidu.com/s/1yN2iBgx3zmpTkoY8u1LWRg 提取码:lfsx 非常完整的django项目源码,分享给撸友们,不管是学习还是深造,都是可以学习借鉴的!! 原文地址:https://www.cnblogs.com/zyxlovesjy/p/12115491.html