quarta-feira, junho 15, 2011

Projeto 'Epoch' - Parte3 - Software

Vamos começar a examinar o software pelas rotinas de interface do microcontrolador PIC com o módulo de relógio DS1302.

Como vimos na parte anterior, a ligação do DS1302 ao PIC é feita por três sinais: CE, SCLK e I/O. O sinal CE (chip enable) deve ser colocado em nível "1" para inicial uma operação e só ser retornado a "0" ao seu final. O sinal SCLK (serial clock) determina quando cada bit deve ser lido ou escrito na linha I/O. Os sinais CE e SCLK são sempre comandados pelo microcontrolador, o sinal I/O é bi-direcional.

As transferência de dados são compostas por bytes (8 bits). O primeiro byte de toda transferência é enviado do microcontrolador para o DS1302 e determina a operação que será realizada e endereça um registrador do relógio ou uma posição da memória Ram do DS1302:


Os bits são transferidos serialmente do menos significativo para o mais. Os bits devem ser enviados pelo transmissor na borda de descida de SCLK e lidos pelo receptor na borda de subida. A figura abaixo mostra os ciclos básicos de leitura e escrita de um byte:


O byte de comando possui 5 bits de endereçamento. No acesso ao relógio, existem 9 registradores; na Ram são 31 posições. Nos dois casos o endereço 11111 indica o modo burst. Neste modo, múltiplos bytes são transferidos com o endereço começando em zero e incrementando automaticamente a cada byte lido ou escrito. A figura abaixo mostra um resumo dos registradores e endereços:


Um cuidado necessário ao escrever nos registradores do relógio é que é preciso desprotegê-los primeiro (escrevendo 0 em WP).

Para concluir esta descrição do DS1302, vejamos o funcionamento do circuito de carga da bateria. O DS1302 alimenta a bateria a partir da sua própria alimentação (no caso 5V), podendo colocar em série 1 ou 2 diodos (para reduzir a tensão) e um dentre 3 resistores (para limitar a corrente):


A programação que usei coloque em série os dois diodos e a resistência de 2K. Desta forma, a tensão máxima será 5 - 2*0,7 = 3,6V e a máxima corrente (supondo a bateria totalmente zerada) será 3.6V / 2K = 1,8 mA.

Vamos, finalmente, ao código. As rotinas abaixo são responsáveis pelo envio e recepção de bytes:
#define  DS1302_CE   PIN_B0
#define DS1302_IO PIN_B1
#define DS1302_SCLK PIN_B2

// Envia um byte ao DS1302
void DS1302_TxByte (byte b)
{
int i;

set_tris_b (0xF8); // I/O é output
for (i = 0; i < 8; i++)
{
if (b & 1)
output_high (DS1302_IO);
else
output_low (DS1302_IO);
delay_us (1);
output_high (DS1302_SCLK);
delay_us (1);
output_low (DS1302_SCLK);
b = b >> 1;
}
set_tris_b (0xFA); // I/O volta a ser input
}

// Recebe um byte do DS1302
byte DS1302_RxByte ()
{
int i;
byte dado;

for (i = 0; i < 8; i++)
{
delay_us (1);
dado = dado >> 1;
if (input (DS1302_IO))
dado |= 0x80;
output_high (DS1302_SCLK);
delay_us (1);
output_low (DS1302_SCLK);
}
return dado;
}
Usando estas rotinas, fica fácil implementar a leitura e escrita tanto em um registrador do relógio como em todos eles (modo burst):
// Alguns registradores do DS1302
#define DS1302_WP 0x0E
#define DS1302_TC 0x10
#define DS1302_CBURST 0x3E

// Lê relogio em modo burst
void DS1302_ReadClock (byte *regs)
{
byte i;

output_high (DS1302_CE);
delay_us (4);
DS1302_TxByte (DS1302_CBURST | 0x81);
for (i = 0; i < 8; i++)
{
*regs++ = DS1302_RxByte ();
delay_us (4);
}
output_low (DS1302_CE);
}

// Escreve relogio em modo burst
void DS1302_WriteClock (byte *regs)
{
byte i;

DS1302_Write (DS1302_WP, 0x00);
DS1302_Write (DS1302_TC, 0xA9); // tricle charger, 2 diodes, 2Kohms

output_high (DS1302_CE);
delay_us (4);
DS1302_TxByte (DS1302_CBURST | 0x80);
for (i = 0; i < 8; i++)
{
DS1302_TxByte (*regs++);
delay_us (4);
}
output_low (DS1302_CE);
}

// Lê um byte de um endereço do DS1302
byte DS1302_Read (byte ender)
{
byte result;

output_high (DS1302_CE);
delay_us (2);
DS1302_TxByte (ender | 0x81);
result = DS1302_RxByte ();
delay_us (2);
output_low (DS1302_CE);
return result;
}

// Escreve um byte em um endereço do DS1302
void DS1302_Write (byte ender, byte dado)
{
output_high (DS1302_CE);
delay_us (2);
DS1302_TxByte (ender | 0x80);
DS1302_TxByte (dado);
delay_us (2);
output_low (DS1302_CE);
}

Nenhum comentário: