¿Por qué disminuye la precisión de mi cámara cuando empiezo a usar las demoras sin operaciones?

Estoy usando un Arduino Mega para ejecutar tres cámaras de escaneo lineal TSL1401R-LF en paralelo. Hice que todo funcionara y la cámara me daba diferentes resultados según la cantidad de luz que les llegaba (es decir, mucha luz me dio un número alto y poca luz me dio un número más pequeño).

Sin embargo, cuando intenté aumentar el FPS de las cámaras al reducir los retrasos sin operaciones, la precisión disminuyó drásticamente y no tengo idea de por qué. De acuerdo con la hoja de datos, los retrasos mínimos para el impulso CLK son 50 ns y un no funcionamiento provoca un retraso de 62,5 ns, por lo que no debería haber ningún problema. ¿Alguien aquí tiene una idea de por qué podría estar sucediendo esto?

Aquí está mi código antes de las no operaciones:

void readPixels()  
{
  digitalWrite(SI, HIGH);
  delayMicroseconds(delayTime/2);
  digitalWrite(CLK, HIGH);
  delayMicroseconds(delayTime/2);
  digitalWrite(SI, LOW);
  delayMicroseconds(delayTime/2);
  digitalWrite(CLK, LOW);
  delayMicroseconds(delayTime);

  for(int i = 0; i < 128; i++)
  { 
    digitalWrite(CLK, HIGH);
    pixelsArray1[i]=analogRead(Cam1Aout);
    pixelsArray2[i]=analogRead(Cam2Aout);
    pixelsArray3[i]=analogRead(Cam3Aout);
    delayMicroseconds(delayTime);
    digitalWrite(CLK, LOW);
    delayMicroseconds(delayTime);
  }

  delayMicroseconds(20);

}

OUTPUT:
Camera 1: 295, 319, 353, 387, 422, 458, 483, 499, 515, 527, 540, 545, 556, 562, 572, 575, 585, 590, 596, 598, 606, 603, 591, 589, 608, 621, 635, 636, 646, 652, 664, 670, 681, 689, 699, 705, 715, 726, 730, 734, 742, 745, 750, 752, 760, 764, 769, 766, 768, 770, 775, 777, 792, 806, 828, 852, 880, 907, 931, 956, 989, 1016, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 980, 986, 935, 935, 893, 892, 856, 847, 812, 801, 774, 760, 731, 713, 683, 647, 605, 564, 513, 470, 410, 369, 323, 289, 347, 376, 
Camera 2: 346, 368, 396, 419, 446, 462, 480, 496, 516, 531, 549, 559, 571, 579, 593, 600, 611, 619, 628, 636, 647, 651, 660, 667, 676, 679, 687, 696, 704, 710, 719, 725, 732, 735, 743, 743, 752, 758, 764, 768, 775, 777, 783, 788, 793, 798, 803, 806, 809, 814, 818, 822, 827, 831, 831, 833, 836, 839, 841, 839, 843, 843, 844, 839, 841, 836, 831, 830, 831, 830, 831, 834, 841, 847, 861, 867, 880, 894, 902, 911, 918, 935, 940, 956, 960, 976, 973, 991, 984, 1007, 998, 1020, 998, 1019, 992, 1014, 986, 997, 964, 969, 937, 937, 908, 910, 878, 875, 843, 838, 808, 797, 768, 750, 720, 696, 667, 639, 611, 582, 556, 524, 499, 471, 449, 423, 399, 371, 396, 399, 
Camera 3: 472, 491, 507, 512, 526, 551, 564, 572, 583, 588, 583, 586, 587, 593, 603, 604, 604, 608, 611, 617, 617, 621, 625, 629, 635, 641, 635, 645, 661, 668, 671, 675, 678, 680, 685, 688, 688, 690, 691, 694, 696, 701, 701, 704, 699, 706, 705, 706, 713, 715, 718, 719, 719, 719, 720, 721, 723, 731, 737, 736, 739, 743, 748, 751, 755, 758, 760, 763, 766, 768, 769, 773, 775, 777, 777, 780, 787, 791, 790, 787, 781, 779, 769, 763, 760, 758, 755, 750, 743, 741, 744, 748, 743, 740, 750, 755, 774, 783, 790, 797, 802, 810, 807, 818, 806, 807, 796, 796, 785, 777, 759, 748, 728, 715, 696, 681, 664, 647, 625, 606, 579, 558, 536, 508, 484, 467, 432, 427, 
Runetime: 2121

