查看: 2860|回复: 2

FPGA驱动LCD

[复制链接]
  • TA的每日心情
    开心
    2014-5-14 13:12
  • 签到天数: 180 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2013-1-21 09:12:26 | 显示全部楼层 |阅读模式
    分享到:
    Text LCD module
    [size=11.818181991577148px]Text LCD modules are cheap and easy to interface using a microcontroller or FPGA.
    [size=11.818181991577148px]Here's a 1 line x 16 characters module:
    [size=11.818181991577148px]
    [size=11.818181991577148px]To control an LCD module, you need 11 IO pins to drive an 8-bits data bus and 3 control signals. The 3 control signals are:
    • E: enable, or "LCD-select". Active high.
    • R/W: read/write. 0 to write, 1 to read.
    • RS: register select, 0 for command bytes, 1 for data bytes.
    [size=11.818181991577148px]Most of the LCD modules are based on the HD44780 chip or compatible. One good information page is available here.
    7 bits design
    [size=11.818181991577148px]Let's drive the LCD module from an FPGA board.
    Here's the block diagram of our design:
    [size=11.818181991577148px]Pluto receives data from the PC serial port, de-serializes it, and send it to the LCD module. The de-serializer is the same module from the serial interfaceproject, so it is just instantiated here.
    module LCDmodule(clk, RxD, LCD_RS, LCD_RW, LCD_E, LCD_DataBus);
    input clk, RxD;
    output LCD_RS, LCD_RW, LCD_E;
    output [7:0] LCD_DataBus;

    wire RxD_data_ready;
    wire [7:0] RxD_data;
    async_receiver deserialer(.clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data));
    [size=11.818181991577148px]Every time a byte becomes available from the serial port, then "RxD_data_ready" is active for one clock period.
    [size=11.818181991577148px]The PC sends us data through the serial port in 8-bits mode. Ideally, we would need to receive 9 bits from the PC, so that we can drive the 8-bits data bus and the "RS" line of the LCD module. For now, let's use the MSB (bit 7) of the data received to drive "RS", and send only 7 bits to the data bus.
    assign LCD_RS = RxD_data[7];
    assign LCD_DataBus = {1'b0, RxD_data[6:0]};   // sends only 7 bits to the module, padded with a '0' in front to make 8 bits

    assign LCD_RW = 0;
    [size=11.818181991577148px]We never read from the LCD module, so the R/W line is tied to ground.
    [size=11.818181991577148px]The last complication is that the "E" signal needs to be active for a long time, 220ns. That's long from the FPGA point of view, since I use a 25MHz clock (40ns period). So "E" needs to be driven for at least 5.5 clocks. Here we drive it for 7 clocks, using a counter to count the clocks.
    reg [2:0] count;
    always @(posedge clk) if(RxD_data_ready | (count!=0)) count <= count + 1;
    [size=11.818181991577148px]The "E" signal is created using a register, so that it is guaranteed to be glitch-free.
    reg LCD_E;
    always @(posedge clk) LCD_E <= (count!=0);
    [size=11.818181991577148px]The waveform looks like that:
    [size=11.818181991577148px]
    [size=11.818181991577148px]The HDL design is here.
    The software
    [size=11.818181991577148px]We initialize the LCD and send some data to be displayed.
    [size=11.818181991577148px]
    [size=11.818181991577148px]Here's the C code to initialize the LCD module and display 'hello'.
    void main()
    {
      OpenComm();

      // initialize the LCD module
      WriteCommByte(0x38);   // "Function Set" in 8 bits mode
      WriteCommByte(0x0F);   // "Display ON" with cursors ON
      WriteCommByte(0x01);   // "Clear Display", can take up to 1.64ms, so the delay
      Sleep(2);

      // display "hello"
      WriteCommByte('h' + 0x80);
      WriteCommByte('e' + 0x80);
      WriteCommByte('l' + 0x80);
      WriteCommByte('l' + 0x80);
      WriteCommByte('o' + 0x80);

      CloseComm();
    }
    [size=11.818181991577148px]The complete code is here.
    [size=11.818181991577148px]To get more info about the HD44780 instruction set, check here.
    8 bits design
    [size=11.818181991577148px]The major drawback is the earlier design is that we send only 7 bits to the LCD data bus. That is a problem because the set DD RAM Address command of the LCD module cannot be used anymore.
    [size=11.818181991577148px]One easy way around that is to use an escape character. We chose character 0x00.
    [size=11.818181991577148px]The new protocol is as follow:
    • To send a command byte, prefix it with 0x00.
    • To send a data byte, just send it, no prefix required.
    [size=11.818181991577148px]The new C code is:
    void main()
    {
      OpenComm();

      // initialize the LCD module
      WriteCommByte(0x00);  WriteCommByte(0x38);   // "Function Set" in 8 bits mode
      WriteCommByte(0x00);  WriteCommByte(0x0F);   // "Display ON" with cursors ON
      WriteCommByte(0x00);  WriteCommByte(0x01);   // "Clear Display", can take up to 1.64ms, so the delay
      Sleep(2);

      WriteCommByte('h');
      WriteCommByte('e');
      WriteCommByte('l');
      WriteCommByte('l');
      WriteCommByte('o');

      WriteCommByte(0x00);  WriteCommByte(0xC0);   // go on second half of LCD
      WriteCommByte('e');
      WriteCommByte('v');
      WriteCommByte('e');
      WriteCommByte('r');
      WriteCommByte('y');
      WriteCommByte('o');
      WriteCommByte('n');
      WriteCommByte('e');

      CloseComm();
    }
    [size=11.818181991577148px]The new HDL code looks like:
    module LCDmodule(clk, RxD, LCD_RS, LCD_RW, LCD_E, LCD_DataBus);
    input clk, RxD;
    output LCD_RS, LCD_RW, LCD_E;
    output [7:0] LCD_DataBus;

    wire RxD_data_ready;
    wire [7:0] RxD_data;
    async_receiver deserialer(.clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data));

    assign LCD_RW = 0;
    assign LCD_DataBus = RxD_data;

    wire Received_Escape = RxD_data_ready & (RxD_data==0);
    wire Received_Data = RxD_data_ready & (RxD_data!=0);

    reg [2:0] count;
    always @(posedge clk) if(Received_Data | (count!=0)) count <= count + 1;

    // activate LCD_E for 6 clocks, so at 25MHz, that's 6x40ns=240ns
    reg LCD_E;
    always @(posedge clk)
    if(LCD_E==0)
      LCD_E <= Received_Data;
    else
      LCD_E <= (count!=6);

    reg LCD_instruction;
    always @(posedge clk)
    if(LCD_instruction==0)
      LCD_instruction <= Received_Escape;
    else
      LCD_instruction <= (count!=7);

    assign LCD_RS = ~LCD_instruction;

    endmodule
    [size=11.818181991577148px]The HD44780 specification shows that "RS" needs to be valid for 10ns after "E" goes low. So you'll note that here "E" is driven for 6 clocks only, and the "LCD_instruction" flag is reset only after clock 7, to give 25ns room.
    [size=11.818181991577148px]
    [size=11.818181991577148px]That's all folks! Your turn to experiment.

    回复

    使用道具 举报

  • TA的每日心情
    开心
    2015-1-1 01:21
  • 签到天数: 354 天

    连续签到: 1 天

    [LV.8]以坛为家I

    发表于 2013-1-23 23:13:34 | 显示全部楼层
    项一个,楼主辛苦!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2013-9-24 17:07
  • 签到天数: 159 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2013-3-11 08:22:13 | 显示全部楼层
    楼主都是买的国外货?Pluto板我也想搞个的,太贵了。www.fpga4fun.com的例子很不错。
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-11-23 08:41 , Processed in 0.124179 second(s), 18 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.