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
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
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.
1 | library ieee; |
2 | use ieee.std_logic_1164.all; |
3 | use ieee.numeric_std.all; |
4 | |
5 | entity ks0108b_ctrl is |
6 | generic ( |
7 | RESET_ACTIVE : std_logic := '0'; |
8 | WAIT_POWER_UP : unsigned(12 downto 0) := to_unsigned(3000, 13); -- 15 ms @ 50 MHz |
9 | WAIT_T1 : unsigned(12 downto 0) := to_unsigned(18, 13); -- 360 ns @ 50 MHz |
10 | WAIT_T2 : unsigned(12 downto 0) := to_unsigned(8, 13); -- 150 ns @ 50 MHz |
11 | WAIT_T3 : unsigned(12 downto 0) := to_unsigned(26, 13); -- 510 ns @ 50 MHz |
12 | WAIT_T4 : unsigned(12 downto 0) := to_unsigned(3, 13); -- 30 ns @ 50 MHz |
13 | WAIT_T5 : unsigned(12 downto 0) := to_unsigned(2, 13) -- 20 ns @ 50 MHz |
14 | );
|
15 | port ( |
16 | clk : in std_logic; -- System Clock |
17 | rst : in std_logic; -- asynchronous Reset, active high |
18 | data_out : in std_logic_vector(7 downto 0); -- Input data |
19 | data_in : out std_logic_vector(7 downto 0); |
20 | dval : in std_logic; -- Write Data command (1 clock cycle) |
21 | rs : in std_logic; -- Data or Command |
22 | rw : in std_logic; -- read or write command |
23 | busy : out std_logic; -- '1' if device busy |
24 | -- LCD signals
|
25 | glcd_db_in : in std_logic_vector(7 downto 0); |
26 | glcd_db_out : out std_logic_vector(7 downto 0); |
27 | glcd_db_enab : out std_logic; |
28 | glcd_e : out std_logic; |
29 | glcd_reset : out std_logic; |
30 | glcd_rw : out std_logic; |
31 | glcd_di : out std_logic; |
32 | glcd_cs : out std_logic |
33 | );
|
34 | end ks0108b_ctrl; |
35 | |
36 | |
37 | architecture behavioral of ks0108b_ctrl is |
38 | |
39 | type State_type is (sINIT, sIDLE, sWAIT_T1, sWAIT_T2, sWAIT_T3, sWAIT_T4, sWAIT_T5); |
40 | signal ctlState : State_type; |
41 | |
42 | signal timer : unsigned(12 downto 0); |
43 | |
44 | begin
|
45 | process (clk, rst) |
46 | begin
|
47 | if (rst = RESET_ACTIVE) then |
48 | busy <= '1'; |
49 | data_in <= (others => '0'); |
50 | glcd_db_out <= (others => '1'); |
51 | glcd_e <= '1'; |
52 | glcd_reset <= '0'; |
53 | glcd_rw <= '1'; |
54 | glcd_di <= '1'; |
55 | glcd_db_enab <= '0'; |
56 | glcd_cs <= '1'; |
57 | timer <= (others => '0'); |
58 | ctlState <= sINIT; |
59 | elsif rising_edge(clk) then |
60 | case ctlState is |
61 | when sINIT => -- Power up wait 15 ms |
62 | glcd_reset <= '0'; |
63 | timer <= timer + 1; |
64 | if (timer = WAIT_POWER_UP) then |
65 | ctlState <= sIDLE; |
66 | end if; |
67 | |
68 | when sIDLE => |
69 | glcd_reset <= '1'; |
70 | busy <= '0'; |
71 | glcd_e <= '1'; |
72 | glcd_db_enab <= '0'; |
73 | |
74 | if (dval = '1') then |
75 | busy <= '1'; |
76 | glcd_e <= '0'; |
77 | glcd_db_out <= data_out; |
78 | timer <= (others => '0'); |
79 | ctlState <= sWAIT_T1; |
80 | end if; |
81 | |
82 | when sWAIT_T1 => |
83 | timer <= timer + 1; |
84 | if (timer = WAIT_T1) then |
85 | glcd_rw <= rw; |
86 | glcd_di <= rs; |
87 | glcd_cs <= '0'; |
88 | timer <= (others => '0'); |
89 | ctlState <= sWAIT_T2; |
90 | end if; |
91 | |
92 | when sWAIT_T2 => |
93 | timer <= timer + 1; |
94 | if (timer = WAIT_T2) then |
95 | glcd_e <= '1'; |
96 | if (rw = '0') then |
97 | glcd_db_enab <= '1'; |
98 | end if; |
99 | timer <= (others => '0'); |
100 | ctlState <= sWAIT_T3; |
101 | end if; |
102 | |
103 | when sWAIT_T3 => |
104 | timer <= timer + 1; |
105 | if (timer = WAIT_T3) then |
106 | glcd_e <= '0'; |
107 | if (rw = '1') then |
108 | data_in <= glcd_db_in; |
109 | end if; |
110 | timer <= (others => '0'); |
111 | ctlState <= sWAIT_T4; |
112 | end if; |
113 | |
114 | when sWAIT_T4 => |
115 | timer <= timer + 1; |
116 | if (timer = WAIT_T4) then |
117 | glcd_cs <= '1'; |
118 | glcd_di <= '1'; |
119 | glcd_rw <= '1'; |
120 | timer <= (others => '0'); |
121 | ctlState <= sWAIT_T5; |
122 | end if; |
123 | |
124 | when sWAIT_T5 => |
125 | timer <= timer + 1; |
126 | if (timer = WAIT_T5) then |
127 | glcd_e <= '1'; |
128 | ctlState <= sIDLE; |
129 | end if; |
130 | |
131 | when others => |
132 | ctlState <= sIDLE; |
133 | end case; |
134 | end if; |
135 | end process; |
136 | end behavioral; |
Ich hoffe ihr könnt damit was anfangen. Laut Simulation ist auch alles in Ordnung.
1 | library ieee; |
2 | use ieee.std_logic_1164.all; |
3 | use ieee.numeric_std.all; |
4 | |
5 | entity ks0108b_cmd is |
6 | generic ( |
7 | RESET_ACTIVE : std_logic := '0'; -- Polaritaet fuer Reset-Signal |
8 | WAIT_SIXTY_US : unsigned(13 downto 0) := to_unsigned(50, 14); -- 1000 ns |
9 | WAIT_FIVEHUNDRED_NS : unsigned(13 downto 0) := to_unsigned(25, 14); -- 1000 ns |
10 | WAIT_TWENTY_NS : unsigned(13 downto 0) := to_unsigned(2, 14) -- 20 ns |
11 | );
|
12 | port ( |
13 | -- master signals
|
14 | clk : in std_logic; |
15 | rst : in std_logic; |
16 | cmd : in std_logic_vector(2 downto 0); |
17 | busy : out std_logic; |
18 | st_busy : out std_logic; |
19 | st_onoff : out std_logic; |
20 | st_reset : out std_logic; |
21 | data_out : in std_logic_vector(7 downto 0); |
22 | data_in : out std_logic_vector(7 downto 0); |
23 | -- graphic lcd interface
|
24 | glcd_db_in : in std_logic_vector(7 downto 0); |
25 | glcd_db_out : out std_logic_vector(7 downto 0); |
26 | glcd_db_enab : out std_logic; |
27 | glcd_e : out std_logic; |
28 | glcd_reset : out std_logic; |
29 | glcd_rw : out std_logic; |
30 | glcd_di : out std_logic; |
31 | glcd_cs : out std_logic |
32 | );
|
33 | end ks0108b_cmd; |
34 | |
35 | architecture behavioral of ks0108b_cmd is |
36 | |
37 | component ks0108b_ctrl is |
38 | generic ( |
39 | RESET_ACTIVE : std_logic := '0'; |
40 | WAIT_POWER_UP : unsigned(12 downto 0) := to_unsigned(3000, 13); -- 15 ms @ 50 MHz |
41 | WAIT_T1 : unsigned(12 downto 0) := to_unsigned(18, 13); -- 360 ns @ 50 MHz |
42 | WAIT_T2 : unsigned(12 downto 0) := to_unsigned(8, 13); -- 150 ns @ 50 MHz |
43 | WAIT_T3 : unsigned(12 downto 0) := to_unsigned(26, 13); -- 510 ns @ 50 MHz |
44 | WAIT_T4 : unsigned(12 downto 0) := to_unsigned(3, 13); -- 30 ns @ 50 MHz |
45 | WAIT_T5 : unsigned(12 downto 0) := to_unsigned(2, 13) -- 20 ns @ 50 MHz |
46 | );
|
47 | port ( |
48 | clk : in std_logic; -- System Clock |
49 | rst : in std_logic; -- asynchronous Reset, active high |
50 | data_out : in std_logic_vector(7 downto 0); -- Input data |
51 | data_in : out std_logic_vector(7 downto 0); |
52 | dval : in std_logic; -- Write Data command (1 clock cycle) |
53 | rs : in std_logic; -- Data or Command |
54 | rw : in std_logic; -- read or write command |
55 | busy : out std_logic; -- '1' if device busy |
56 | -- Graphic LCD signals
|
57 | glcd_db_in : in std_logic_vector(7 downto 0); |
58 | glcd_db_out : out std_logic_vector(7 downto 0); |
59 | glcd_db_enab : out std_logic; |
60 | glcd_e : out std_logic; |
61 | glcd_reset : out std_logic; |
62 | glcd_rw : out std_logic; |
63 | glcd_di : out std_logic; |
64 | glcd_cs : out std_logic |
65 | );
|
66 | end component; |
67 | |
68 | -- Graphic LCD command definitions
|
69 | constant NO_CMD : std_logic_vector(2 downto 0) := "000"; |
70 | constant READ_DD : std_logic_vector(2 downto 0) := "001"; |
71 | constant WRITE_DD : std_logic_vector(2 downto 0) := "010"; |
72 | constant ST_READ : std_logic_vector(2 downto 0) := "011"; |
73 | constant SET_ADDR_Y : std_logic_vector(2 downto 0) := "100"; |
74 | constant SET_D_SL : std_logic_vector(2 downto 0) := "101"; |
75 | constant SET_ADDR_X : std_logic_vector(2 downto 0) := "110"; |
76 | constant DISP_ONOFF : std_logic_vector(2 downto 0) := "111"; |
77 | |
78 | signal dval : std_logic := '0'; -- Write Data command (1 clock cycle) |
79 | signal di : std_logic := '0'; -- Data or Instruction |
80 | signal busyi : std_logic; -- '1' if device busy |
81 | signal rw : std_logic; -- '1' if device busy |
82 | |
83 | signal i_data_out : std_logic_vector(7 downto 0); |
84 | signal i_data_in : std_logic_vector(7 downto 0); |
85 | signal cmd_i : std_logic_vector(2 downto 0); |
86 | |
87 | type State_type is (sIDLE, sDVAL_HIGH, sWAIT); |
88 | signal State : State_type; |
89 | |
90 | begin
|
91 | DISPLAY: ks0108b_ctrl |
92 | generic map ( |
93 | RESET_ACTIVE => RESET_ACTIVE |
94 | )
|
95 | port map ( |
96 | clk => clk, |
97 | rst => rst, |
98 | dval => dval, |
99 | rs => di, |
100 | busy => busyi, |
101 | data_in => i_data_in, |
102 | data_out => i_data_out, |
103 | rw => rw, |
104 | glcd_db_in => glcd_db_in, |
105 | glcd_db_out => glcd_db_out, |
106 | glcd_db_enab => glcd_db_enab, |
107 | glcd_e => glcd_e, |
108 | glcd_reset => glcd_reset, |
109 | glcd_rw => glcd_rw, |
110 | glcd_di => glcd_di, |
111 | glcd_cs => glcd_cs |
112 | );
|
113 | |
114 | LCDPrc: process (clk, rst) |
115 | begin
|
116 | if (rst = RESET_ACTIVE) then |
117 | i_data_out <= (others => '0'); |
118 | data_in <= (others => '0'); |
119 | dval <= '0'; |
120 | st_busy <= '1'; |
121 | st_onoff <= '0'; |
122 | st_reset <= '0'; |
123 | busy <= '1'; |
124 | di <= '0'; |
125 | rw <= '0'; |
126 | cmd_i <= (others => '0'); |
127 | state <= sIDLE; |
128 | elsif rising_edge(clk) then |
129 | dval <= '0'; |
130 | |
131 | case state is |
132 | when sIDLE => |
133 | if (busyi = '0') then |
134 | busy <= '0'; |
135 | |
136 | case cmd is |
137 | when NO_CMD => |
138 | null; |
139 | |
140 | when READ_DD => -- command read display data |
141 | cmd_i <= cmd; -- store command |
142 | di <= '1'; |
143 | rw <= '1'; |
144 | dval <= '1'; |
145 | busy <= '1'; |
146 | state <= sDVAL_HIGH; |
147 | |
148 | when WRITE_DD => -- command write display data |
149 | cmd_i <= cmd; -- store command |
150 | i_data_out <= data_out; |
151 | di <= '1'; |
152 | rw <= '0'; |
153 | dval <= '1'; |
154 | busy <= '1'; |
155 | state <= sDVAL_HIGH; |
156 | |
157 | when ST_READ => -- command status read |
158 | cmd_i <= cmd; -- store command |
159 | di <= '0'; |
160 | rw <= '1'; |
161 | dval <= '1'; |
162 | busy <= '1'; |
163 | state <= sDVAL_HIGH; |
164 | |
165 | when SET_ADDR_Y => -- command set address y |
166 | cmd_i <= cmd; -- store command |
167 | i_data_out <= "01" & data_out(5 downto 0); |
168 | di <= '0'; |
169 | rw <= '0'; |
170 | dval <= '1'; |
171 | busy <= '1'; |
172 | state <= sDVAL_HIGH; |
173 | |
174 | when SET_D_SL => -- set display start line |
175 | cmd_i <= cmd; -- store command |
176 | i_data_out <= "11" & data_out(5 downto 0); |
177 | di <= '0'; |
178 | rw <= '0'; |
179 | dval <= '1'; |
180 | busy <= '1'; |
181 | state <= sDVAL_HIGH; |
182 | |
183 | when SET_ADDR_X => -- set address X |
184 | cmd_i <= cmd; -- store command |
185 | i_data_out <= "10111" & data_out(2 downto 0); |
186 | di <= '0'; |
187 | rw <= '0'; |
188 | dval <= '1'; |
189 | busy <= '1'; |
190 | state <= sDVAL_HIGH; |
191 | |
192 | when DISP_ONOFF => -- command display on/off |
193 | cmd_i <= cmd; -- store command |
194 | i_data_out <= "0011111" & data_out(0); -- Command Display On |
195 | di <= '0'; |
196 | rw <= '0'; |
197 | dval <= '1'; |
198 | busy <= '1'; |
199 | state <= sDVAL_HIGH; |
200 | |
201 | when others => |
202 | null; |
203 | end case; |
204 | end if; |
205 | |
206 | when sDVAL_HIGH => |
207 | state <= sWAIT; |
208 | |
209 | when sWAIT => |
210 | if (busyi = '0') then |
211 | if (cmd_i = READ_DD) then |
212 | data_in <= i_data_in; |
213 | end if; |
214 | |
215 | if (cmd_i = ST_READ) then |
216 | st_busy <= i_data_in(7); |
217 | st_onoff <= i_data_in(5); |
218 | st_reset <= i_data_in(4); |
219 | end if; |
220 | cmd_i <= NO_CMD; -- store command |
221 | state <= sIDLE; |
222 | end if; |
223 | |
224 | when others => |
225 | state <= sIDLE; |
226 | end case; |
227 | end if; |
228 | end process; |
229 | end behavioral; |
Ralf schrieb:
1 | 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:
1 | WAIT_POWER_UP : integer := 15000/20; -- 15ms/20ns (50MHz) |
2 | :
|
3 | :
|
4 | 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:
1 | WAIT_SIXTY_US : unsigned(13 downto 0) := to_unsigned(50, 14); -- 1000 ns |
2 | WAIT_FIVEHUNDRED_NS : unsigned(13 downto 0) := to_unsigned(25, 14); -- 1000 ns |
3 | 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"
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
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
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.
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"
@ 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.
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
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
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.