Publi

Controlando un display de 7 segmentos con Arduino y un botón por interrupción.

Arduino 7 segmentosRetomo mis posts sobre Arduino (después de muchísimo tiempo) con un pequeño ejemplo que monté hace unos años, pero el post nunca llegó a salir. Se trata de la utilización de un display de 7 segmentos desde la propia placa Arduino. Estos displays, al final son 7 barras led con las que podemos representar números y letras dependiendo de las que encendemos y las que no. En mi ejemplo, haremos un contador de 0 a 9 controlado por un pulsador.

El display

Para ver más a fondo cómo funciona el display, lo más fácil es ver este dibujo:

Este ejemplo muestra un display de cátodo común, ya que las patillas centrales van a gnd (sólo hay que conectar una) y las demás (a, b, c, d, e, f, g, dp) podrán ir conectadas a VCC. Todo depende de las que queramos encender. En mi caso, alimento cada uno de los leds con unos 3.6V.

También existen displays de ánodo común, como el que utilizo en este proyecto. En este caso conecto el display a VCC (5V) y cada uno de los segmentos van conectados a GND a través de una resistencia.

Conectar el display directamente a Arduino

Es una opción. Nuestro display tiene 7 segmentos, y normalmente los Arduinos (Duemilanove, Uno, Mega…) suelen tener muchos pines de entrada-salida (más de 7) configurables y capaces de soportarlo. Así que perfectamente podemos conectar cada segmento a un pin del Arduino y hacer un programa que escriba valores en dichos pines.

En principio no es mala idea, nosotros controlaremos al 100% la iluminación de los segmentos. Aunque podemos tener varios problemas:

  • Nuestro Arduino se calentará. El display al final pide potencia, y puede que nuestro Arduino trabaje en exceso para entregar la potencia necesaria para iluminar el display. Podemos poner resistencias más grandes, pero el display lucirá menos.
  • Son muchos pines, para controlarlos podemos usar digitalWrite() y es un poco tostón controlar los digitalWrite() de cada número que vamos a representar. Por otro lado podemos hacerlo usando los PORT del Atmega (lo veremos en el ejemplo), pero claro, el PORTD (pines 0-7) no podemos acapararlo entero porque por ejemplo las interrupciones de algunos Arduino (como el mío, un Duemilanove, aunque sea algo viejo) están en los pines 2 y 3 por lo que no podremos utilizarlos. Y el PORTB (pines 8-13) sólo son 6 por lo que no son suficientes. Así que tendremos que pasar parte por un puerto y parte por otro puerto. Pero a fin de cuentas, si queremos hacer algo más con nuestra placa hemos gastado muchos pines para este propósito.
  • Impensable ya pasar el montaje a un circuito a base de ATtiny, por ejemplo.

Por poner un ejemplo más, si queremos utilizar puertos en lugar de digitalWrite(), lo primero es mirar el datasheet del chip que estemos utilizando, en mi caso Atmega168. Tendremos que mirar a qué pines corresponden los puertos. Yo utilizaré el puerto D y el puerto B para conectar el display.

Lo primero será crear un array con los valores de los segmentos que corresponderán a cada dígito. Daremos valores en hexadecimal porque serán más fáciles de manejar cuando tienes algo de vicio, sobre todo cuando debemos mirar el valor en binario de cada uno de los valores. En mi array, como mi display es de ánodo común tengo que mandar ceros a los dígitos que quiero encender. En este caso, sólo será un ejemplo, haremos algo así:

1
int disp[10]={0x81, 0xdd, 0x43, 0x45, 0x1d, 0x25, /**/0x21, 0xcd, 0x01, 0x05};

Con este array, hacemos que la posición 0 contenga 1000 0001 (al 0 sólo le falta un segmento, el último bit no lo usaremos), en la posición 1 tendremos 1101 1101 (el 1 sólo tiene dos segmentos activos), en la posición 2 tendremos 0100 0011 (el dos sólo tiene 2 segmentos desactivados), etc.

Y, para enviar los valores a los puertos podríamos utilizar el desplazamiento binario (shift):

1
2
PORTB = disp[5] >> 3; // A la parte baja del PORTB
PORTD = disp[5] << 4; // A la parte alta del PORTD

Los puertos

