Aumentar la frecuencia de muestreo (Solucionado)

Hola,

La configuración y función analogReadFast() está preparado para que el ADC funcione con una resolución de 8 bits. Como este script era para mandar por serie en tiempo real, de esta forma se gana mucha velocidad ya que sólo recoges 1 byte y envias el mismo por serie.
La función analogRead() de Arduino, funciona con toda la resolución del ADC, que son 10 bits (necesitas 2 bytes para guardar la info). O deberías usar la función map para guardarlo en 1 byte.

Saludos

Igor R.

Hola Igor R,
Sabía que la función analogReadFast() devolvía solo el byte ADCH (como has dicho), pero yo me aseguraba previamente que el valor del sensor estuviera entre 0 y 254, como puedes ver en los datos recogidos por el 1º método. Por eso definí el tipo de varible como byte mydata[mysize].
Es decir, en el caso de que analogRead() diera un valor de 100, que ocupa 2 bytes, pero por su valor se podría representar con 1 byte; el byte ADCH que envia la funcion analogReadFast() ¿no contendría el mismo valor?. Pensaba que, en este caso, ADCH contendría el valor 100 y ADCL contendría el valor 0

En el ejemplo "fast" el bit ADLAR (registro ADMUX) es el que te cambia la representación del ADC (justificación a la izquierda).

Me refiero, un 108 en resolución 10 bits es similar a un 27 en resolución 8 bits. [ Voltaje= ADC*Vcc/(2^n -1) ]

:wink:

Bien, creo que empiezo a entender algo. Lo que describes se explica bien en la siguiente imagen:

  • Es decir, Si ADLAR=1 la información que se envia desde el sensor se va a desplazar 2 bits a la izquierda, de forma que el bit 0 y el bit 1 se eliminan. Entiendo que esto supone un pequeño error, pues 4 números distintos se corresponderán con el mismo nº después de suprimir estos bits:
    1100100 -- 100
    1100101 -- 101 ---- tras la conversión (ADC) todos dan ---> 11001 -- 25
    1100110 -- 102
    1100111 -- 103
  • Pero viendo otra vez la imagen, si pongo ADLAR=0 y me aseguro que la información del sensor sea menor de 255, y después, en vez de coger el byte ADCH, escojo el byte ADCL. ¿estaría resuelto el problema? .

Si lo anterior es correcto y quiero poner ADLAR=0, en la siguiente linea ¿bastaría con sustituir (1<<ADLAR) por (0<<ADLAR), o hay que cambiar algo mas?

ADMUX=(1<<ADLAR)|(0<<REFS1)|(1<<REFS0)|(0<<MUX3)|(1<<MUX2)|(0<<MUX1)|(0<<MUX0);

La función analogRead() de Arduino te devuelte un word (ADCH y ADCL). No sé exactamente que pasa cuando fuerzas un cast a un byte en:
mydata[ i ]=analogRead(PINS_SENSOR_BARRIER);
No tienes más que probar, así que ya nos dirás... :wink:
Yo tengo como costumbre hacer en esos casos (byte bajo de un word) => mybyte=(byte)(myword & 0xFF);

En el ejemplo FAST, pierdes resolución (de 10 bits a 8 bits), puedes leerte la entrada que escribí:

Por otro lado,en dicho ejemplo (FAST), no puedes quitar ADLAR=1, ya que cómo puedes ver, sólo recoge ADCH, con la captura que has puesto anteriormente, puedes ver que pasaría....

int analogReadFast()
{
	ADCSRA|=(1<<ADSC); //  sbi(ADCSRA, ADSC);
	// ADSC is cleared when the conversion finishes
	while (bit_is_set(ADCSRA, ADSC));
        return ADCH;
}

Por cierto, no se por qué puse en su día un int a devolver.... debería ser uint8_t

:wink:

Pues, tras varios intentos, creo que he conseguido lo que queria. Ahora, para comparar resultados, tengo 3 códigos:
1- "no rápido: analogRead()
2- "rápido: ADLAR=1 Byte ADCH"
Estos códigos anteriores ya están puestos anteriormente
3- "rápido: ADLAR=0 byte ADCL"
Éste último código es:

