[Aporte] Comunicación I2C para AVR

Hola a todos, estuve leyendo en estas últimas semanas sobre problemas de algunos usuarios que tienen con la comunicación i2c. En esta ocasión les comparto una librería que uso con arquitectura AVR para comunicarme mediante i2c con cualquier dispositivo y también les comparto un ejemplo de un reloj calendario con Atmega328p y con el módulo DS1307. En realidad pueden usar cualquier otro dispositivo que mantenga esta comunicación, la librería sirve para todo.

Les invito a todo aquel interesado a analizar el código y modificarla a su criterio ya que todo esta hecho en Ansi C, les dejo una imagen y archivo del proteus funcionando, así mismo el archivo del código.

Pdt: Me doy otro espacio y les comparto una librería para PIC. Aprovechando quisiera pedir la aprobación de los moderadores para brindar poco a poco un tutorial/curso sobre los nuevos microcontroladores AVR de Microchip (familia(s) mejorada(s)), el cual quería brindarlo desde el año pasado.

Simulacion.png



* Código principal:
C:
#define F_CPU 16000000UL
#include <avr/io.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <util/delay.h>
#include "I2C/I2C_Master.h"
#include "UART/UART_328pb.h"

#define ADDR_DS1307 0b11010000
#define ADDR_DS3232 0b11010000

#define F_12        1
#define F_24        0
#define AM          0
#define PM          1
#define NULL_DS     0

void DS1307_Config_Hora(uint8_t horas, uint8_t minutos, uint8_t segundos, uint8_t formt, uint8_t Am_Pm);
void DS1307_Config_Fecha(uint8_t diaSemana, uint8_t diaMes, uint8_t mes, uint8_t anio);
void DS1307_Read_Hora(uint8_t *ptrHora, uint8_t *ptrMinutos, uint8_t *ptrSegundos, char *ptrAmPm);
void DS1307_Read_Fecha(uint8_t *ptrDiaSemana, uint8_t *ptrDiaMes, uint8_t *ptrMes, uint8_t *ptrAnio);

uint8_t horas = 0, minutos = 0, segundos = 0;
uint8_t diaSemana = 0, diaMes = 0, mes = 0, anio = 0;
char strHora[20], strAmPm[5], strFecha[20];


int main(void)
{
    //Lcd_Init();
    //Lcd_Clear();
    
    Serial_begin(9600);
    
    I2C_Init();
    //DS1307_Config_Horario(15, 58, 10, F_24, NULL_DS);
    DS1307_Config_Hora(3, 42, 0, F_12, PM); // 11:27:00 PM
    DS1307_Config_Fecha(7, 22, 5, 22); // Domingo / 22 / Mayo / 2022
    
    while (1)
    {
        DS1307_Read_Hora(&horas, &minutos, &segundos, strAmPm);
        DS1307_Read_Fecha(&diaSemana, &diaMes, &mes, &anio);
        
        sprintf(strHora, "%02u:%02u:%02u %s \r\n", horas, minutos, segundos, strAmPm);
        sprintf(strFecha, "%02u/%02u/20%u \r\n", diaMes, mes, anio);
        
        printString_UART(strHora);
        printString_UART(strFecha);
        //printString_UART("Hola");
        //Lcd_Write_String(strFecha);
        
        _delay_ms(500);
    }
}




void DS1307_Read_Fecha(uint8_t *ptrDiaSemana, uint8_t *ptrDiaMes, uint8_t *ptrMes, uint8_t *ptrAnio)
{
    //Inicio la comunicacion
    I2C_Start();
    I2C_Write_Address_Slave(ADDR_DS1307, I2C_WRITE);
    I2C_Write_Data_Slave(0x03); //Apunto a la dirección de Memoria de dia
    I2C_Start(); //Start Repetido
    I2C_Write_Address_Slave(ADDR_DS1307, I2C_READ);
    I2C_Read_Data(ptrDiaSemana, I2C_ACK);
    I2C_Read_Data(ptrDiaMes, I2C_ACK);
    I2C_Read_Data(ptrMes, I2C_ACK);
    I2C_Read_Data(ptrAnio, I2C_nACK);
    I2C_Stop();
    
    *ptrDiaSemana = (*ptrDiaSemana & 0b00000111);
    *ptrDiaMes    = ( (((*ptrDiaMes>>4) & 0b00000011) * 10) + (*ptrDiaMes & 0b00001111) );
    *ptrMes       = ( ((((*ptrMes>>4) & 0b00000001)) * 10) + (*ptrMes & 0b00001111) );
    *ptrAnio      = ( ((*ptrAnio>>4) * 10) + (*ptrAnio & 0b00001111) );
}

void DS1307_Read_Hora(uint8_t *ptrHora, uint8_t *ptrMinutos, uint8_t *ptrSegundos, char *ptrAmPm)
{
    //Inicio la comunicacion
    I2C_Start();
    I2C_Write_Address_Slave(ADDR_DS1307, I2C_WRITE);
    I2C_Write_Data_Slave(0x00); //Apunto a la dirección de Memoria de segundos
    I2C_Start(); //Start Repetido
    I2C_Write_Address_Slave(ADDR_DS1307, I2C_READ);
    I2C_Read_Data(ptrSegundos, I2C_ACK);
    I2C_Read_Data(ptrMinutos, I2C_ACK);
    I2C_Read_Data(ptrHora, I2C_nACK);
    I2C_Stop();
    
    if( (*ptrHora & (1<<6)) != 0) //Evaluar si el formato es 12 Horas
    {
        if( (*ptrHora & (1<<5)) != 0 )
        {
            strcpy(ptrAmPm, "PM");
        }
        else
        {
            strcpy(ptrAmPm, "AM");
        }
        *ptrHora = ( (((*ptrHora>>4) & 0b00000001) * 10 ) + (*ptrHora & 0b00001111) );       
    }
    else
    {
        *ptrHora = ( (((*ptrHora>>4) & 0b00000011) * 10) + (*ptrHora & 0b00001111) );
    }
    *ptrSegundos = ( ((*ptrSegundos>>4) * 10) + (*ptrSegundos & 0b00001111) );
    *ptrMinutos  = ( ((*ptrMinutos>>4) * 10) + (*ptrMinutos& 0b00001111) );
}

void DS1307_Config_Fecha(uint8_t diaSemana, uint8_t diaMes, uint8_t mes, uint8_t anio)
{
        diaSemana = (diaSemana & 0b00000111);
        diaMes    = ((diaMes/10)<<4)|(diaMes%10);
        mes       = ((mes/10)<<4)|(mes%10);
        anio      = ((anio/10)<<4)|(anio%10);
        
        //Inicio la comunicacion
        I2C_Start();
        I2C_Write_Address_Slave(ADDR_DS1307, I2C_WRITE);
        I2C_Write_Data_Slave(0x03); //Apunto a la dirección de Memoria de dia
        I2C_Write_Data_Slave(diaSemana);
        I2C_Write_Data_Slave(diaMes);
        I2C_Write_Data_Slave(mes);
        I2C_Write_Data_Slave(anio);
        I2C_Stop();
}

void DS1307_Config_Hora(uint8_t horas, uint8_t minutos, uint8_t segundos, uint8_t formt, uint8_t Am_Pm)
{
    //DS1307_Config_Horario(15, 32, 12, F_12, PM);
    horas    = ( ((horas/10)<<4) | (horas%10) );
    minutos  = ( ((minutos/10)<<4) | (minutos%10) );
    segundos = ( ((segundos/10)<<4) | (segundos%10) );
    
    switch(formt)
    {
        case F_12:
        //consultamos si es PM o AM
        if (Am_Pm == PM)
        {
            horas = (horas | 0b01100000);
        }
        else if(Am_Pm == AM)
        {
            horas = (horas | 0b01000000);
        }
        //Inicio la comunicacion
        I2C_Start();
        I2C_Write_Address_Slave(ADDR_DS1307, I2C_WRITE);
        I2C_Write_Data_Slave(0x00); //Apunto a la primera dirección de Memoria
        I2C_Write_Data_Slave(segundos);
        I2C_Write_Data_Slave(minutos);
        I2C_Write_Data_Slave(horas);
        I2C_Stop();
        break;
        //**********************************************
        
        case F_24:
        horas = (horas & 0b00111111);
        //Inicio la comunicacion
        //Inicio la comunicacion
        I2C_Start();
        I2C_Write_Address_Slave(ADDR_DS1307, I2C_WRITE);
        I2C_Write_Data_Slave(0x00); //Apunto a la primera dirección de Memoria
        I2C_Write_Data_Slave(segundos);
        I2C_Write_Data_Slave(minutos);
        I2C_Write_Data_Slave(horas);
        I2C_Stop();
        break;
    }
}
 

Adjuntos

  • Reloj_Calendario.rar
    51.3 KB · Visitas: 26
  • Proteus_Reloj_Calendario.rar
    32.6 KB · Visitas: 18
Saludos, la librería me sirve sólo para que un dispositivo funcione como Maestro?
En este caso tienes un único maestro que es el micro Atmega328p y puedes conectarle muchos esclavos. Si deseas otro maestro, usa otro micro y vuelves a conectarle otros esclavos, lo bueno es que no necesariamente tiene que ser el Atmega328p, puede ser cualquier otro AVR, ya que sus registros son casi iguales. El código principal que inicia la comunicación i2c, escribe, lee la dirección del esclavo y para la comunicación son los archivos I2C_Master.h y I2C_Master.c


Saludos;
 
Atrás
Arriba