Reloj HH:MM:SS con RTC y display de 7 segmentos

Este es un simple reloj con display de 7 segmentos que nos mostrara las horas, los minutos y los segundos, la idea es utilizar displays de 7 segmentos de buen tamaño (al rededor de 2" de altura ya que el proyecto se implementara en un estudio de grabación y se suelen usar estos relojes de ese tamaño aproximadamente).
El sistema esta basado en un microcontrolador PIC16F883, pero podría ser cualquier otro, básicamente se elige este porque el costo es reducido y tiene una cantidad de pines que nos permitirá interconectar todo sin problemas.
El sistema funciona con oscilador externo (no es critico porque el reloj en si sera externo entonces no tenemos una necesidad precisa en el microcontrolador, por ende lo podremos poner como oscilador interno sin problemas.
El corazón de este proyecto es un RTC (Reloj en tiempo real) en base al DS1302, pero podríamos usar cualquier otro RTC, sinceramente use este porque lo tenia a mano.
Este RTC tiene 3 pines de datos, pero en realidad usaremos 2 como dato y 1 como sincronismo que es el que nos entregara un pulso cada 1s exactamente.
Este RTC cuenta con un oscilador a cristal de 32768kHz y una pila de 3V de backup para que el sistema siga en hora aunque apaguemos el circuito.
Los 6 displays están en paralelo (en cuanto a los segmentos) y controlaremos el encendido de cada uno de forma independiente con el común de cada uno.
En este caso se utilizan display cátodo común, pero podríamos usar de ánodo común, es lo mismo ya que en el firmware tenemos que invertir los bits del puerto que genera el numero nada mas.
En mi caso lo ideal es usar 3 displays dobles ya que vienen interconectados, digo de usarlos así para separarlos por HH MM SS mediante dos leds redondos quedando HH : MM : SS, pero podríamos dejarlo sin los dos puntos y el resultado sera el mismo, empleando display triples o cuádruples, también podemos hacerlo con display simples pero tendremos que cablear mas o realizar un PCB mas complejo.
El proceso de muestra en los displays es multiplexado, para ello usamos el timer0 y en el elegimos el display a activar y el dato a mostrar en ese display, es decir, activamos display 1, mostramos dato de display 1, activamos display 2 y mostramos el dato del display 2, así sucesivamente para los 6 displays de una forma muy rápida que a la visión aparecen los 6 dígitos encendidos cada uno con su valor correspondiente.
Luego definimos un vector donde pondremos los 10 valores posibles de los display del 0 al 9, y este vector sera el que llamaremos continuamente para elegir los valores a mostrar.
Luego tendremos la configuración del timer y del microcontrolador en si y por ultimo la función principal que sera la que realiza la repetición de proceso.
En esta función principal tendremos 3 etapas:
1) El if del botón en pin RC5, este botón configura los minutos del reloj, es decir, cada vez que presionemos este botón se incrementara en una unidad el minutero, si mantenemos presionado se incrementara automáticamente, por cada incremento realiza la actualización al DS1302 mediante la función rtc_set_datatime();
2) El if del botón en pin RC4, este botón configura las horas del reloj, es decir, cada vez que presionemos este botón se incrementara en una unidad las horas, si mantenemos presionado se incrementara automáticamente, por cada incremento realiza la actualización al DS1302 mediante la función rtc_set_datatime();
3) El resto del código funcionara siempre que no estén presionados los botones de hora o minuto, este ultimo bloque realiza la función rtc_get_time(); donde toma el tiempo que va contando el DS1302 y lo guarda en las tres variables de su argumento HH, MM y SS, luego estas tres variables las separaremos en unidades y decenas mediante la división por 10 y el resto de esa división, por ultimo tomaremos las 6 variables correspondientes a cada dígito y la enviaremos a la función timer0_mux() para que se actualice en los displays.
La función rtc_get_time() como se puede ver se ejecutara casi a tiempo de proceso del microcontrolador pero no cambiara su valor hasta que el DS1302 lo decida, es decir el segundero se actualizara cada un segundo por mas que llamemos a la función rtc_get_time() cada 1us, por ello no es necesario usar un cristal externo en el microcontrolador y podremos utilizar el interno del mismo.

  1. #include <16F883.h>
  2. #FUSES NOWDT
  3. #FUSES INTRC
  4. #FUSES MCLR
  5. #use delay(int=4000000)
  6. #define RTC_RST    PIN_C0
  7. #define RTC_SCLK   PIN_C1
  8. #define RTC_IO     PIN_C2
  9. #include <DS1302.C>
  10. #int_timer0
  11. void timer0_mux(int8 horD, int8 horU, int8 minD, int8 minU, int8 segD, int8 segU){
  12.    output_a(0b00000001);
  13.    output_b(horD);
  14.    delay_ms(3);
  15.    output_a(0b00000010);
  16.    output_b(horU);
  17.    delay_ms(3);
  18.    output_a(0b00000100);
  19.    output_b(minD);
  20.    delay_ms(3);
  21.    output_a(0b00001000);
  22.    output_b(minU);
  23.    delay_ms(3);
  24.    output_a(0b00010000);
  25.    output_b(segD);
  26.    delay_ms(3);
  27.    output_a(0b00100000);
  28.    output_b(segU);
  29.    delay_ms(3);  
  30.    set_timer0(0);
  31. }
  32. int valorDisp[10]={0b00111111,0b00000110,
  33.                    0b01011011,0b01001111,
  34.                    0b01100110,0b01101101,
  35.                    0b01111101,0b00000111,
  36.                    0b01111111,0b01101111};
  37. int8 horU, horD, minU, minD, segU, segD, HHSet, MMSet, HH, MM, SS;
  38. void main(){
  39.    setup_timer_0(RTCC_INTERNAL|RTCC_DIV_64);
  40.    set_timer0(0);
  41.    enable_interrupts(INT_TIMER0);
  42.    enable_interrupts(GLOBAL);
  43.    while(TRUE){
  44.       if(input(PIN_C5)==1){
  45.          MMSet = MMSet + 1;
  46.          if(MMSet > 59)
  47.             MMSet = 0;
  48.          minD = HHSet/10;
  49.          minU = HHSet%10;
  50.          timer0_mux(valorDisp[horD], valorDisp[horU], valorDisp[minD], 
  51.                     valorDisp[minU], valorDisp[0], valorDisp[0]);
  52.          rtc_set_datetime(0,0,0,0,HHSet,MMSet);
  53.          delay_ms(200);  
  54.       }  
  55.       if(input(PIN_C4)==1){
  56.          HHSet = HHSet + 1;
  57.          if(HHSet > 23)
  58.             HHSet = 0;
  59.          horD = HHSet/10;
  60.          horU = HHSet%10;
  61.          timer0_mux(valorDisp[horD], valorDisp[horU], valorDisp[minD], 
  62.                     valorDisp[minU], valorDisp[0], valorDisp[0]);
  63.          rtc_set_datetime(0,0,0,0,HHSet,MMSet);
  64.          delay_ms(200);
  65.       }
  66.       rtc_get_time(HH,MM,SS);
  67.       horD = HH/10;
  68.       horU = HH%10;
  69.       minD = MM/10;
  70.       minU = MM%10;
  71.       segD = SS/10;
  72.       segU = SS%10;
  73.       timer0_mux(valorDisp[horD], valorDisp[horU], valorDisp[minD], 
  74.                   valorDisp[minU], valorDisp[segD], valorDisp[segU]);
  75.    }    
  76. }

En cuanto al circuito podremos ver que es muy simple, el microcontrolador, los 6 displays, el RTC y por ultimo los transistores del multiplexado que en este caso los tengo representados por compuertas NOT para la simulación pero estoy usando 2N3904 con una resistencia de 2k2 en su base. 

Podremos utilizar el pin de sincronismo del DS1302 para activar un transistor extra y este puede activar los leds separadores de dígitos para que se de el aspecto HH : MM : SS, pero también puede quedar fijo como en algunos relojes.