Warum invertiert meine I2C-Funktion Bytes?

Ich arbeite mit einem MSP430G2553 und einem BMP180-Sensor. Ich habe folgende Funktion in meinem Programm

uint16_t leer_temp_des() {
    unsigned char temp[2];
    escribir_com_bmp180(CTRL_MEAS, LEER_TEMP);
    retardo_ms(RET_LEC_TEMP);
    leer_2_bytes_bmp180(OUT_MSB, temp);
    printf("t0 %u\n", (uint16_t) temp[0]);
    printf("t1 %u\n", (uint16_t) temp[1]);
    uint16_t temperatura = ((uint16_t) (temp[0] << 8) | (uint16_t) temp[1]);
    printf("t %u\n", temperatura);
    //temperatura <<= 8;
    //temperatura |= temp[1];
    return temperatura;
}

Was diese andere Funktion aufruft

void leer_2_bytes_bmp180(uint8_t reg, uint8_t *v) {
    inicioI2C(DIR_I2C_BMP180);
    envioI2C(reg);
    reinicioI2C(DIR_I2C_BMP180, false);
    *v = recepcionI2C(false);
    *(v + 1) = recepcionI2C(false);
    finI2C();
}

I2C-Code

void inicializarI2C() {
    UCB0CTL1 |= UCSWRST;
    UCB0CTL0 &= ~(UCA10 | UCSLA10 | UCMM);
    UCB0CTL0 |= (UCMST | UCMODE1 | UCMODE0 | UCSYNC);
    UCB0CTL1 |= UCSSEL1 | UCSSEL0;
    UCB0CTL1 |= UCTR;
    UCB0STAT = 0;
    UCB0BR0 = 160;
    UCB0BR1 = 0;
    UCB0I2COA = 0x1A;
    UCB0I2CSA = 0x77;
    P1DIR |= BIT6;
    P1SEL |= BIT6;
    P1SEL2 |= BIT6;
    P1SEL |= BIT7;
    P1SEL2 |= BIT7;
    UCB0CTL1 &= ~UCSWRST;
}

void inicioI2C(uint8_t dir) {
    UCB0I2CSA = dir;
    UCB0CTL1 |= UCTR;
    UCB0CTL1 |= UCTXSTT;
    while((IFG2 & UCB0TXIFG) == 0) {}

    //while((UCB0STAT & UCNACKIFG) == 0) {}
}

void reinicioI2C(uint8_t dir, bool rw) {
    UCB0I2CSA = dir;
    if(rw) {
        UCB0CTL1 |= UCTR;
    }
    else {
        UCB0CTL1 &= ~UCTR;
    }
    //UCB0CTL1 |= UCTR;
    UCB0CTL1 |= UCTXSTT;
    while((UCB0CTL1 & UCTXSTT) != 0) {
        if((UCB0STAT & UCNACKIFG) != 0) {
            break;
            /*UCB0STAT &= ~UCNACKIFG;
            UCB0CTL1 |= UCTXSTP;
            while((UCB0CTL1 & UCTXSTP) != 0) {}*/
        }
    }
}

uint8_t envioI2C(uint8_t dato) {
    //UCB0CTL1 |= UCTR;
    //UCB0I2CSA = dir;
    //UCB0CTL1 |= UCTXSTT;
    UCB0TXBUF = dato;
    while((UCB0CTL1 & UCTXSTT) != 0) {}
    if((UCB0STAT & UCNACKIFG) != 0) {
        UCB0STAT &= ~UCNACKIFG;
        UCB0CTL1 |= UCTXSTP;
        //while((UCB0CTL1 & UCTXSTP) != 0);
        return 0;
    }
    while((IFG2 & UCB0TXIFG) == 0);
    return 1;
    //retardo_ms(1);
}

uint8_t recepcionI2C(bool parar) {
    //UCB0CTL1 =& ~UCTR;
    //UCB0I2CSA = dir;
    //UCB0CTL1 |= UCTXSTT;
    if(parar) {
        if((UCB0STAT & UCNACKIFG) != 0) {
            UCB0STAT &= ~UCNACKIFG;
        }
        UCB0CTL1 |= UCTXSTP;
    }
    while(!(IFG2 & UCB0RXIFG));
    uint8_t dato = UCB0RXBUF;
    //while(!(IFG2 & UCB0RXIFG));
    if(parar) {
        while((UCB0CTL1 & UCTXSTP) != 0) {}
    }
    return dato;
}

void finI2C() {
    if((UCB0STAT & UCNACKIFG) != 0) {
        UCB0STAT &= ~UCNACKIFG;
    }
    UCB0CTL1 |= UCTXSTP;
    while((UCB0CTL1 & UCTXSTP) != 0) {}
}

Offensichtlich soll temp[0] das höchstwertige Byte speichern, während temp[1] das niedrigstwertige Byte speichern wird, aber das Gegenteil passiert: temp[0] speichert LSB und temp[1] speichert MSB. Ich habe die I2C-Daten bereits mit einem Logikanalysator überprüft, aber die Sequenz ist das Gegenteil von dem, was meine Funktion druckt.

