Utilizando o RTC DS1340 pela I²C



               Por fim encerraremos o tópico de comunicação I²C utilizando neste artigo o RTC DS1340 como exemplo, com toda parte de rotinas de comunicação feita, basta enviar os dados necessários para comunicação com dispositivo, antes disso, uma breve descrição do que faz um RTC, e finalizando com as rotinas necessárias para a implementação.

Um relógio de tempo real (RTC ou real-time clock, em inglês) é um relógio de computador que mantém o controle do tempo presente. Os RTCs estão presentes na maioria dos dispositivos eletrônicos que precisam manter um controle preciso do tempo.

Os benefícios de usar um RTC podem ser o baixo consumo de energia, liberar o sistema principal para tarefas mais críticas, maior precisão e costumam ter uma fonte de energia alternativa, podendo continuar a contagem do tempo enquanto a fonte principal está desligada.

Para o nosso exemplo iremos usar o DS1340 da Maxim, que entre outras características comuns aos RTCs tem um carregador (trickle-charge) para bateria, o relógio fornece um calendário completo, detecta falhas de energia e muda automaticamente para a fonte de backup, calibração via software.

O DS1340 suporta o barramento I²C bidirecional e o protocolo de transmissão dados. Um dispositivo que manda dados pelo barramento é definido como transmissor. O Dispositivo que controla as mensagens é o mestre.

Os dispositivos que são controlados pelo mestre são considerados escravos. O dispositivo mestre é que gera o relógio serial (SCL - serial clock), controla o acesso ao barramento e gera as condições de INICIO E PARADA que controlam o barramento.

O DS1340 opera como escravo no barramento da I²C. Conexões ao barramento são feitas pelas portas de entrada/saída SDA e SCL. Dentro das especificações do barramento o modo padrão (taxa máxima de 100khz) e modo rápido (taxa máxima de 400khz) estão definidos.
O seguinte protocolo de barramento foi definido:
1.                     Transferência de dados somente poderá ser iniciada quando o barramento não estiver ocupado.
2.                      Durante a transferência de dados, a linha de dados deve se manter estável enquanto a linha do relógio (clock) estiver alta. Mudanças na linha de dados enquanto a linha do relógio estiver alta será interpretada como sinais de controle.

Apenas duas rotinas serão necessárias para termos o básico da utilização, como extra incluiu uma terceira rotina para configurar o tricle-charge.

MAPA DE ENDEREÇAMENTO
End.
Bit7
Bit6
Bit5
Bit4
Bit3
Bit2
Bit1
Bit0
Função
Faixa
00H
EOSC
10 SECONDS
SECONDS
SECONDS
0-59
01H
X
10 MINUTES
MINUTES
MINUTES
0-59
02H
CEB
CB
10 HOURS
HOURS
CENT/HOUR
0-1;0-23
03H
X
X
X
X
X
DAY
DAY
01-07
04H
X
X
10DATE
DATE
DATE
01-31
05H
X
X
X
10MONTH
MONTH
MONTH
01-12
06H
10 YEAR
YEAR
YEAR
00-99
07H
OUT
FT
S
CAL4
CAL3
CAL2
CAL1
CAL0
CONTROL

08H
TCS3
TCS2
TCS1
TCS0
DS1
DS0
ROUT1
ROUT0
T.CHARGER

09H
OSF
0
0
0
0
0
0
0
FLAG


typedef struct tagPERIODO
{
    int iDia;
    int iMes;
    int iAno;
    int iHor;
    int iMin;
    int iSeg;
}SPERIODO;


#define ADDRTC          0xD0           

typedef enum tagADD_REG
    {       
        eSECONDS,                       // 0x00
        eMINUTES,                       // 0x01      
        eHOUR,                          // 0x02
        eDAY,                           // 0x03
        eDATE,                          // 0x04
        eMONTH,                         // 0x05
        eYEAR,                          // 0x06
        eCONTROL,                       // 0x07
        eTRICKLE_CHARGER,               // 0x08
        eFLAG                           // 0x09

    }ADD_REG;   

