• 【原创】如何使用DE2的1602LCD 之一(quartus)(verilog)(digital logic)


    1. 缘起

        会了点HDL和数字逻辑基础后,操作DE2上的开关,led,7断码数码管都没啥问题,但至此好像也只能玩玩n年前教科书上都有的lab,啥数字钟,汽车尾灯,交通灯之类。浪费了DE2的资源,未免对不起DE2的价钱。板子上最明显的东东莫过于那个1602的LCD了,遂想玩玩这个东东。伟大的教育培养的惯性思维:照书本来,狗来狗去,国产的似乎就只有一本关于DE2的书(上交的一个博士写的),拜读一下,大半是DE2附带的光盘lab的中译版,。。。总之,DE2的玩法,没在这本书里找到,估计又是应付国内职评的产物。没期望过,所以没啥失望,好在web上tw的教育网页可打开,那边的学生都推荐OO无双说过的那两本,遍历卓越等,大陆没引进,所以没得卖。再看看老美的,别个用乔治亚理工的helben写的那本,down个电子版(同样国内没引进,amazon上60多刀)。遗憾没得随书的DVD,里面的source code都在DVD上,只能看个大概。看来国内的高人都没得时间写书。要玩转这个LCD,还的靠自己。一切还是老老实实的从DE2附带的DEMO开始(感谢terasic,起码提供了demo)。

    2. 涅槃

        手边最基本、最可靠的东东就是DE2光盘里的DEMO和datasheet了,俺喜欢从低级的玩起,所以代码就从default开始。先看datasheet,这个LCD的控制器是HD4470,说是较通用的型号,以前没玩过,不知道有多通用,简化版的datasheet有16页,主要说明这块LCD可以显示英文、数字、标点符号和日文。中文估计得自己加字库,(加到哪里,flash?啥,俺现在还比较糊,所以先不考虑显示中文。)

        总结一下这块LCD:共计11条指令,指令格式用RS,RW,DB7-0共10位的方式表示。

    1.     display_clear = 00_0000_0001

                功能:DDRAM设为20H、光标回到起始位置、AC置0.

       2. return_home = 00_0000_001x

           功能:光标回到起始位置、AC置0、DDRAM内容不变。

       3. mode_set = 00_0000_01 l/D S

           功能:设定每次输入一位数据后,光标的移动方向,并且设定每次写入的一个字符是否移动。

                    l/D  0-左移;1-右移。

                    S    0-屏不动;1-屏右移1。

        4. display_on = 00_0000_1 D C B

            功能:控制显示的开关,光标显示/关闭,以及光标是否闪烁。

                    D 0-显示关;1-显示开。

                    C 0-无光标;1-有光标。

                    B 0-光标闪;1-不闪。

        5. cursor or display shift = 00_0001_S/C R/L xx

            功能:S/C   R/L

                     0       0     光标左移1格,且AC-1

                     0       1     光标右移1格,且AC+1

                     1       0     显示屏上的字符全左移1格,但光标不动

                     1       1     显示屏上的字符全右移1格,但光标不动

        6. function set = 00_001DL_NFxx

            功能:设定数据总线位数,显示的行数及字型。

                     DL 0-4 bit  1-8 bit

                     N   0-1行    1-2行

                     F   0-5*8dots  1-5*11dots

        7. set CGRAM address = 00_01_AC5-0

        8. set DDRAM address = 00_1_AC6-0

        9. read busy flag and address = 01_BF_AC6-0

           BF 1-忙, 0-可接受外部数据或指令。

        10. write data to RAM = 10_D7-0

              字符码写入DDRAM,显示字符;自定义图形存入CGRAM。

        11. read data from RAM = 11_D7-0

    除了上述11条指令,比较难的就是时序,读、写的时序,每条指令的执行时间。对于LCD_EN,有2中有效状态,高电平和下降沿。其为高电平时读取信息;下降沿时执行指令。在将E置高电平之前,先设置好RS和RW,在E下降沿到来之前,准备好写入的命令或数据。只需在适当的地方加上延时,就可以满足要求。

    DDRAM里的内容是要显示的字符的地址,DDRAM的地址是显示字符的位置。AC在显示字符串时用。

    3. 代码

        目的是显示如图所示的字符。

    Image002

    代码:

    1 module LCD_top(
    2 CLOCK_50, // 50 MHz
    3   LCD_ON, // LCD Power ON/OFF
    4   LCD_BLON, // LCD Back Light ON/OFF
    5   LCD_RW, // LCD Read/Write Select, 0 = Write, 1 = Read
    6   LCD_EN, // LCD Enable
    7   LCD_RS, // LCD Command/Data Select, 0 = Command, 1 = Data
    8   LCD_DATA // LCD Data bus 8 bits
    9 );
    10
    11 input CLOCK_50; // 50 MHz
    12 inout [7:0] LCD_DATA; // LCD Data bus 8 bits
    13 output LCD_ON; // LCD Power ON/OFF
    14 output LCD_BLON; // LCD Back Light ON/OFF
    15 output LCD_RW; // LCD Read/Write Select, 0 = Write, 1 = Read
    16 output LCD_EN; // LCD Enable
    17 output LCD_RS; // LCD Command/Data Select, 0 = Command, 1 = Data
    18
    19 // LCD ON
    20 assign LCD_ON = 1'b1;
    21 assign LCD_BLON = 1'b1;
    22
    23 wire DLY_RST;
    24
    25 Reset_Delay r0 ( .iCLK(CLOCK_50),.oRESET(DLY_RST) );
    26
    27 LCD_TEST u5 ( // Host Side
    28 .iCLK(CLOCK_50),
    29 .iRST_N(DLY_RST),
    30 // LCD Side
    31 .LCD_DATA(LCD_DATA),
    32 .LCD_RW(LCD_RW),
    33 .LCD_EN(LCD_EN),
    34 .LCD_RS(LCD_RS) );
    35
    36 endmodule
    37
    1 module LCD_TEST ( // Host Side
    2 iCLK,iRST_N,
    3 // LCD Side
    4 LCD_DATA,LCD_RW,LCD_EN,LCD_RS );
    5 // Host Side
    6 input iCLK,iRST_N;
    7 // LCD Side
    8 output [7:0] LCD_DATA;
    9 output LCD_RW,LCD_EN,LCD_RS;
    10 // Internal Wires/Registers
    11 reg [5:0] LUT_INDEX;
    12 reg [8:0] LUT_DATA;
    13 reg [5:0] mLCD_ST;
    14 reg [17:0] mDLY;
    15 reg mLCD_Start;
    16 reg [7:0] mLCD_DATA;
    17 reg mLCD_RS;
    18 wire mLCD_Done;
    19
    20 parameter LCD_INTIAL = 0;
    21 parameter LCD_LINE1 = 5;
    22 parameter LCD_CH_LINE = LCD_LINE1+16;
    23 parameter LCD_LINE2 = LCD_LINE1+16+1;
    24 parameter LUT_SIZE = LCD_LINE1+32+1;
    25
    26 always@(posedge iCLK or negedge iRST_N)
    27 begin
    28 if(!iRST_N)
    29 begin
    30 LUT_INDEX <= 0;
    31 mLCD_ST <= 0;
    32 mDLY <= 0;
    33 mLCD_Start <= 0;
    34 mLCD_DATA <= 0;
    35 mLCD_RS <= 0;
    36 end
    37 else
    38 begin
    39 if(LUT_INDEX<LUT_SIZE)
    40 begin
    41 case(mLCD_ST)
    42 0: begin
    43 mLCD_DATA <= LUT_DATA[7:0];
    44 mLCD_RS <= LUT_DATA[8];
    45 mLCD_Start <= 1;
    46 mLCD_ST <= 1;
    47 end
    48 1: begin
    49 if(mLCD_Done)
    50 begin
    51 mLCD_Start <= 0;
    52 mLCD_ST <= 2;
    53 end
    54 end
    55 2: begin
    56 if(mDLY<18'h3FFFE) // 5.2ms
    57 mDLY <= mDLY+1;
    58 else
    59 begin
    60 mDLY <= 0;
    61 mLCD_ST <= 3;
    62 end
    63 end
    64 3: begin
    65 LUT_INDEX <= LUT_INDEX+1;
    66 mLCD_ST <= 0;
    67 end
    68 endcase
    69 end
    70 end
    71 end
    72
    73 always
    74 begin
    75 case(LUT_INDEX)
    76 // Initial
    77 LCD_INTIAL+0: LUT_DATA <= 9'h038; //Fun set
    78 LCD_INTIAL+1: LUT_DATA <= 9'h00C; //dis on
    79 LCD_INTIAL+2: LUT_DATA <= 9'h001; //clr dis
    80 LCD_INTIAL+3: LUT_DATA <= 9'h006; //Ent mode
    81 LCD_INTIAL+4: LUT_DATA <= 9'h080; //set ddram address
    82 // Line 1
    83 LCD_LINE1+0: LUT_DATA <= 9'h120; // http://halflife.cnblogs.com
    84 LCD_LINE1+1: LUT_DATA <= 9'h168; // h
    85 LCD_LINE1+2: LUT_DATA <= 9'h174; // t
    86 LCD_LINE1+3: LUT_DATA <= 9'h174; // t
    87 LCD_LINE1+4: LUT_DATA <= 9'h170; // p
    88 LCD_LINE1+5: LUT_DATA <= 9'h13A; // :
    89 LCD_LINE1+6: LUT_DATA <= 9'h12F; // /
    90 LCD_LINE1+7: LUT_DATA <= 9'h12F; // /
    91 LCD_LINE1+8: LUT_DATA <= 9'h168; // h
    92 LCD_LINE1+9: LUT_DATA <= 9'h161; // a
    93 LCD_LINE1+10: LUT_DATA <= 9'h16C; // l
    94 LCD_LINE1+11: LUT_DATA <= 9'h166; // f
    95 LCD_LINE1+12: LUT_DATA <= 9'h16C; // l
    96 LCD_LINE1+13: LUT_DATA <= 9'h169; // i
    97 LCD_LINE1+14: LUT_DATA <= 9'h166; // f
    98 LCD_LINE1+15: LUT_DATA <= 9'h165; // e
    99 // Change Line
    100 LCD_CH_LINE: LUT_DATA <= 9'h0C0;
    101 // Line 2
    102 LCD_LINE2+0: LUT_DATA <= 9'h12E; // .
    103 LCD_LINE2+1: LUT_DATA <= 9'h163; // c
    104 LCD_LINE2+2: LUT_DATA <= 9'h16E; // n
    105 LCD_LINE2+3: LUT_DATA <= 9'h162; // b
    106 LCD_LINE2+4: LUT_DATA <= 9'h16C; // l
    107 LCD_LINE2+5: LUT_DATA <= 9'h16F; // o
    108 LCD_LINE2+6: LUT_DATA <= 9'h167; // g
    109 LCD_LINE2+7: LUT_DATA <= 9'h173; // s
    110 LCD_LINE2+8: LUT_DATA <= 9'h12E; // .
    111 LCD_LINE2+9: LUT_DATA <= 9'h163; // c
    112 LCD_LINE2+10: LUT_DATA <= 9'h16F; // o
    113 LCD_LINE2+11: LUT_DATA <= 9'h16D; // m
    114 LCD_LINE2+12: LUT_DATA <= 9'h120;
    115 LCD_LINE2+13: LUT_DATA <= 9'h120;
    116 LCD_LINE2+14: LUT_DATA <= 9'h120;
    117 LCD_LINE2+15: LUT_DATA <= 9'h120;
    118 default: LUT_DATA <= 9'h000;
    119 endcase
    120 end
    121
    122 LCD_Controller u0 ( // Host Side
    123 .iDATA(mLCD_DATA),
    124 .iRS(mLCD_RS),
    125 .iStart(mLCD_Start),
    126 .oDone(mLCD_Done),
    127 .iCLK(iCLK),
    128 .iRST_N(iRST_N),
    129 // LCD Interface
    130 .LCD_DATA(LCD_DATA),
    131 .LCD_RW(LCD_RW),
    132 .LCD_EN(LCD_EN),
    133 .LCD_RS(LCD_RS) );
    134
    135 endmodule
    1 module LCD_Controller ( // Host Side
    2 iDATA,iRS,
    3 iStart,oDone,
    4 iCLK,iRST_N,
    5 // LCD Interface
    6 LCD_DATA,
    7 LCD_RW,
    8 LCD_EN,
    9 LCD_RS );
    10 // CLK
    11 parameter CLK_Divide = 16;
    12
    13 // Host Side
    14 input [7:0] iDATA;
    15 input iRS,iStart;
    16 input iCLK,iRST_N;
    17 output reg oDone;
    18 // LCD Interface
    19 output [7:0] LCD_DATA;
    20 output reg LCD_EN;
    21 output LCD_RW;
    22 output LCD_RS;
    23 // Internal Register
    24 reg [4:0] Cont;
    25 reg [1:0] ST;
    26 reg preStart,mStart;
    27
    28 /////////////////////////////////////////////
    29 // Only write to LCD, bypass iRS to LCD_RS
    30 assign LCD_DATA = iDATA;
    31 assign LCD_RW = 1'b0;
    32 assign LCD_RS = iRS;
    33 /////////////////////////////////////////////
    34
    35 always@(posedge iCLK or negedge iRST_N)
    36 begin
    37 if(!iRST_N)
    38 begin
    39 oDone <= 1'b0;
    40 LCD_EN <= 1'b0;
    41 preStart<= 1'b0;
    42 mStart <= 1'b0;
    43 Cont <= 0;
    44 ST <= 0;
    45 end
    46 else
    47 begin
    48 ////// Input Start Detect ///////
    49 preStart<= iStart;
    50 if({preStart,iStart}==2'b01) // latch ?
    51 begin
    52 mStart <= 1'b1;
    53 oDone <= 1'b0;
    54 end
    55 //////////////////////////////////
    56 if(mStart) //generate LCD_EN
    57 begin
    58 case(ST)
    59 0: ST <= 1; // Wait Setup, tAS >= 40ns
    60 1: begin
    61 LCD_EN <= 1'b1;
    62 ST <= 2;
    63 end
    64 2: begin
    65 if(Cont<CLK_Divide)
    66 Cont <= Cont+1;
    67 else
    68 ST <= 3;
    69 end
    70 3: begin
    71 LCD_EN <= 1'b0;
    72 mStart <= 1'b0;
    73 oDone <= 1'b1;
    74 Cont <= 0;
    75 ST <= 0;
    76 end
    77 endcase
    78 end
    79 end
    80 end
    81
    82 endmodule
    1 module Reset_Delay(iCLK,oRESET);
    2 input iCLK;
    3 output reg oRESET;
    4 reg [19:0] Cont;
    5
    6 always@(posedge iCLK)
    7 begin
    8 if(Cont!=20'hFFFFF) //21ms
    9 begin
    10 Cont <= Cont+1;
    11 oRESET <= 1'b0;
    12 end
    13 else
    14 oRESET <= 1'b1;
    15 end
    16
    17 endmodule

    仿真结果:

    lcd_test

    4. 小结

        上面的代码也是terasic的default文件的设计思想,利用2个FSM来实现写入数据,并显示。在LCD_Controller里的FSM是写操作的过程,如下图

    write

    关键是产生有效的E。LCD_TEST里的FSM则是初始化数据,调用LCD_Controller写入数据,设置合理延时(5.2ms),循环递进显示下一条数据。难点在于这2个FSM咋联系起来,一说子函数调用,简单,关键是这里从一个FSM的某个状态进入另一个FSM的某个状态,被调用的FSM的多个状态又包含在上层的FSM的一个状态中,其间的时序,关联就靠start和done来实现,这一点也是仿真了demo,才分析出来的。可能比较麻烦,所以才没人把每步的时序分解说明。看来,读代码的“黑屋修炼”是不可太期望别个会帮助的。

    ps: 在仿真时,用qII花了不少时间,用modelsim,提示内存不足。

  • 相关阅读:
    java操作Redis
    Redis安装和基本操作
    IDEA使用教程+JRebel破解
    java环境配置
    qtp10安装步骤(比较完整)
    c++第一章1.6
    软件测试第二章作业
    c++作业22题
    c++第二周阶段小测2
    oracle12c数据库第一周小测验
  • 原文地址:https://www.cnblogs.com/halflife/p/2096463.html
Copyright © 2020-2023  润新知