Madrid-España
Offline
Newbie
Karma: 0
Posts: 7
|
 |
« on: January 13, 2012, 02:14:41 pm » |
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: 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 CLKPS 3..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
Edison Member
Karma: 22
Posts: 1374
|
 |
« Reply #1 on: January 13, 2012, 02:55:35 pm » |
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
|
|
|
|
|
Málaga, Spain
Offline
Edison Member
Karma: 34
Posts: 2031
|
 |
« Reply #2 on: January 13, 2012, 03:44:11 pm » |
Si quieres algo un pelín más rápido puedes probar con este código: 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
Edison Member
Karma: 22
Posts: 1374
|
 |
« Reply #3 on: January 13, 2012, 04:16:15 pm » |
Creo que sacaron hace poco la función DigitalWriteFast, pero todavía no le he echado un vistazo, puede ser interesante
|
|
|
|
|
Logged
|
|
|
|
|
Onda
Offline
Jr. Member
Karma: 0
Posts: 81
This is personal...
|
 |
« Reply #4 on: January 13, 2012, 08:45:38 pm » |
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 
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Edison Member
Karma: 12
Posts: 1554
|
 |
« Reply #5 on: January 14, 2012, 06:18:11 am » |
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
Newbie
Karma: 0
Posts: 7
|
 |
« Reply #6 on: January 14, 2012, 10:36:38 am » |
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
Edison Member
Karma: 22
Posts: 1374
|
 |
« Reply #7 on: January 14, 2012, 10:59:10 am » |
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Edison Member
Karma: 12
Posts: 1554
|
 |
« Reply #8 on: January 14, 2012, 11:18:10 am » |
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
Edison Member
Karma: 34
Posts: 2031
|
 |
« Reply #9 on: January 14, 2012, 11:44:29 am » |
 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
Jr. Member
Karma: 0
Posts: 81
This is personal...
|
 |
« Reply #10 on: January 14, 2012, 07:28:00 pm » |
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; 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; 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: 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
|
|
|
|
|
Madrid-España
Offline
Newbie
Karma: 0
Posts: 7
|
 |
« Reply #11 on: January 15, 2012, 10:13:12 am » |
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. 
|
|
|
|
|
Logged
|
|
|
|
|
Madrid-España
Offline
Newbie
Karma: 0
Posts: 7
|
 |
« Reply #12 on: January 15, 2012, 12:30:08 pm » |
Esto ya es otra cosa. He cargado este código: 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
Edison Member
Karma: 34
Posts: 2031
|
 |
« Reply #13 on: January 15, 2012, 01:04:22 pm » |
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
Newbie
Karma: 0
Posts: 7
|
 |
« Reply #14 on: January 15, 2012, 01:23:48 pm » |
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. 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
|
|
|
|
|
|