Acelerando Arduino

A

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!

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad