Entendiendo el código fuente de la comunicación I2C

1

Entonces, en mis estudios universitarios, recibimos algunos ejemplos de código fuente que funcionan con un sensor de luz OPT3001. Nuestros estudios no se centran en las tareas de ingeniería en general, pero esta es una conferencia que aborda algunos de los temas de Ingeniería Eléctrica. Estudio Física si alguien se pregunta.

No estoy familiarizado con los protocolos de comunicación de Ingeniería Eléctrica, pero he leído algunas guías en línea sobre la comunicación I2C para ayudarme a entender el código fuente dado. Solo tengo algunos conocimientos de programación de Android, pero también autodidacta.

Me gustaría entender más, es por eso que te pregunto si puedes ayudarme. Solo copiaré una función de todo el código para que no sea demasiado larga y si pueden guiarme con algunos comentarios en cada línea, ¡qué bien estoy muy agradecido!

void UpdateLight(void)
{
    uint8_t error = 0;
    // start conversion
    I2C_Start();
    error |= I2C_Write(OPT_ADDR_W);
    error |= I2C_Write(OPT_CONFIG_REG);
    error |= I2C_Write(OPT_CONFIGURATION_H);
    error |= I2C_Write(OPT_CONFIGURATION_L);
    I2C_Stop();

    // wait for conversion
    _delay_ms(150);

    // set result register
    I2C_Start();
    error |= I2C_Write(OPT_ADDR_W);
    error |= I2C_Write(OPT_RESULT_REG);
    I2C_Stop();

    // read data and update scratchpad
    I2C_Start();
    error |= I2C_Write(OPT_ADDR_R);
    if (error == 0)
    {
        scratchpad[1] = I2C_Read(1);
        scratchpad[0] = I2C_Read(0);
    }
    else
    {
        // report error value
        scratchpad[1] = 0xFF;
        scratchpad[0] = 0xFF;
    }
    I2C_Stop();
}

Algunas definiciones que se utilizan en el código anterior:

volatile uint8_t scratchpad[9] = {0x50, 0x05, 0x0, 0x0, 0x7f, 0xff, 0x00, 0x10, 0x0};   // initial scratchpad

Este no es un requisito para saberlo en mi universidad, es solo un ejercicio en el que lo realiza previamente el profesor y todo lo que necesitamos hacer es usar este sensor de luz para medir la iluminación. Pero quería enseñarme algo más si es posible.

Estoy familiarizado con la Hoja de datos de OPT3001, por lo que entiendo un poco las direcciones necesarias, pero también no completamente.

Aquí hay un enlace a la hoja de datos si alguien necesita: hoja de datos OPT3001

Agradecido por cualquier ayuda / comentario / entrada. Espero que esto aún se aplique a las reglas de StachExchange, no sabía dónde más publicar esta pregunta.

    
pregunta David Kasabji

3 respuestas

3

Parece ser muy evidente, pero ese debe ser mi sesgo de confirmación; D

Primero, las líneas que comienzan con // son Comentarios. En su totalidad para su beneficio. Como es cualquier texto después de un # (que no se usa aquí).

I2C Start hand shake
Write device's Address
Write device's configuration address
Write the higher byte of the configuration desired
Write the lower byte of the configuration desired.
I2C Stop hand shake

Wait a bit.

Start
write device address
switch to device's results address
stop

Start
Read from device address
if no error
read the first byte from the results
read the second byte
Stop.

Tenga en cuenta que el OPT3001 tiene registros de 16 bits. Eso es dos bytes de 8 bits. El estándar i2c funciona en bytes de 8 bits. Así que tienes que leer dos bytes para obtener los resultados completos. Normalmente, con un orden de bytes más significativos, los bits más altos (15-8) se denominan alto / primer byte.

I2C funciona dirigiéndose al dispositivo en modo de escritura, diciéndole que cambie a un registro / dirección interna, luego escriba o lea desde ese punto. Este dispositivo no es diferente. Eso es lo básico de I2C

    
respondido por el Passerby
2

Un patrón típico para comunicarse con dispositivos I2C es que para escribir información en el dispositivo se debe emitir un "Inicio de I2C" en el bus, generar un byte que diga que se quiere escribir en un dispositivo en particular junto con uno o dos bytes. que identifican una dirección dentro del dispositivo que uno quiere escribir, y luego seguir todo eso con los datos que se escribirán. Después de hacer todo eso, uno debe emitir una "Parada I2C" al autobús. Cada vez que se escribe un byte en un dispositivo I2C, el controlador informará si el dispositivo indicó que lo ha recibido. Si el dispositivo no indica la recepción exitosa de ningún byte, toda la operación debe considerarse un fracaso.

Para leer desde dispositivos I2C, uno comienza por "escribir" la dirección desde la que se desea leer, utilizando el procedimiento anterior pero sin enviar ningún dato después de la dirección. Después de hacer eso, uno debería emitir un "I2C Stop" y "I2C Start" seguido de un byte que dice que uno querrá leer el dispositivo en lugar de escribirlo. A su vez, esto debe ir seguido de solicitudes para leer los datos y luego una "Parada I2C". Como se implementó comúnmente, el controlador debe indicar a la rutina de lectura de bytes si cada byte de datos solicitado será el último. Parece que esta rutina particular de lectura de bytes utiliza un valor de parámetro de 1 para indicar que seguirán más datos, y 0 para indicar la última solicitud de una transacción.

No sé cómo exactamente tu implementación está haciendo todo, pero parece que sigue el patrón descrito anteriormente; con suerte eso te ayudará a comenzar.

    
respondido por el supercat
1
uint8_t error = 0;   //set error to 0, clear it
// start conversion
I2C_Start();   //call function to send start bit onto I2C bus to signify start of transaction
error |= I2C_Write(OPT_ADDR_W);  //write device id of slave onto bus with write bit set remember that I2C is 7 bits with the last bit for read or write (well there are 10bit but lets assume this is 7 bit.
error |= I2C_Write(OPT_CONFIG_REG);//put the address of the 16bit config register in the slave onto the bus
error |= I2C_Write(OPT_CONFIGURATION_H);//put the high byte of the config register onto the bus to set it
error |= I2C_Write(OPT_CONFIGURATION_L);//put the low byte of the config register onto the bus to set it
I2C_Stop();//put the stop bit onto the I2C bus to signify the end of the transmission

// wait for conversion
_delay_ms(150); //wait for your slave device to do what it does

// set result register
I2C_Start();  //start another transaction on the I2C bus
error |= I2C_Write(OPT_ADDR_W); //put the device id of slave on the bus again with write bit set
error |= I2C_Write(OPT_RESULT_REG);//send slave the address for the result register, you are in effect prepping the device by giving it this address, in the next step you will read from it
I2C_Stop();//put the stop bit out on the bus again

// read data and update scratchpad
I2C_Start();//now put the start bit out again, I can't remember this may technically be a restart
error |= I2C_Write(OPT_ADDR_R); //put the device ID of the slave out onto the bus with the read bit set.  You are telling the sensor I now want to read the 16bit result value from you
if (error == 0)//if none of the previous function calls resulted in any errors at all
{
    scratchpad[1] = I2C_Read(1);//read 1 byte from the I2C bus and store in scratchpad
    scratchpad[0] = I2C_Read(0);//read 2nd byte from the I2C bus and store in scratchpad
}
else
{   //whoops you had some kind of error previosly
    // report error value
    scratchpad[1] = 0xFF;//set scratch pad to known value
    scratchpad[0] = 0xFF;//set scratch pad to known value
}
I2C_Stop();//put stop bit out to end transaction no matter what otherwise slave will think transaction never ended.
}
    
respondido por el Some Hardware Guy

Lea otras preguntas en las etiquetas