Bus I2C - DS1307



1. Introducción:

 Usaremos una placa comercial conocida como "Tiny Rtc".




2. Características:

 Esta placa integra una memoria EEPROM 24c32 y un DS1307 compartiendo un bus I2C.
La tensión de alimentación debe estar comprendida entre 4.5 - 5.5Vdc

El DS1307 es un reloj de tiempo real que de forma autónoma puede llevar cuenta de la hora y fecha, para esto necesita una batería de respaldo.

Características del ds1307:







3. Registros:

 Los datos de fecha y hora se almacenan en formato BCD y así en un registro de 8 bits se pueden almacenar los dos dígitos:



 Para realizar la configuración y consulta de datos, accederemos al siguiente registro:


Nota: Destacamos el bit 7 (CH) de la dirección 0x00. Este bit es el que pone en funcionamiento al rtc. Debe ponerse a 0 para que funcione.

En la dirección 0x07, configuramos lo siguiente:


 En la siguiente tabla podemos ver cómo configurar la salida de onda cuadrada:




4. Funciones de conversión BCD:

  Como hemos dicho, el ds1307 usa un registro de 8 bits para guardar dos dígitos. Los cuatro bits de mayor peso serán las decenas y los cuatro bits de menor peso las unidades.

He encontrado unas funciones sencillas, con las que podemos convertir un valor almacenado en un byte, en dos dígitos: 10011001 -> 99.




5. Ejemplo imprimir hora por puerto serie:

  Este código, inicializa el RTC con una fecha y hora. Luego imprime cada segundo la hora. Cada minuto imprime también la fecha.

					
			
/*
 * File:   main.c
 * Author: Eduardo
 *
 * Created on 5 de junio de 2017, 17:16
 */


#include 
#include "config.h"
#include "typedefs.h"
#include "usart.h"




#define SLAVE_I2C_ADDRESS 0x68      // Dirección del DS1307 en el bus


//PROTOTIPO DE LAS FUNCIONES USADAS
void delay_ms(uint16_t);
void escucharMensaje(void);
void i2cInit(void);
void busWrite (uint8_t data, int direc);
unsigned char busRead (int direc);
void IdleI2C();
void StartI2C();
void repStartI2C();
void WriteI2C(unsigned char d);
char ReadI2C();
void Ack();
void NotAckI2C();
void StopI2C();
void imprimirValorInt (int valor);
uint8_t bcd2bin(uint8_t bcd);
uint8_t bin2bcd(uint8_t bin);
void cambiarHora (uint8_t horaCambio, uint8_t minCambio, uint8_t segCambio);
void printHora ();
void cambiarFecha (uint8_t diaCambio, uint8_t mesCambio, uint8_t anoCambio, uint8_t dsemanaCambio);
void printFecha();


uint8_t hora;
uint8_t minuto;
uint8_t segundo;
uint8_t dia;
uint8_t mes;
uint8_t ano;
uint8_t diaSemana;


void main(void) 
{
    USART_Initialize();     // Inicializamos el puerto Serie
    i2cInit();              // Inicializamos la comunicacion I2C

    delay_ms(500);

        
    while (true)
    {
        printf("Cambiar hora y fecha \n");
        cambiarHora (23, 59, 00);
        cambiarFecha (31, 12, 17, 7);
        


        while (true)
        {
        printHora ();
        if (segundo == 0)
            printFecha ();
        delay_ms(1000);
        
        }
    }
}



/******************************************************************************
*                       FUNCION CAMBIAR HORA
* 
*   - Funcion que permite cambiar la hora del DS1307
*   - Recibe como parametros la hora, minutos y segundos a cambiar
 *****************************************************************************/
void cambiarHora (uint8_t horaCambio, uint8_t minCambio, uint8_t segCambio)
{   
    horaCambio = (bin2bcd(horaCambio)& 0x7F);   //Para habilitar el RTC bit 7 = 0
    minCambio = bin2bcd(minCambio);
    segCambio = bin2bcd(segCambio);
    
    busWrite (horaCambio, 0x02);
    delay_ms(50);
    busWrite (minCambio, 0x01);
    delay_ms(50);
    busWrite (segCambio, 0x00);
    delay_ms(50);
}

/******************************************************************************
*                       FUNCION QUE IMPRIME LA HORA
* 
*   - Funcion que permite consulta la hora del DS1307 y la imprime por la uart
 *****************************************************************************/
void printHora ()
{
    hora = bcd2bin(busRead (0x02));
    minuto = bcd2bin(busRead (0x01));
    segundo = bcd2bin(busRead (0x00));
    
    printf("Hora: ");
    imprimirValorInt (hora);
    printf(":");
    imprimirValorInt (minuto);
    printf(":");
    imprimirValorInt (segundo);
    printf("\n");
}

