Pages: [1] 2   Go Down
Author Topic: Velocidad de ejecución de instrucciones (Solucionado)  (Read 2594 times)
0 Members and 1 Guest are viewing this topic.
Madrid-España
Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hace tres meses que ando enredando con el arduino. Hoy he medido la velocidad máxima de conmutación de una de sus salidas digitales mediante un osciloscopio con este sketch:

Code:
int ledPin =  12;  
void setup()  
{                
  pinMode(ledPin, OUTPUT);    
}
void loop()                    
{
  digitalWrite(ledPin, HIGH);  
  digitalWrite(ledPin, LOW);  
}

Ya que el micro que lleva, un atmega168, trabaja a 16MHz y utiliza un ciclo por instrucción según el datasheet, pensé que la frecuencia medida en el pin 12 sería de 8MHz pero sólo tengo 125KHz.
Casualmente 125KHz x 64 = 8MHz
He seguido consultando el datasheet y he visto que tiene unos "fuse", el equivalente a la palabra de configuración de los PICs. Hay unos bits CLKPS3..0, que se supone configuran un prescaler aplicado al reloj. ¿Podría ser que el prescaler viene configurado a 64 DEC?.
¿Como puedo acceder a ese dato desde la pantalla Arduino?
El arduino es el que vende Elektor, un friduino igual que el Diecimila pero con el chip de soldadura superficial.

Gracias
« Last Edit: January 21, 2012, 02:00:32 pm by troglodita » Logged

Offline Offline
Edison Member
*
Karma: 23
Posts: 1375
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ten en cuenta que es una llamada a una función, que ya de por si mete un overhead, más luego ejecutar todo el código de dentro de la función que no son dos líneas. En la carpeta del IDE de Arduino tienes el código fuente disponible para ver que hace la función.
Logged

Mercadillo electrónico. Kit iniciación a Arduino, shield LCD a color y más cosas!

Málaga, Spain
Offline Offline
Edison Member
*
Karma: 38
Posts: 2173
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Si quieres algo un pelín más rápido puedes probar con este código:

Code:
int ledPin =  12;

void setup() 
{               
  pinMode(ledPin, OUTPUT);   
}
void loop()                   
{
  for(;;)
  {
  //  digitalWrite(ledPin, HIGH);
    PORTB = 0b00100000;

  //  digitalWrite(ledPin, LOW); 
     PORTB = 0b00000000;
  }
}

Deberías conseguir una señal de 4MHz con una relación 1/4 entre el tiempo en 1 y 0.
Logged

   

Offline Offline
Edison Member
*
Karma: 23
Posts: 1375
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Creo que sacaron hace poco la función DigitalWriteFast, pero todavía no le he echado un vistazo, puede ser interesante
Logged

Mercadillo electrónico. Kit iniciación a Arduino, shield LCD a color y más cosas!

Onda
Offline Offline
Jr. Member
**
Karma: 0
Posts: 91
This is personal...
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Con lo que dice FM, si copias varias veces las líneas de PORTB (15 o 20c&P), tendrías cerca de los 8MHz (para 2 instrucciones, o sea, los 16MHZ), te saltarías varios overheads de las subrutinas, como dice Chiva.

Es una cuestión de gestionar recursos, si te interesa código muy eficiente, deberias repetir el encender led apagar led millones de veces, para evitar saltos de memoria (que es uno de los factores que hace que no te de la frecuencia del reloj), pero necesitarías una memoria bastante grande.
Por otro lado, si sacrificas un poco de eficiencia en beneficio de ahorrar líneas de código, puedes meter problemas más complejos, a costa de perder un poco de rendimiento.
El truco consiste en jugar con estos dos factores  smiley-mr-green
Logged

---
Saludos,
José Chorva
www.pepechorva.com
@pepechorva on twitter

0
Offline Offline
Edison Member
*
Karma: 16
Posts: 1579
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

copachino, creo que no se está hablando del ADC....

Por cierto, como curiosidad,con una sola instrucción podrías hacer un toggle de un pin (copio del datasheet). Lo digo para que el duty sea 50%:
Writing a logic one to PINxn toggles the value of PORTxn, independent on the value of DDRxn. Note that the SBI instruction can be used to toggle one single bit in a port.

No todas las instrucciones son de 1 ciclo, por ejemplo SBI son 2 ciclos.
« Last Edit: January 14, 2012, 06:41:49 am by Igor R » Logged


Madrid-España
Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Con el código que he metido, el duty que tengo es prácticamente del 50%, luego tarda lo mismo en poner el puerto a 0 que en finalizar la funcion loop(), volver al principio de la función y poner el puerto a 1.
La cuestión es: teniendo una frecuencia de reloj de 16MHz y una instrucción por ciclo de relog, ¿cómo es que no puedo conmutar un puerto a más de 125KHz?. Si puedo, el lunes lo repetiré con un pic 16F84.
Logged

Offline Offline
Edison Member
*
Karma: 23
Posts: 1375
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

 smiley-cry
Logged

Mercadillo electrónico. Kit iniciación a Arduino, shield LCD a color y más cosas!

0
Offline Offline
Edison Member
*
Karma: 16
Posts: 1579
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

troglodita, vuelve a releer los post y las explicaciones que te han dado. Como  bien te han dicho, la función digitalWrite por detrás hace muchas cosas, no es la más "óptima". Te han puesto ejemplos de como hacerlo directamente manipulando el puerto. Y repito una vez más, no todas las instrucciones son de 1 ciclo.
« Last Edit: January 14, 2012, 11:19:51 am by Igor R » Logged


