Multiplexado de displays sin ocupar ciclos de tiempo del programa principal


Es muy común ver por ahí dando vueltas circuitos que involucren displays de siete segmentos conectados con sus segmentos en paralelo y controlando individualmente el encendido de todo el dígito, técnica de multiplexado muy difundida a tal punto que desde hace años existen como único componente bloques de dos, tres o hasta cuatro de estos dígitos. Los hay del tipo cátodo común donde a los segmentos hay que darle positivo y a los dígitos negativo o su inverso el ánodo común. Pequeños, mas grandes, enormes! Existen cantidades para hacer dulce... En una configuración típica de un reloj donde tenemos cuatro dígitos como mínimo (sin mostrar segundero) si dedicamos un milisegundo por cada display para que consiga obtener brillo y atendemos el encendido de este display por medio de demoras de tiempo en el código mismo, los famosos delay_ms(1) en este caso entenderemos que a una velocidad de funcionamiento del microcontrolador de 4MHz, con un tiempo por instrucción de 1 sobre 1.000.000 porque recordemos que demanda cuatro ciclos de reloj un ciclo de instrucción en los PIC veremos que por cada vez que estamos atendiendo un display el programa deja de hacer MIL instrucciones por lo que detectar en el tiempo preciso cuando sucede algo se vuelve mil veces menos efectivo. Si a eso le sumamos que algunas rutinas de display atienden de un saque los cuatro dígitos juntos tendremos que podríamos ver una entrada, por ejemplo, cada 4.000 ciclos de instrucción y eso teniendo presente que paremos solo 1 milisegundos por dígito. Si hacemos un stop de 3ms por display que no es nada descabellado tendremos 1 mirada a una entrada cada 12.000 instrucciones perdidas en display... Francamente un delirio!

No hay que usar un PIC16F627 necesariamente, yo usé ese porque estaba a mano y en verdad en esta nota se trata una técnica que incluso excede los PIC's y se aplica a cualquier sistema programado. El display que utilicé es del tipo CC nuevamente porque es el que tengo acá. Las resistencias de 10 ohms restringen la circulación de corriente para no sobrepasar el límite eléctrico de las salidas del microcontrolador. El conexionado entre el microcontrolador y el display puede parecer algo desordenado pero responde a que en el protoboard con el uC de un lado y el display del otro quería que los alambres sean lo mas cortos posible y sin tantos recorridos.

En esta foto se puede observar en el papel el estudio o análisis de a que pata del display se conecta cada segmento y cada dígito y un detalle que creo es muy importante nótese que en la pantalla de la computadora está la simulación en Proteus de este circuito, en general Proteus ayuda muchísimo en los desarrollos sobre todo en las fases de depuración pero aquí vemos que no es 100% fiable dado que en el protoboard queda claro que esto funciona muy bien se ve clarito 1 2 3 mientras que en la pantalla de la computadora se ve cualquier cosa en leos dígitos del display.

void main()
{
   setup_timer_2(T2_DIV_BY_16,62,1); //1.0 ms overflow, 1.0 ms interrupt
   enable_interrupts(INT_TIMER2);
   enable_interrupts(GLOBAL);

   while(TRUE)
   {
      //TODO: User Code
   }
}

Como se ve en el segmento copiado del programa arriba todo lo que se hace es configurar el timer 2 con los datos necesarios para producir interrupciones de 1ms en adelante, cambiando el último número logramos cambiar la cantidad de milisegundos entre interrupciones. Este es un dato de 8 bits por lo que como muy lerdo puede hacer una espera de hasta 255 mili segundos. Como determiné estos valores ? Simple, con el asistente para nuevo proyecto o Project Wizard del CCS C. Una vez configurado el sistema de temporización habilito las interrupciones por esta vía y el programa queda relegado al bloque While infinito que sigue que en este caso para mas claridad NO HACE NADA.

#INT_TIMER2
void TIMER2_isr(void)
{
   if(DIGITO==1)
   {
      output_b(0b00101000);
      output_low(DIGITOC);
      output_high(DIGITOD);
      output_high(DIGITOU);
   }
   if(DIGITO==2)
   {
      output_b(0b10110011);
      output_high(DIGITOC);
      output_low(DIGITOD);
      output_high(DIGITOU);
   }
   if(DIGITO==3)
   {
      output_b(0b10111010);
      output_high(DIGITOC);
      output_high(DIGITOD);
      output_low(DIGITOU);
   }
   DIGITO++;
   if(DIGITO==4) DIGITO=1;
}

En tanto una vez cada milisegundo se ejecuta este código, la rutina de atención a la interrupción. Hay una posición de memoria del tipo char (entero de ocho bits) que contiene un número, al arrancar el programa se la definió con 1 como valor. Entonces en la primer interrupción el programa chequea por igualdad de dicha posición de memoria ante 1 y como veritica coloca en el puerto B los 1 y 0 o positivo y masa necesarios para que los segmentos dibujen un 1 con un poco de esmero se nota que solo hay dos unos por lo cual son los segmentos B y C del display y seguidamente se pone a masa el CC correspondiente al dígito de centenas y se pone a positivo los CC correspondientes a los dígitos de decenas y unidades. Por ello en el display se muestra el 1 en el display de la izquierda. Los otros dos if no verifican porque DIGITO sigue valiendo 1  antes de salir de la interrupción se le suma 1 al dato presente en dicha posición de memoria. Se pregunta si hay 4, no lo hay, sólo hay 2 y se sale. A donde ? A donde estaba la ejecución del programa antes de interrumpir, o sea al ciclo vacío del While en el loop. Cuanto demoró esto en ejecutarse ? 2 ciclos de máquina para entrar aquí, dos para la verificación de cada if (o sea 8 ciclos de máquina) y cinco ciclos de máquina en el if que sí haya entrado porque enviar algo al puerto demanda 2 ciclos y manipular bits solo 1. Entonces demoramos los 1000 microsegundos de un milisegundos con solo 15 microsegundos con un oscilador de 4MHz. Dentro de mil microsegundos o un milisegundos se vuelve a interrumpir ahora entra al segundo if mas sigue la misma lógica. Dentro de otro milisegundo interrumpe por tercera vez e ingresa al tercer if pero al antes de salir DIGITO quedará en 4 por lo que el if lo volverá 1 quedando así listo para repetir una pasada desde el comienzo.

Como siempre les dejo para descargar el fuente, el archivo ya compilado y listo para grabar en el micro que de mucho no sirve! y el proyecto de simulación que tiene sus problemas!.

Proyecto CCS C    /   Archivo HEX para grabar el uC  /  Proyecto de Proteus para simular el circuito

No quise ponerle ni botones para incrementar o decrementar el conteo ni nada mas que lo esencial, ni siquiera una tabla de caracteres! Porque lo importante es la técnica de temporización

 

Pablo Canello, 06/04/2020