Was ist die Byte-Reihenfolge Ihres Compilers? Big-Endian oder Little-Endian? Anstatt zwei Bytes in ein uint16 umzuwandeln, schreiben Sie Code, um die Byte-Reihenfolge explizit zu bestimmen – zB: uint16_t val = byte[0] << 8 + byte[1];
@Kartman Es ist Little Endian.
"Offensichtlich soll temp[0] das höchstwertige Byte speichern, während temp[1] das niedrigstwertige Byte speichern wird" - Warum ist das offensichtlich ? So funktioniert eine Little-Endian-Maschine nicht. Das „niederwertige“ Byte wird im Speicher mit dem niedrigeren Adresswert abgelegt. Das ist völlig normal. Können Sie erklären, warum das für Sie ein Problem ist?
@brhans Soweit ich weiß, spielt es keine Rolle, ob mein Mikrocontroller Little-Endian ist oder nicht, da ich ihm in der Funktion 'leer_2_bytes_bmp180' sage, dass er das erste Byte in v[0] und das zweite in v speichern soll [1].
Es macht einen Unterschied, welche Endianess Ihre CPU/Ihr Compiler hat. Ist Ihr Zeigerwert wortausgerichtet? Gehen Sie mit einem Debugger oder Simulator durch, um zu sehen, was wirklich passiert.
Welche der printf-Zeilen liefert Ihnen unerwartete Ergebnisse?
@brhans Der Schlüssel hier ist, dass er recepcionI2Cjeweils ein Byte in der Reihenfolge liefern soll, in der sie vom Sensor gesendet werden, der zuerst MSB ist.
@DamienD Alle von ihnen. t[1] druckt 110, das in der vom Logikanalysator gezeigten I2C-Sequenz an erster Stelle steht, t[0] druckt einen Wert, der sich ständig ändert und an zweiter Stelle steht, und t führt die Operation mit invertierten Bytes aus.
Alternativhypothese: Die Bytes werden nicht vertauscht, sie sind um eins verschoben und das erste Byte ist Müll. Können Sie Ihren I2C-Code und die Werte der Registerkonstanten zeigen?
@DamienD Ich bin jetzt nicht zu Hause, also kann ich meinen I2C-Code vorerst nicht hinzufügen, aber hier ist dieser andere Beitrag, den ich in Reddit geöffnet habe, wo ich meine I2C-Funktionen zeige: reddit.com/r/embedded/comments/onqww5/…
Ziemlich sicher, dass das Problem in Ihrem I2C-Code liegt. Schauen Sie hier, um dies zu überprüfen: stackoverflow.com/a/62906449
@DamienD Ich werde die STOP-Bedingung verschieben, um zu sehen, was passiert. Danke.
Auf sowas würde ich tippen. Ein Problem im I2C-Fluss, das dazu führt, dass der Lesepuffer zur falschen Zeit oder einmal zu oft ausgetauscht wird.
@DamienD Du hattest recht. Die Bytes wurden nicht getauscht und es gab ein Problem mit dem I2C-Zyklus.
Schön, dass du es reparieren konntest!

Antworten (2)

Es gab ein Problem mit der STOP-Bedingung. Im Datenblatt heißt es, dass Sie, falls Sie ein einzelnes Byte lesen möchten, vor dem Lesen die STOP-Bedingung senden, aber es erklärt nicht, was zu tun ist, wenn Sie mehr als ein Byte lesen, und deshalb habe ich das gesendet STOP nach dem Lesen des letzten Bytes in diesem Fall. Anscheinend hat der Sensor also nach jedem I2C-Zyklus ein zusätzliches Byte gesendet. Dieses Byte wurde nicht in einer Variablen gespeichert, sondern im RXBUF-Register, und jedes Mal, wenn ein neuer Zyklus begann, wurde v[0] dieser Wert zugewiesen, während v[1] das Byte zugewiesen wurde, das das erste sein sollte one.This ist die einzige Funktion, die ich ändern musste

void leer_2_bytes_bmp180(uint8_t reg, uint8_t *v) {
    inicioI2C(DIR_I2C_BMP180);
    envioI2C(reg);
    reinicioI2C(DIR_I2C_BMP180, false);
    *v = recepcionI2C(false);
    *(v + 1) = recepcionI2C(true);        
}

Ich sende einen wahren Wert an recepcionI2C, um anzuzeigen, dass dies das letzte Byte ist, das gelesen wird, und ich habe finI2C entfernt , da die STOP-Bedingung im letzten recepcionI2C -Aufruf gesendet wird.

Kann keinen offensichtlichen Mangel erkennen. Ich würde eine read_uint16 () -Funktion ausprobieren, die ein anderes Idiom verwendet, um die Ergebnisse zu vergleichen:

uint16_t val = read_byte();
val <<= 8;
val |= read_byte();

Empfehlen Sie auch die Verwendung 0x%xals printf-Format, um Probleme mit der Byte-Reihenfolge zu beheben.