InicioHazlo Tu MismoConstruyendo nuestro propio RTC para Arduino o PIC
Actualmente, cuando realizamos proyectos que requieren cierto nivel de flexibilidad, pensar en utilizar un microcontrolador siempre es una buena idea y, siempre que el proyecto lo requiera, es interesante la aplicación de un RTC, es decir, un reloj en tiempo real. Además debo admitir que desde mi infancia tengo un interés muy particular en los relojes y la medición del tiempo.

Si bien podemos adquirir el módulo armado, en esta publicación veremos cómo construir nuestro propio RTC dada su simplicidad y el reducido número de componentes necesarios.

El circuito integrado que utilizaremos será el DS1307, fabricado actualmente por Maxim y Cypress. Su consumo de energía es bajo, dispone de reloj y calendario (ambos codificación BCD) con ajuste de año bisiesto hasta el año 2100 y ajuste automático de fin de mes (para meses con menos de 31 días). El reloj, por su parte, opera en formato de 12/24 horas (configurable) con indicador de AM/PM en caso de operar en formato de 12 horas. Además, el circuito integrado dispone de 56 bytes de memoria SRAM de escritura ilimitada y un pin de salida con señal lógica programable (pin 7: SQW/OUT).

Presentado el componente principal de nuestro proyecto, observamos en la siguiente imagen el circuito:



DS1307 Datasheet

Como puede verse en el circuito, la cantidad de componentes externos es mínima. Las resistencias y el capacitor cerámico pueden adquirirse en cualquier comercio de electrónica y con respecto al cristal de cuarzo de 32768 kHz, recomiendo obtenerlo de un reloj (bastante lógico no?) en desuso ya que suelen ser más precisos y estables. En mi caso particular, al cristal lo obtuve de un reloj de pared en desuso como puede observarse en la siguiente imagen:



La conexión de los pines de salida del DS1307 a la placa Arduino UNO es la siguiente:

DS1307 --> Arduino UNO
SDA(Pin 5) --> Arduino UNO (Pin A4)
SCL(Pin 6) --> Arduino UNO (Pin A5)

La alimentación para este circuito puede obtenerse directamente de la placa Arduino UNO. Existe en dicha placa un terminal que provee 5V.

En la siguiente imagen se observa el reloj ya armado:



Antes de continuar, recomiendo una lectura profunda y analítica de la hoja de datos del circuito integrado DS1307.

Habiendo interconectado exitosamente nuestro reloj con la placa Arduino procederemos a escribir el programa cuya función será controlar nuestro reloj, nos permitirá: establecer fecha y hora, consultar fecha y hora, controlar el pin SQW/OUT y hacer un volcado de toda la memoria. Si bien existe una gran cantidad de librerías para controlar el DS1307, escribiremos nuestro propio programa con fines didácticos y además economizar recursos que a veces son desperdiciados con el uso de grandes librerías de las cuáles sólo necesitamos un par de funciones. En primer lugar, necesitamos comunicarnos con nuestro reloj a través del bus I2C, afortunadamente Arduino proporciona la librería "Wire" que nos permitirá comunicarnos con dispositivos I2C. En segundo lugar debemos conocer los registros y direcciones de memoria del DS1307. Los registros y sus direcciones se observan en la siguiente imagen:

rtc arduino


Podemos dividir la memoria del DS1307 en 3 (tres) segmentos:
1) 00h-006h: Registros de Fecha y Hora (resaltado amarillo).
2) 07h: Registro de Control (resaltado verde).
3) 08h-03Fh: RAM 56 bytes.

Registros de Fecha y Hora
El contenido de los registros de fecha y hora se almacena en codificación BCD. El registro "Día de la semana" (03h) se incrementa a la medianoche, el rango de valores es de 01-07 y la correspondencia con los días de la semana la define el usuario. El bit 7 del primer registro (00h) detiene o inicia el reloj, cuando el bit se establece en 1 se desactiva el oscilador, cuando el bit se establece en 0 sucede lo contrario. Inicialmente, el oscilador se encuentra desactivado (CH=1) y los registros de fecha y hora se inician con la fecha 01/01/00 01 00:00:00. El bit 6 del registro de hora (02h) permite seleccionar formato de 12/24 horas. En caso de seleccionar el formato de 24 horas (nuestro caso), el bit 5 del registro de hora (02h) nos indicará la veintena (20 a 23 horas). Cada vez que cambiemos el formato de 12/24 horas deberemos volver a introducir la hora. Por último, el ingreso de valores inválidos de fecha y hora darán como resultado un comportamiento inesperado.

Registro de Control
El registro de control (07h) se utiliza para controlar la operación del pin de salida SQW/OUT (pin 7). En la siguiente tabla observamos los posibles valores con sus respectivos resultados.



He resaltado la frecuencia de 1Hz. No es menos importante señalar que puede obtenerse una frecuencia de 32768 kHz (la misma del cristal) con la cual podríamos, por ejemplo, poner en funcionamiento otro DS1307 sin necesidad de un segundo cristal.

Memoria RAM
Por último, disponemos de 56 bytes de SRAM para utilizarla según nuestras necesidades particulares. Son demasiadas las aplicaciones como para enumerarlas.

El programa que permite controlar nuestro reloj es el siguiente:


/*
* Comandos:
* T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99)
*  (segs )(mins )(hora )(dia)(dMes )(mes  )(anio )
* Ejemplo, establecer fecha y hora: 19-Ago-13 @ 15:32:11 del dia 1 (Lunes) de la semana.
* utilizar el comando: T1132151190813
*
* W(0-5) Establece SQW/OUT 0=0|1=1|2=1Hz|3=4096kHz|4=8192kHz|5=32768kHz
* Ejemplo, establecer la salida SQW/OUT en 1Hz
* utilizar el comando: W2
*
* Q Consultar fecha y hora
*
* D Volcado de Memoria
*
* NOTA: La norma ISO 8601 (convención internacional que indica el orden de los días de la semana) establece que la semana comienza el Lunes.
*/

#include <Wire.h>

#define DS1307_I2C_ADDRESS 0x68 //Direccion DS1307
#define DS1307_TIMEKEEPER_REGISTERS 7 //Cantidad de registros de Fecha y Hora
#define DS1307_BASE_ADDRESS 0x00 //Registro 00h
#define DS1307_CTRLREG_ADDRESS 0x07 //Registro de control
#define DS1307_MEMORY 64 //Total de memoria

// Convertir valores decimales a valores BCD
byte decToBcd(byte val)
{
    return ((val / 10 * 16) + (val % 10));
}

//Convertir valores BCD a valores decimales
byte bcdToDec(byte val)
{
    return ((val / 16 * 10) + (val % 16));
}

//Convertir valores ASCII a valores decimales
byte asciiToVal(int tens, int units)
{
    return ((byte)((tens - 48) * 10 + (units - 48)));
}

//Establecer fecha DS1307
void setDateDs1307(int* buffer)
{
    byte rtcDate[DS1307_TIMEKEEPER_REGISTERS];
    
    rtcDate[0] = asciiToVal(buffer[0], buffer[1]); //segundos
    rtcDate[1] = asciiToVal(buffer[2], buffer[3]); //minutos
    rtcDate[2] = asciiToVal(buffer[4], buffer[5]); //horas
    rtcDate[3] = asciiToVal(48, buffer[6]); //dia de la semana
    rtcDate[4] = asciiToVal(buffer[7], buffer[8]); //dia del mes
    rtcDate[5] = asciiToVal(buffer[9], buffer[10]); //mes
    rtcDate[6] = asciiToVal(buffer[11], buffer[12]); //anio
    
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.write(DS1307_BASE_ADDRESS);
    Wire.write(decToBcd(rtcDate[0]));
    Wire.write(decToBcd(rtcDate[1]));
    Wire.write(decToBcd(rtcDate[2]));
    Wire.write(decToBcd(rtcDate[3]));
    Wire.write(decToBcd(rtcDate[4]));
    Wire.write(decToBcd(rtcDate[5]));
    Wire.write(decToBcd(rtcDate[6]));
    Wire.endTransmission();
}

