Aventuras con Arduino

      No hay comentarios en Aventuras con Arduino

Hará como un mes que me llegó mi primer Arduino, se trataba de un Arduino Nano y estoy encantado con el juguete.

Arduino Nano (Frontal)
Arduino Nano (Frontal)

El caso es que como tenía por ahí los típicos displays de 7 segmentos pues me puse a jugar con un contador de segundos, tremenda tontería, lo se.

La técnica básica para visualizar números en este tipo de displays es unir todos los segmentos e ir visualizando dígito por dígito a gran velocidad… de esta forma, igual que en el cine, engañamos al ojo y ve todos los dígitos encendidos cuando en realidad sólo hay uno.

El problema entonces se trata de extraer para cada display el dígito correspondiente y ahí es donde me he dado de bruces con algo que tenía olvidado.

Veamos, si tenemos el número 1234 y queremos visualizar las decenas lo que hacemos es descartar los millares y las centenas y dividir el resto entre 10:

int Decenas = (Numero % 100) / 10;

Bueno, pues esto está mal… nos funcionará estupendamente en cualquier ordenador y cualquier programa pero no funcionará bien en Arduino y nos dará errores de redondeo.

El problema no es la división, al ser Decenas de tipo entero se truncarán los decimales, que es lo que queremos. Arduino no tiene funciones de redondeo, por lo que para evaluar un número de coma flotante (float) como entero (int) lo hace descartando la parte fraccional.

¿Entonces?, el problema es que antes de la división realizamos un módulo de Numero. Módulo es la operación matemática que nos devuelve el resto de una división. ¿Arduino calcula mal el módulo?, no. Para calcular el resto de una división hay que hacer varias operaciones, todas ellas en coma flotante y Arduino (más concretamente el microcontrolador Atmega328) no tiene unidad de coma flotante y ahí radica el problema.

Al no tener FPU, todos los cálculos se hacen en la ALU, que es muy limitada al tratarse de un microcontrolador de 8 bits. Así que lo que nos encontramos, al realizar el módulo, un cálculo redondeado que nos introduce un error en el siguiente cálculo…

La manera correcta de hacer lo que queremos es:

int Centenas = int(Numero / 100) % 10;

Ahora extraemos las centenas descartando primero las decenas y unidades (dividiendo entre 100) y luego descartando el resto de dígitos de la izquierda (módulo 10). Es importante que antes de realizar el módulo le indiquemos al microcontrolador que nos convierta el float resultante de la división en un int. No obstante el IDE de Arduino nos devolverá un error y no compilará si no lo hacemos.

Se puede hacer un bucle para ir extrayendo todos los dígitos:

for (int d = 0; d < NumeroDeDigitos; d++) {
  int N = int(Numero / pow(10, d)) % 10;
}

Y ya está, problema resuelto… pero que me ha tenido loco dos semanas.

Ahora vendrá el listo y me dirá que cómo no me he dado cuenta antes… pero el listo se olvida que todas las CPU desde el i486 llevan la FPU incorporada:

https://commons.wikimedia.org/wiki/File%3A80486DX2_arch.svg
Appaloosa [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/)], via Wikimedia Commons

Y claro, eso fue ayer mismo por el 1989, casi nada. Además el i386 ya era un procesador de 32 bits (año 1986) y aunque careciera de FPU, su ALU daba mejores resultados que la sencilla lógica de 8 bits del Atmega, no obstante estamos hablando de 4 veces más bits para representar los mismos números, por lo que es evidente que la precisión es mucho mayor.

Otro día, ya os contaré más cosas de Arduino.

Deja una respuesta

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