Málaga, Spain
Offline Offline
Edison Member
*
Karma: 38
Posts: 2173
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

 smiley-sweat vamos a sudar tinta! Si no lees las respuestas que se publican en respuesta a tu duda, va a ser complicadillo...

La funcion digitalWrite del entorno Arduino es un "mojón" ya que hace muchas cosas en la trastienda para simplemente cambiar un bit de un registro del micro.
Logged

   

Onda
Offline Offline
Jr. Member
**
Karma: 0
Posts: 91
This is personal...
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Vamos a ver...
digitalWrite(pin, value); es una FUNCIÓN. Las funciones implican un salto en la memoria del microcontrolador donde está el programa, es como si estás leyendo un libro y te dice "ver capítulo anterior", tienes que saltar unas páginas para volver atrás, se pierde algo de tiempo. De hecho, la función digitalWrite(); incluye llamadas a funciones;
Code:
void digitalWrite(uint8_t pin, uint8_t val)
{
uint8_t timer = digitalPinToTimer(pin);
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t *out;

if (port == NOT_A_PIN) return;

// If the pin that support PWM output, we need to turn it off
// before doing a digital write.
if (timer != NOT_ON_TIMER) turnOffPWM(timer);

out = portOutputRegister(port);

uint8_t oldSREG = SREG;
cli();

if (val == LOW) {
*out &= ~bit;
} else {
*out |= bit;
}

SREG = oldSREG;
}
14 lineas más las que añadir las 6 llamadas a funciones que implementa y los saltos condicionales (los if). A esto súmale el doble (porque tienes que encender y apagar el led). Con esta función pierdes un montón de rendimiento.

PORTB= xxxxxxx; es una INSTRUCCIÓN. Las instrucciones, no todas son de un ciclo, en este caso sí lo es. Esta vendría a equivaler en ASM;
Code:
ldi PORTB, xxxxxxx
Pero para que haya frecuencia, se necesitan ciclos, los ciclos comprenden una subida y una bajada de señal, en este caso, encender un led, apagar un led. Por tanto necesitas 2 instrucciones para que el led parpadee y puedas tomar frecuencias, pero ten en cuenta que leerás la mitad de la frecuencia del reloj, porque usas 2 instrucciones.

prueba a tomar la frecuencia con el código como te comentaba en mi post anterior, pon en loop:
Code:
PORTB = 0b00000000;
PORTB = 0b11111111;
Como unas 50 veces y toma tiempos. Te dará muy aproximado a 8MH, porque son 2 instrucciones.
« Last Edit: January 14, 2012, 08:00:54 pm by PepeChorva » Logged

---
Saludos,
José Chorva
www.pepechorva.com
@pepechorva on twitter

Madrid-España
Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Perdón. Hasta ahora había programado los PICs directamente en ensamblador y pensé que en arduino la instrucción digitalWrite(ledPin, HIGH) se traducía en una sola instrucción en código máquina. smiley-confuse
Logged

Madrid-España
Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Esto ya es otra cosa.
He cargado este código:

Code:
int testPin =  12;    

void setup()   {                
  // initialize the digital pin as an output:
  pinMode(testPin, OUTPUT);    
}

void loop()                    
{
PORTB = 0b00000000;
PORTB = 0b00010000;
PORTB = 0b00000000;
PORTB = 0b00010000;
}

Y ahora sí que el arduino tarda en desactivar y activar el puerto (dos instrucciones) 125 nseg (nanosegundos) que corresponde a una frecuencia de 8 MHz.



Tarda 62,5 nseg (nanosegundos) en ejecutar una instrucción,que corresponde a una frecuencia de 16 MHz.
Desde que acaba de ejecutar la última instrucción y vuelve a la primera del bucle gasta 10 ciclos de reloj, 625 nseg (nanosegundos).
Se ve un rizado a lo largo de la señal de 32MHz, el doble del oscilador, no sé si es normal o nó. Tambien he comprobado que en los otros puertos que tendrían que estar a 0 se cuela un poco de la señal del puerto 12.

Gracias a todos por la ayuda. Me he leido varios manuales del arduino y ninguno hablaba de la gran diferencia que hay entre usar "digitalWrite(ledPin, HIGH)" y "PORTB 0b00010000" .
« Last Edit: January 15, 2012, 12:34:01 pm by troglodita » Logged

Málaga, Spain
Offline Offline
Edison Member
*
Karma: 38
Posts: 2173
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Depende de que placa sea, si es una Arduino oficial, el rizado que tiene es peor de lo que imaginaba. Si es un clon pues a saber, ...

Si la contrastsmos con la vinciDuino, parece que tienes a la filarmónica de Viena tocando dentro de esa placa, voy a ver si publico una imagen para comparar.
Logged

   

Madrid-España
Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hola fm.

He probado tu código y es más rapido, ya que el bucle for tarda sólo 2 ciclos de reloj en retornar que la funcion loop que tarda 10.

Code:
int testPin =  12;

void setup() 
{               
  pinMode(testPin, OUTPUT);   
}
void loop()                   
{
  for(;;)
  {
    PORTB = 0b00010000;
    PORTB = 0b00000000;
    PORTB = 0b00010000;
    PORTB = 0b00000000;
    PORTB = 0b00010000;
    PORTB = 0b00000000;
  }
}

PD. Leí en las normas del foro que cuando un hilo estaba solucionado había que cambiar el título del hilo añadiendo (Solucionado) pero no he visto cómo hacerlo.
Logged

Pages: [1] 2   Go Up
Jump to: