Acelerando Arduino

    Introducción

    Para muchos de nosotros, comenzamos a programar en computadoras de escritorio y servidores, que parec√≠an tener una memoria y un poder de procesamiento infinitos (bueno, dependiendo de cu√°ndo comenzaste a programar, supongo). Hab√≠a pocas razones para optimizar su c√≥digo, ya que de todos modos no era probable que superara los l√≠mites del sistema. Y luego, cuando ingres√≥ a los sistemas integrados, hubo un rudo despertar. Pasar de un sistema tan poderoso a uno mucho m√°s peque√Īo y menos capaz, como un Arduino, fue un poco impactante. De repente, tuvo que pensar en ahorrar ciclos de CPU y memoria, lo que no siempre resulta f√°cil para los programadores que reci√©n comienzan.

    Como ver√° a lo largo de este art√≠culo, el c√≥digo r√°pido no solo es importante para realizar c√°lculos, sino m√°s a√ļn para las operaciones de E / S. Si se aventura en la rob√≥tica (como drones u otros sistemas de control), esto se volver√° a√ļn m√°s claro ya que gran parte del trabajo realizado por el microcontrolador resulta en IO. Por lo general, un ciclo de retroalimentaci√≥n m√°s r√°pido significaba un mejor rendimiento.

    Despu√©s de algunas semanas de discutir con un microcontrolador para exprimir hasta el √ļltimo gramo de potencia de procesamiento posible para un controlador de vuelo de drones, pens√© en escribir un art√≠culo para ayudarlo a encontrar formas de mejorar la velocidad y la eficiencia de sus propios proyectos.

    A lo largo de este art√≠culo, me centrar√© en Arduino Uno, ya que parece ser la placa m√°s com√ļn que existe, aunque gran parte de este art√≠culo tambi√©n deber√≠a aplicarse a las otras placas.

    Por qué los arduinos son lentos

    Velocidad de reloj

    En primer lugar, es tan r√°pido como su reloj (sin tener en cuenta los procesadores de m√ļltiples n√ļcleos), que el Arduino Uno por defecto usa un cristal de 16Mhz. Lo que eso significa es que el microcontrolador ATmega puede ejecutar hasta 16 millones de instrucciones por segundo. Ahora, 16 millones de instrucciones por segundo pueden parecer mucho (y lo es, m√°s o menos), pero cuando se considera todo lo que necesita hacer un Arduino para ejecutar incluso operaciones simples, en realidad no es tanto. Para muchos proyectos, los ciclos de reloj se comparten entre cosas como c√°lculos, comunicaci√≥n I2C, lectura y escritura en pines y registros, y muchas m√°s operaciones.

    Incluso entonces, los comandos aparentemente simples pueden tomar bastantes ciclos de reloj, como establecer un pin digital en alto. Esta es una de las operaciones de E / S más simples que puede realizar en un Arduino, pero en realidad lleva mucho tiempo (¡más de 50 ciclos de reloj!) Debido a la cantidad de código utilizado en el digitalWrite() método, que abordaré en la siguiente sección. Por lo tanto, un reloj más rápido le permitiría ejecutar las instrucciones a un ritmo más rápido.

    Comprobaciones de seguridad y validación

    Entonces, fuera de la velocidad del reloj, ¬Ņpor qu√© los Arduinos son lentos? Bueno, principalmente tiene que ver con algunas de las llamadas a m√©todos est√°ndar y los objetos que usamos en nuestro c√≥digo. Estos son solo algunos de los principales culpables:

    • digitalWrite()
    • digitalRead()
    • pinMode()

    Muchos de estos métodos tienen los mismos inconvenientes, así que echemos un vistazo al código de uno de los métodos más utilizados, digitalWrite():

    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;
    }
    

    Como puede ver, aqu√≠ est√°n sucediendo muchas cosas. ¬ŅPero no deber√≠a ser mucho m√°s sencillo? Todo lo que tenemos que hacer es poner el pin alto o bajo en un registro, ¬Ņverdad? Resulta que los creadores de Arduino decidieron que era m√°s importante agregar controles de seguridad y validaci√≥n al c√≥digo que hacer que el c√≥digo sea r√°pido. Despu√©s de todo, esta es una plataforma dirigida m√°s a los principiantes y la educaci√≥n que a los usuarios avanzados y las aplicaciones de uso intensivo de CPU.

    Las primeras l√≠neas usan el pin par√°metro para encontrar el correspondiente timer, bity port para el pin dado. los port en realidad es solo un registro mapeado en memoria, que controla m√ļltiples pines. Para activar o desactivar solo el pin que queremos, necesitamos determinar a qu√© bit del registro corresponde nuestro pin, que es el digitalPinToBitMask() funci√≥n hace.

    Una vez que hayamos encontrado el timer, bity port, verificamos para asegurarnos de que sea un pin v√°lido. Esta l√≠nea no es necesaria para digitalWrite() para hacer su trabajo, pero act√ļa como una red de seguridad para los programadores menos experimentados (e incluso los experimentados). Odiar√≠amos escribir en la ubicaci√≥n de memoria incorrecta y corromper el programa.

    los if (timer != NOT_ON_TIMER) ... La l√≠nea est√° ah√≠ para asegurarnos de que finalizamos cualquier uso previo de PWM del pin antes de escribir una “constante” alta o baja. Muchos de los pines en Arduinos tambi√©n se pueden usar para salida PWM, que requiere un temporizador para operar cronometrando los ciclos de trabajo. Si es necesario, esta l√≠nea apagar√° el PWM. De nuevo, para asegurarnos de que no veamos ning√ļn comportamiento extra√Īo, esta es una verificaci√≥n de seguridad destinada a ayudar al usuario.

    Y finalmente, en las √ļltimas l√≠neas, estamos configurando el valor dado para el puerto dado.

    Las comprobaciones de seguridad ralentizan un poco la ejecuci√≥n, pero tambi√©n facilitan mucho la depuraci√≥n. De esta manera, cuando algo sale mal, es menos probable que tenga un comportamiento extra√Īo que lo deje rasc√°ndose la cabeza. No hay nada m√°s frustrante que tener un c√≥digo aparentemente l√≥gico y no obtener el resultado esperado. La programaci√≥n de microcontroladores es muy diferente a la programaci√≥n de aplicaciones de escritorio o tel√©fono (aunque tambi√©n tienen su parte justa de dificultades). Dado que est√° trabajando directamente con hardware y no tiene un sistema operativo que lo mantenga seguro, los problemas pueden ser dif√≠ciles de encontrar.

    Si la velocidad no es su objetivo, le recomiendo que contin√ļe utilizando este m√©todo proporcionado por Arduino. No tiene sentido exponerse a un riesgo innecesario si no le ayuda a alcanzar su objetivo final.

    Tambi√©n debe saber que las llamadas al m√©todo no siempre son lentas debido a la cantidad de c√≥digo que ejecuta, pero un factor que contribuye podr√≠a deberse a las limitaciones f√≠sicas del dispositivo. Por ejemplo, analogRead() tarda unos 100 microsegundos por llamada debido a la resoluci√≥n que proporciona y al reloj que se suministra. Una resoluci√≥n de ADC m√°s baja disminuir√≠a el tiempo que toma cada llamada. Sin embargo, incluso entonces, mientras que el hardware es en √ļltima instancia el factor limitante aqu√≠, el c√≥digo Arduino establece de manera conservadora la frecuencia de muestreo m√°xima de ADC en solo 9600Hz (mientras que es capaz de alrededor de 77Khz). Entonces, si bien los Arduinos son mucho m√°s lentos de lo necesario, no siempre se debe a las opciones de dise√Īo y las compensaciones. Hay una buena discusi√≥n sobre esto. aqu√≠y documentaci√≥n aqu√≠.

    Cómo acelerar Arduino

    Para ser claros, en realidad no estamos haciendo que Arduino sea m√°s r√°pido, sino que estamos haciendo que el c√≥digo sea m√°s eficiente. Se√Īalo esta distinci√≥n porque el uso de estos trucos no nos dar√° un reloj m√°s r√°pido (aunque podemos acelerar el reloj, que hablar√© m√°s adelante), simplemente ejecutar√° menos c√≥digo. Esta es una distinci√≥n importante porque tener un reloj m√°s r√°pido nos brinda otros beneficios, como tener temporizadores m√°s precisos, comunicaci√≥n m√°s r√°pida, etc.

    Adem√°s, tenga en cuenta que al usar el c√≥digo a continuaci√≥n, est√° haciendo algunas concesiones. Los programadores que desarrollaron Arduino no eran simplemente codificadores p√©simos que no pod√≠an escribir c√≥digo r√°pido, sino que tomaron conscientemente la decisi√≥n de agregar validaciones y verificaciones de seguridad a m√©todos como digitalWrite() ya que beneficia a sus clientes objetivo. Solo aseg√ļrese de comprender qu√© puede (y qu√© saldr√° mal) con este tipo de compensaciones.

    De todos modos, vamos al código.

    Escritura digital

    Ahora, no voy a mostrarte cómo acelerar cada método, pero muchos de los mismos conceptos de aquí se pueden aplicar a otros métodos como pinMode(). La cantidad mínima de código que necesita escribir en un pin es:

    #define CLR(x,y) (x&=(~(1<<y)))
    #define SET(x,y) (x|=(1<<y))
    

    SIP eso es.

    Como puede ver, vamos directo al grano en estas macros. Para usarlos, deber√° hacer referencia tanto al puerto como a la posici√≥n del bit directamente en lugar de usar convenientemente los n√ļmeros de pin. Por ejemplo, estar√≠amos usando la macro as√≠:

    SET(PORTB, 0);
    

    Esto terminaría escribiendo un HIGH valor al pin 8 en su Arduino Uno. Es un poco difícil, pero mucho más rápido. Esto también significa que somos más propensos a hacer algo mal, como hacer referencia a un puerto inexistente, escribir sobre un PWM activo o una serie de otras cosas.

    La macro nos da un gran impulso, gastando un estimado 2 ciclos (Frecuencia de 8 Mhz), mientras que digitalWrite() usa una friolera 56 ciclos (Frecuencia 285Khz).

    De serie

    Desafortunadamente para algunas tareas, como si necesita usar la comunicación en serie, no hay mucho que pueda hacer para mejorar la velocidad, pero hay algunas optimizaciones que puede tener en cuenta.

    La comunicaci√≥n en serie se usa com√ļnmente para enviar informaci√≥n de estado o depuraci√≥n al IDE de escritorio, lo que significa que probablemente tenga Serial.println() declaraciones a lo largo de su c√≥digo. Es f√°cil olvidarse de estas declaraciones despu√©s del desarrollo, por lo que si est√° buscando un aumento de velocidad y ya no necesita depurar, intente eliminar todas las println() llamadas y eliminaci√≥n Serial del c√≥digo por completo. Solo tenerlo inicializado (y ni siquiera usar Serial.println()) significa que est√° desperdiciando muchos ciclos en las interrupciones TX y RX. En Un caso, se midi√≥ que con solo tener Serial habilitado ralentiz√≥ el digitalWrite()s en aproximadamente un 18%. Eso es un mont√≥n de ciclos desperdiciados debido al c√≥digo muerto.

    Velocidad de reloj

    Aunque me he centrado principalmente en las mejoras de software que puede realizar, no olvide que siempre existe la mejora “simple” de acelerar el tiempo. Lo digo de esta manera porque, despu√©s de todo, no es realmente una simple mejora plug-and-play. Para acelerar el reloj en un Arduino, debe insertar un nuevo cristal en la placa, lo que puede ser dif√≠cil o no seg√ļn sus habilidades de soldadura.

    Una vez que haya instalado un nuevo oscilador de cristal, a√ļn debe actualizar el cargador de arranque para reflejar el cambio; de lo contrario, no podr√° recibir c√≥digo a trav√©s del puerto serie. Y por √ļltimo, deber√° cambiar el F_CPU valor a la velocidad de reloj adecuada. Si aument√≥ el reloj a 20Mhz (el reloj m√°s r√°pido para el que est√° clasificado el ATmega), por ejemplo, deber√° modificar algunos archivos en el IDE de Arduino:

    • En preferences.txt, cambio
      • desde: build.f_cpu=16000000L
      • a: build.f_cpu=20000000L
    • En el makefile, cambio
      • desde: F_CPU = 16000000
      • a: F_CPU = 20000000

    De acuerdo a esta publicación, el ATmega328 se puede overclockear a 30Mhz, pero no lo recomiendo =)

    Conclusión

    Espero que hayas encontrado algo en esta publicación que puedas aplicar fácilmente a tus proyectos, o al menos espero que te anime a explorar el código fuente de Arduino para encontrar tus propias optimizaciones. El Arduino es un microcontrolador muy capaz, pero puede ser capaz de mucho más.

    ¬ŅTiene alguna optimizaci√≥n propia que le gustar√≠a compartir? ¬°H√°znoslo saber en los comentarios!

    ¡No olvide registrarse en nuestra lista de correo para recibir nuestros mejores artículos directamente en su bandeja de entrada!

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *