www.mikrocontroller.net

Forum: FPGA, VHDL & Co. Ansteuerung Grafic Display


Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich möchte eine grafische LCD 128x64 per
von einem FPGA ansteuern. Dieses enthält
zwei KS0108B Chips von Samsung jeweils für
eine Hälfte des Displays. Ich habe aber nun
ein Problem, dass keine Zeichen auf dem Display
erscheinen. Ich habe mich mit dem Timing
an das Datenblatt des KS0108b Chip gehalten.
Das Timing sieht auf dem Osziloskop auch
vernünftig aus. Trotzdem sieh man nichts
auf dem Display. Hat schon mal jemand
so ein grafisches Display mit einem FPGA
angesteuert und könnte mir eventuell Tips
geben was man dabei beachten muss. Oder
eventuell ein Beispiel oder Link für eine
Applikation mit einem LCD-FPGA.
Vielen Dank.


Gruß

Ralf

Autor: Uwe N. (ex-aetzer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du Hilfe benötigst, dann brauchen die Leute hier mehr Infos:
1. dein VHDL-Code
2. Schematic
3. wenn möglich, Foto vom Aufbau

Gruss Uwe

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, hast natürlich recht. Ich dachte
nur es hat jemand vielleicht Erfahrung
wegen kritischem Timing oder so.
Ich habe mal den VHDL Code angehängt.
Der erste Teil ist die Befehlssequenz zum
Lesen und Schreiben. Der Zweite Teil stellt
dann die höheren Befehle zur Verfügung.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ks0108b_ctrl is
  generic (
    RESET_ACTIVE  : std_logic             := '0';
    WAIT_POWER_UP : unsigned(12 downto 0) := to_unsigned(3000, 13);  -- 15 ms @ 50 MHz
    WAIT_T1       : unsigned(12 downto 0) := to_unsigned(18, 13);    -- 360 ns @ 50 MHz
    WAIT_T2       : unsigned(12 downto 0) := to_unsigned(8, 13);     -- 150 ns @ 50 MHz
    WAIT_T3       : unsigned(12 downto 0) := to_unsigned(26, 13);    -- 510 ns @ 50 MHz
    WAIT_T4       : unsigned(12 downto 0) := to_unsigned(3, 13);     --  30 ns @ 50 MHz
    WAIT_T5       : unsigned(12 downto 0) := to_unsigned(2, 13)      --  20 ns @ 50 MHz
  );
  port (
    clk      : in std_logic;                    -- System Clock
    rst      : in std_logic;                    -- asynchronous Reset, active high
    data_out : in std_logic_vector(7 downto 0); -- Input data
    data_in  : out std_logic_vector(7 downto 0);
    dval     : in std_logic;                    -- Write Data command (1 clock cycle)
    rs       : in std_logic;                    -- Data or Command
    rw       : in std_logic;                    -- read or write command
    busy     : out std_logic;                   -- '1' if device busy
    -- LCD signals
    glcd_db_in   : in  std_logic_vector(7 downto 0);
    glcd_db_out  : out std_logic_vector(7 downto 0);
    glcd_db_enab : out std_logic;
    glcd_e       : out std_logic;
    glcd_reset   : out std_logic;
    glcd_rw      : out std_logic;
    glcd_di      : out std_logic;
    glcd_cs      : out std_logic
  );
end ks0108b_ctrl;


architecture behavioral of ks0108b_ctrl is

  type State_type is (sINIT, sIDLE, sWAIT_T1, sWAIT_T2, sWAIT_T3, sWAIT_T4, sWAIT_T5);
  signal ctlState : State_type;

  signal timer : unsigned(12 downto 0);

begin
  process (clk, rst)
  begin
    if (rst = RESET_ACTIVE) then
      busy         <= '1';
      data_in      <= (others => '0');
      glcd_db_out  <= (others => '1');
      glcd_e       <= '1';
      glcd_reset   <= '0';
      glcd_rw      <= '1';
      glcd_di      <= '1';
      glcd_db_enab <= '0';
      glcd_cs      <= '1';
      timer        <= (others => '0');
      ctlState     <= sINIT;
    elsif rising_edge(clk) then
      case ctlState is
        when sINIT =>      -- Power up wait 15 ms
          glcd_reset <= '0';
          timer      <= timer + 1;
          if (timer = WAIT_POWER_UP) then
            ctlState <= sIDLE;
          end if;

        when sIDLE =>
          glcd_reset   <= '1';
          busy         <= '0';
          glcd_e       <= '1';
          glcd_db_enab <= '0';

          if (dval = '1') then
            busy        <= '1';
            glcd_e      <= '0';
            glcd_db_out <= data_out;
            timer       <= (others => '0');
            ctlState    <= sWAIT_T1;
          end if;

        when sWAIT_T1 =>
          timer <= timer + 1;
          if (timer = WAIT_T1) then
            glcd_rw  <= rw;
            glcd_di  <= rs;
            glcd_cs  <= '0';
            timer    <= (others => '0');
            ctlState <= sWAIT_T2;
          end if;
        
        when sWAIT_T2 =>
          timer <= timer + 1;
          if (timer = WAIT_T2) then
            glcd_e <= '1';
            if (rw = '0') then
              glcd_db_enab <= '1';
            end if;
            timer    <= (others => '0');
            ctlState <= sWAIT_T3;
          end if;

        when sWAIT_T3 =>
          timer <= timer + 1;
          if (timer = WAIT_T3) then
            glcd_e   <= '0';
            if (rw = '1') then
              data_in <= glcd_db_in;
            end if;
            timer    <= (others => '0');
            ctlState <= sWAIT_T4;
          end if;

        when sWAIT_T4 =>
          timer <= timer + 1;
          if (timer = WAIT_T4) then
            glcd_cs  <= '1';
            glcd_di  <= '1';
            glcd_rw  <= '1';
            timer    <= (others => '0');
            ctlState <= sWAIT_T5;
          end if;

        when sWAIT_T5 =>
          timer <= timer + 1;
          if (timer = WAIT_T5) then
            glcd_e   <= '1';
            ctlState <= sIDLE;
          end if;

        when others =>
          ctlState <= sIDLE;
      end case;
    end if;
  end process;
end behavioral;


Ich hoffe ihr könnt damit was anfangen.
Laut Simulation ist auch alles in Ordnung.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ks0108b_cmd is
  generic (
    RESET_ACTIVE        : std_logic             := '0';  -- Polaritaet fuer Reset-Signal
    WAIT_SIXTY_US       : unsigned(13 downto 0) := to_unsigned(50, 14);  -- 1000 ns
    WAIT_FIVEHUNDRED_NS : unsigned(13 downto 0) := to_unsigned(25, 14);  -- 1000 ns
    WAIT_TWENTY_NS      : unsigned(13 downto 0) := to_unsigned(2, 14)    -- 20 ns
  );
  port (
    -- master signals
    clk      : in  std_logic;
    rst      : in  std_logic;
    cmd      : in  std_logic_vector(2 downto 0);
    busy     : out std_logic;
    st_busy  : out std_logic;
    st_onoff : out std_logic;
    st_reset : out std_logic;
    data_out : in  std_logic_vector(7 downto 0);
    data_in  : out std_logic_vector(7 downto 0);
    -- graphic lcd interface
    glcd_db_in   : in  std_logic_vector(7 downto 0);
    glcd_db_out  : out std_logic_vector(7 downto 0);
    glcd_db_enab : out std_logic;
    glcd_e       : out std_logic;
    glcd_reset   : out std_logic;
    glcd_rw      : out std_logic;
    glcd_di      : out std_logic;
    glcd_cs      : out std_logic
  );
end ks0108b_cmd;

architecture behavioral of ks0108b_cmd is

  component ks0108b_ctrl is
    generic (
      RESET_ACTIVE  : std_logic             := '0';
      WAIT_POWER_UP : unsigned(12 downto 0) := to_unsigned(3000, 13);  -- 15 ms @ 50 MHz
      WAIT_T1       : unsigned(12 downto 0) := to_unsigned(18, 13);    -- 360 ns @ 50 MHz
      WAIT_T2       : unsigned(12 downto 0) := to_unsigned(8, 13);     -- 150 ns @ 50 MHz
      WAIT_T3       : unsigned(12 downto 0) := to_unsigned(26, 13);    -- 510 ns @ 50 MHz
      WAIT_T4       : unsigned(12 downto 0) := to_unsigned(3, 13);     --  30 ns @ 50 MHz
      WAIT_T5       : unsigned(12 downto 0) := to_unsigned(2, 13)      --  20 ns @ 50 MHz
    );
    port (
      clk      : in  std_logic;                    -- System Clock
      rst      : in  std_logic;                    -- asynchronous Reset, active high
      data_out : in  std_logic_vector(7 downto 0); -- Input data
      data_in  : out std_logic_vector(7 downto 0);
      dval     : in  std_logic;                    -- Write Data command (1 clock cycle)
      rs       : in  std_logic;                    -- Data or Command
      rw       : in  std_logic;                    -- read or write command
      busy     : out std_logic;                    -- '1' if device busy
      -- Graphic LCD signals
      glcd_db_in   : in  std_logic_vector(7 downto 0);
      glcd_db_out  : out std_logic_vector(7 downto 0);
      glcd_db_enab : out std_logic;
      glcd_e       : out std_logic;
      glcd_reset   : out std_logic;
      glcd_rw      : out std_logic;
      glcd_di      : out std_logic;
      glcd_cs      : out std_logic
    );
  end component;

  -- Graphic LCD command definitions
  constant NO_CMD     : std_logic_vector(2 downto 0) := "000";
  constant READ_DD    : std_logic_vector(2 downto 0) := "001";
  constant WRITE_DD   : std_logic_vector(2 downto 0) := "010";
  constant ST_READ    : std_logic_vector(2 downto 0) := "011";
  constant SET_ADDR_Y : std_logic_vector(2 downto 0) := "100";
  constant SET_D_SL   : std_logic_vector(2 downto 0) := "101";
  constant SET_ADDR_X : std_logic_vector(2 downto 0) := "110";
  constant DISP_ONOFF : std_logic_vector(2 downto 0) := "111";

  signal dval  : std_logic := '0';  -- Write Data command (1 clock cycle)
  signal di    : std_logic := '0';  -- Data or Instruction
  signal busyi : std_logic;         -- '1' if device busy
  signal rw    : std_logic;         -- '1' if device busy

  signal i_data_out : std_logic_vector(7 downto 0);
  signal i_data_in  : std_logic_vector(7 downto 0);
  signal cmd_i      : std_logic_vector(2 downto 0);

  type State_type is (sIDLE, sDVAL_HIGH, sWAIT);
  signal State : State_type;

begin
  DISPLAY: ks0108b_ctrl
    generic map (
      RESET_ACTIVE => RESET_ACTIVE
    )
    port map (
      clk          => clk,
      rst          => rst,
      dval         => dval,
      rs           => di,
      busy         => busyi,
      data_in      => i_data_in,
      data_out     => i_data_out,
      rw           => rw,
      glcd_db_in   => glcd_db_in,
      glcd_db_out  => glcd_db_out,
      glcd_db_enab => glcd_db_enab,
      glcd_e       => glcd_e,
      glcd_reset   => glcd_reset,
      glcd_rw      => glcd_rw,
      glcd_di      => glcd_di,
      glcd_cs      => glcd_cs
    );

  LCDPrc: process (clk, rst)
  begin
    if (rst = RESET_ACTIVE) then
      i_data_out <= (others => '0');
      data_in    <= (others => '0');
      dval       <= '0';
      st_busy    <= '1';
      st_onoff   <= '0';
      st_reset   <= '0';
      busy       <= '1';
      di         <= '0';
      rw         <= '0';
      cmd_i      <= (others => '0');
      state      <= sIDLE;
    elsif rising_edge(clk) then
      dval <= '0';

      case state is
        when sIDLE =>
          if (busyi = '0') then
            busy <= '0';

            case cmd is
              when NO_CMD =>
                null;

              when READ_DD =>      -- command read display data
                cmd_i <= cmd;      -- store command
                di    <= '1';
                rw    <= '1';
                dval  <= '1';
                busy  <= '1';
                state <= sDVAL_HIGH;

              when WRITE_DD =>     -- command write display data
                cmd_i     <= cmd;  -- store command
                i_data_out <= data_out;
                di        <= '1';
                rw        <= '0';
                dval      <= '1';
                busy      <= '1';
                state     <= sDVAL_HIGH;

              when ST_READ =>      -- command status read
                cmd_i <= cmd;      -- store command
                di    <= '0';
                rw    <= '1';
                dval  <= '1';
                busy  <= '1';
                state <= sDVAL_HIGH;

              when SET_ADDR_Y =>   -- command set address y
                cmd_i     <= cmd;  -- store command
                i_data_out <= "01" & data_out(5 downto 0);
                di        <= '0';
                rw        <= '0';
                dval      <= '1';
                busy      <= '1';
                state     <= sDVAL_HIGH;

              when SET_D_SL =>     -- set display start line
                cmd_i <= cmd;      -- store command
                i_data_out <= "11" & data_out(5 downto 0);
                di        <= '0';
                rw        <= '0';
                dval      <= '1';
                busy      <= '1';
                state     <= sDVAL_HIGH;

              when SET_ADDR_X =>   -- set address X
                cmd_i     <= cmd;  -- store command
                i_data_out <= "10111" & data_out(2 downto 0);
                di        <= '0';
                rw        <= '0';
                dval      <= '1';
                busy      <= '1';
                state     <= sDVAL_HIGH;

              when DISP_ONOFF =>   -- command display on/off
                cmd_i     <= cmd;  -- store command
                i_data_out <= "0011111" & data_out(0);  -- Command Display On
                di        <= '0';
                rw        <= '0';
                dval      <= '1';
                busy      <= '1';
                state     <= sDVAL_HIGH;

              when others =>
                null;
            end case;
          end if;

        when sDVAL_HIGH =>
          state <= sWAIT;

        when sWAIT =>
          if (busyi = '0') then
            if (cmd_i = READ_DD) then
              data_in <= i_data_in;
            end if;

            if (cmd_i = ST_READ) then
              st_busy  <= i_data_in(7);
              st_onoff <= i_data_in(5);
              st_reset <= i_data_in(4);
            end if;
            cmd_i <= NO_CMD; -- store command
            state <= sIDLE;
          end if;

        when others =>
          state <= sIDLE;
      end case;
    end if;
  end process;
end behavioral;

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ralf schrieb:
WAIT_POWER_UP : unsigned(12 downto 0) := to_unsigned(3000, 13); -- 15 ms @ 50 MHz
Wie kommst du da auf 3000 für 15ms?
Ich würde sagen: 50MHz => 20ns
Und damit 15000ns/20ns = 750

Warum machst du die ganzen Zähler nicht einfach als integer und lässt 
den Synthesizer rechnen? Dann schreibst du da einfach:
 WAIT_POWER_UP : integer := 15000/20;  -- 15ms/20ns (50MHz)
 :
 :
  signal timer : integer range 0 to WAIT_POWER_UP;

Ich habe den Verdacht, dass du deine ganzen Zeiten nochmal anschauen und 
die Kommentare und/oder die Zeiten löschen bzw. korrigieren solltest:
    WAIT_SIXTY_US       : unsigned(13 downto 0) := to_unsigned(50, 14);  -- 1000 ns
    WAIT_FIVEHUNDRED_NS : unsigned(13 downto 0) := to_unsigned(25, 14);  -- 1000 ns
    WAIT_TWENTY_NS      : unsigned(13 downto 0) := to_unsigned(2, 14)    -- 20 ns


> Oder eventuell ein Beispiel oder Link für eine Applikation
> mit einem LCD-FPGA.
Hier ist was für ein Character-Display:
Beitrag "Re: EA DOG-M initialisieren"

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Lothar,

ja die 3000 sind nicht richtig. Kommt daher
dass ich verschieden Zeiten ausprobiert habe.
Deine Vorschläge in aller Ehren, sind eventuell
auch richtig. Aber die Zähler funktionieren so
wie sie sollen.
Auch dein Beispiel mit einem Character -Display
hilft mir leider nicht weiter. Ich habe schon
mehrere Character - Displays am FPGA laufen.
Nur jetzt habe ich zum ersten mal ein Graphik-
Display dran. Deshalb die Frage nach dem Timing
bzw. die Zeit in der man Befehle schicken kann.

Gruß

Ralf

Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ralf schrieb:
> Deshalb die Frage nach dem Timing
> bzw. die Zeit in der man Befehle schicken kann.

Das Timing steht im Datenblatt zum Displaycontroller und nicht im 
FPGA-Forum...

Duke

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist ja gerade das Problem.
Wenn ich mir die Signale dem Oszilloskop
anschaue passt alles. Im Datenblatt steht
aber leider keine Initialiesrungssequenz
wie z. B. bei Character Displays.

Autor: Lattice User (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann an der Initialisierung liegen.

Diese Frage ist u.U. im µC Forum besser aufgehoben, eine schnelle Suche 
ergibt dass schon einige ein deartiges Display am ATMega betrieben 
haben.
z.B. hier:
Beitrag "KS0108B Displaytech 64128A"

Autor: Harald Flügel (hfl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Ralf

Ich habe schonmal ein grafisches LCD an einem FPGA betrieben, aber das 
hatte wenigstens einen Display-Controller. Der genannte KS0108B ist ja 
gerade mal ein "driver", braucht also eine ganze Reiher spezieller 
Eingangsignale und Spannungen. Wenn die Pegel und das Timing (mit den 
Scope gemessen) korrekt sind, dann brauchst Du den Fehler nicht im FPGA 
zu suchen. Dann bleiben nur noch:

- IC oder Display ist defekt
- Spannung ist nicht eingeschaltet
- Datenblatt ist falsch

Much fun,
Harald

P.S. Bei dem Display, das ich im Einsatz hatte, lag der letzte der 
genannten Fehler vor. Ganz groß stand im Datenblatt geschieben "Do not 
apply a signal to this pin, it must be tied to GND". Hat sich dann 
leider als falsch herausgestellt.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ralf schrieb:
> Deine Vorschläge in aller Ehren, sind eventuell auch richtig.
Ja, das sind sie.
> Aber die Zähler funktionieren so wie sie sollen.
Klar, das hatte ich nicht bezweifelt. Nur könnte man sie schöner 
schreiben...  ;-)

> Im Datenblatt steht aber leider keine Initialiesrungssequenz
> wie z. B. bei Character Displays.
Und Google? Z.B. mit
KS0108B  initialize
http://forum.lcdinfo.com/viewtopic.php?t=554
http://www.geocities.com/dinceraydin/lcd/gfxintro.htm

Autor: gnihihi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
leuchtet dein display? ich hatte mal das problem, dass augenscheinlich 
NICHTS funzte, die simulation aber fehlerfrei lief. ich habe dann 
versuchsweise den pin für die displaybeleuchtung auf masse gezogen. das 
stand leider nicht im datenblatt und war reiner zufall.
danach funktionierte alles sahne

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Morgen,

die Ansteuerung des Display funktioniert nun.
Die Pegel der CS-Leitungen waren in meiner
Ansteuerung falsch. An diesem Display sind sie High-Aktiv.
Außerdem sind alle Pixel nach dem Initialisieren
eingeschaltet. Ich hatte fälschlicherweise angenommen
das diese nach dem Initialisieren ausgeschaltet sind
und wollte alle Pixel einschalten. Da diese schon
eingeschaltet waren, sah ich natürlich keine Reaktion.

Also vielen Dank an alle die mir mit Ratschlägen
geholfen haben.

Gruß

Ralf

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.