¿Fórmula/ecuación de conversión OPT3001?

Estoy luchando por encontrar una fórmula de conversión real para convertir el valor RAW en un resultado que tenga sentido. Estoy usando un sensor OPT3001 de TI y ATTiny25 como MUC.

Encontré un ejemplo en línea, pero no da el valor correcto en Lux, la ecuación utilizada allí es esta:

/*Convert to LUX*/
//extract result & exponent data from raw readings
result = raw&0x0FFF;
exponent = (raw>>12)&0x000F;
lux=0.01*pow(2,eksponent)*result;

Esto da valores incorrectos de acuerdo con mi medidor de lux que tengo como referencia y, por ejemplo, el medidor de lux dice que es 420 lux dentro de la oficina. La ecuación anterior me da el resultado 110 Lux , que no es realista. El sensor funciona bien ya que lo reemplacé por uno nuevo, para asegurarme al 100% de que no es un problema relacionado con el sensor.

El resultado es muy parecido si solo tomo result = raw&0x0FFF;, pero luego falla en valores más altos (cuando coloco mi sensor debajo de la bombilla de la lámpara).

No creo que sea un error dentro de mi código I2C, pero adjuntaré aquí también la parte de mi código para UpdateLight:

// I2C OPT address
#define OPT_ADDR_W  0x88
#define OPT_ADDR_R  0x89

// I2C OPT registers
#define OPT_RESULT_REG  0x00
#define OPT_CONFIG_REG  0x01

// OPT config bits - automatic full scale mode, 100 ms conversion, single-shot
#define OPT_CONFIGURATION_H 0xC2
#define OPT_CONFIGURATION_L 0x00

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

Y esta es mi antigua fórmula que se me ocurrió, pero da resultados aún más ridículos que la anterior de Internet:

   // calculate light ( LUX = (MANTISA << EXPONENT) / 100 )
   light = (int16_t)((float)((uint32_t)((spad[SPAD_MSB] << 8) | spad[SPAD_LSB]) << (uint32_t)((spad[SPAD_MSB] >> 4) & 0x0F)) / (83865.60 / LIGHT_MULTIPLIER));
   debug_printf("L_RAW: %02X%02X\r\n", spad[SPAD_MSB], spad[SPAD_LSB]);
   debug_printf("L_VAL: %d\r\n", light); 

Agregando las líneas de salida de las declaraciones de impresión anteriores:

L: 5460 - 824

L: 5464 - 824

L: 5462 - 824

L: 5462 - 824

El primer valor debe ser RAW MSB y LSB, y el segundo es el valor convertido a Lux, utilizando la fórmula anterior.

Además, me sorprende que dentro de la hoja de datos, TI no proporcionó una fórmula de conversión.

Cualquier ayuda es muy apreciada.

¿Cuál es el valor bruto que está obteniendo? Por cierto, parece (según el ejemplo de conversión) que la representación es en realidad una de punto flotante estándar.
@EugeneSh. Raw1: 5463 , Raw2: 5461 tomada en ambiente Office Light. La ecuación anterior me da un resultado de 148.16Lux, lo cual es incorrecto. Más cerca del resultado real es si solo tomo result = raw & 0x0FFF ... Sin embargo, este falla con un brillo más alto (la lámpara brilla sobre él). Extraño..
Los números parecen sospechosos ya que 5461 en hexadecimal es 0x1555, que alternan ceros y unos. ¿Puede ser que hayas cambiado el SDA y el SCL?
@EugeneSh. mmm Podría ser, pero necesito verificar en el laboratorio nuevamente, no esperaba ese tipo de problema, esperaba que estuviera orientado al software. aquí hay un ejemplo en bruto bajo la luz: 8437 (hexadecimal)
Bueno, si tienes algunos valores diferentes, supongo que la conexión debería estar bien.
Por cierto, no ha proporcionado las palabras de configuración, podría ser que solo esté trabajando con un rango diferente al que está asumiendo.
@EugeneSh. He editado mi pregunta, si eso es lo que buscas. Lo sentimos, todavía no es el mejor con la parte del software.
@EugeneSh. Configuración 0xC200
@Passerby La configuración se ve bien, aunque estoy un poco preocupado por los bits 10: 9. No estoy muy seguro de qué es el modo de "apagado". Tal vez debería cambiarse a "continuo".
@EugeneSh. 10:9 se establece en 01 con esa configuración. Modo de conversión de una toma/única, eso es normal.
@Passerby Ah, cierto. me perdí uno..
Consulte la comunidad E2E de TI , donde los profesionales de TI suelen participar mucho. Y no sé, pero esta pregunta es tal vez la misma que la tuya.

Respuestas (1)

