¿Por qué mi función I2C está invirtiendo bytes?

Estoy trabajando con un sensor MSP430G2553 y un BMP180. tengo la siguiente funcion en mi programa

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;
}

Que llama a esta otra función

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();
}

código I2C

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) {}
}

Obviamente, se supone que temp[0] almacenará el byte más significativo mientras que temp[1] almacenará el byte menos significativo, pero sucede lo contrario: temp[0] está guardando LSB y temp[1] está guardando MSB. Ya verifiqué los datos I2C con un analizador lógico pero la secuencia es la opuesta a la que imprime mi función.

¿Cuál es el orden de bytes de su compilador? ¿Big Endian o Little Endian? En lugar de convertir dos bytes en un uint16, escriba código para determinar explícitamente el orden de los bytes, por ejemplo: uint16_t val = byte[0] << 8 + byte[1];
@Kartman Es little endian.
"Obviamente, se supone que temp[0] almacena el byte más significativo mientras que temp[1] almacenará el byte menos significativo" - ¿Por qué es esto tan obvio ? Así no es como funciona una máquina little-endian. El byte 'menos significativo' se almacena en la memoria con el valor de dirección más bajo. Esto es perfectamente normal. ¿Puede explicar por qué es un problema para usted?
@brhans Según tengo entendido, no importa si mi microcontrolador es little-endian o no porque dentro de la función 'leer_2_bytes_bmp180' le digo que almacene el primer byte en v[0] y el segundo en v [1].
Hace una diferencia qué endianess es su cpu/compilador. ¿Está alineada la palabra de valor de su puntero? Paso a paso con un depurador o simulador para ver lo que realmente está sucediendo.
¿Cuál de las líneas printf te está dando resultados inesperados?
@brhans, la clave aquí es que recepcionI2Cse supone que produce un byte a la vez en el orden en que son enviados por el sensor, que es MSB primero.
@DamienD Todos ellos. t[1] está imprimiendo 110 que viene primero en la secuencia I2C mostrada por el analizador lógico, t[0] está imprimiendo un valor que siempre está cambiando y viene segundo y t está haciendo la operación con los bytes invertidos.
Hipótesis alternativa: los bytes no se intercambian, están apagados por uno y el primer byte es basura. ¿Puede mostrar su código I2C y los valores de las constantes de registro?
@DamienD No estoy en casa ahora, así que no puedo agregar mi código I2C por ahora, pero aquí está esta otra publicación que abrí en reddit donde muestro mis funciones I2C: reddit.com/r/embedded/comments/onqww5/…
Bastante seguro de que el problema está en su código I2C. Eche un vistazo aquí para verificar dos veces: stackoverflow.com/a/62906449
@DamienD Voy a mover la condición STOP para ver qué sucede. Gracias.
Yo apostaría por algo así. Algún problema en el flujo de I2C que hace que el búfer de lectura se intercambie en el momento equivocado o demasiadas veces.
@DamienD Tenías razón. Los bytes no se intercambiaron y hubo un problema con el ciclo I2C.
¡Me alegro de que pudieras arreglarlo!

Respuestas (2)

Hubo un problema con la condición STOP. En la hoja de datos dice que en caso de que quiera leer un solo byte, envíe la condición STOP antes de leer, pero no explica qué hacer cuando está leyendo más de un byte, y es por eso que estaba enviando el STOP después de leer el último byte en este caso. Entonces, aparentemente el sensor enviaba un byte adicional después de cada ciclo I2C. Este byte no se guardaba en una variable, sino que se guardaba en el registro RXBUF, y cada vez que comenzaba un nuevo ciclo se le asignaba este valor a v[0], mientras que a v[1] se le asignaba el byte que se suponía que era el primero. one.Esta es la única función que necesitaba cambiar

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);        
}

Envío un valor verdadero a recepcionI2C para indicar que este es el último byte que se leerá y eliminé finI2C porque la condición STOP se envía en la última llamada de recepcionI2C .

No puedo ver ningún defecto evidente. Probaría una función read_uint16() que usa un idioma diferente para comparar los resultados:

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

También aconseje usar 0x%xcomo formato printf para depurar problemas de orden de bytes.