/******************************************************************************
*                       FUNCION CAMBIAR FECHA
* 
*   - Funcion que permite cambiar la fecha del DS1307
*   - Recibe como parametros el dia, mes, ano y dia de la semana
 *****************************************************************************/
void cambiarFecha (uint8_t diaCambio, uint8_t mesCambio, uint8_t anoCambio, uint8_t dsemanaCambio)
{   
    dsemanaCambio = bin2bcd(dsemanaCambio);   //Para habilitar el RTC bit 7 = 0
    diaCambio = bin2bcd(diaCambio);
    mesCambio = bin2bcd(mesCambio);
    anoCambio = bin2bcd(anoCambio);
    
    busWrite (dsemanaCambio, 0x03);
    delay_ms(50);
    busWrite (diaCambio, 0x04);
    delay_ms(50);
    busWrite (mesCambio, 0x05);
    delay_ms(50);
    busWrite (anoCambio, 0x06);
    delay_ms(50);
}

/******************************************************************************
*                       FUNCION QUE IMPRIME LA FECHA
* 
*   - Funcion que permite consulta la fecha del DS1307 y la imprime por la uart
 *****************************************************************************/
void printFecha ()
{
    diaSemana = bcd2bin(busRead (0x03));
    dia = bcd2bin(busRead (0x04));
    mes = bcd2bin(busRead (0x05));
    ano = bcd2bin(busRead (0x06));
    
    printf("Fecha: ");
    imprimirValorInt (dia);
    printf("/");
    imprimirValorInt (mes);
    printf("/");
    imprimirValorInt (ano);
    printf(" ");
    imprimirValorInt (diaSemana);
    printf("\n");
}

/******************************************************************************
*                       FUNCION IMPRIMIR INT 
* 
*   - Funcion que permite imprimir un int por la uart
 *****************************************************************************/
void imprimirValorInt (int valor)
{
    unsigned char intString[4];
    itoa( intString, valor,10);
    printf(intString);
}


/******************************************************************************
*                       FUNCION CONVERSION A DECIMAL
* 
*   - Funcion que convierte un valor que representa 2 digitos 
*   (4 bits por digito) a un valor decimal
 *****************************************************************************/
uint8_t bcd2bin(uint8_t bcd)
{
  return (bcd / 16 * 10) + (bcd % 16);
}


/******************************************************************************
*                       FUNCION CONVERSION A BCD
* 
*   - Funcion que convierte un decimal en 2 dígitos independientes - los 
*   primero 4 bits representan un dígito y los otros 4 el otro  
 *****************************************************************************/
uint8_t bin2bcd(uint8_t bin)
{
  return (bin / 10 * 16) + (bin % 10);
}


/******************************************************************************
*                       FUNCION RETARDO DE mS
* 
*   - Funcion que realiza un retardo en mS segun el valor que recibe
 *****************************************************************************/
void delay_ms(uint16_t i)
{
    for ( uint16_t x=0; x Fosc = 48MHz
  SSPSTAT = 0;          //Ponemos a 0 este registro
}


/******************************************************************************
*                FUNCION ENCARGADA DE ESCRIBIR REGISTROS EN EL SLAVE
* 
*   - Funcion realiza la secuencia necesaria para escribir registros en el
*   SLAVE. 
*   - Recibe como parametros el dato y la direccion del registro a escribir 
 *****************************************************************************/
void busWrite (uint8_t data, int direc)
{
    StartI2C();         // Enviamos la condicion de inicio

    WriteI2C(((SLAVE_I2C_ADDRESS <<1) & 0xFE));     // Indicamos con que Slave nos queremos comunicar
                                                    // Indicamos que vamos a escribirle
    WriteI2C(direc);    // Indicamos el registro del Slave al que queremos apuntar
       
    WriteI2C(data);     // Enviamos el dato a escribir en dicho registro
    
    StopI2C();          // Enviamos la condicion de fin
}



/******************************************************************************
*                FUNCION ENCARGADA DE LEER REGISTROS EN EL SLAVE
* 
*   - Funcion realiza la secuencia necesaria para leer registros en el
*   SLAVE. 
*   - Recibe como parametro la direccion del registro a leer
 *****************************************************************************/
char busRead (int direc)
{
    unsigned char dataOut = 0;
    
    StartI2C();         // Enviamos la condicion de inicio

    WriteI2C(((SLAVE_I2C_ADDRESS <<1) & 0xFE));     // Indicamos con que Slave nos queremos comunicar
                                                    // Indicamos que vamos a escribirle
    WriteI2C(direc);    // Indicamos el registro del Slave al que queremos apuntar
    
    StartI2C();         // Volvemos a enviar la condicion de inicio
    
    WriteI2C(((SLAVE_I2C_ADDRESS <<1) | 0x01));     // Indicamos con que Slave nos queremos comunicar
                                                    // Indicamos que vamos a leer
    dataOut = ReadI2C();        // Recogemos el dato del registro
    
    StopI2C();                  // Enviamos la condicion de fin
    
    return dataOut;
}



/******************************************************************************
*        FUNCION ENCARGADA DE COMPROBAR QUE NO ESTA OCUPADO EL BUS
* 
*   - Funcion que realiza una espera hasta que se puede usar el medio
 *****************************************************************************/
    void IdleI2C()                  
    {
        while ((SSPSTAT & 0x04) || (0x1F & SSPCON2));
    }
    
    
/******************************************************************************
*        FUNCION ENCARGADA DE ENVIAR UNA CONDICION DE INICIO
* 
*   - Funcion que indica que se va a transmitir por el bus
 *****************************************************************************/    
    void StartI2C()                 // Funcion que envia la condicion de inicio
    {
        IdleI2C();                  // Esperamos el momento para transmitir
        SEN = 1;                    // SSPCON2 bit0. Envía la condición de Inicio
        while (SSPCON2bits.SEN);    // Esperamos hasta que termine el Inicio
    }
    

/******************************************************************************
*        FUNCION ENCARGADA DE ENVIAR UNA CONDICION DE REINICIO
* 
*   - Funcion que indica que se continua con la transmision por el bus
* Esta funcion se usa cuando el Master no quiere enviar una condicion de Fin
* para no perder el bus ante otro Master.  
 *****************************************************************************/       
    void repStartI2C()
    {
        IdleI2C();                  // Esperamos el momento para transmitir
        RSEN=1;                     // SSPCON2 bit1. Envía la condición Reinicio
        while (SSPCON2bits.RSEN);   // Esperamos hasta que termine el Reinicio
    }
    
 
/******************************************************************************
*        FUNCION ENCARGADA DE ENVIAR DATOS AL BUS I2C
* 
*   - Funcion que escribe en el buffer de salida
 *****************************************************************************/       
    void WriteI2C(unsigned char d)   
    {
        IdleI2C();          // Esperamos el momento para transmitir
        SSPBUF = d;         // Copiamos el dato al buffer   	
    }
    
    
/******************************************************************************
*        FUNCION ENCARGADA DE LEER DATOS DEL BUS I2C
* 
*   - Funcion que escribe en el buffer de salida
 *****************************************************************************/       
    uint8_t ReadI2C() 
    {
        uint8_t a;
        IdleI2C();          // Esperamos el momento para transmitir
        RCEN = 1;           // Habilitamos la recepcion I2C
        IdleI2C();          // Esperamos el momento para transmitir
        a = SSPBUF;         // Copiamos el contenido del buffer
        IdleI2C();          // Esperamos el momento para transmitir
        NotAckI2C();        // Indicamos al Slave que hemos recogido el dato
        return a;           // Devuelve el valor recibido
    }
    
 
/******************************************************************************
*     FUNCION ENCARGADA DE INDICAR QUE NO HUBO PROBLEMAS EN LA COMUNICACION
* 
*   - Funcion que responde indicando que los datos se han transmitido correctamente
 *****************************************************************************/       
    void Ack()
    {
        ACKDT = 0;		//Indica que esperamos respuesta
        ACKEN = 1;		//Enviamos ACK
    }

    
/******************************************************************************
*     FUNCION ENCARGADA DE INDICAR QUE HUBO PROBLEMAS EN LA COMUNICACION
* 
*   - Funcion que responde indicando que los datos no se han trasmitido correctamente
 *****************************************************************************/         
    void NotAckI2C()
    {
        ACKDT = 1;		//Indica que no esperamos respuesta
        ACKEN = 1;		//Enviamos ACK
        while (ACKEN);	//Esperamos hasta que el Esclavo lo reciba
        SSPIF = 0;		//Borramos la bandera de recepción
    }
    
    
/******************************************************************************
*               FUNCION ENCARGADA DE ENVIAR UNA CONDICION DE FIN
* 
*   - Funcion que indica que se deja el bus libre
 *****************************************************************************/       
    void StopI2C()          
    {
        IdleI2C();                  // Esperamos el momento para transmitir
        PEN = 1;                   	// Habilitamos el bit de Stop
        while (SSPCON2bits.PEN);    // Esperamos hasta que la condicion de Stop finalice
    }			
					


Nota: La función printf ("%d", valor) debería imprimir un entero, pero no funciona. Por eso hay una función para imprimir enteros; lo que hace es convertirlos en array de caracteres y luego los imprime con printf.

Tendremos que investigar un poco más sobre la función printf.


- Consultando hora y fecha:



6. Descargas:



www.microedu.es

Si chove, non orballa!