www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik I2C-Timingproblem?!


Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich versuche verzweifelt einen Drucksensor von VTI (SCP1000) per 
I2C-Interface anzusprechen. Der Sensor hängt an einem MSP430 mit 
I2C-Schnittstelle. Laut Datenblatt des Sensors sollte vor dem Auslesen 
von Druckwerten eine Routine zur Abfrage durchgeführt werden, die 
anzeigt, ob der Sensor richtig initialisiert wurde und ob 
Prüfsummenfehler im integrierten EEPROM aufgetreten sind. Also tue ich, 
wie mir gehießen.

Das Problem: Die Routine gibt beim debuggen im Einzelschrittbetrieb 
keinen Fehler zurück - also alles ok. Lasse ich den Controller 
ungezügelt auf den Bus los, krieg ich nen Fehler für eine falsche 
EEPROM-CRC zurück. Ähnliches auch, wenn ich die Startup-Routine im 
Einzelschrittbetrieb durchmache und später Werte mit vollem Takt 
auslesen oder schreiben will. Dann bleibe ich irgendwo in den 
I2C-Funktionen zum schreiben oder lesen an der Abfrage der 
Interruptflags hängen.

Jetzt drängt sich natürlich der Bustakt auf. Den hab ich aber auch schon 
variiert. Ich habe von 100kHz bis 1kHz schon alles durchgemacht. 
Pullup-Widerstände hängen auch dran: 10k, vielleicht ein bisschen hoch, 
sollten aber dennoch gehen. Den Bus kann ich mit einem Oszi mit 
Bus-Analyzer verfolgen, die Signale schauen sauber aus und es werden 
auch die Start-/Stop- und Acknowledge-Signale richtig bedient.

Ich bin vollkommen überfragt. Vielleicht fällt euch was ein?

Hier sind die wichtigsten Routinen:

In der Main-Funktion wird die Portrichtung gesetzt und der Sensor 
anschließend aus dem Power-Down-Mode genommen und die Zeit abgewartet, 
die er zur Initialisierung braucht. Solange schläft der MSP einen seiner 
Ruhezustände um Energie zu sparen. Dann wird die Initialisierungsabfrage 
aufgerufen:
   //Initialize Ports and Directions for specific sensor
    P6DIR |= SCP_PD;                //Port-pin for power-down pin on SCP as output
    P6OUT |= SCP_PD;                //hold PD-pin high to remain in power down
                                    //mode during the stabilization of the power supplies

    // Initialize Hardware:
    initI2C();

    //Setup timer for periodic wakeup
    TACTL |= (!TASSEL1 + TASSEL0);      //Timer A clock source is ACKL
    TACTL |= (ID1 + ID0);               //Input divider = 8
    TACTL |= TAIE;                      //enable timer interrupt
    

    for (;;){
      P6OUT &= ~SCP_PD;                 //release power down mode for normal operation
      TACCR0 = 0x01FF;                  //wait ~60ms for sensor startup time (FFh = 62,256ms / 1FFh = 125ms)
      START_TIMER;

      // MSP power down (LPM3: all but ACKL disabled), general interrupts enable
      _BIS_SR(GIE + CPUOFF + SCG1 + SCG0);

      if (!checkStartupStatus()) {      //startup fail? -> no error:
(...)
}

Die Funktion "checkStartupStatus()" im Detail:
/*------------------------------------------------------------------------------
//::FUNCTION: checkStartupStatus()
------------------------------------------------------------------------------*/
//  DESCRIPTION:
//  ~~~~~~~~~~~~
/**  \brief Checks whether start-up procedure is finished. The STATUS register
*           (0x07) can be read to verify that start-up procedure is finished.
*           If the STARTUP bit (LSB) of the STATUS register (0x07) is '0', the
*           start-up procedure is finished successfully. If the STARTUP bit of
*           the STATUS register is '1', the start-up procedure is still running.
*
*           The start-up status check includes EEPROM checksum error result
*           check. If the content of DATARD8 is 0x00, EEPROM checksum
*           calculation indicated an error and the start-up procedure is failed.
*           Correct EEPROM checksum calculation result is indicated by content
*           of 0x01 in DATARD8.
*
*    \return 0 - If start-up procedure has finished without errors
*            1 - If start-up procedure has failed with unfinished initialization
*            2 - If start-up procedure has failed with EEPROM checksum error
*            3 - If start-up procedure has failed with both errors
*/
/*----------------------------------------------------------------------------*/

uint8_t checkStartupStatus(void) {
  uint8_t success = 0;

  for (int retries = 0; retries < 6; retries++) {
    ((readI2C(STATUS) & 0x01) == 0 ? (success = 0) : (success = 1));
    if (!success)
      break;
  }

  ((readI2C(DATARD8) & 0x01) == 1 ? (success += 0) : (success += 2));

  return success;
}
Hier wird im Einzelschritt "success = 0" zurückgegeben, bei voller 
Geschwindigkeit "success = 2".

Die Initialisierung des I2C-Ports:
void initI2C(void) {
  P3SEL   |= 0x0A;        // Assign pins to i2c module function

  U0CTL   |= SWRST;       // hold usart in software reset
  U0CTL   |= SYNC;        // select "SYNC" bit and...
  U0CTL   |= I2C;         // ..."I2C" bit to select I2C mode
  U0CTL   &= ~XA;         // 7 bit addresses
  U0CTL   &= ~SWRST;    // Start USART1

  U0CTL   &= ~I2CEN;      // disable I2C mode for further configuration
  I2CTCTL &= ~I2CWORD;    // I2C data register is 8bit
  I2CTCTL &= ~I2CRM;      // number of bytes transmitted is controlled by I2CNDAT
  I2CTCTL = I2CSSEL_2;    // SMCLK as clock source. After PUC ( power up clear)
                          // SMCLK is sourced from DCOCLK with ~800kHz
  I2CPSC = 0x03;          // Set Prescaler. Divide SMCLK / 3 (Values more then 4
                          // are not recommended). Use I2CSCLL and I2CSCLH instead.
//  I2CSCLH = 0xFF;         // set i2c clock high-periode
//  I2CSCLL = 0xFF;         // set i2c clock low-periode
  U0CTL   |= I2CEN;       // I2C is now 8Bit-mode, activated and stuff from above
}
I2CSCLH und I2CSCLL nehme ich, um den Bustakt zu drosseln. Schaut am 
Oszi optimal aus, die High- und Low-Zeiten im Sensordatenblatt werden 
eingehalten.

Und zuletzt die Funktionen zum schreiben und lesen auf dem Bus:
uint8_t readI2C(uint8_t addr2) {
  I2CNDAT = 1;                              // number of bytes to send
  I2CSA = SCP1000_ADDRESS;                  // Slave Adresse
  U0CTL |= MST;                             // Master mode
  I2CTCTL |= I2CSTT+I2CTRX;                 // Start + Transmit mode (no stop bit!)
  while ((I2CIFG & TXRDYIFG) == 0);         // Wait for transmitter to be ready
  I2CDRB = addr2;                            // write register addr. to sca3000
  while ((I2CIFG & ARDYIFG) == 0);

  I2CNDAT = 1;                              // number of bytes to read
  U0CTL |= MST;                             // Master mode
  I2CTCTL &= ~I2CTRX;                       // RX Mode
  I2CTCTL |= I2CSTT + I2CSTP;               // Repeated Start + Stop
  while ((I2CTCTL & I2CSTP) == I2CSTP);     // Wait for Stop Condition
  I2CIFG = 0x00;                            // Clear I2C interrupt Flags
  return I2CDRB;

}


void writeI2C(uint8_t addr, uint8_t data) {
  I2CNDAT = 0x02;                           // Bytes to send (1 for addr. + 1 data byte)
  I2CSA = SCP1000_ADDRESS;                  // Slave Adresse
  U0CTL |= MST;                             // Master mode
  I2CTCTL |= I2CSTT+I2CSTP+I2CTRX;          // Start + Stop + Transmit mode
  while ((I2CIFG & TXRDYIFG) == 0);         // Wait for transmitter to be ready
  I2CDRB = addr;                            // write register addr. to sca3000
  while ((I2CIFG & TXRDYIFG) == 0);         // Wait for transmitter to be ready
  I2CDRB = data;                            // write data addr. to sca3000
//  while ((I2CTCTL & I2CSTP) == 0x02);       // Wait for Stop Condition
  while ((I2CTCTL & I2CSTP) == I2CSTP);       // Wait for Stop Condition
//  I2CIFG = 0x00;                            // Clear I2C interrupt Flags
}

Vielen Dank, dass ihr euch die Zeit nehmt!

Viele Grüße
Daniel

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Steht im Datenblatt des Drucksensors von VTI (SCP1000) was darüber, wie 
lange die EEPROM-CRC-Berechnung im Sensor dauert und ob eine 
Mindestwartezeit eingehalten werden muss?

Ich würde versuchsweise mal eine Wartezeit vor folgender Zeile einbauen:
  ((readI2C(DATARD8) & 0x01) == 1 ? (success += 0) : (success += 2));

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Gedanke kam mir auch und das hab ich gestern schon ausprobiert. 
Keine Wirkung. Trotzdem wollte ich es gerade nochmal machen. 
Komischerweise kommt er jetzt nur bis zur Abfrage des Registers 
"DATARD8" (also Funktion "checkStartupStatus()"
bei
"((readI2C(DATARD8) & 0x01) == 1 ?...")

Dann hängt er in der Funktion "readI2C()"
bei
"while ((I2CIFG & ARDYIFG) == 0)" fest.

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Halt, nee. Jetzt gehts wieder.
Entferne ich die Wartefunktion vor der Prüfsummenabfrage wird die 
Funktion richtig durchlaufen. Warte ich vorher ein paar Zyklen, bleibe 
ich an besagter Stelle hängen.

Komisch, weil gestern lief er auch mit Wartefunktion bis zum 
Rückgabewert.

Ein Kontaktproblem schließe ich aber auch aus, der Aufbau ist auf 
Lochrasterplatine gelötet und die Leitungen habe ich schon 3x 
kontrolliert und durchgeprüft. 100nF hab ich auch an den 
Versorgungsleitungen.

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab jetzt die Hardware auch nochmal neu aufgebaut. Bringt nur leider 
auch nix. Verhält sich genauso wie der alte Aufbau auf Lochraster.

Hat jemand schonmal den I2C-Bus vom MSP430 verwendet? Den Code für die 
Funktionen hab ich mit diversen Application Notes von TI verglichen - 
ich denke also, dass ich den Bus richtig bediene. Kann mich da 
wenigstens jemand bestätigen?

Danke
Daniel

Autor: Eugen Kannsnet (kannsnet)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Daniel
Hast du mittlerweile eine Lösung gefunden? Ich hab nämlich das selbe 
Problem mit dem SCP1000.

Gruß Eugen

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]
  • [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.