Aquí está después de agregar los no-ops:

void readPixels()  
{
  digitalWrite(SI, HIGH);
  delayMicroseconds(delayTime/2);
  digitalWrite(CLK, HIGH);
  delayMicroseconds(delayTime/2);
  digitalWrite(SI, LOW);
  delayMicroseconds(delayTime/2);
  digitalWrite(CLK, LOW);
  delayMicroseconds(delayTime);

  for(int i = 0; i < 128; i++)
  { 
    digitalWrite(CLK, HIGH);
    pixelsArray1[i]=analogRead(Cam1Aout);
    pixelsArray2[i]=analogRead(Cam2Aout);
    pixelsArray3[i]=analogRead(Cam3Aout);
    __asm__("nop\n\t");
    digitalWrite(CLK, LOW);
    __asm__("nop\n\t");
  }

  delayMicroseconds(20);

}

OUTPUT:
Camera 1: 147, 141, 142, 143, 145, 145, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 146, 145, 145, 143, 145, 143, 146, 146, 145, 145, 144, 147, 147, 147, 147, 148, 147, 147, 147, 147, 147, 147, 147, 147, 147, 148, 147, 147, 147, 148, 147, 147, 148, 147, 151, 152, 153, 154, 156, 156, 158, 158, 159, 161, 162, 163, 165, 166, 167, 167, 168, 169, 167, 172, 174, 174, 174, 174, 175, 176, 176, 177, 177, 179, 179, 179, 179, 179, 178, 178, 176, 175, 174, 172, 172, 170, 168, 167, 165, 163, 162, 161, 159, 158, 156, 154, 155, 152, 151, 150, 148, 146, 146, 143, 143, 141, 139, 137, 135, 131, 129, 127, 125, 122, 120, 140, 172, 
Camera 2: 162, 161, 164, 164, 167, 166, 165, 167, 169, 167, 170, 171, 171, 169, 172, 171, 172, 171, 172, 172, 172, 172, 173, 172, 173, 172, 174, 173, 174, 173, 174, 174, 176, 174, 175, 170, 175, 174, 176, 175, 185, 175, 176, 175, 176, 175, 176, 176, 176, 176, 176, 176, 177, 176, 177, 176, 176, 176, 177, 176, 177, 176, 177, 176, 176, 176, 176, 176, 176, 175, 176, 175, 176, 176, 177, 177, 179, 177, 179, 179, 179, 179, 181, 181, 183, 183, 184, 184, 185, 185, 186, 185, 187, 186, 187, 187, 188, 185, 186, 184, 185, 183, 183, 181, 181, 179, 179, 176, 176, 174, 174, 172, 171, 170, 167, 164, 164, 162, 161, 159, 159, 156, 157, 154, 153, 152, 169, 198, 
Camera 3: 185, 187, 188, 191, 189, 190, 192, 192, 193, 193, 192, 194, 194, 194, 195, 199, 196, 196, 196, 197, 199, 198, 198, 198, 199, 199, 199, 199, 196, 201, 201, 201, 201, 201, 202, 203, 202, 202, 203, 203, 204, 204, 204, 203, 204, 204, 204, 204, 204, 204, 205, 205, 205, 206, 206, 206, 205, 206, 206, 206, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 208, 207, 209, 209, 208, 208, 209, 209, 207, 208, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 208, 209, 209, 209, 211, 211, 211, 211, 211, 211, 211, 210, 210, 209, 209, 207, 207, 206, 206, 204, 203, 203, 201, 199, 200, 198, 195, 195, 195, 193, 167, 179, 
Runetime: 1987

Aquí está conmigo usando While Loops:

void readPixels()  
{
  digitalWrite(SI, HIGH);
  delayMicroseconds(delayTime/2);
  digitalWrite(CLK, HIGH);
  delayMicroseconds(delayTime/2);
  digitalWrite(SI, LOW);
  delayMicroseconds(delayTime/2);
  digitalWrite(CLK, LOW);
  delayMicroseconds(delayTime);

  int i = 0;
  while(i < 128)
  {
    i++;
    digitalWrite(CLK, HIGH);
    pixelsArray1[i]=analogRead(Cam1Aout);
    pixelsArray2[i]=analogRead(Cam2Aout);
    pixelsArray3[i]=analogRead(Cam3Aout);
    digitalWrite(CLK, LOW);
  }

  delayMicroseconds(20);

}

