I2C Master: Wie liest man mit wiederholter Startbedingung?

  1. Ich habe einen PIC16F18875 auf einem Board, auf dem er als I2C-Master konfiguriert ist.
  2. 7-Bit-Adressierung, 100-kHz-Takt
  3. Auf dem Bus befinden sich mehrere verschiedene I2C-Slave-Geräte.
  4. verwende MPLAB X IDE mit MCC-Plugin
  5. Verwenden des von MCC generierten Codes zum Interagieren mit den Peripheriegeräten
  6. Ich kann die meisten Slave-ICs auf der Platine lesen, wie einige ADCs, und erhalte vernünftige Werte zurück
  7. Der UCD9090 PMIC gibt mir jedoch nur 0xFF zurück, egal welches Register zu lesen versucht wird
  8. Oszilloskop zeigt eine gute Signalintegrität
  9. Wenn ein I²C eines RaspberryPi an denselben Bus angeschlossen ist, kann ich den 9090 problemlos lesen
  10. der Logikanalysator zeigt, dass der Slave den zurückzulesenden Wert sendet, ein nack und der letzte Stopp mit 36ms (Milli) Verzögerung nach dem Adresslesen (siehe Bild unten)
  11. es wird gezeigt, dass der verwendete Bibliothekscode ein START+STOP erzeugt, nachdem die Adress- und Register#-Bytes gesendet wurden
    • mit i2c_masterOperation(), i2c_setBuffer() usw
  12. während die Anzeige des RaspberryPi-Logikanalysators zeigt, dass es den REPEATED START macht
  13. Dementsprechend benötigen einige Slave-Geräte einen WIEDERHOLTEN START anstelle von STOP + START, um lesbar zu sein
  14. daher sieht es für mich so aus, als wäre das das Problem

Nun, aus der von MCC generierten I2C-Schnittstellen-Header-Datei sehe ich keine Möglichkeit, dem Code mitzuteilen, dass er die Methode des wiederholten Starts anstelle von Stopp-Start verwenden soll. Habe auch nichts Konfigurierbares auf der spärlich gefüllten I2C-Seite in MCC gesehen. Also, bevor ich in autp-generiertem Code rumfummele ...

Gibt es eine Möglichkeit, das "richtig" zu machen? Andernfalls muss ich wohl ihren Bibliothekscode durchsuchen und herausfinden, wie ich ihn ändern kann, um das Notwendige zu tun.

Eine Illustration des Versuchs, den UCD9090 mit MCC I2C lib zu lesen

Update : Was ich versucht habe, nachdem ich von brhans auf die Statusmaschine aufmerksam gemacht wurde, die die Art der setCallbackX-Funktionen ändert, ist Folgendes:

i2c_error_t i2c_readRegRepeatStart(uint8_t reg, void* data, uint8_t size)
{       
    i2c_error_t ret;    

    __i2c_buf.reg = reg;
    i2c_setBuffer(&__i2c_buf.reg, 1);
    i2c_setDataCompleteCallback( i2c_restartRead, 0 );
    ret = i2c_masterOperation(false); // writing the start,addr,reg,restart part
    i2c_setDataCompleteCallback( i2c_returnStop, 0 );
    if (ret == I2C_NOERR)
    {        
        i2c_setBuffer(data, size);
        ret = i2c_masterOperation(true); // reading the data bytes
    }
    return (ret);
}

Ich nenne das so: i2c_readRegRepeatStart( registerNum, &someUshortVar, 2 ). Dieser Code erzeugt eine Sequenz auf dem Bildschirm des Logikanalysators, die gut beginnt:

  • Start
  • W, Adr, Quit
  • W, Daten, Bestätigung
  • Repeat Start - gut, das wollte ich sehen, anstatt Stop/Start
  • R, Adr, Quit
  • R, Daten, Bestätigung
  • R, Daten, Bestätigung

So weit, so gut, oder? Naja fast. Anstatt jetzt ein STOP, folgt hier:

  • R, Daten, Bestätigung ( 19 Mal )
  • R, Data, Nack (einmal)
  • Stoppen
  • Start
  • R, Adr, Quit
  • R, Daten, Bestätigung
  • R, Daten, Bestätigung
  • Stoppen

Ich habe noch nicht herausgefunden, was los ist. Es scheint im Prinzip, was getan werden soll, und ich habe zurückverfolgt, wo der Pufferzeiger und die Größe in der MCC-Bibliothek festgelegt sind, und es hat eindeutig den Wert 2 (Größe), dann wird der Treiber mit der Durchführung der I2C-Operation beauftragt , und es macht diese seltsamen Sachen.

Oberflächlich sieht es so aus, als müssten Sie I2C_SetDataCompleteCallbackdie FSM-Sequenz von der Standardeinstellung auf ändern, I2C_CallbackReturnStopbevor I2C_CallbackRestartReadSie den I2C-Betrieb starten.
Warum schnüffeln Sie nicht im Bus nach der Raspberry Pi-Instanziierung, da das funktioniert, und sehen, was los ist?
@brhans: Ah, also gibt es eine Möglichkeit, dies zu ändern, die ich übersehen habe ... interner Rückruf, das ist lustig. Werde versuchen! @ Scott: Habe ich, steht in der vielleicht etwas wortreichen Liste, Punkt 12.
Es scheint, dass wir unterschiedliche Bibliotheken haben, Dinge werden unterschiedlich benannt, aber ich sehe ähnliche Elemente. Habe es noch nicht zum schnellen Probieren hinbekommen. Werde weiter recherchieren.
Noch nicht da, siehe aktualisierten Beitrag

Antworten (1)

