zoukankan      html  css  js  c++  java
  • 进阶项目(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); /*粗调对比度,可设置范围 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 Pictures12864-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模块被有序输出。将代码下载到开发板既可以看到对应的正确显示结果。

  • 相关阅读:
    关于多机处理问题
    Prime
    Djkstra
    jbdc总结
    mysql存储过程
    sqlHelper 类型的编写
    JDBC开发
    java线程
    java事件监听机制
    坦克大战编程
  • 原文地址:https://www.cnblogs.com/mengyi1989/p/11518264.html
Copyright © 2011-2022 走看看