En la página 20de la hoja de datos se da la descripción detallada del cálculo. Del valor de ejemplo dado de los comentarios, 0x5461obtenemos el 0x5valor del exponente, que corresponde a 0.32 lux per LSB. La mantisa es 0x461o 1121 en decimal. El valor resultante es 1121*0.32=358.72luxel que está cerca del rango esperado.

El código de conversión de trabajo:

#include <stdio.h>
#include <stdint.h>

int main(void) {
    uint16_t raw = 0x5461;
    uint16_t result, exponent;
    float lux;

    result = raw & 0x0FFF;
    exponent = (raw>>12) & 0x000F;

    lux=0.01*(1<<exponent)*result;

    printf("%f\n", lux);
    return 0;
}

O ejecutarlo aquí .

Preste mucha atención a los tipos de datos utilizados, es posible que también le hayan causado problemas.

Entonces, ¿piensa que el valor 5461¸ y otros no son valores HEX? ¿Los consideras valores decimales? Pensé que estos eran valores HEX
Todavía me pregunto de dónde vienen esos 110 lux, si pongo los valores incorrectos obtengo solo 27 lux... Pero sí, el endianness es una gran trampa en la transferencia de datos, supongo que todos tropiezan con eso.
No sé, me dices. ¿Cómo los estás leyendo?
He agregado la parte del código donde convierto el valor bruto y luego lo imprimo.
@AndroidNFC si no hay indicación (como hexadecimal, 0x o _h), la mayoría de la gente los leerá como decimal. Si toma esos valores como hexadecimales, termina con 360 lux y no con 148 lux como se indica en su comentario.
Entonces, ¿cuáles son exactamente los resultados de las impresiones?
@Arsenal Mencioné que todavía no soy muy bueno programando, por lo que el código podría ser un poco complicado debido a la ayuda de Internet y otros. Pero según entendí, los valores impresos deberían estar en HEY. Con la ecuación anterior en mi pregunta (la primera), recibo un valor de 148 lux = 0.01*2exp5*463
@EugeneSh. Según tengo entendido y según la hoja de datos, deberían ser los bytes MSB y LSB.
Me refiero a las líneas de salida reales, ¿puedes mostrarlas? De todos modos, el resultado de este número en cualquier caso debería ser 360 lux, que podría estar en el rango.
@AndroidNFC No olvide en su cálculo, que 463están en hexadecimal.
Supongo que la parte endianness de la respuesta es irrelevante, que.
@Arsenal y Eugene He editado mi pregunta agregando el resultado de las declaraciones impresas.
Edité la respuesta para que contenga solo la parte relevante.
@EugeneSh. ¿Podría proporcionarme un código C de la parte de la hoja de datos que publicó? No estoy seguro de cómo seleccionar los 15:12 bits de mi resultado, para poder almacenarlo como exponente, según la hoja de datos.
Mira la actualización.
@EugeneSh. gracias por la actualización. ¿Podría explicarme por qué es necesario realizar las operaciones AND? (Soy nuevo en C, principalmente desarrollo aplicaciones de Android, pero quería hacer un proyecto más electrónico)
Es demasiado amplio para comentarios y fuera de tema para la respuesta. mira aquí
¡Muchas gracias! Aceptaré su respuesta, pero aún no puedo acercarme a los valores con mi medidor de lux, si prueba el valor hexadecimal 843B... obtengo 2772,48 lux y el medidor de lux dijo 1864 lux... creo que 1000 lux es una gran diferencia.
Lo siento, no puedo ayudarte con los datos proporcionados. No lo olvide, esa luxes una medida subjetiva (coincidente con el ojo humano), muy afectada por las longitudes de onda además de la intensidad. Por lo tanto, realmente necesita proporcionar condiciones idénticas para ambos sensores al medir. Y puede probar la prueba continua para verificar cómo cambian los valores al aumentar o disminuir la luz. Si cambian gradualmente sin muchos saltos, es una buena indicación de que se miden correctamente (hasta el sesgo del sensor...)
@AndroidNFC, su luxómetro de referencia y el OPT3001 pueden tener respuestas angulares diferentes, lo que hace que tomen lecturas diferentes, consulte la página 8, donde puede ver la respuesta normalizada frente al ángulo; si son diferentes, obtendrá lecturas diferentes.
@Arsenal y Eugene, pueden ser ciertos, porque no pude colocarlos muy iguales debajo de la lámpara, por ejemplo, también. Y de hecho coloqué el sensor más cerca de la lámpara que del medidor, así que si dices que es muy sensible, ¡esto podría explicar estas diferencias!
El resultado (en lotes de 0,01 lux) es la parte fraccionaria, multiplicada por 2^n, donde n es la parte exponencial. Por lo tanto, en lotes de 0,01 lux, puede usar resultado = fracción_parte << n; donde n es la parte del exponente. (Desplazar a la derecha N bits es lo mismo que multiplicar por 2^n). Así result = (fraction_part << n) / 100 para lux completo; y donde n = la parte del exponente.