Ich weiss, dass dieses Thema schon in einem Thread behandelt wurde, aber die Lösungen waren etwas unbefriedigend für mich, da für mich zu kompliziertes Interface . Ich versuche einen einfachen i2c Slave controller für ein fpga zu programmieren. Dieser soll eigentlich nur dazu dienen Bytes zu empfangen. Auf Opencores gibt es zwar einen I2C Master controller, aber der nützt mir recht wenig, da halt Master Controller und nicht Slave. Das interface stelle ich mir so vor: generic : i2c_slave_adress port : sys_clock : in scl_in : in sda_in : in Data_ready : out -- Daten vorhanden Data_read_clock : in -- Lesetakt für daten Data_read : in -- Lesen ausführen Data : out -- Payload Den ganzen std_logic Kram habe ich mal weggelassen. Hat jemand schon mal sowas gemacht (evtl. CPLD als Porterweiterung) das auch funktioniert hat. Den IP-Core will ich später mal benutzen, um mir debug informationen von meinem Cypress EZ-USB expansion modul zu holen, da der Hersteller blöderweise vergessen hat die RXD und TXD Leitungen zu verdraten. Somit habe ich keinen Zugang zur UART auf dem USB Controller, was sich beim debuggen echt dämlich macht. Danke an jeden, der dazu etwas schreiben kann oder zur Hilfe beiträgt. Schon fertiger VHDL Code ist natürlich sehr willkommen. :D, bitte kein freakiger Verilog Code, ich möchte das auch in etwa verstehen können was da passiert, wenn ich Anpassungen am Interface vornehmen muss. G. Tobias
Ok, hier unten. In der Simulation (zusammen mit dem Master von Opencores) funktioniert alles einwandfrei. Ich übernehme aber keine Haftung - war zu schnell reingehackt. Hoffe, Du blickst da durch, wenn nicht, dann frag', ich versuche mich zu erinnern ;-) Kest library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use IEEE.STD_LOGIC_UNSIGNED.all; -- ------------------------------------------------------------------------ - entity i2c_core_slave is port ( clk : in std_logic; nReset : in std_logic; Dout : out std_logic_vector(7 downto 0); data_valid : out std_logic; SCL : in std_logic; SDA : inout std_logic ); end entity i2c_core_slave; -- ------------------------------------------------------------------------ - architecture behav of i2c_core_slave is type states is (s_idle, s_start, s_read_a, s_ack); signal state, next_state : states; signal oSCL, oSDA : std_logic := '0'; signal SDAt, SCLb : std_logic; signal sda_pad_o, sda_pad_i, sda_padoen_oe : std_logic; signal rx_reg : std_logic_vector(7 downto 0); signal rx_done : std_logic; signal int_reg : std_logic_vector(7 downto 0); signal stop_sy : std_logic := '0'; signal count : std_logic_vector(2 downto 0) := "000"; -- ------------------------------------------------------------------------ - begin -- SDA <= sda_pad_o when (sda_padoen_oe = '0') else 'Z'; sda_pad_i <= SDA; SDAt <= '0' when (SDA = '0') else '1'; SCLb <= '0' when (SCL = '0') else '1'; -- ------------------------------------------------------------------------ - -- Statemachine für i2c-Bus (slave) -- ------------------------------------------------------------------------ - process (nReset, clk)--, state, SDA, stop_sy, count) begin if nReset = '0' then rx_done <= '0'; sda_pad_o <= '0'; sda_padoen_oe <= '0'; data_valid <= '0'; next_state <= s_idle; int_reg <= (others => 'Z'); Dout <= (others => 'Z'); elsif clk'event and clk='1' then case state is when s_idle => data_valid <= '0'; rx_done <= '0'; sda_padoen_oe <= '0'; if SDA = '0' then -- sda_padoen_oe <= '0'; next_state <= s_start; else next_state <= s_idle; -- sda_padoen_oe <= '0'; end if; when s_start => data_valid <= '0'; rx_done <= '0'; sda_padoen_oe <= '1'; if stop_sy = '0' then next_state <= s_read_a; else next_state <= s_idle; end if; when s_read_a => -- hier SDA übernehmen und schiften data_valid <= '0'; rx_done <= '0'; if (count = "000" and stop_sy='0') then next_state <= s_ack; else next_state <= s_read_a; end if; when s_ack => -- acknowledge senden (SDA auf 0) data_valid <= '1'; rx_done <= '1'; sda_pad_o <= '0'; sda_padoen_oe <= '0'; int_reg <= rx_reg; Dout <= rx_reg; next_state <= s_start; -- idle end case; end if; end process; -- ------------------------------------------------------------------------ - -- stop erkennen, (a)synchron -- ------------------------------------------------------------------------ - process (nReset, clk)--, SCLb, SDAt, state, oSDA) begin if nReset = '0' then stop_sy <= '0'; oSDA <= '1'; elsif clk'event and clk='1' then if state = s_idle then stop_sy <= '0'; end if; if (oSDA = '0' and SDAt = '1') then -- also steigende Flanke oSDA <= '1'; if SCLb = '1' then -- und SCL hoch, => stop Signal stop_sy <= '1'; else stop_sy <= '0'; end if; elsif (oSDA = '1' and SDAt = '0') then -- vorsichtshalber auch die fallende erkennen oSDA <= '0'; end if; end if; end process; -- ------------------------------------------------------------------------ - -- states weiterschalten -- auf die fallende SCL-Flanke triggern -- wenn genug CLKs vorhanden, kann man auch SCL als CLK nehmen, erstmal aber nicht -- -- ------------------------------------------------------------------------ - process (clk, nReset, state, stop_sy, oSCL, SCLb, rx_done) begin if nReset = '0' then state <= s_idle; oSCL <= '1'; count <= "000"; rx_reg <= (others => 'Z'); elsif (clk'event and clk = '1') then -- Register if stop_sy = '1' then state <= s_idle; end if; if state = s_idle then count <= "000"; end if; if (oSCL = '1' and SCLb = '0' ) then -- fallende Flanke? -- and stop_sy='0' state <= next_state; -- nächster Zustand oSCL <= '0'; -- aktuellen Wert merken elsif (oSCL = '0' and SCLb = '1') then -- steigende Flanke? if (state = s_idle) or (state = s_ack) then -- Zähler reseten count <= "000"; else if rx_done = '0' then rx_reg <= rx_reg(6 downto 0) & SDAt; -- shiften count <= count+"001"; -- 8 Bits abzählen end if; end if; oSCL <= '1'; end if; end if; end process; -- ------------------------------------------------------------------------ - end architecture behav;
am Ausgang hast Du einfach die Daten, die ankommen. (Serial auf Parallel), das war's auch schon. Wenn die richtige Adresse angekommen ist, dann... tja, ist alles rudimentär, ich weis ;-) Aber immerhin :-) Kest
also mehr ein "Sniffer", ein Slave müsste ja mit seinem Acknowledge entsprechend reagieren, abhängig davon, ob er angesprochen wurde oder nicht. Besteht allgemeines Interesse an einem Slave in VHDL, der sowohl beschrieben als auch gelesen werden kann und frei adressierbar ist + im Sniffer-Mode arbeiten kann ? Habe gerade ein Projekt, wo sich sowas ganz gut machen würde, aber ich bräuchte ein paar Tester, die sich den Code anschauen und simulieren + Bug-Reports liefern.
WAs dazu kommt ist einfach mal eine kleine (wirklich sehr kleine) Statemachine, die läuft eben los, wenn die Adresse erkannt wird. Ich wollte sie blos nicht jetzt aus einem Projekt rausreißen, weil man durcheinander kommt... Aber Sniffer ist ja schon fast zu 90% ein I2C slave ;-) Kommt, Leute, macht doch auch was :-) Übrigens, ein Tipp, diese "kleine" Statemachine, ist bei Opencores, bei dem I2C Master mitdabei (da wird irgendwie ein Mixrocontroller emuliert oder so... etwas suchen müsst ihr schon ;-)) Kest
also der Multimaster bei Opencores hat mir nicht so gefallen, war auch ganz schön umfangreich... Teile des Slaves habe ich schon fertig, wie gesagt, man kann auch von dem Slave lesen und nach jedem Transfer gibts einen Status, also z.B. TX_UNDERRUN, RX_OVERFLOW, NO_ACK ... Aber einfach so zum kopieren und abheften gibts den nich :-)))
Interesse an einem i2c Slave Controller würde ich dann mal anmelden, am besten mit Richtungsumschaltung, damit man den auch ins FPGA packen kann, bzw. einen IO Buffer für die FPGA Pins benutzen kann. sprich sda_in : in sda_out : out sda_read : out -- wenn '1',dann sda_in lesen wenn '0' dann sda out schreiben. verdrahtung im fpga top level würde dann so aussehen IOBUF port map (O => sda_in, IO => FPGA_pin_sda, I => sda_out; T => sda_read ); Würde mich als Tester und zur Evaluierung anbieten, da ich das sowieso brauchen werde, bzw. das zum laufen kriegen muss. Gruß Tobias
hallo würde mich auch dafür interressieren. wüsste bloß nicht wie ich das mit dem ack hinbekommen sollte. da ich mich auch bloß privat damit beschäfftige. wäre warscheinlich ganz hilfreich zum verständnis. das i2c protokoll habe ich mir schon angeschaut und auch verstanden. hätte auch nicht das problem mit zu versuchen den zu erstellen. kann mich nur weiter bringen. bin schon super glücklich das meine selbstprogrammierte funkuhr super läuft. die musste ich ja schließlich ganze 4mal umprogrammieren (grins) erst alles asynchron, dann hier das forum gefunden und dan musste ich das noch alles synchron schreiben. das forum hier hat mir schon sehr geholfen. mfg
Übrigens, das Code, was ich oben gepostet habe, ist schon mehr als ein Sniffer - ACK bekommt man jeweils nach jedem empfangenen Byte zurück (ist mir gerade eingefallen ;-)) Ich bin der Meinung, dass solche kleine Sachen man selber reinhacken kann. Dabei lernt man sehr schnell dazu - bzw. welche Fehler dann auftauchen. Klar, wenn man nur fertigen SAchen nimmt, ist schon praktisch, aber na ja... jedem das seine. Für mich wäre sowas interessant, wenn man einfach alles anschaut, wie das andere gemacht haben. Ich, für mein Teil, optimiere nur selten - programmiert, getestet, synthetisiser, getestet, geändert, synthetisiert, getestet - okay. Wenn da irgendwelche Latches sind oder so, beachte ich nicht. Nur, wenn ich dann Zeit habe - das ist aber oft nicht der Fall :-/ Ich gucke also sehr gerne mal die Sachen von Dir an, FPGA_User :-) Kest Kest
Fremden Code zu lesen ist meist nicht sehr einfach. Da ist es meist viel einfacher es selber zu schreiben. Ich habe erst einmal den Code von Kest ein bisschen aufgeräumt. Hier jetzt ein paar Tipps um die Lesbarkeit des Codes zu verbessern: 1. Bei der Signaldeklaration für jedes Signal oder zum mindestens für gleiche Signale eine Zeile verwenden. 2. Bei Signalen, die nicht auf die Verwendung schließen lassen, noch einen Kommentar anfügen. Eine Vereinfachung habe ich auch noch gefunden: Neu: stop_sy <= SCLb; -- und SCL hoch, => stop Signal Alt: if SCLb = '1' then stop_sy <= '1'; else stop_sy <= '0'; end if;
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 | |
7 | entity i2c_core_slave is |
8 | port ( |
9 | clk : in std_logic; |
10 | nReset : in std_logic; |
11 | Dout : out std_logic_vector(7 downto 0); |
12 | data_valid : out std_logic; |
13 | SCL : in std_logic; |
14 | SDA : inout std_logic |
15 | );
|
16 | end entity i2c_core_slave; |
17 | |
18 | ---------------------------------------------------------------------------
|
19 | |
20 | architecture behav of i2c_core_slave is |
21 | |
22 | type states is (s_idle, s_start, s_read_a, s_ack); |
23 | signal state, next_state : states; |
24 | |
25 | signal oSCL : std_logic := '0'; |
26 | signal oSDA : std_logic := '0'; |
27 | signal SCLb : std_logic; |
28 | signal SDAt : std_logic; |
29 | |
30 | signal sda_pad_i : std_logic; |
31 | signal sda_pad_o : std_logic; |
32 | signal sda_padoen_oe : std_logic; |
33 | |
34 | signal rx_reg : std_logic_vector(7 downto 0); |
35 | signal rx_done : std_logic; |
36 | signal int_reg : std_logic_vector(7 downto 0); |
37 | signal stop_sy : std_logic := '0'; |
38 | signal count : std_logic_vector(2 downto 0) := "000"; |
39 | |
40 | ---------------------------------------------------------------------------
|
41 | |
42 | begin
|
43 | |
44 | -- SDA <= sda_pad_o when (sda_padoen_oe = '0') else 'Z';
|
45 | sda_pad_i <= SDA; |
46 | SDAt <= '0' when (SDA = '0') else '1'; |
47 | SCLb <= '0' when (SCL = '0') else '1'; |
48 | |
49 | ---------------------------------------------------------------------------
|
50 | -- Statemachine für i2c-Bus (slave)
|
51 | ---------------------------------------------------------------------------
|
52 | |
53 | process (nReset, clk) --, state, SDA, stop_sy, count) |
54 | begin
|
55 | if nReset = '0' then |
56 | rx_done <= '0'; |
57 | sda_pad_o <= '0'; |
58 | sda_padoen_oe <= '0'; |
59 | data_valid <= '0'; |
60 | next_state <= s_idle; |
61 | int_reg <= (others => 'Z'); |
62 | Dout <= (others => 'Z'); |
63 | elsif clk'event and clk='1' then |
64 | |
65 | case state is |
66 | |
67 | when s_idle => |
68 | data_valid <= '0'; |
69 | rx_done <= '0'; |
70 | sda_padoen_oe <= '0'; |
71 | |
72 | if SDA = '0' then |
73 | -- sda_padoen_oe <= '0';
|
74 | next_state <= s_start; |
75 | else
|
76 | next_state <= s_idle; |
77 | -- sda_padoen_oe <= '0';
|
78 | end if; |
79 | |
80 | when s_start => |
81 | data_valid <= '0'; |
82 | rx_done <= '0'; |
83 | sda_padoen_oe <= '1'; |
84 | |
85 | if stop_sy = '0' then |
86 | next_state <= s_read_a; |
87 | else
|
88 | next_state <= s_idle; |
89 | end if; |
90 | |
91 | when s_read_a => -- hier SDA übernehmen und schiften |
92 | data_valid <= '0'; |
93 | rx_done <= '0'; |
94 | |
95 | if (count = "000" and stop_sy='0') then |
96 | next_state <= s_ack; |
97 | else
|
98 | next_state <= s_read_a; |
99 | end if; |
100 | |
101 | when s_ack => -- acknowledge senden (SDA auf 0) |
102 | data_valid <= '1'; |
103 | rx_done <= '1'; |
104 | sda_pad_o <= '0'; |
105 | sda_padoen_oe <= '0'; |
106 | int_reg <= rx_reg; |
107 | Dout <= rx_reg; |
108 | next_state <= s_start; -- idle |
109 | |
110 | end case; |
111 | |
112 | end if; |
113 | |
114 | end process; |
115 | |
116 | ---------------------------------------------------------------------------
|
117 | -- stop erkennen, (a)synchron
|
118 | ---------------------------------------------------------------------------
|
119 | |
120 | process (nReset, clk) |
121 | begin
|
122 | if nReset = '0' then |
123 | stop_sy <= '0'; |
124 | oSDA <= '1'; |
125 | elsif clk'event and clk='1' then |
126 | |
127 | if state = s_idle then |
128 | stop_sy <= '0'; --??? Reihenfolge |
129 | end if; |
130 | |
131 | if (oSDA = '0' and SDAt = '1') then -- also steigende |
132 | Flanke
|
133 | oSDA <= '1'; |
134 | stop_sy <= SCLb; -- und SCL hoch, => |
135 | stop Signal |
136 | elsif (oSDA = '1' and SDAt = '0') then -- vorsichtshalber |
137 | auch die fallende erkennen |
138 | oSDA <= '0'; |
139 | end if; |
140 | end if; |
141 | end process; |
142 | |
143 | ---------------------------------------------------------------------------
|
144 | -- states weiterschalten
|
145 | -- auf die fallende SCL-Flanke triggern
|
146 | -- wenn genug CLKs vorhanden, kann man auch SCL als CLK nehmen, erstmal
|
147 | aber nicht |
148 | ---------------------------------------------------------------------------
|
149 | |
150 | process (clk, nReset) |
151 | begin
|
152 | if nReset = '0' then |
153 | state <= s_idle; |
154 | oSCL <= '1'; |
155 | count <= "000"; |
156 | rx_reg <= (others => 'Z'); |
157 | elsif (clk'event and clk = '1') then -- Register |
158 | |
159 | if stop_sy = '1' then |
160 | state <= s_idle; |
161 | end if; |
162 | |
163 | if state = s_idle then |
164 | count <= "000"; |
165 | end if; |
166 | |
167 | if (oSCL = '1' and SCLb = '0' ) then -- fallende |
168 | Flanke? -- and stop_sy='0' |
169 | state <= next_state; -- nächster Zustand |
170 | oSCL <= '0'; -- aktuellen Wert |
171 | merken
|
172 | elsif (oSCL = '0' and SCLb = '1') then -- steigende |
173 | Flanke? |
174 | if (state = s_idle) or (state = s_ack) then -- Zähler |
175 | reseten
|
176 | count <= "000"; |
177 | else
|
178 | if rx_done = '0' then |
179 | rx_reg <= rx_reg(6 downto 0) & SDAt; -- shiften |
180 | count <= count+"001"; -- 8 Bits abzählen |
181 | end if; |
182 | end if; |
183 | oSCL <= '1'; |
184 | end if; |
185 | end if; |
186 | end process; |
187 | |
188 | ---------------------------------------------------------------------------
|
189 | |
190 | end architecture behav; |
MfG Holger
@Tobias welchen Vorteil siehst Du in der Auftrennung von SDA in SDA_in und SDA_out ? Die FPGA-Pins sind doch bidirektional, die Synthese erkennt automatisch, wie der Input-Buffer und der Output-Tristate-Buffer an den Core angeschlossen werden müssen. von außen kommen ja auch nur 2 Leitungen, SDA und SCL. Egal, werde bis Freitag mal den Slave-Core präsentieren, geplant ist TX und RX doppelt gepuffert, also man kann schon das nächste Byte einschreiben während das vorherige über I2C rausgeht, FIFOs sollten sich ohne große Kopfstände anschließen lassen (in der 1 Version evt. noch nicht), es soll keine FPGA-spezifischen Elemente geben, Transfer bricht ab, wenn kein ACK vom Master kommt (bei Read vom Slave).
Die Auftrennung finde ich nicht mal so dumm :-o Ich kann nur aus Erfahrung sagen, dass bidirektionale Pins oft stress machen (sogar auch in der Simulation). Da sind dann plötzlich kurzschlüsse oder was weis ich. Hab mal was gemacht, und dann mit dem Opencores-Core von i2c getestet und es klapte nicht (in der Simulation). In der Hardware hat man dann entsprechend Pulldown/Up Widerstände und da hat es schon funktioniert. Na ja... wir nähern uns langsam dem Wishbone ;-)) Kest
Hast ja recht mit den Bidirektionalen Pins, habe halt festgestellt, wenn man so eine Auftrennung vornimmt, lassen sich leichter Fehler erkennen, weil man besser erkennen kann, woher die Signale kommen. Ansonsten sieht man bei der Simulation nur ein 'X' und man weiss nicht, woher der Kurzschluss kommt. Außerdem muss man ja nicht die Hersteller Synthese Bibliotheken verwenden, sondern kann den IO Buffer auch "per hand" erstellen und ist dann völlig plattformunabhängig. Hauptsache es funktioniert process ....... if T = '1' then IO <= 'Z'; O <= IO ; else I <= O; O <= IO; end if; end process; Übrigens Danke für deine Hilfe !
meinen Slave habe ich in der Codesammlung abgelegt, SDA ist erstmal nicht aufgetrennt, hab versucht, alles so verständlich wie möglich zu schreiben, für den einen oder anderen sind bestimmt ein paar neue VHDL-Features zu entdecken. bitte mal damit rumspielen, wer Zeit hat.
Hi Jungs, ich hab mir mal das I2C Projekt auf Opencore angeschaut und find es recht kompliziert. Hat jemand ein besseres Projekt was er mir mal geben kann? Hab versucht es komplett mit Automaten zu realisieren und irgendwie ist da noch der Wurm drin. Wäre euch echt dankbar.
was bedeutet oder was beschreiben genau die signale sda-pad-i und sda pad-o und sda-padoen-oe und int-reg
hmm,immer noch niemand geantwortet! :( aber mal eine andere frage: wenn ich register des sensors auslese (register 0x0C bis 0x0F),die im default-zustand mit werten belegt sind, dann steht nach dem senden des repeated-start und der anschließenden Registeradresse sowie den 4 oben genannten adressen immer ein ACK.das sehe ich auf dem oszi. wenn ich nun die achsenregister auslese fehlt das ACK nach dem repeated-start(+sensoradresse) und nach dem lesen des jeweiligen LOW-byte der x-bzw. y-achse! im avr-studio steht aber im statusregister nach 0x10 (steht für repeated start) als nächstes der status (0x40) -> slave has been transmitted,ACK has been received! kann das sein?????
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.