zoukankan      html  css  js  c++  java
  • 【原创】如何使用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,提示内存不足。

  • 相关阅读:
    1062 Talent and Virtue (25 分)
    1083 List Grades (25 分)
    1149 Dangerous Goods Packaging (25 分)
    1121 Damn Single (25 分)
    1120 Friend Numbers (20 分)
    1084 Broken Keyboard (20 分)
    1092 To Buy or Not to Buy (20 分)
    数组与链表
    二叉树
    时间复杂度与空间复杂度
  • 原文地址:https://www.cnblogs.com/halflife/p/2096463.html
Copyright © 2011-2022 走看看