Kompassmodul "cmps03" per I2C in VHDL ansprechen

Gast #1495866
Lesenswert?

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;
Moderator (Firma: Titel) Persönliche Seite #1495883
Lesenswert?

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?
Gast #1495910
Lesenswert?

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..
Gast #1495959
Lesenswert?

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?
Gast #1496008
Lesenswert?

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!!

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren