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 32768Hz 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 XT
  4. #FUSES MCLR
  5. #use delay(clock=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 = MMSet/10;
  49.          minU = MMSet%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.





25 comentarios:

  1. Hola buen blog aprendo muchas cosa gracias.solo un par de preguntas no entiendo esta parte del codigo

    timer0_mux(valorDisp[horD], valorDisp[horU], valorDisp[minD],
    valorDisp[minU], valorDisp[segD], valorDisp[segU]);
    Podrias explicarmela por favor
    Y la otra es para ver los numeros en una matriz en vez de un display supongo que solo habria que cambiar la parte donde se ponen los datos para desplegar los numeros en los display por los de la matriz esta bien??
    Gracias por tu tiempo

    ResponderEliminar
    Respuestas
    1. Hola como estas?, Gracias!
      Lo que hace la función timer0_mux(); es el multiplexado de los dígitos de 7 segmentos, es decir, es la que hace el barrido dígito a dígito, y en el argumento de la función le pasas los valores que queres que muestre, en la declaración de la función tenes timer0_mux(int8 horD, int8 horU, int8 minD, int8 minU, int8 segD, int8 segU), después vos la llamas en el programa como imer0_mux(valorDisp[horD], valorDisp[horU], valorDisp[minD], valorDisp[minU], valorDisp[segD], valorDisp[segU]); lo que hace es asignar cada valor de cada dígito, si queres hacer una prueba para comprender mas fácil por tramos, podes hacer imer0_mux(1, 2, 3, 4, 5, 6); y te va a mostrar (de forma fija) el numero 12:34:56 en los 6 dígitos.
      Para usar una matriz tenes que hacer algunas cosas mas ya que tenes el concepto de frames por segundo, tenes que refrescar el mapa de bits de la matriz cada tanto para que no se vea y tenes que crear los caracteres o números para después llamarlos, te diría que mires alguno de mis otros post de matrices de led de 8x8 o 8x32 para tener una idea.
      Saludos!

      Eliminar
  2. perfecto ya quedo entendido lo probe con un ds1307 y su libreria y funciono perfecto
    gracias

    ResponderEliminar
    Respuestas
    1. Buenísimo!, Cualquier cosa avísame!, Saludos!

      Eliminar
    2. con el Ds1307 como quedaria el codigo puedes mandarme

      Eliminar
  3. Respuestas
    1. Hola, el núcleo del programa no cambia mucho, pero hay que hacer la biblioteca para el sensor, es decir, acá se usa la biblioteca "#include " habría que hacerla para este otro... Leer el datasheet y hacer todo el manejo de registros que requiera. Pero el programa no cambia mucho.
      Saludos.

      Eliminar
  4. Hola seba te pido por favor si podrias mandar cuales serian las modificaciones necesarias para usarlo con el Ds1307 lo que pasa es que estuve probando con varias librerias y no me funcionan
    Gracias

    ResponderEliminar
    Respuestas
    1. Hola, a nivel programa no cambia casi nada, pero tendria que ver la biblioteca del 1307 para ver si funciona bien en base a lo que mencionas que no te funciona la biblioteca. En si el 1307 es i2c lo cual deberia ser sencillo de implementar hasta sin la biblioteca porque se puede acceder a los registros por el i2c directamente., pero no lo he probado, dejame ver y lo subo al post.
      Saludos.

      Eliminar
  5. buenos dias, podrian ayudarme con la programacion para arduino

    ResponderEliminar
  6. hola buenos dias queria saber la palabra de configuracion del pic

    ResponderEliminar
  7. Buenas noches, me podrian pasar la libreria 16F883.h

    ResponderEliminar
  8. Hola lo arme, no se por que el display sale 00 63 y cuando muevo los botones parece cambiar pero se regresa a ese valor alguna sugerencia?

    ResponderEliminar
    Respuestas
    1. Hola, pudiste simularlo en Proteus?, te dio algun warning la compilacion?

      Eliminar
    2. Si me da 2 warnings la compilacion en proteus si funciona pero en proteus da igual si dejo o no los resistores que van a 5v del DS1302 ahorita ya lo hice en PCB pero me hace cosas raras

      Eliminar
    3. Estoy haciendo el reloj pero no con Dispalys leds sino con unos dispositivos que se llaman tubos numitron, son visualizadores incandescentes ,le paso el circuito que arme
      http://www.mediafire.com/file/94pavwwgs81xs1w/PCB2.pdsprj/file

      Eliminar
    4. Este comentario ha sido eliminado por el autor.

      Eliminar
    5. El warning que sale en simulacion es "Logic contention(s) detected on net #00002." por lo que veo en internet ese warning es por corto y es en el nodo de la pata de i/o del RTC

      Eliminar
    6. Pude armarlo en breadboard y funciono pero ahora no se que es lo que esta pasand

      Eliminar
    7. Hola, me parece que el problema era algun tipo de ruido de mi fuente de laboratorio (hecha por mi) añadi un pequeño condensador a la entrada y parece ser que se solucioo el problema

      Eliminar
  9. PD quite el oscilador externo en el pic y cambie las salidas de control de los catodos par ponerlos directo sin transistor por el momento

    ResponderEliminar
    Respuestas
    1. Podrias tener una sobre carga de corriente, cuando tengas los segmentos encendidos en simultaneo, al no usar driver.

      Eliminar
  10. Haha no se que tenia pero ya, un detalle con el codigo la ultima señal de control en la linea 29 dura mas que los otros pulsos, no dura 3ms dura como 5,4 ms por la instruccion que sigue, eso provoca que el digito de las unidades de los segundos brille mas, simplemente despues de ese retardo apague todas las señales de control y listo. saludos

    ResponderEliminar
    Respuestas
    1. Si, puede ser, habria que ver bien los ciclos de demora de las instrucciones como para calcularlo bien. o usar un timer.
      Saludos.

      Eliminar