Das hat geholfen. Habe es aufgegeben, dafür das MCC-Zeug zu verwenden, außer dem zusammengeklickten grundlegenden Peripherie-Init-Zeug. ( Beachten Sie, dass im offiziellen Microchip-Forum gesagt wurde, dass sich die MCC-I2C-Bibliothek derzeit ändert und nicht viel getestet wird. ) Ich habe im Grunde nur einen weiteren Parameter hinzugefügt, um anzugeben, ob ich stop,start verwenden möchte; oder Neustart (auch bekannt als wiederholte Startbedingung), wodurch es so gemacht wird, dass für "Neustart" die Schreibblockroutine das STOP am Ende weglässt und die Leseblockroutine restart() anstelle von start() aufruft. Beachten Sie auch, dass dieser Code linksbündige Slave-Adressen erwartet (Lshift 1).

http://picforum.ric323.com/viewtopic.php?f=76&t=755#

Hier ein Auszug der relevantesten Sachen, inkl. diejenigen meiner Änderungen, die ich erwähnt habe - Sie müssen möglicherweise Dinge ändern, um die richtigen Registerstrukturen vor den Registernamen in diesem Code zu qualifizieren, wenn Sie auch den von MCC generierten Init-Code verwenden, obwohl Sie MCC dann nicht für das eigentliche Geschäft verwenden. Wenn Sie die read-block-Routine verwenden und repeatStart=true übergeben, wird anstelle von stop,start eine wiederholte Startbedingung ausgeführt. Andernfalls wird es stoppen, starten. Damit konnte ich das Gerät lesen, das zuvor das Lesen verweigern würde, sodass die Frage beantwortet ist - nur ohne die MCC-I2C-Zustandsmaschine zu verwenden, sondern sie mit direktem Registerzugriff zu umgehen.

// Send an I2C START
// Return 0 if all ok, 1 if bus collision
__bit i2c_start(void)
{
    BCLIF = 0;  //Clear 'Bus collision" flag
    SEN = 1;    //initiate a START cycle
    while (SEN);    //wait until it has been sent
    return BCLIF;   //return value of BCLIF flag
}

// Send an I2C STOP
void i2c_stop(void)
{
    PEN = 1;    //initiate a STOP cycle
    while (PEN);    //wait until it has been sent
}

// Send an I2C REPEATED START
void i2c_restart(void)
{
    RSEN = 1;    //initiate a REPEATED START cycle
    while (RSEN);    //wait until it has been sent
}

//Send one byte. Return 0 if ACK received, or 1 if NAK received
__bit i2c_sendbyte(unsigned char dat)
{
    SSPBUF = dat;
    while (R_W);    //wait until byte sent and ACK/NAK received
    return ACKSTAT;
}

//Receive one byte. ackflag=0 to send ACK, or 1 to send NAK in reply
unsigned char i2c_recvbyte(unsigned char ackflag)
{
    RCEN = 1;   // initiate a RECEIVE cycle
    ACKDT = ackflag;    //specify if we should send ACK or NAK after receiving
    while (RCEN);   //wait until RECEIVE has completed
    ACKEN = 1;  //initiate an ACK cycle
    while (ACKEN);  //wait until it has completed
    return SSPBUF;
}

//Send an array of data to an I2C device.
//Return 0 if all OK, 1 if bus error, 2 if slave address NAK, 3 if slave register NAK, 4 if slave data NAK
unsigned char i2c_writeblock(unsigned char slave_address, unsigned char start_reg, unsigned char buflen, const unsigned char * bufptr, bool stop)
{
    if (i2c_start() )   //send a start, and check if it succeeded
        return 1;   //abort if bus collision
    //send the I2C slave address (force R/W bit low)
    if (i2c_sendbyte(slave_address & 0xFE))
    {
        i2c_stop(); //if address was NAKed, terminate the cycle
        return 2;   //and return error code
    }
    //send the device register index
    if (i2c_sendbyte(start_reg))
    {
        i2c_stop(); //if register was NAKed, terminate the cycle
        return 3;   //and return error code
    }
    //send the data. buflen might be zero!
    for (; buflen>0; --buflen)
    {
        if (i2c_sendbyte(*bufptr++))
        {
            i2c_stop(); //if register was NAKed, terminate the cycle
            return 4;   //and return error code
        }

    }
    if (stop)
      i2c_stop();
    return 0;   //no error
}

//Receive an array of data from an I2C device.
//Return 0 if all OK, 1 if bus error, 2 if slave address NAK, 3 if slave register NAK
unsigned char i2c_readblock(unsigned char slave_address, unsigned char start_reg, unsigned char buflen, unsigned char * bufptr, bool repeatStart)
{
    //do a dummy zero length write cycle to set the register address
    unsigned char retval = i2c_writeblock(slave_address, start_reg,0,0, !repeatStart);
    if (retval)
    {
        return retval;  //abort if there was an error
    }

    if (repeatStart)
      i2c_restart(); // restart can't fail - if bus remains asserted, there can be no collision
    else
    //now start the READ cycle
      if (i2c_start() )   //send a start, and check if it succeeded
        return 1;   //abort if bus collision

    //send the I2C slave address (force the R/W bit high)
    if (i2c_sendbyte(slave_address | 0x01))
    {
        i2c_stop(); //if address was NAKed, terminate the cycle
        return 2;   //and return error code
    }
    //receive the data.
    for (; buflen>0; --buflen)
    {
        unsigned char ackflag = (buflen == 1);   //1 if this is the last byte to receive => send NAK
        *bufptr++ = i2c_recvbyte(ackflag);
    }
    i2c_stop();
    return 0;   //no error
}