Veamos primero un diagrama de los pines del Atmega368, por ejemplo (muy parecido al del Atmega168:

Vemos que hay pines etiquetados como PB0..PB7, PC0..PC6, PD0..PD7. Corresponderán a los puertos B, C y D respectivamente. Si hemos manejado Arduino anteriormente habremos visto cómo digitalRead/digitalWrite nos puede solucionar la vida cuando se trata de leer o escribir el valor de un pin concreto. Aunque lo que yo quiero es definir el valor completo de un puerto. Es decir, enviar, por ejemplo un valor 11011011 de una sola vez a todos los pines del puerto D, por ejemplo.

Para ello, lo primero que tenemos que hacer será definir una máscara de entrada/salida de los pines del puerto, lo que anteriormente hacíamos con pinMode([pin], OUTPUT); pero esta vez lo haremos definiendo en la función setup() o en cualquier otro sitio la variable DDRD dando 1 al bit correspondiente con el pin que queremos que sea de salida (OUTPUT) o 0 si queremos que sea de entrada (INPUT):

1
2
3
DDRD=0xff; // Puerto D entero como salida
DDRD=0xdb; // 11011011
DDRD= 0 | 1 << PD5 | 1 << PD3 | 1 << PD1; // Sólo pins 1, 3 y 5 como salida.

Con la última forma de hacerlo, podemos pensar que se realizan operaciones en el chip e irá más lento, pero al ser PD5, PD3 y PD1 constantes, en tiempo de compilación se procesará el número y el programa real se ejecutará con una sola instrucción, por lo que es tan rápido como el caso que hay justo encima en el que nosotros hemos calculado el número.

Ahora para escribir valores en el puerto puerto, escribiremos la variable PORTD y para leer valores, leeremos la variable PIND.

Decodificador de 7segmentos 7447, 7448

Para arreglar los problemas mencionados anteriormente (potencia y número de pins), vamos a utilizar el circuito integrado 7447, ya que nuestro display es de ánodo común. Si fuera de cátodo común utilizaríamos el 7448. Este es un circuito TTL decodificador de BCD (binary-coded decimal, o decimal codificado en binario) a display de 7 segmentos. Ya que el sistema utilizado es digital no se garantiza que la salida para valores mayores a 9 sea la misma en todos los chips. Dada su tecnología requiere una corriente baja de entrada, de algunos microamperios, y proporciona una corriente de salida del orden de los miliamperios, por lo que puede hacer las veces de amplificador de la señal haciendo que nuestro Atmega no sufra.Haremos las conexiones como en el siguiente diagrama (como en la foto de portada):

Éstos son los materiales utilizados:

  • Arduino Duemilanove
  • Display de 7 segmentos de ánodo común
  • DM74LS74N (decodificador BCD-7 segmentos)
  • 1 diodo led (es para jugar, sólo parpadeará indefinidamente
  • 8 resistencias de 1K
  • 1 resistencia de 10K (para el pulsador)
  • 1 switch

El esquema del proyecto será el siguiente:

Vamos al código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int num=0;

ISR(INT0_vect)
{
  num++;
  if (num>9)
    num=0;
   
  PORTB=0xf0 & PORTB | 0x0f & num;
}

void setup()
{
  // put your setup code here, to run once:
  DDRB=0xff;
  DDRD= 0;
  pinMode(LED_BUILTIN, OUTPUT);
  PORTB=0xf0 & PORTB | 0x0f & num;

  EICRA=0x03;
  EIMSK=1;
 
  sei();
}

void loop()
{
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);                      
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
}

Empecemos por setup(). En donde con DDRB preparamos el puerto B como salida y el puerto D como entrada. Como es el puerto entero, le damos un 0 y listo. Preparamos la salida del led, aunque no haría falta especificar el pinMode porque el LED_BUILTIN apunta al pin 13 que es del puerto B que ya está marcado como salida, pero dejo esa línea ahí por si cambiamos el led de sitio. Simplemente es un led que parpadea para demostrar el funcionamiento de la interrupción.

Seguidamente presentamos el número en el display, aunque ya que el display y el led comparten puerto (el puerto B para todos), quise poner la parte alta del puerto B tal cual está (0xf0 & PORTB) y en la parte baja del puerto B pongo el número a representar (0x0f & num).

EICRA y EIMSK son dos valores que tengo que establecer para que mi Atmega escuche a las interrupciones hardware. Esto depende mucho del chip y no en todos los Arduinos funcionará igual y, aunque la plataforma Arduino tiene su propia manera de hacer las interrupciones, he querido poner esta forma aquí.

Por último llamo a sei() para activar el flag de interrupciones de Atmega.

Luego en el loop() sólo me encargaré de actualizar el estado del led, apagarlo y encenderlo sin preocuparme por el pulsador. Por último, tenemos ISR(INT0_vect), una función donde estableceremos las acciones que se llevan a cabo cuando se activa la interrupción. En este caso incrementar el número y enviarlo a PORTB.

También podría interesarte...

Leave a Reply