Forum: Projekte & Code [VHDL] 16x2 LCD Textcontroller / HD44780


von Läubi .. (laeubi) Benutzerseite


Angehängte Dateien:

Lesenswert?

Da ich im Internet immer nur die Info gefunden habe ein Text LCD 
Controller sei zu "komplex" und man solle doch einen Softcore verwenden 
habe ich mir selber einen geschrieben.
Unterstüzt zur Zeit 16x2 im 8-Bit Modus, sollte sich aber leicht 
anpassen lassen.

Braucht etwa 80 Slices, wobei 32 auf einen Schattenspeicher entfallen, 
d.h. man kann in die Komponente mit vollem Systemtakt schreiben und der 
Controller überträgt die Daten dann kontinuierlich an das LCD.

Getestet habe ich das ganze mit meinem Spartan3A Startet Board mit 
folgendem Code, es wird einfach bis zum init gewartet und dann 
kontinuierlich in die erste Zeile ein '#' Zeichen geschrieben.
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.STD_LOGIC_ARITH.ALL;
4
use IEEE.STD_LOGIC_UNSIGNED.ALL;
5
6
entity LCD16x2TEST is
7
    Port ( clk_50M : in  STD_LOGIC;
8
           LCD_DB  : out  STD_LOGIC_VECTOR (7 downto 0);
9
           LCD_E   : out  STD_LOGIC;
10
           LCD_RW  : out STD_LOGIC;
11
           LCD_RS  : out  STD_LOGIC
12
    );
13
end LCD16x2TEST;
14
15
architecture Behavioral of LCD16x2TEST is
16
component CHAR_LCD_CTR is
17
    Port ( clk_50M            : in  STD_LOGIC;
18
           LCDData            : out  STD_LOGIC_VECTOR (7 downto 0);
19
           LCD_Enable         : out  STD_LOGIC;
20
           LCD_RW             : out STD_LOGIC;
21
           LCD_RegisterSelect : out  STD_LOGIC;
22
           init_done          : out std_logic;
23
           address            : in std_logic_vector(4 downto 0);
24
           data               : in std_logic_vector(7 downto 0);
25
           wr                 : in std_logic
26
    );
27
end component;
28
signal init_done  : std_logic;
29
signal address    : std_logic_vector(4 downto 0) := (others =>'0');
30
signal data       : std_logic_vector(7 downto 0) := x"23";
31
signal wr         : std_logic := '0';
32
constant end_Addr : integer := 15;
33
begin
34
35
    TEST: process 
36
    begin
37
    wait until rising_edge(clk_50m);    
38
        if init_done = '1' then
39
            wr      <= '1';
40
            if address /= end_Addr then
41
                address <= address + 1;
42
            else
43
                address <= (others => '0');
44
            end if;
45
        end if;
46
    end process;
47
48
LCD16x2: CHAR_LCD_CTR 
49
    Port map(clk_50M, LCD_DB, LCD_E, LCD_RW, LCD_RS, init_done, address, data, wr);
50
end Behavioral;

Die UCF Constraints für das Board:
1
NET "CLK_50M"       LOC = "E12"  | IOSTANDARD = LVCMOS33 | PERIOD = 20.000 ;
2
OFFSET = IN  10.000 VALID 20.000 BEFORE "CLK_50M" ;
3
OFFSET = OUT 20.000 AFTER "CLK_50M" ;
4
##############################################################################
5
# Character Display (LCD)
6
##############################################################################
7
NET "LCD_DB<0>"     LOC = "Y13"  | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ;
8
NET "LCD_DB<1>"     LOC = "AB18" | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ;
9
NET "LCD_DB<2>"     LOC = "AB17" | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ;
10
NET "LCD_DB<3>"     LOC = "AB12" | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ;
11
NET "LCD_DB<4>"     LOC = "AA12" | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ;
12
NET "LCD_DB<5>"     LOC = "Y16"  | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ;
13
NET "LCD_DB<6>"     LOC = "AB16" | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ;
14
NET "LCD_DB<7>"     LOC = "Y15"  | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ;
15
NET "LCD_E"         LOC = "AB4"  | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ;
16
NET "LCD_RS"        LOC = "Y14"  | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ;
17
NET "LCD_RW"        LOC = "W13"  | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ;

Verbesserungsmöglichkeiten und konstruktive Vorschläge sind gerne 
Willkommen.

von LOSIjunior (Gast)


Lesenswert?

Hallo Herr Läubi,
Ich bin auf der Suche nach einem möglichst einfachen Display controller.
Dieser sollte endlos einen logic_vector am Display anzeigen.

Mein FPGA: Altera MAX II EMP2210F324C3N / 50 MHz
LC-Display: EA DIP204B-4NLW mit 4x20 Zeichen
dieser enthällt den KS0073 kontroller welcher zu 100% mit dem HD44780 
kompatibel ist.

Das Signal welches am Display angezeigt werden sollte ist:
display      : IN   std_logic_vector(639 downto 0);

Dieses ist folgendermassen zu interpretieren: (80 ASCII zeichen)

display(639 downto 632)   1. Zeile(oben) 1. ASCII-Zeichen (links)
display(159 downto 152)   4. Zeile(unten) 1. ASCII-Zeichen (links)
display(007 downto 000)   4. Zeile(unten) 20. ASCII-Zeichen (rechts)
... etc ...

Die Signale zum Display sind:
lcd_data    : OUT  std_logic_vector(7 downto 0);
lcd_rs      : OUT  std_logic;
lcd_rw      : OUT  std_logic;
lcd_e      : OUT  std_logic

Der Code sollte möglichst wenig Logik zellen im FPGA verwenden.

Kann du mir erklären, wie ich das implementieren kann?

Mit freundlichen Grüsse
LOSIjunior

von nur (Gast)


Lesenswert?

Hallo,
es wäre auch schon wenn deine codes synthezierbar wäre.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

nur schrieb:
> Hallo,
> es wäre auch schon wenn deine codes synthezierbar wäre.

Zumindest auf XILINX FPGAs mit Block-RAM ist er das, ohne etwas mehr 
Infos kann aber niemand sagen wo es bei dir ggf. hängt.

LOSIjunior schrieb:
> Das Signal welches am Display angezeigt werden sollte ist:
> display      : IN   std_logic_vector(639 downto 0);

Was spricht gegen die RAM Lösung?

LOSIjunior schrieb:
> Der Code sollte möglichst wenig Logik zellen im FPGA verwenden

Der Verbrauch ist oben angegeben.

von Xyz X. (Firma: xyz) (khmweb)


Lesenswert?

Newbie-Antwort:
Ich hab auch Fehler bei der Synthesis für die oben gezeigten Codes 
(einkopiert in neue Files vhd und ucf):

ERROR:Xst:2585 - Port <LCD_Enable> of instance <LCD16x2> does not exist 
in definition <CHAR_LCD_CTR>. Please compare the definition of block 
<CHAR_LCD_CTR> to its component declaration to detect the mismatch.

ERROR:Xst:2585 - Port <LCD_RW> of instance <LCD16x2> does not exist in 
definition <CHAR_LCD_CTR>. Please compare the definition of block 
<CHAR_LCD_CTR> to its component declaration to detect the mismatch.

ERROR:Xst:2585 - Port <LCDData> of instance <LCD16x2> does not exist in 
definition <CHAR_LCD_CTR>. Please compare the definition of block 
<CHAR_LCD_CTR> to its component declaration to detect the mismatch.

CHAR_LCD_CTR.vhd hab ich noch nicht ausprobiert. Was mache ich falsch?

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Karl-Heinz M. schrieb:
> CHAR_LCD_CTR.vhd hab ich noch nicht ausprobiert. Was mache ich falsch?

Was heißt das? Ohne die Datei funktioniert es nicht, das oben angegeben 
ist nur der Testcode für das Modul welches in CHAR_LCD_CTR.vhd steckt!

von Xyz X. (Firma: xyz) (khmweb)


Lesenswert?

Das heißt: Ich hab die beiden kopiert, aber nicht die andere benutzt. 
Ich brauche also den Testcode nicht?

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Karl-Heinz M. schrieb:
> Ich brauche also den Testcode nicht?

Nur wenn du es auf ein "echtes" Board bringen willst, dann mußt du aber 
ggf. die Pinzuordnungen anpassen.

Wenn du das ganze nur synthetisieren/simulieren möchtest reicht auch das 
reine Modul.

Falls deine Erfahrung in VHDL noch recht neu ist würde ich erstmal mit 
einem einfachem Beispiel beginnen.

von Basti (Gast)


Angehängte Dateien:

Lesenswert?

Hmm... So ganz hab ich das jetzt noch nicht verstanden. Welcher der 
beiden Blöcke soll denn nun an die realen HardwarePorts gekoppelt 
werden?

von Xyz X. (Firma: xyz) (khmweb)


Lesenswert?

Läubi .. schrieb:
> Falls deine Erfahrung in VHDL noch recht neu ist würde ich erstmal mit
> einem einfachem Beispiel beginnen.

Dank Dir, aber mein Prob ist trotzdem nicht viel anders als das von 
Basti. Offenbar hatte jeder hier damit Probleme für die ich keine Lösung 
seh und das nicht nachvollziehen kann.

Im XILINX Tut sieht das für mich ziemlich ähnlich aus, wie das hier.

von Basti (Gast)


Lesenswert?

Nach kurzem drübersehen vermute ich, dass ich den Block CHAR_LCD_CTR 
brauche. An diesen übergebe ich nun meine gewünschte Andresse (also 
Zeile 1 Zeichen 1) und meine Daten. ????

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Basti schrieb:
> Nach kurzem drübersehen vermute ich, dass ich den Block CHAR_LCD_CTR
> brauche. An diesen übergebe ich nun meine gewünschte Andresse (also
> Zeile 1 Zeichen 1) und meine Daten. ????

So ist es. Der CHAR_LCD_CTR ist im Prinzip für dich ein 
Write-Only-Memory, welches die Geschriebenen Daten auf dem LCD ausgibt.
Alles auf der linken Seite deiner Grafik ist dabei das, was du benutzt, 
das auf der rechten Seite wird an die Pins des LCD verdrahtet (Ausnahme: 
Init done).

Das Beispiel zeigt nur wie man das ganze verwenden könnte muss aber 
wie gesagt für das konkrete Board angepasst werden!

von Basti (Gast)


Lesenswert?

Also drahte ich das INIT_DONE an meinen eigenen Block. Wenn da was 
kommt, heißt das für mich, ich kann daten rüber schieben? Super!

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Basti schrieb:
> Also drahte ich das INIT_DONE an meinen eigenen Block. Wenn da was
> kommt, heißt das für mich, ich kann daten rüber schieben?

So ist es. Aus Platz- und Anpassungsgründen nutze ich das Ram auch für 
die Init Sequenz des LCD, wenn vorher geschrieben wird wird ggf. diese 
überschrieben, deshalb im Usercode erst auf init_done warten.

von Xyz X. (Firma: xyz) (khmweb)


Lesenswert?

Das sind jetzt mal klare Hinweise, besten Dank, Läubi! Damit kann man 
auch was anfangen. Schaun wir mal...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.