// *********************************
//  datos rápidos ADLAR=0 byte ADCL
// *********************************

#define mysize 40

unsigned long tStart;
unsigned long tEnd;
byte mydata[mysize];
unsigned int bytes_datos[mysize];

void setup()
{
  Serial.begin(115200);
  delay(200);

    
  //Prescaler
  //ADPS2 - ADPS1 - ADPS0 - Division Factor
  //0         0        0        ->2
  //0         0        1        ->2
  //0         1        0        ->4
  //0         1        1        ->8
  //1         0        0        ->16
  //1         0        1        ->32
  //1         1        0        ->64
  //1         1        1        ->128
  //Configure to Prescaler=32
  bitWrite(ADCSRA,ADPS2,1); // sbi(ADCSRA, ADPS2);
  bitWrite(ADCSRA,ADPS1,0); // cbi(ADCSRA, ADPS1);
  bitWrite(ADCSRA,ADPS0,1); // sbi(ADCSRA, ADPS0);
  
 // Input Channel Selections 
 
//  MUX3210    Single Ended Input
//     0000          ADC0
//     0001          ADC1
//     0010          ADC2
//     0011          ADC3
//     0100          ADC4
//     0101          ADC5
//     0110          ADC6
//     0111          ADC7
//     1000          ACD8

// Entrada A4
ADMUX=(0<<REFS1)|(1<<REFS0)|(0<<MUX3)|(1<<MUX2)|(0<<MUX1)|(0<<MUX0);//(1<<ADLAR)|

}

void loop()
{

  Serial.println("espero corte:");
  mydata[0] =analogReadFast();
  while (analogReadFast()>mydata[0]-10){};
  tStart=micros();
  for (int i=1; i<mysize;i++)
  {
     mydata[i]=analogReadFast();
  }
  tEnd=micros();
  
    for (int i=0; i<mysize;i++)
  {
    Serial.print(mydata[i],DEC); Serial.print(" "); 

  }
  Serial.println(""); 
  Serial.println("NEW ACQUISITION");
  Serial.print("tStart=");
  Serial.println(tStart);
  Serial.print("tEnd=");
  Serial.println(tEnd); 
  Serial.print("Tiempo: ");
  Serial.println(tEnd-tStart);
  Serial.print("nPoints=");
  Serial.println(mysize);
}

int analogReadFast()
{
	ADCSRA|=(1<<ADSC); //  sbi(ADCSRA, ADSC);
	// ADSC is cleared when the conversion finishes
	while (bit_is_set(ADCSRA, ADSC));
        byte BAJO=ADCL;
        byte ALTO=ADCH;
        return BAJO; 
}

La modificaciónes aplicadas son:

  • la línea que define el registro ADMUX, en la que he suprimido la parte que asigna 1 al byte ADLAR, por lo que ahora ADLAR = 0. Ésto es necesario para hacer un desplazamiento de 2 bits hacia la derecha

  • la función analogReadFast(), en la que, después de leer el byte bajo y el alto, envio solo el byte bajo(ADCL). por lo que siempre que el valor del sensor sea < 255 la transmisión de datos será real y la rapidez mucho mayor como puede verse a continuación:

DATOS NO RAPIDOS
esperando corte...
196 181 173 166 158 147 130 108 89 74 61 50 40 34 32 33 35 40 44 52 60 67 77 93 113 134 150 162 174 185 192 195 196 195 196 196 196 195 196 195
NEW ACQUISITION
tStart=1601608
tEnd=1605976
Tiempo: 4368
nPoints=40
esperando corte...
Los resultado son correctos, pero la duración es grande debido a que la transmisión de cada dato ocupa 2 bytes.

DATOS RAPIDOS(ADLAR=1; ADCH)
espero corte:
49 39 38 38 37 37 36 35 35 34 33 32 31 30 29 28 27 26 25 24 23 22 22 21 20 19 18 18 17 16 16 15 14 14 13 12 12 11 11 10
NEW ACQUISITION
tStart=2872060
tEnd=2873156
Tiempo: 1096
nPoints=40
espero corte:
Los resultados tienen corta duración, pues cada dato ocupa 1 sólo byte, pero a consta de hacer un desplazamiento de 2 bytes a la izquierda, por que el valor es menor y hay una ligera disminución de la precisión

DATOS RAPIDOS(ADLAR=0; ADCL)
espero corte:
195 183 179 177 174 170 168 165 161 158 154 150 145 142 138 133 129 124 119 113 109 104 100 96 92 88 85 81 79 76 73 71 68 66 63 61 59 57 56 53
NEW ACQUISITION
tStart=2254152
tEnd=2255248
Tiempo: 1096
nPoints=40
espero corte:
Los datos se transmiten igual de rápidos que en el caso anterior, pues siguen ocupando 1 byte. Ahora hay un desplazamienos de 2 bits a la derecha, y el valor es real(no se reduce su tamaño), coincidiendo con los de los primeros datos, siempre que no se supere el límite de ese byte, que es 255.

Gracias Igor R, ha sido un placer.

Una pregunta.... que tipo de sensor estas usando? Si es una barrera, por que usas entrada analogica? :fearful:

nota.- ni enyes ni acentos

Pues estoy usando un emisor de luz infrarroja y un fototransistor para detección de gotas y fotografiar colisiones. Con los datos de corte de la barrera hago gráficos que me ayudan a tomar decisiones.

Puede que la rapidez en los datos no sea tan importante para esta aplicación, pero también lo quiero aplicar a construir gráficos del destello de flashes fotográficos y hacer comparaciones.

Todas estas operaciones implican guardar los datos en un array antes de la transmisión serial. Si recien tomado un dato, lo enviara via serial, el cálculo de los tiempos sería diferente e incluiría un retardo por el tiempo de transmisión.

Además, guardando una matriz de datos de 1 sólo byte me ahorro memoria con respecto al método tradicional de 2 bytes.

Lo decia porque si era una barrera para detecta solo 2 estados (ON/OFF) puedes usar una entrada digital que es mucho mas rapido.

Igor R: "No sé exactamente que pasa cuando fuerzas un cast a un byte en:
mydata[ i ]=analogRead(PINS_SENSOR_BARRIER);
No tienes más que probar, así que ya nos dirás... "

En los 2 últimos códigos, que usan la función analogReadFast(), los tiempos de lecturas son mas rápidos, pero es debido a que incorporan lineas que modifican el preescaler(cambiando los bits ADPS0, ADPS1 Y ADPS2 del registro ADCSRA). Es decir, si quito del código estas lineas que modifican estos bits, el tiempo de lectura con analogRead() y con analogReadFast() tienen poca variación.

Con respecto al comentario del encabezamiento, he comprobado que hacer byte mydata[mysize]; y mydata*=analogReadFast();* tiene el mismo efecto que poner ADLAR = 0 y hacer un desplazamiento a la derecha de 2 bits, cogiendo el byte ADCL, siempre que el valor del dato no supere a 255, y todo esto fue lo que incorporé al 3º código.
Por todo esto, de momento:
- Seguiré usando analogRead() frente a analogReadFast()
- No usaré ADLAR = 1, sino ADLAR = 0 que es el que tiene por defecto.
- Limitaré la luz que llega al sensor para que no supere el valor máximo que puede tener 1 byte, es decir, 255
- Y, por supuesto, seguiré modicando el preescaler para aumentar la velocidad de ADC

- También estudiaré tu oferta de usar entradas digitales, aunque lo bueno de usar la entrada analógica es que puede establecer el límite de la barrera de forma fácil, y supongo que en una entrada digital tendría que añadir un circuito electrónico que me permita hacer el cambio de estado en distintos niveles.

Hola:

analogRead() frente analogFast() tienen que tener bastante diferencia en cuestión de tiempo, otra cosa es que sea suficiente para tu aplicación (no tienes más que comparar código).
En teoría, para trabajar con resolución de 10 bits, deberías configurar el prescaler como máximo a 200 kHz (según datasheet). Ese es el motivo de que se forzó a 8 bits,ya que se configuró a más velocidad.

Me refiero, el código es bastante sencillo he intentaba ir a la máxima velocidad posible incluyendo envio por serie para monitorizar en "tiempo real".
Ahora ya dependiendo de la aplicación de cada uno, puedes adaptarlo a tus necesidades.

Salu2

Igor R.

Ufff.., lo siento Igor R, por querer usar tiempos pequeños no me di cuenta que sobrapasaba la frecuencia permitida (al forzar preescaler con 10 bits).

Tambíen supongo que si uso la función analogFast() pero incorporando la lectura tanto del byte ADCL como del ADCH no debería haber diferencias con analogRead(), pues estoy leyendo 2 bytes.

Me tendré que resignar y perder resolución si quiero mas rapidez.

Gracias. Creo que todo me ha sido muy util para conocer mejor el funcionamiento del proceso.

Hola,

El amigo Juan, me ha pasado el link de su página con un programa creado en gambas usando lo que se ha comentado en este post.
http://www.seta43.netau.net/ardu_os.html

:wink:

pues he probado ese "osciloscopio" pero me aparece un error al intentar abrir el puerto....... ¿que sera?

copachino:
pues he probado ese "osciloscopio" pero me aparece un error al intentar abrir el puerto....... ¿que sera?

¿Te has dao' cuenta tú también, no?

yOPERO:

copachino:
pues he probado ese "osciloscopio" pero me aparece un error al intentar abrir el puerto....... ¿que sera?

¿Te has dao' cuenta tú también, no?

¿A ti también te ha pasado?
o no entiendo lo que quieres decir...

buenos dias, estoy tratando de cambiar la velocidad de muestreo del adc de mi arduino due, pero no se como hacerlo, por lo que les agradeceria cualquier ayuda que me puedan brindar......
mi correo: davichi_89@yahoo.com

Hola Amigos, tengo una duda con este TEMA de Frecuencia de Muestreo ?????????????????????????????????????????????????????????????' :~

El código ADCSRA|= (1<<ADPS2)|(1<<ADPS1)|(0<<ADPS0) se activa para todos los puertos analógicos o solo para uno, y si es así explíquenme como activar para todos juntos por favor

Y para el Arduino Mega 2560 la frecuencia de muestreo por defecto es 15.6kHz es decir (16MHz/1024=15.6kHz), pero esta frecuencia tengo que dividirla pata 13 ???? o no, si es así seria 1.2kHz, y es muy bajo para procesar señales,

es que necesito adquirir una señal eléctrica CA para calcular THD.. y para calcular THD es necesario aumentar la frecuencia o no?????

GRACIAS............

¿Como vas a calcular THD? ¿con Arduino? ¿FFT? ¿tiene que ser a "tiempo real"?
¿Estamos hablando de Total Harmonic Distortion (THD)?

Así a primeras sin pensarlo mucho, suena a mucha caña para el micro del Arduino Mega....
Si vas a usar la librería FFT que hay de Arduino, me miraría primero las limitaciones que tiene... que son unas cuantas....No vaya a ser que no cumpla tus requerimientos.

Acerca del tema de la frecuencia del ADC, si la información tratada en los numerosos post de este tema no cubren tus dudas, lo mejor es que te descargues el datasheet del micro y eches un vistazo. Es la mejor fuente de información cuando necesitas estas cosas.

Saludos,

Igor R.

Exacto Total Harmonic Total (THD)..

Mi problema es que necesito definir la misma frecuencia de muestreo del ADC para todos los puertos analogicos.

Y si con la libreria FFT para arduino necesito resolverlo.

Esta es la libreria:
https://code.google.com/p/neuroelec/downloads/detail?name=ffft_Library.zip&can=2&q=

Saludos