Como usar Interrupciones para aumentar la velocidad de mi Programa

Buenas tardes Estimados,

Estoy Elaborando un programa para capturar y almacenar valores analógicos (en memoria SD), usando 2 periféricos; Un teclado de 4x4 y un LCD.

He definido un periodo de tiempo de 10 segundos para tomar la mayor cantidad de muestras, la cual es de aproximadamente 500 muestras. Necesito aumentar la velocidad de toma de muestras, por ello acudo a sus conocimientos y experiencia, ¿que opciones hay?.

Algo que ya probé es echar mano de una memoria SD de mayor velocidad de escritura, pero no he llegado al resultado que quiero, la cual es de 1000 muestras en 1 segundo. Me han sugerido usar Interrupciones pero leyendo sobre el tema no veo como las interrupciones me puedan ayudar.

Quedo atento a sus comentarios, y de antemano ¡MUCHAS GRACIAS!

void loop() {

case 'C':
   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("Capturando...");      //EN ESTA LINEA TERMINA LA INTERACCIÓN CON EL LCD
   tiempo2=millis();
       
            while (tiempo1<tiempo2+10000){ 
            tiempo1=millis();
            valor_leido=analogRead(A0);
            temperatura=((valor_leido*500.0)/1024);               
            Serial.print("Tiempo(ms)=");
            Serial.print(millis());
            Serial.print("TEMPERATURA=");
            Serial.println(temperatura);
               
      dataFile = SD.open("AGS8_2.TXT", FILE_WRITE); //EMPIEZO A GUARDAR LOS DATOS EN SD
                       if (dataFile)// Si fichero es correcto empiezo a escribir
                          {
                          dataFile.println("Tiempo(ms),TEMPERATURA");
                           dataFile.print(millis());
                           dataFile.print("  ");
                           dataFile.println(temperatura);                                                      
                          }
                           else
                          {
                           Serial.println("Error al escribir en AGS8_2.TXT!!!!!!\n");
                          }                                                     
                           dataFile.close();
                                       }  
              
                                       Serial.println("ESCRITURA EXITOSA SD!!!!!!\n");
                                       lcd.setCursor(0, 0);
                                       lcd.print("ESCRITURA EXITOSA SD!!!!!!\n");

 break; 
} // Cierre del loop

Hi,
Mi sugerencia es si quiere almacenar lo mas senales analogas lo mas rapido posile olvidate de mandar mensajes seriales. Almacenas los valores y despues los displeagas en la pantalla. Cuando mandas los mesajes seriale esto hace que las lecturas analogas se hagan mas lentas.Solaamente una sugerencia.

Hola tauro0221,

Si en efecto me di cuenta al realizar pruebas y comentando la parte de los print del puerto serie.

He Almacenado los valores primero(después desplegarlos) como bien me comentas y eso me ayudo muchísimo. Me encuentro revisando algunos ajustes a mi código para volverlo mas eficiente. Muchas Gracias!!

Si Alguien sabe si las interrupciones me pueden ayudar en algo con los objetivos de mi programa, les estaré muy agradecido, he leído que las interrupciones son potentes y ayudan con la eficiencia del código. Pero no he logrado comprenderlas del todo y sobre todo como usarlas en el contexto deseado.

Hi,
Otra cosa que se me paso por alto mencionarte es que si usa floating point cuando haces la conversion tomas mas tiempo. Por lo tanto como no tienes definida la temperatura o se si la haces ne float o intger seria mejor que la almacena en counts y despaues que termine de scanear los puntos entonces haces la conversion.

Abre el fichero antes del while y cierralo despues del while. Con esto la operación de apertura y cierre del fichero no te restará velocidad.

void loop() {

case 'C':
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Capturando...");      //EN ESTA LINEA TERMINA LA INTERACCIÓN CON EL LCD
  tiempo2 = millis();

  dataFile = SD.open("AGS8_2.TXT", FILE_WRITE); //EMPIEZO A GUARDAR LOS DATOS EN SD
  if ( dataFile ) {
    // Haz las lectura y graba datos.
    while ( tiempo1 < tiempo2 + 10000 ) {
      tiempo1 = millis();
      valor_leido = analogRead(A0);
      Serial.print("Tiempo(ms)=");
      Serial.print(millis());
      Serial.print("TEMPERATURA=");
      Serial.println(temperatura);
      dataFile.println("Tiempo(ms),TEMPERATURA");
      dataFile.print(millis());
      dataFile.print("  ");
      dataFile.println(temperatura);
    }
    dataFile.close(); // Cierra el fichero despues del while.
    Serial.println("ESCRITURA EXITOSA SD!!!!!!\n");
    lcd.setCursor(0, 0);
    lcd.print("ESCRITURA EXITOSA SD!!!!!!\n");
   }
   else {
    Serial.println("Error al escribir en AGS8_2.TXT!!!!!!\n");
   }
  break;
}

Puede que debido a las limitaciones de Arduino y la libreria el código no funcione correctamente. Cuando abres un fichero, carga en memoria el "sector" donde está, y no hace las operaciones de escritura hasta que llamas a flush() o close(). Esto hace que la operación de apertura y la de cierre sean operaciones mas lentas. Sin embargo al sacar open/close del while escribes en el buffer de memoria muchos datos y puede que llegue a desbordarse con la perdida de datos. Creo recordar que el tamaño del bufer de memoria es de 512 bytes.

No sé por qué es necesario hacer una captura de 1000 muestras por cada 10 segundos de la temperatura.

En cuanto a las interrupciones no te van a ayudar mucho. Una interrupción debe ser algo simple y rápido. Si usaras por ejemplo el Timer1 para que haga la captura de memoria, ralentizarias todo el Arduino ya que abririas/cerrarias el fichero constamente.

Hola tauro0221,
Me encuentro revisando el caso de la conversión a temperatura, para modificar mi código y poder realizar los valores del AD al final del escaneo de datos. Gracias!.

Hola victorjam,

Probaré tema del While, aunque anteriormente ya había probado algunos cambios de posición del close del fichero y me marcaba error en la compilación. Voy a rectificar tal cual así como me dices y realizar pruebas para asegurarme que el buffer no se desborde.

Y muchas gracias por aclararme el tema de las interrupciones, para no perder tiempo en ello.

Si me surge alguna duda relacionada a lo planteado les estaré comentando...

Saludos!.

Tienes confundido el concepto de las interrupciones.
Las interrupciones son un recurso rápido pero justamente porque se consultan, se hace algo y se termina rapidamente. Ahora eso no acelera NADA si tu haces todo el resto mal!!

Si usas un TIMER y este genera interrupciones cada X tiempo en useg o mseg entonces lo que haces es muestrear ordenadamente por alguna razón que tenga que ver con algún proceso de cálculo determinado. Ejemplo: Cuando muestreas la señal de audio y luego haces FFT o cuando haces lo mismo con la seña de 50 o 60 Hz para saber el TrueRMS (verdadero valor eficaz).

La conversión del ADC es de 100useg mas o menos, así que en teoría 1000 muestras te llevarán 100x 100useg = 10000 useg o sea 10 mseg.
10 mseg es un tiempo mas que aceptable mas los 5 mseg que creo demora una grabación en la SD no tendrías problemas.
Evidentemente tu problema es otro.

Tomar 1000 muestras de temperatura me parece una locura, pero además que proceso te devuelve cambios de temperatura cada 100 useg, si lo hay, creo que no lo puedes medir. Asi que analiza mejor las cosas.

Existe en la librería SDFat si mal recuerdo un ejemplo donde se toman muestras a alta velocidad, es complejo de entender de modo que si quieres hacer modificaciones estarás frito pero el ejemplo funciona perfecto en UNO/NANO/PRO Micro e incluso MEGA.
Implementa un doble buffer circular que permite almacenar datos loggeados y luego volcarlos en la SD sin perder tiempo e incluso enviarlos por Serial.
SDfat library

El ejemplo que mencioné AnalogBinLogger.ino

Hola surbyte!!,

En cuanto a la velocidad que busco es esta:
1000 muestras en 1 segundo, mi tiempo máximo para muestrear va a ser durante 10 segundos, así que seria obtener 10,000 muestras por los 10 segundos. Esto es, para poder medir la temperatura en un proceso de templado (Donde se manejan estas velocidades en el cambio de temperatura). Pero al parecer después de realizar algunas pruebas, ya alcance el rango velocidad deseada.

Como bien mencionas necesito las interrupciones para poder muestrear de forma uniforme(ordenada), necesito precisión en mi proceso, ya hasta ahora usando la función millis() para el rango de tiempo, el programa muestrea pero entre cada muestra no hay el mismo espacio de tiempo. Aun no se como implementarlas, sinceramente no tengo conocimiento. Me podrías guiar o decir que código agregar , o alguna buena documentación, ya que he buscado pero aun no tengo idea. Por favor.

Referente a tu ultimo parrafo "SDFat" ya había encontrado algo de código pero no alcance a comprenderlo por eso mejor lo dejé de lado.

Y bueno, si quieres 1000 muestras x segundos es 1 x cada milisegundo, pero el problema no es ese.
Deberías usar por ejemplo TimerOne para citar una librería fácil de usar que te permita tomar muestras cada 1 milisegundo.
Entonces cuando necesites usar la SD lo podrás hacer sin dudar que se pierda la muestra correspondiente.

Inténtalo a ver como resulta.

Surbyte, discúlpame pero, no entendí bien,

¿Se refiere a usar la biblioteca #include < TimerOne . h > , Para usar interrupciones con el temporizador1?

Estoy muy confundido entre las interrupciones de tipo externa e interrupciones internas(timers), es que veo en algunos ejemplos que en ambas están usando palabras y funciones iguales.

Seria usar algo como esto que encontré en github?

#include < TimerOne . h >

// Este ejemplo usa la interrupción del temporizador para hacer parpadear un LED
// y también demuestra cómo compartir una variable entre
// la interrupción y el programa principal.


const int led =  LED_BUILTIN ;  // el pin con un LED

 configuración nula ( nula )
{
  pinMode (led, SALIDA );
  Temporizador 1 . inicializar ( 150000 );
  Temporizador 1 . attachInterrupt (blinkLED); // LED parpadeante para que se ejecute cada 0,15 segundos
  Serial . comenzar ( 9600 );
}


// La interrupción hará parpadear el LED y se mantendrá
// seguimiento de cuántas veces ha parpadeado.
int ledState =  LOW ;
volatile unsigned long blinkCount =  0 ; // usa volatile para variables compartidas

void blinkLED ( vacío )
{
  if (ledState ==  LOW ) {
    ledState =  HIGH ;
    blinkCount = blinkCount +  1 ;  // aumenta cuando el LED se enciende
  } más {
    ledState =  BAJO ;
  }
  digitalWrite (led, ledState);
}


// El programa principal imprimirá el contador de parpadeos
// al monitor serial Arduino
 bucle vacío ( vacío )
{
blinkCopy largo   sin firmar;  // tiene una copia del blinkCount

  // para leer una variable que escribe el código de interrupción,
  // debe deshabilitar temporalmente las interrupciones, para asegurarse de que
  // no cambia mientras estemos leyendo. Para minimizar el tiempo
  // sin interrupciones, haz una copia rápidamente y luego
  // usa la copia mientras permite que la interrupción siga funcionando.
  noInterrupts ();
  blinkCopy = blinkCount;
  interrumpe ();

  Serial . print ( " blinkCount = " );
  Serial . println (blinkCopy);
  retraso ( 100 );
}

O estoy frito. :confused:

Si pero simplemente copiaste el ejemplo de blink usando TimerOne.

Te hubieras jugado probando lo que necesitas a ver como se comporta con lo que necesitas pero descarto que lo hará bien.

Hola a todos!,

Me podrian auxiliar por favor

Del tema de las interrupciones estoy usando una librería llamada TimerOne para trabajar con el timer 1 del Arduino.
Pero no me funciona no interrumpe cada 1 milisegundo, sigo obteniendo muestras de forma no uniforme.

mi definición de la interrupción es esta:

void setup()
{
  Timer1.initialize(1000);      //Configura el TIMER en 1 MiliSegundo
  Timer1.attachInterrupt(AlmacenamientoArray) ; //Configura la interrupción del Timer 1

.......}

Y la rutina que estoy usando es este:

int AlmacenamientoArray(){
       for (a=1; a<1000; a++)
              {
               //valor_leido=analogRead(A0);
               //temperatura=((valor_leido*500)/1024);    
               Arreglo[a]= analogRead(A0);
               return   Arreglo[a];                        
             }
               
        }

Lo demás del void loop lo deje casi igual como al principio con la excepción que ahora tomo el arreglo de la rutina de interrupción para mandarlo a la memoria SD.

Mi ultima pregunta que antecede ya lo solucioné.....sin embargo ya no logro resolver es como crear archivos en una SD, nombrados de forma secuencial. E buscado bastante e intentado y lo único que he encontrado es definiendo el nombre del archivo dentro del codigo por ejemplo "test21.txt", pero solo seria un unico archivo y ademas lo tengo que definir dentro de mi codigo de arduino.

void setup() {
  Serial.begin(9600);
  pinMode(pin_lector, OUTPUT);
  char texto[] = "Este es un texto de prueba";
 
  bool val = SD.begin(pin_lector);
 
  if(not val)
    Serial.println("No se ha podido inicializar la tarjeta SD");
 
    else
  {
    archivo = SD.open([b]"test21.txt"[/b], FILE_WRITE); //Abrimos el fichero
      if(not archivo)
      Serial.println("No se ha podido abrir el fichero");
 
    //De lo contrario escribimos el texto
    else
    {
       archivo.println(texto);
       archivo.close();
    }
  }
}

Lo que necesito y busco es saber si se podrá de alguna manera no definir explicitamente ese nombre en el codigo y poder generar varios archivos, cada que quiera cambiar otro archivo creando y escribiendo en él lo pueda hacer. El nombre que quiero que contengan esos archivos creados en la SD seria por ejemplo test00001, test00002, test00003, test00004........y así sucesivamente.

En primera seria saber si esto que describo se puede hacer y si es así me pueden dar ideas, por favor.

La forma de crear un archivo es simple, solo que estas pretendiendo aprender muchas cosas de golpe y todo requiere tiempo y esfuerzo.

Yo uso este método sprintf()
sprintf te permite armar un array de caracteres como lo desees.
En tu caso sería algo fijo como test con 5 lugares llenos de 0 salvo números.
Eso se crea de este modo.
Defines un char buffer[10]; que almacenará el nombre creado

sprinft(buffer, "test%05d.txt", contador);
archivo = SD.open(buffer, FILE_WRITE);

eso creará nombres como test00001.txt a test99999.txt

Gracias surbyte, lo estoy implementando.