Propósito
Carregar no DS1340 os novos dados de data e hora
Parâmetros
ptrSP - ponteiro da estrutura com a nova informação de data e hora
Retorno
1 - SUCESSO - atualizou os registradores de data e hora do DS1340
0 - ERRO - a informação não foi salva
Protocolo
  Condições de Start e Stop são reconhecidas como o inicio e fim da transferência serial. O hardware faz o reconhecimento do endereço após receber o byte com endereço do dispositivo escravo e o bit de direção.
   O byte com endereço do dispositivo escravo é o primeiro a ser recebido após o dispositivo mestre ter gerado a condição de START.
  O byte de endereço do dispositivo escravo contem o endereço de 7 bits do DS1340, que é 1101 000, seguido pelo bit de direção (R/W),
que é 0 para escrita. Após enviar o byte de endereço o DS1340 responde com uma confirmação, logo em seguida envia o endereço do registrador a ser acessado do DS1340 com o mesmo confirmando.
  Isto configura o DS1340 a apontar para o registrador desejado, o dispositivo mestre pode então transmitir os bytes de dados, com o DS1340 confirmando cada byte. O ponteiro do registrador incrementa a cada byte transferido.
  Para terminar a escrita de dados o dispositivo mestre deve gerar uma condição de STOP.
Descrição
Inicia a comunicação com o dispositivo, e o 1º passo é configurar o bit EOSC.
    O bit 7 do registrador 0 é o que habilita oscilador (EOSC):
                1 - o oscilador fica desabilitado.
                0 - o oscilador fica habilitado.
Após ajustar os valores de Hexa para BCD (formato usado pelo DS1340), inicia o 2º passo ao escrever a informação de tempo e calendário acessando os registradores apropriados.   Por fim acessa o registrador de flag para limpar o OSF.
    O bit OSF(Oscilator Stop Flag):
                0 - Oscilador não parou.
                1 - indica se o oscilador parou ou esteve parado por algum período, usado para verificar a validade da hora e do calendário.

BOOL ds1340_AtualizaRelogio(SPERIODO *ptrSP)
{
    SPERIODO sTemp;
    BOOL bOk = TRUE;
           
    // primeiro passo habilita o oscilador
    i2c_Start();           
    bOk = i2c_Write(ADDRTC); // endereco do escravo + hab escrita
    if (bOk)    bOk = i2c_Write(eSECONDS); //end. do 1ºregistrador
    if (bOk)    bOk = i2c_Write(0x00);// zero no segundo, limpa o bit EOSC
    i2c_Stop();

    if (bOk) {
        // leituras estaoh em Hexa, para o RTC muda para BCD
        sTemp.iSeg = ByteHexToBcd(ptrSP->iSeg);
        sTemp.iMin = ByteHexToBcd(ptrSP->iMin);
        sTemp.iHor = ByteHexToBcd(ptrSP->iHor);
        sTemp.iDia = ByteHexToBcd(ptrSP->iDia);
        sTemp.iMes = ByteHexToBcd(ptrSP->iMes);
        sTemp.iAno = ByteHexToBcd(ptrSP->iAno);
    }

    // a partir daqui carregamos a data e hora passada
    i2c_Start();
    if (bOk)    bOk = i2c_Write(ADDRTC);// endereco do escravo + hab escrita
    if (bOk)    bOk = i2c_Write(eSECONDS); // endereco dos segundos
    if (bOk)    bOk = i2c_Write(sTemp.iSeg); // segundo
    if (bOk)    bOk = i2c_Write(sTemp.iMin); // minuto          
    if (bOk)    bOk = i2c_Write(sTemp.iHor); // hora
    if (bOk)    bOk = i2c_Write(0x01);       // dia da semana(naoh usado)      
    if (bOk)    bOk = i2c_Write(sTemp.iDia); // dia                                                  
    if (bOk)    bOk = i2c_Write(sTemp.iMes); // mes     
    if (bOk)    bOk = i2c_Write(sTemp.iAno); // ano     
    if (bOk)    bOk = i2c_Write(0x40);       // controle    

    i2c_RepeatedStart();
    if (bOk)    bOk = i2c_Write(ADDRTC);// endereco do escravo + hab escrita
    if (bOk)    bOk = i2c_Write(eFLAG); // aponta para o flag do DS1340
    if (bOk)    bOk = i2c_Write(0x00);// limpa o bit do OSF
    i2c_Stop();

    return bOk;
}           

Propósito
Carregar no Dspic as informações de data e hora do RTC
Parâmetros
ptrSP - ponteiro de uma estrutura para armazenar a data e hora
Retorno
1 - SUCESSO - conseguiu carregar a informação de data e hora
0 - ERRO - falha ao tentar carregar informação
Protocolo
O primeiro byte é recebido e manipulado da mesma forma que no modo de escrita. Entretanto, neste modo, o bit de direção indica que direção da transferência está invertida.
O DS1340 transmite dados pela linha SDA, enquanto o dispositivo mestre controla o pulso pela linha SCL.
Condições de Start e Stop são reconhecidas como o inicio e fim da transferência serial. O hardware faz o reconhecimento do endereço após receber o byte com endereço do dispositivo escravo e o bit de direção.
O byte com endereço do dispositivo escravo é o primeiro a ser recebido após o dispositivo mestre ter gerado a condição de START.
O byte de endereço do dispositivo escravo contem o endereço de 7 bits do DS1340, que é 1101 000, seguido pelo bit de direção (R/W), que é 1 para leitura. Após enviar o byte de endereço o DS1340 responde com uma confirmação.
A partir de então o DS1340 fica configurado para transmitir dados e inicia a mesma, usando os dados indicados pelo ponteiro do registrador, se o ponteiro do registrador não foi escrito antes de configurar o modo de leitura, ele usara o ultimo endereço armazenado.
Para terminar a leitura de dados o dispositivo mestre deve enviar para o DS1340 uma não confirmação NACK seguido por uma condição de STOP.
Descrição
O 1º passo é certificar-se de que o ponteiro de registradores tem o endereço desejado, para isso, escrevemos a posição desejada Seguindo para o 2º passo, começamos a leitura, adicionando o bit De direção de transferência ao endereço do DS1340.
Conforme as leituras forem executadas armazena a informação lida na estrutura correspondente.
Após comunicar com o RTC DS1340 pelo protocolo do I²C, ajusta o formato dos valores de data e hora lidos como BCD, para o formato usado pelo Dspic.


BOOL ds1340_CarregaRelogio(SPERIODO *ptrSP)
{
    BOOL bOk = TRUE;

    ptrSP->iSeg = 0;
    ptrSP->iMin = 0;
    ptrSP->iHor = 0;
    ptrSP->iDia = 1;
    ptrSP->iMes = 1;
    ptrSP->iAno = 0;

    // primeiro,carrega no ponteiro de registradores, o endereco desejado
    i2c_Start();
    if (bOk)    bOk = i2c_Write(ADDRTC);  //escravo + hab escrita
    if (bOk)    bOk = i2c_Write(eSECONDS);//endereco do 1ºRegistrador do RTC

    // segundo, inicia o procedimento de leitura
    i2c_RepeatedStart();
    if (bOk)    bOk = i2c_Write(ADDRTC | 1);//  escravo + hab leitura
    if (bOk) {
        ptrSP->iSeg = i2c_Read(ACK);// le os dados no endereco passado
        ptrSP->iMin = i2c_Read(ACK);
        ptrSP->iHor = i2c_Read(ACK);
        i2c_Read(ACK);
        ptrSP->iDia = i2c_Read(ACK);
        ptrSP->iMes = i2c_Read(ACK);
        ptrSP->iAno = i2c_Read(NACK);
    }
    i2c_Stop();

    // ajusta os valores lidos eliminando
    // as informacoes desnescessarias      
                        //                 ____
    ptrSP->iSeg &= 0x7F;// elimina o bit 7 EOSC, da leitura
    ptrSP->iHor &= 0x3F;// elimina os bits 7 e 6, indicacao seculo (CEB e CB)

    // as leituras estaoh em formato BCD, modifica o horario para Hexa
    ptrSP->iSeg = ByteBcdToHex(ptrSP->iSeg);
    ptrSP->iMin = ByteBcdToHex(ptrSP->iMin);
    ptrSP->iHor = ByteBcdToHex(ptrSP->iHor);
    ptrSP->iDia = ByteBcdToHex(ptrSP->iDia);
    ptrSP->iMes = ByteBcdToHex(ptrSP->iMes);
    ptrSP->iAno = ByteBcdToHex(ptrSP->iAno);

    return bOk;
}