OUTPUT:
Camera 1: 224, 161, 172, 179, 185, 198, 199, 203, 206, 208, 208, 211, 212, 213, 214, 216, 216, 217, 217, 218, 217, 219, 219, 216, 215, 217, 220, 222, 223, 223, 224, 226, 225, 229, 231, 231, 232, 235, 235, 236, 237, 238, 240, 240, 240, 240, 241, 242, 242, 242, 242, 243, 243, 246, 248, 252, 255, 262, 266, 271, 276, 281, 287, 291, 296, 304, 307, 314, 319, 326, 332, 331, 337, 337, 335, 352, 359, 359, 359, 361, 366, 371, 371, 372, 376, 382, 384, 384, 382, 380, 380, 377, 372, 366, 361, 355, 351, 344, 339, 331, 323, 318, 312, 307, 301, 294, 290, 284, 280, 273, 268, 263, 257, 252, 248, 241, 231, 227, 219, 209, 199, 191, 182, 172, 163, 155, 147, 176, 
Camera 2: 216, 193, 196, 203, 207, 212, 216, 219, 222, 223, 227, 231, 231, 234, 236, 238, 239, 241, 242, 242, 244, 247, 247, 248, 249, 252, 252, 252, 254, 255, 258, 259, 259, 259, 260, 262, 262, 263, 263, 265, 272, 268, 267, 268, 268, 270, 270, 271, 271, 272, 273, 274, 275, 275, 275, 276, 276, 277, 277, 278, 277, 280, 278, 278, 277, 278, 276, 276, 275, 275, 275, 275, 276, 277, 278, 280, 282, 285, 287, 289, 291, 294, 295, 299, 301, 304, 306, 310, 312, 313, 316, 319, 319, 321, 322, 323, 323, 322, 319, 318, 315, 312, 308, 305, 302, 298, 294, 289, 284, 280, 275, 270, 263, 257, 251, 244, 238, 232, 227, 223, 216, 211, 206, 202, 197, 193, 188, 207, 
Camera 3: 0, 231, 235, 238, 240, 243, 247, 250, 252, 254, 255, 255, 255, 255, 257, 259, 260, 260, 260, 262, 260, 263, 264, 265, 266, 267, 268, 268, 269, 273, 274, 276, 275, 276, 277, 278, 278, 279, 279, 280, 280, 280, 281, 281, 281, 281, 283, 283, 283, 284, 284, 285, 285, 285, 286, 286, 286, 286, 287, 289, 289, 289, 290, 291, 291, 292, 293, 293, 294, 294, 295, 295, 295, 295, 295, 296, 297, 298, 299, 299, 298, 297, 296, 295, 294, 294, 292, 292, 291, 290, 289, 291, 291, 290, 289, 291, 292, 295, 298, 300, 302, 304, 304, 307, 307, 307, 305, 304, 304, 303, 300, 297, 294, 289, 287, 283, 280, 275, 272, 269, 263, 259, 255, 252, 246, 241, 237, 212, 
Runetime: 1991

Algunas notas:

  • He optimizado el analogRead();método con prescalers para que sean más rápidos de lo habitual.

  • Si bien la salida del segundo código muestra alguna diferencia cuando sostengo un objeto en la línea de visión de las cámaras, es una diferencia mucho menor y solo funciona cuando lo sostengo directamente frente a las cámaras.

Si desea optimizar aún más el código, use la biblioteca digitalWriteFast que se ocupa de alternar el nivel de registro.
¿Puede darme una sinopsis de por qué usaríamos digitalWriteFast en lugar de solo digitalWrite?

Respuestas (2)

Lo que probablemente sucedió es que el compilador decidió optimizar su código y los tiró nopa la basura. Debe obligar al compilador a mantener intacto su ensamblaje definiéndolo volatile:

__asm__ __volatile__ ("nop\n\t");

Alternativamente, podría retrasar haciendo algo útil, por ejemplo:

int i = 0;
while(i < 128)
{
i++;
digitalWrite(CLK, HIGH);
pixelsArray1[i]=analogRead(Cam1Aout);
pixelsArray2[i]=analogRead(Cam2Aout);
pixelsArray3[i]=analogRead(Cam3Aout);
digitalWrite(CLK, LOW);
}

PD. La caída en la sensibilidad que está viendo bien puede ser normal. Su hoja de datos dice:

El tiempo de integración mínimo para cualquier conjunto dado está determinado por el tiempo requerido para registrar todos los píxeles en el conjunto y el tiempo para descargar los píxeles. El tiempo requerido para descargar los píxeles es una constante. Por lo tanto, el período de integración mínimo es simplemente una función de la frecuencia del reloj y la cantidad de píxeles en la matriz. Una velocidad de reloj más lenta aumenta el tiempo de integración mínimo y reduce el nivel máximo de luz para la saturación en la salida. El tiempo de integración mínimo que se muestra en esta hoja de datos se basa en la frecuencia de reloj máxima de 8 MHz.

Dado que su código optimizado se ejecuta más rápido, queda menos tiempo para la integración, lo que reduce el valor de salida.

Además, intente agregar un retraso después digitalWrite(CLK, HIGH);para respetar el tstiempo mencionado en la forma de onda en la página 5.

Gracias. Ambas son buenas ideas, pero sigo teniendo el mismo problema. He publicado una actualización con salida si quieres echarle un vistazo.
@CrystalPritzker Mira mi edición, lo que ves puede ser normal.
Así que agregué un Prescaler de reloj para reducir la velocidad de 16 MHz a 8 MHz y eso no cambió los números. ¿Significa esto que el período mínimo de integración no es el problema?
@CrystalPritzker Sí, eso probablemente demuestre que el problema está en otra parte, pero, sinceramente, no tengo ideas. ¿Tiene un alcance para verificar los tiempos?
Verifiqué el tiempo haciendo parpadear primero un LED y viendo si el retraso era mayor. (es decir, un delay(1000);comando tardaría 2 segundos en lugar de uno). Sin embargo, está bien, gracias por sus sugerencias.

Cuando usa digitalWrite, está haciendo un montón de cosas detrás de escena además de establecer el pin. Esto es necesario porque Arduino combina PWM y la conmutación regular, por lo que hay una arquitectura de fondo impulsada por interrupciones que necesita administración . Al final, un simple interruptor de pin digitalWriterequiere 100 ciclos de reloj, según este compañero . Por lo tanto, su NOPes una gota en el cubo de tiempo.

Si desea un mejor control sobre el tiempo, lo que desea usar son los controles AVR de nivel inferior o las bibliotecas que los implementan. Un osciloscopio sería realmente útil para la depuración, pero de lo contrario, se verá obligado a confiar en modelos mentales.

¿Puedes ampliar lo que quieres decir? ¿ Por qué afectaría NOPsi digitalWritese NOPejecuta después de digitalWrite? ¿El comando no se ejecuta de arriba a abajo o simplemente no entiendo la programación? Para su información, este es mi primer proyecto Arduino.
Antes, su temporización era impuesta por las llamadas a delayMicroseconds. Si desea bajar al nivel de tiempo de ~ 100 ns, debe tener en cuenta cuánto tardan en ejecutarse las funciones. NOP toma un ciclo de reloj, pero digitalWrite toma mucho más tiempo, por lo que es la mayor parte de su tiempo. Entonces, para obtener una imagen completa, debe considerar los detalles de nivel inferior. Del mismo modo, la lectura analógica es la que más tiempo lleva.
Optimicé por lectura analógica y toma una fracción del tiempo original. Lo que todavía me confunde es cómo la escritura digital podría causar este problema. ¿Está diciendo eso porque la escritura digital tarda más en ejecutarse que está alterando mi sincronización? Porque no veo cómo me afectaría tomar más tiempo.
Mi respuesta fue más dirigida a por qué su tiempo podría no ser el que esperaba. Pero sí, no estoy seguro de cómo eso explicaría lo que estás viendo. No estoy familiarizado con el sensor que está utilizando, pero intentaré encontrar algo de tiempo para echarle un vistazo más de cerca.
Si puede, sería de gran ayuda, he estado investigando este problema durante un par de días sin éxito real.