Hallo! Ich habe hier ein Spartan 3 Board sowie ein cmps03 Kompassmodul liegen. Dieses habe ich bereits erfolgreich per PWM auslesen können, funktioniert ohne Probleme. Nun wollte ich das Ganze gern per I2C ansteuern, nur leider funktioniert das bisher nicht richtig. Ich habe das I2C Protokoll erstmal (vielleicht ein wenig unübersichtlich) mit einem recht langen Automaten implementiert, laut LogicAnalyzer sende ich auch das richtige raus (so wie es im Datenblatt steht). Geschwindigkeit ist unter 100kHz,das sollte auch nicht das Problem sein. Ich poste gleich mal den Automaten, vielleicht kann mir ja hier jemand den entscheidenden Hinweis geben.. :-) Vielen Dank schonmal!! main: process(clk) variable I2Cstate: integer range 0 to 30:=0; variable adr_cnt: integer range 0 to 15:=15; begin if clk='1' and clk'event then if btn = '1' then -- Kalibrierung, Pin 6 kurzzeitig auf GND ziehen calibrate <= '0'; else calibrate <= 'Z'; end if; case I2Cstate is when 0 => SDA <= '1'; SCL <= '1'; ready<='1'; I2Cstate := I2Cstate+1; when 1=> if Start = '1' and ready = '1' then SDA <= '0'; ready<='0'; I2Cstate := I2Cstate+1; else I2Cstate := 1; end if; when 2 => SCL<='1'; I2Cstate := I2Cstate+1; wen 3 => -- Kompassadresse C0 case adr_cnt is when 15|13|11|9|7|5|3 => SCL <= '0'; SDA <= Adr((adr_cnt-1)/2); adr_cnt := adr_cnt-1; when 14|12|10|8|6|4|2 => SCL <= '1'; adr_cnt := adr_cnt-1; when 1 => SCL <= '0'; SDA <= Adr(adr_cnt-1); adr_cnt := adr_cnt-1; when 0 => SCL <= '1'; adr_cnt := 15; I2Cstate := I2Cstate+1; end case; when 17 => SCL <= '0'; I2Cstate := I2Cstate+1; when 4|9|19|24 => SCL <= '0'; SDA <= 'Z'; I2Cstate := I2Cstate+1; when 5|10|20|25 => -- ACK ACK <= SDA; SCL <= '1'; if ACK = '0' then ACK <='1'; SCL <='0'; I2Cstate := I2Cstate+2; end if; when 6|7|11|12|16|21|26|27|29 => I2Cstate := I2Cstate+1; when 8 => -- Registeradresse (1=Kompass Wert) case adr_cnt is when 15|13|11|9|7|5|3 => SCL <= '0'; SDA <= RegNr((adr_cnt-1)/2); adr_cnt := adr_cnt-1; when 14|12|10|8|6|4|2 => SCL <= '1'; adr_cnt := adr_cnt-1; when 1 => SCL <= '0'; SDA <= RegNr(adr_cnt-1); adr_cnt := adr_cnt-1; when 0 => SCL <= '1'; adr_cnt := 15; I2Cstate := I2Cstate+1; end case; when 22 => SCL <= '0'; SDA <= '0'; I2Cstate := I2Cstate+1; when 13|30 => SDA <= '1'; -- repeated Startbit I2Cstate := I2Cstate+1; when 14|28 => SCL <= '1'; I2Cstate := I2Cstate+1; when 15 => SDA <= '0'; I2Cstate := I2Cstate+1; when 18 => -- Kompassadresse C1 (gesetztes R/W Bit) case adr_cnt is when 15|13|11|9|7|5|3 => SCL <= '0'; SDA <= AdrRW((adr_cnt-1)/2); adr_cnt := adr_cnt-1; when 14|12|10|8|6|4|2 => SCL <= '1'; adr_cnt := adr_cnt-1; when 1 => SCL <= '0'; SDA <= AdrRW(adr_cnt-1); adr_cnt := adr_cnt-1; when 0 => SCL <= '1'; adr_cnt := 15; I2Cstate := I2Cstate+1; end case; when 23 => -- Kompass Wert lesen SDA <= 'Z'; case adr_cnt is when 15|13|11|9|7|5|3 => SCL <= '0'; cmps((adr_cnt-1)/2) <= SDA; adr_cnt := adr_cnt-1; when 14|12|10|8|6|4|2 => SCL <= '1'; adr_cnt := adr_cnt-1; when 1 => SCL <= '0'; cmps(adr_cnt-1) <= SDA; adr_cnt := adr_cnt-1; when 0 => SCL <= '1'; adr_cnt := 15; i2csig <= cmps; -- Kompasswert übergeben I2Cstate := I2Cstate+1; end case; when 31 => I2Cstate := 0; end case; end if; end process;
Nur kurz überflogen, aber das hier gibt mir zu denken:
1 | when 5|10|20|25 => -- ACK |
2 | ACK <= SDA; SCL <= '1'; |
3 | if ACK = '0' then |
4 | ACK <='1'; |
5 | SCL <='0'; |
6 | I2Cstate := I2Cstate+2; |
7 | end if; |
Du hast hier einen Takt Latency, ist dir das Verhalten von Signalen in einem Prozess klar? ACK wird gelesen, aber die Abfrage wird auf den alten ACK-Wert gemacht! Das hier macht genau das selbe:
1 | when 5|10|20|25 => -- ACK |
2 | if ACK = '0' then |
3 | ACK <='1'; |
4 | SCL <='0'; |
5 | I2Cstate := I2Cstate+2; |
6 | end if; |
7 | ACK <= SDA; SCL <= '1'; |
Hast du das gewollt? Ähnliches kann ich mir auch mit dem Tristate-Schalten vorstellen... Solche Latency-Geschichten findest du am ehesten mit einer Simulation der FSM. Hast du die schon simuliert?
Hallo! Ja, mit den Acknowledges hab ich schon ein wenig hin und her geändert, daher wohl grade die nicht so tolle Variante. die if-Anweisung kann dort meines Erachtens auch komplett weggelassen werden, die Zuweisung ACK <= SDA ist so gewollt. Ich habe mir die Signale per Logic Analyzer angeschaut, es sah alles so aus, wie es sein soll, richtiger Takt und SDA / SCL haben die Korrekten Werte. Ich habe grade zufällig noch was interessantes herausgefunden, was mir einen neuen Ansatz geben könnte.. Wenn ich die 1,8k Pull-Ups zwischen SDA /SCL und +5V kurz entferne und wieder verbinde, wird mir ein Wert angezeigt, der durchaus richtig sein könnte. sobald die Pull-Ups wieder drin sind, bleibt der Wert fest stehen und egal wie ich den Kompass drehe, es verändert sich nichts. Wenn ich aber an einer geänderten Position wiederum kurz die Pull-Ups entferne und kurz darauf wieder verbinde, dann habe ich auch einen geänderten Wert, der auch durchaus realistisch ist. Das wiederum würde bedeuten, dass die I2C Schnittstelle korrekt arbeitet und es anscheinend ein Problem mit den Pegeln gibt.. da muss ich mich nochmal ein wenig mit beschäftigen..
> habe hier ein Spartan 3 Board ... > ... die 1,8k Pull-Ups zwischen SDA /SCL und +5V kurz entferne Das mit den 5V und der IO-Spannung hast du beachtet? Der S3 ist nicht 5V IO-tolerant. Gut, mit den Pullups kann das noch gehen, aber "schön" schreibt sich anders...
Also sollte ich lieber eine externe 5V Spannungsquelle nehmen und die nicht vom Spartan "anzapfen"?
> Also sollte ich lieber eine externe 5V Spannungsquelle nehmen und die > nicht vom Spartan "anzapfen"? Die Frage die sich stellt ist: Wo hat dein S3 5 Volt? Am IO-Pin eines S3 haben 5V nichts verloren (wobei handverlesene Ausnahmen nur die Regel bestätigen).
ich glaub jetzt weiß ich, was du meinst. Die Versorgungsspannung des Moduls ist 5V, die nehme ich direkt vom Board über den Connector. Die I/O Pins haben allerdings eine wesentlich geringere Spannung, da hab ich gar nicht dran gedacht.. Schande über mein Haupt! D.h. so wie ich das Verbunden habe, habe ich nen 5V Pullup auf die I/O Leitungen gehauen, was natürlich viel zu hoch ist.. richtig?
> D.h. so wie ich das Verbunden habe, habe ich nen 5V Pullup auf die I/O > Leitungen gehauen, was natürlich viel zu hoch ist.. richtig? Ja. Aber ich denke der wird das Dank der internen Schutzdioden überlebt haben. Allerdings hast du an deinem I2C-Bus keine 5V-Pegel. Das könnte Probleme machen...
das Board hats überlebt, ja. :-) hm, naja, auf jeden Fall scheint hier der Hund begraben zu liegen.. Ich werd auf dieser Ebene mal weitersuchen. Vielen Dank auf jeden Fall schonmal für deine Hilfe!!
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.