//Obtener fecha DS1307
byte* getDateDs1307()
{
    byte* rtcDate = (byte*) malloc(DS1307_TIMEKEEPER_REGISTERS * sizeof(byte));
    
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.write(DS1307_BASE_ADDRESS);
    Wire.endTransmission();
    Wire.requestFrom(DS1307_I2C_ADDRESS, DS1307_TIMEKEEPER_REGISTERS);
    
    rtcDate[0] = bcdToDec(Wire.read() & 0x7f);
    rtcDate[1] = bcdToDec(Wire.read());
    rtcDate[2] = bcdToDec(Wire.read() & 0x3f);
    rtcDate[3] = bcdToDec(Wire.read());
    rtcDate[4] = bcdToDec(Wire.read());
    rtcDate[5] = bcdToDec(Wire.read());
    rtcDate[6] = bcdToDec(Wire.read());
    
    return rtcDate;
}

//Establecer SQW/OUT
void setSqwOutput(int out)
{
    byte control;
    
    switch(out)
    {
        case '0':
        {
            control = 0x00; //SQWOUT = 0
            
            break;
        }
        case '1':
        {
            control = 0x80; //SQWOUT = 1
            
            break;
        }
        case '2':
        {
            control = 0x10; //SQWOUT = 1Hz
            
            break;
        }
        case '3':
        {
            control = 0x11; //SQWOUT = 4096kHz
            
            break;
        }
        case '4':
        {
            control = 0x12; //SQWOUT = 8192kHz
            
            break;
        }
        case '5':
        {
            control = 0x13; //SQWOUT = 32768kHz
            
            break;
        }
        default:
        {
            control = 0x00; //SQWOUT = 0
            
            break;
        }
    }
    
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.write(DS1307_CTRLREG_ADDRESS);
    Wire.write(control);
    Wire.endTransmission();
}

//Mostrar fecha con formato
String showDate(byte* rtcDate)
{
    String fullDate;
    
    if (rtcDate[2] < 10)
    {
        fullDate.concat("0");
    }
    fullDate.concat(rtcDate[2]);
    fullDate.concat(":");
    
    if (rtcDate[1] < 10)
    {
        fullDate.concat("0");
    }
    fullDate.concat(rtcDate[1]);
    fullDate.concat(":");
    
    if (rtcDate[0] < 10)
    {
        fullDate.concat("0");
    }
    fullDate.concat(rtcDate[0]);
    fullDate.concat(" ");
    
    if (rtcDate[4] < 10)
    {
        fullDate.concat("0");
    }
    fullDate.concat(rtcDate[4]);
    fullDate.concat("/");
    
    switch (rtcDate[5])
    {
        case 1:
        {
                fullDate.concat("Ene");
                
                break;
        }
        case 2:
        {
                fullDate.concat("Feb");
                
                break;
        }
        case 3:
        {
                fullDate.concat("Mar");
                
                break;
        }
        case 4:
        {
                fullDate.concat("Abr");
                
                break;
        }
        case 5:
        {
                fullDate.concat("May");
                
                break;
        }
        case 6:
        {
                fullDate.concat("Jun");
                
                break;
        }
        case 7:
        {
                fullDate.concat("Jul");
                
                break;
        }
        case 8:
        {
                fullDate.concat("Ago");
                
                break;
        }
        case 9:
        {
                fullDate.concat("Sep");
                
                break;
        }
        case 10:
        {
                fullDate.concat("Oct");
                
                break;
        }
        case 11:
        {
                fullDate.concat("Nov");
                
                break;
        }
        case 12:
        {
                fullDate.concat("Dic");
                
                break;
        }
        default:
        {
                fullDate.concat("Err_mes");
                
                break;
        }
    }
    
    fullDate.concat("/");
    fullDate.concat("20");
    if (rtcDate[6] < 10)
    {
        fullDate.concat("0");
    }
    fullDate.concat(rtcDate[6]);
    
    fullDate.concat(" Hoy es:");
    switch (rtcDate[3])
    {
        case 1:
        {
                fullDate.concat(" Lunes");
                
                break;
        }
        case 2:
        {
                fullDate.concat(" Martes");
                
                break;
        }
        case 3:
        {
                fullDate.concat(" Miercoles");
                
                break;
        }
        case 4:
        {
                fullDate.concat(" Jueves");
                
                break;
        }
        case 5:
        {
                fullDate.concat(" Viernes");
                
                break;
        }
        case 6:
        {
                fullDate.concat(" Sabado");
                
                break;
        }
        case 7:
        {
                fullDate.concat(" Domingo");
                
                break;
        }
        default:
        {
                fullDate.concat("Err_dia");
                
                break;
        }
    }
    
    return fullDate;
}

//Obtener volcado de memoria
byte* getMemoryDump()
{
    byte* dump = (byte*) malloc(DS1307_MEMORY * sizeof(byte));
    
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.write(DS1307_BASE_ADDRESS);
    Wire.endTransmission();
    Wire.requestFrom(DS1307_I2C_ADDRESS, DS1307_MEMORY);
    
    for (int i = 0; i < 64; i++)
    {
        dump[i] = Wire.read();
    }
    
    return dump;
}

void setup()
{
    Wire.begin();
    Serial.begin(57600);
}

void loop()
{
    if (Serial.available())
    {
        byte* rtc;
        int command = Serial.read();
        
        switch(command)
        {
            case 'T':
            {
                    delay(50);
                    int i = 0;
                    int buffer[13];
                    while(Serial.available() && i < 14)
                    {
                        buffer[i] = Serial.read();
                        i++;
                    }
                    setDateDs1307(buffer);
                    rtc = getDateDs1307();
                    Serial.println(showDate(rtc));
                    free(rtc);
                    
                    break;
            }
            case 'Q':
            {
                    rtc = getDateDs1307();
                    Serial.println(showDate(rtc));
                    free(rtc);
                    
                    break;
            }
            case 'D':
            {
                    rtc = getMemoryDump();
                    for (int i = 0; i < 64; i++)
                    {
                        Serial.print(i);
                        Serial.print(":");
                        Serial.println(rtc[i], HEX);
                    }
                    free(rtc);
                    
                    break;
            }
            case 'W':
            {
                delay(50);
                if(Serial.available())
                {
                    command = Serial.read();
                    setSqwOutput(command);
                }
                break;
            }
            default:
            {
                    Serial.println("Comando no valido.");
                    
                    break;
            }
        }
    }
}


Lógicamente es perfectible y cada uno puede adaptarlo a sus necesidades particulares.
Datos archivados del Taringa! original
26puntos
523visitas
0comentarios
Actividad nueva en Posteamelo
0puntos
1visitas
0comentarios
Dar puntos:

Dejá tu comentario

0/2000

Autor del Post

h
hardrive9000🇦🇷
Usuario
Puntos0
Posts7
Ver perfil →
PosteameloArchivo Histórico de Taringa! (2004-2017). Preservando la inteligencia colectiva de la internet hispanohablante.

CONTACTO

18 de Septiembre 455, Casilla 52

Chillán, Región de Ñuble, Chile

Solo correo postal

© 2026 Posteamelo.com. No afiliado con Taringa! ni sus sucesores.

Contenido preservado con fines históricos y culturales.