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

这一生的挚爱 提交于 2019-11-29 13:10:48

 写在前面的话

液晶(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); /*粗调对比度,可设置范围 0x200x27*/

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

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

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); /*粗调对比度,可设置范围 0x200x27*/

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

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

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/*粗调对比度,可设置范围0x200x27*/

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/*微调对比度的值,可设置范围0x000x3f*/

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        :   zxopenwjf@126.com

 *   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        :   zxopenwjf@126.com

 *   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模块被有序输出。将代码下载到开发板既可以看到对应的正确显示结果。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!