Display LED con PIC16F628A






      En este post veremos como podemos manejar cuatro displays LED de 7 segmentos y punto decimal con un microcontrolador PIC16F628A. Veremos también como realizar una serie de proyectos de aplicación (contador de 0 a 9, contador de 0 a 9999 y reloj digital), todo ello programado en lenguaje C con el compilador CCS.

      Para esta práctica vamos a necesitar los siguientes materiales:
  • Una placa para prototipos
  • Un PIC16F628A
  • Un cristal de 8MHz
  • Cuatro Displays LED con punto decimal de cátodo común
  • Cuatro transistores MOSFET BS170
  • Dos pulsadores micro-switch
  • Dos condensadores cerámicos de 33pF
  • Dos resistencias de 4.7kΩ
  • Ocho resistencias de 470Ω
  • Cable rígido
  • Fuente de alimentación
  • Hardware y Software necesario para programar el PIC (¿Cómo programar un PIC?)


      Empezaremos explicando como funciona un Display LED. Los displays LED son componentes optoelectrónicos que por el uso de LEDs, son capaces de generar luz con muy poca intensidad de corriente. Estos LEDs están dispuestos en una carcasa en forma de 8. . Encendiendo los LEDs como convenga, podremos realizar cualquier número del 0 al 9.


      Un display LED de 7 segmentos que tenga punto decimal, constará de 8 LEDs (uno para cada segmento y otro para el punto decimal). De la carcasa salen una serie de patillas de conexión, por lo general en un display de estas características deben ser 10 (8 para cada LED y las 2 sobrantes están internamente conectadas y son el común). Existen dos tipos de displays: de ánodo común y de cátodo común. Nosotros utilizaremos de cátodo común, es decir, el positivo es la patilla del LED en cuestión mientras que el negativo será el común. En nuestro caso, manteniendo una de las patillas del común a tierra (o negativo) y aplicando un voltaje positivo (5V) a una de las patillas a través de una resistencia de protección de 470Ω, se encenderá el LED correspondiente a esa patilla:



      Cada segmento del display se diferencia mediante una letra de la siguiente manera. Podemos ver también la disposición de las patillas del display (imagen vista desde arriba):


      Ahora procedemos a montar en la placa para prototipos un display de cátodo común y un microcontrolador PIC16F628A (DataSheet) y realizaremos el siguiente montaje:



      RB0 → A
      RB1 → B
      RB2 → C
      RB3 → D
      RB4 → E
      RB5 → F
      RB6 → G

      Hecho esto, procederemos a realizar el código del programa para poder mostrar los diez símbolos numéricos: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
      Para ello creamos un nuevo archivo en nuestra carpeta de proyectos CCS con el nombre que queramos y extensión *.c, por ejemplo “contador09.c” y lo abrimos.

      Escribimos el siguiente código:

//
//            CONTADOR 0-9
//
// Cuenta del 0 al 9 cada segundo 
//
//             PIC16F628A
//
// Uso de display de 7 segmentos
//
// Copyright © 2015 Aaron G.
// All rights reserved.


#include <16f628a.h>
#fuses hs,nowdt,nomclr,nobrownout,nolvp,nocpd,noprotect
#use delay(xtal=8000000)

int8 x=0;
const int8 Seg7[10]={63,6,91,79,102,109,125,7,127,111};

void main(void){
      set_tris_b(0);
      while(1){
            output_b(Seg7[x]);
            delay_ms(1000);
            x++;
            if(x==10) reset_cpu();
      }
}


      Este programa cuenta del 0 al 9 cada segundo. Para poder generar los dígitos, utilizamos un array de longitud 10 (Seg7[]) y en cada posición de éste, se encuentra un número entero que al ser representado de forma binaria en el puerto B, se encenderán los LEDs necesarios para generar el símbolo numérico correspondiente a esa posición en el array. Por ejemplo, el número 2, para generarlo es necesario sacar por el puerto B el valor entero 91 (ya que el 0 es el 63 y el 1 es el 6). El número 91 en binario es 01011011, al ser representado en el puerto B, el bit menos significativo coincide con el pin RB0. De esta manera, quedarán en estado alto los pines RB0, RB1, RB3, RB4 y RB6 y tal y como tenemos conectado el display, se encenderán los LEDs correspondientes a los segmentos A, B, D, E y G, generando así el número 2. Cada segundo se aumenta en una unidad el valor de la variable x y en cuanto ésta llega a 10, se resetea el microcontrolador para volver a empezar desde 0.



      Ahora bien, ¿y si queremos representar mas cifras? … pues será necesario incorporar mas displays. En la siguiente practica realizaremos un contador de 0 a 9999 utilizando 4 displays. Si conectásemos cada display como lo hemos hecho anteriormente, necesitaríamos 4 puertos del microcontrolador (32 pines de salidas digitales), pero el PIC16F628A solo tiene 2. Así que vamos a recurrir a una técnica llamada barrido secuencial.
      Consiste en encender solo un display en cada instante representando el valor de éste y después encender el siguiente y mostrar el valor de éste otro, todo ello utilizando solo un puerto para encender los LEDs del display y otros cuatro pines del otro puerto para encender el display correspondiente en cada momento. Por lo tanto, será necesario conectar todos los displays en paralelo excepto sus comunes y colocar un transistor MOSFET BS170 (DataSheet) en cada uno de ellos, con el objetivo de poder manejar el encendido de cada display por separado desde el otro puerto del PIC.

      Si se realiza el barrido completo a una frecuencia lo suficientemente alta (más de 50Hz), no se notará en ningún momento que esté solo encendido uno, debido a la persistencia de la visión.
De esta manera, podremos controlar hasta 8 displays con solo 2 puertos del microcontrolador.

      Realizaremos el siguiente montaje en la placa para prototipos:


      RB0 → A
      RB1 → B
      RB2 → C
      RB3 → D
      RB4 → E
      RB5 → F
      RB6 → G
      RB7 → P (punto decimal)

      RA0 → Q1
      RA1 → Q2
      RA2 → Q3
      RA3 → Q4

      En este circuito se han añadido otros tres displays conectados en paralelo y controlados cada uno por los cuatro transistores MOSFET BS170. También se ha añadido una octava resistencia de 470Ω para poder manejar el punto decimal en el siguiente proyecto, así como dos pulsadores con sus respectivas resistencias pullup de 4.7kΩ en MCLR y RA4.

      Abrimos CCS y escribimos el siguiente código:
//
//            CONTADOR 0-9999
//
// Cuenta del 0 al 9999 aumentando 
// en una unidad cada vez que se 
// pulsa un pulsador conectado a
// RA4.
//
//             PIC16F628A
//
// Barrido secuencial de displays
//
// Copyright © 2015 Aaron G.
// All rights reserved.


#include <16f628a.h>
#fuses hs,nowdt,mclr,nobrownout,nolvp,nocpd,noprotect
#use delay(xtal=8000000)
#define puls pin_a4

int16 x=0;
const int8 Seg7[10]={63,6,91,79,102,109,125,7,127,111};

void Display_num(void){
      output_a(0b00000001);
      if(!((x/1000)%10)) output_b(0);
      else output_b(Seg7[(x/1000)%10]);
      delay_ms(4);
      output_b(0);
      output_a(0b00000010);
      if((!((x/100)%10))&&(x<1000)) output_b(0);
      else output_b(Seg7[(x/100)%10]);
      delay_ms(4);
      output_b(0);
      output_a(0b00000100);
      if((!((x/10)%10))&&(x<100)) output_b(0);
      else output_b(Seg7[(x/10)%10]);
      delay_ms(4);
      output_b(0);
      output_a(0b00001000);
      output_b(Seg7[x%10]);
      delay_ms(4);
      output_b(0);
}

void main(void){
      set_tris_a(0x10);
      set_tris_b(0x00);
      while(1){
            if(!input(puls)){
                  while(!input(puls)) Display_num();
                  x++;
                  if(x==10000) reset_cpu();
            }
            Display_num();
      }
}


      Con este programa cargado en el microcontrolador, tendremos un contador de 0 a 9999. Al pulsar y soltar el pulsador ++, aumenta en uno el contador y al llegar a 9999, se resetea el sistema para volver a empezar.
      Se utiliza una función (Display_num()) para realizar el barrido a los displays. Primero enciende el display de la izquierda, después representa el dato durante 4ms y finalmente vuelve a poner a 0 el puerto de datos para que quede libre en el siguiente display. Unos condicionales if(), nos permiten encender sólo el display cuando se necesite, con el objetivo de evitar que aparezcan ceros a la izquierda. Se utilizan operaciones de residuo de división para poder extraer las cifras de las unidades, decenas, centenas y miles de la variable x, para poder representar cada cifra en un display.
      El botón marcado como R que está conectado al pin MCLR del microcontrolador, nos permite resetearlo para poder poner a 0 el sistema en cualquier momento.


      Vamos a realizar un último proyecto con el circuito que tenemos montado en la placa de prototipos, se trata de un reloj digital. Para ello vamos a utilizar el Timer1 del microcontrolador y un algoritmo que vaya actualizando la hora mediante las interrupciones de éste. Manteniendo el circuito anterior, los pulsadores van a ser ahora, para aumentar las horas: el que está conectado a RA4 y para aumentar los minutos: el que está conectado a MCLR.


      Abrimos CCS y escribimos este código:

//
//            RELOJ DIGITAL
//
// Muestra hora y minutos con 4 displays 
// de 7 segmentos de catodo comun. Se  
// realiza un barrido a los 4 displays 
// cada 16ms con el objetivo de hacer  
// parecer que estan todos encendidos 
// cuando en realidad solo se enciende  
// uno, simplificando considerablemente 
// el circuito.
//
// La hora puede ser ajustada mediante
// dos pulsadores que aumentan las horas
// y los minuto respectivamente con cada
// pulsacion.
// 
//             PIC16F628A
//
//           TIMER1 (16bits)
//
// Copyright © 2015 Aaron G.
// All rights reserved.


#include <16f628a.h>
#fuses hs,nowdt,noput,nomclr,nobrownout,nolvp,nocpd,noprotect
#use delay(xtal=8000000)
#define freq_inst 2000000
#define ph pin_a4
#define pm pin_a5

         // Cabecera: dispositivo, configuracion, frecuencia, constantes.



int1 dt=true;
int32 Ticker;
int8 Hora=0,Minuto=0,Segundo=0;
const int8 Seg7[2][10]={{63,6,91,79,102,109,125,7,127,111},
              {191,134,219,207,230,237,253,135,255,239}};

         // Variables y constantes globales: dt (encender y apagar el punto
         // decimal cada medio segundo); Ticker (mantener constante la base de 
         // tiempo real); Hora,Minuto,Segundo (Hora actual); Seg7[][] 
         // (Representar cada simbolo numerico del 0 al 9 con punto y sin punto 
         // respectivamente a cada dimension).
         


#int_TIMER1

void TIMER1_isr(void){
   Ticker-=131072;
   if(Ticker<131072){
      Ticker+=freq_inst;
      dt=!dt;
      if(dt){
         Segundo++;
         if(Segundo==60){
            Minuto++;
            Segundo=0;
            if(Minuto==60){
               Hora++;
               Minuto=0;
               if(Hora==24) Hora=0;
            }
         }
      }
   }
}

         // TIMER1_isr(): funcion de interrupcion del timer1. Mantiene una base
         // de tiempo constante y preciso basandose en el tiempo de instrucción
         // (1/4 de la frecuencia del cristal). Refresca las variables de la hora.
         
         
         
void RTC_Init(void){
   Ticker=freq_inst;
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
   enable_interrupts(INT_TIMER1);
}

         // RTC_Init(): funcion para iniciar las interrupciones por 
         // desbordamiento del timer1.



void Display_hora(void){
   output_a(0b00000001);
   if(!((Hora/10)%10)) output_b(0);
   else output_b(Seg7[false][(Hora/10)%10]);
   delay_ms(4);
   output_b(0);
   output_a(0b00000010);
   output_b(Seg7[dt][Hora%10]);
   delay_ms(4);
   output_b(0);
   output_a(0b00000100);
   output_b(Seg7[false][(Minuto/10)%10]);
   delay_ms(4);
   output_b(0);
   output_a(0b00001000);
   output_b(Seg7[false][Minuto%10]);
   delay_ms(4);
   output_b(0);
}

         // Display_hora(): funcion para hacer el barrido de los displays
         // representando los valores de las variables Hora y Minuto.



void Puls_read(void){
   if(!input(ph)){
      while(!input(ph)) Display_hora();
      Hora++;
      if(Hora==24) Hora=0;
   }
   if(!input(pm)){
      while(!input(pm)) Display_hora();
      Minuto++;
      if(Minuto==60) Minuto=0;
   }
}

         // Puls_read(): funcion para leer el estado de los pulsadores y si
         // estos se pulsan, incrementar las variables Hora y Minuto.



void main(){                           // funcion main()
   RTC_Init();                         // Inicio del programa: iniciar el RTC
   enable_interrupts(GLOBAL);          // asi como establecer los pines de 
   setup_comparator(nc_nc_nc_nc);      // entrada-salida.
   set_tris_a(0x30);
   set_tris_b(0x00);
   while(1){                           // funcion while(): bucle infinito, 
      Display_hora();                  // ejecuta las funciones Display_hora()
      Puls_read();                     // y Puls_read()
   }
}



      En el código podemos ver que estamos utilizando un array de dos dimensiones. Esto es para poder manejar el punto decimal, ya que lo que pretendemos es hacerlo parpadear cada medio segundo. De esta manera, al referirse a la posición 0 (o valor booleano false) de la primera dimensión del array, podremos generar el dígito que especifiquemos en la segunda dimensión (símbolos del 0 al 9) sin punto. En cambio, si especificamos que vamos a utilizar la posición 1 de la primera dimensión del array (o valor booleano true), se generará el símbolo numérico que especifiquemos en la segunda dimensión, pero ahora con el punto decimal. Se define también un flag (dt) para hacer parpadear el punto decimal del segundo display cada medio segundo. Este flag se invierte cada medio segundo, haciendo parpadear el punto decimal del segundo display.

2 comentarios:

  1. hola, ya hice el contador ademas le hice unas modificaciones que cuente del 9 al 0, ahora como le puedo hacer para que al llegar al 0 se prenda un led? saludos y gracias

    ResponderEliminar
  2. Un saludo.
    Hoy veo este blogspot por primera vez, me parece muy importante, lo seguiré visitando, pienso que voy a aprender mucho de el, soy principiante en el tema de los microcontroladores.
    Gracias por ponerlo a disposición de quienes lo necesitamos.

    ResponderEliminar