REGISTRADOR DE CARGA FLUTUANTE
Bit 7
Bit 6
Bit 5
Bit 4
Bit 3
Bit 2
Bit 1
Bit 0

tcs 3
tcs 2
tcs 1
tcs 0
ds 1
ds 0
rout 1
rout 0
função
x
x
x
x
0
0
x
x
desabilitado
x
x
x
x
1
1
x
x
Desabilitado
x
x
x
x
x
x
0
0
Desabilitado
1
0
1
0
0
1
0
1
sem diodo, 250 resistor
1
0
1
0
1
0
0
1
um diodo, 250 resistor
1
0
1
0
0
1
1
0
sem diodo, 2k resistor
1
0
1
0
1
0
1
0
um diodo, 2k resistor
1
0
1
0
0
1
1
1
sem diodo, 4k resistor
1
0
1
0
1
0
1
1
um diodo, 4k resistor
1
0
1
0
0
0
0
0
Valor ao iniciar

#define HABILITA_CARGA_FLUTUANTE        1
//#define HABILITA_CARGA_FLUTUANTE        0

//#define USA_DIODO                       1
#define USA_DIODO                       0

#define RESISTOR                        250
//#define RESISTOR                        2000
//#define RESISTOR                        4000

#ifndef HABILITA_CARGA_FLUTUANTE
    #error "CARGA FLUTUANTE DO DS1340 NAO CONFIGURADO"
#elif (HABILITA_CARGA_FLUTUANTE == 1)
    #define TCS_CONFIG      0xA     // 1010       
#elif (HABILITA_CARGA_FLUTUANTE == 0)
    #define TCS_CONFIG      0x0
#else
    #error "VALOR INVALIDO PARA HABILITAR CARGA FLUTUANTE"
#endif

#ifndef USA_DIODO
    #error "USO DE DIODO NAO CONFIGURADO"
#elif (USA_DIODO == 1)
    #define DS_CONFIG      0x2      // 10
#elif (USA_DIODO == 0)
    #define DS_CONFIG      0x1      // 01   
#else
    #error "VALOR INVALIDO PARA CONFIGURAR DIODO"
#endif

#ifndef RESISTOR
    #error "USO DE RESISTOR NAO CONFIGURADO"
#elif (RESISTOR == 250)
    #define ROUT_CONFIG      0x1      // 01
#elif (RESISTOR == 2000)
    #define ROUT_CONFIG      0x2      // 10
#elif (RESISTOR == 4000)
    #define ROUT_CONFIG      0x3      // 11
#else
    #error "VALOR INVALIDO PARA CONFIGURAR RESISTOR"
#endif

#define TRICKLE_CHARGER_CFG (TCS_CONFIG << 4 | DS_CONFIG << 2 | ROUT_CONFIG)


Propósito
Configurar o controle de carga da bateria usada pelo DS1340
Parâmetros
nenhum
Descrição
O RTC possui um registrador que controla a flutuante, o registrador possui os seguintes bits acessíveis:
   TCS (Trickle Charger Select) <7:4> - controlam a seleção da carga flutuante que para evitar acionamento acidental, aceita somente como entrada 1010 para ser habilitado.
    DS (Diode Select) <3:2> - configura se existe algum diodo conectado entre o Vcc e Vbackup.
           01 - sem diodo
           10 - com diodo
   ROUT (Resistor OUT) <1:0> - Selecionam o valor do resistor conectado entre o Vcc e Vbackup.
           01 - 250 resistor
           10 - 2k resistor
           11 - 4k resistor

void ds1340_CfgCargaFlutuante(void)
{
    i2c_Start();
    i2c_Write(ADDRTC);              // endereco do escravo + hab escrita
    i2c_Write(eTRICKLE_CHARGER);    // carrega endereco do controle de carga
    i2c_Write(TRICKLE_CHARGER_CFG); // configuracao o carregador flutuante
    i2c_Stop();
}          

Finalizamos o assunto que foi iniciado em postagens anteriores, o assunto era amplo e por isso a necessidade de fragmentar o assunto,  lembrando que esse exemplo de comunicação entre dispositivos pode ser feito também através de uma SPI, mas isso é assunto para uma próxima postagem, até breve.