sexta-feira, junho 17, 2011

Projeto 'Epoch' - Parte4 - Software (cont)

Na parte anterior vimos a interface com o módulo de relógio DS1302. Veremos hoje o resto do software.

Display

O tratamento do display não é muito diferente daquilo que já fiz em outros projetos. Um vetor de 16 bytes armazena o valor a apresentar no display, na forma de um dígito por byte. A rotina de tratamento da interrupção de timer faz dois tratamentos:
  • A cada segundo soma um ao valor a apresentar
  • A cada três interrupções seleciona um par de dígitos (sinais S2 a S0 do display) e coloca os valores correspondentes nos sinais Ax e Bx. Reparar que a ordem de seleção dos dígitos é diferente nas duas metades.
Eis o código correspondente:
#define  DISP_S0 PIN_C0
#define DISP_S1 PIN_C1
#define DISP_S2 PIN_C2

#define TAXA_DISP 3 // taxa de varredura do display
#define INTS_SEG 3906 // interrupções por segundo

// valor a apresentar no display
int8 valor[16];

//////////////////////////////////////////////
// Interrupcao de tempo real
// Ocorre a cada 512 uSeg
#int_RTCC
void RTCC_isr()
{
static unsigned int16 cont_seg = INTS_SEG;
static unsigned int8 cont_disp = TAXA_DISP;
static unsigned int8 disp = 0;

// Atualização da contagem de tempo
if (--cont_seg == 0)
{
int8 i=15;
while ((i > 0) && (++valor[i] == 10))
valor[i--] = 0;
cont_seg = INTS_SEG;
}

// Atualização do Display
if (--cont_disp == 0)
{
// seleciona o digito atual
if (disp & 1)
output_high (DISP_S0);
else
output_low (DISP_S0);
if (disp & 2)
output_high (DISP_S1);
else
output_low (DISP_S1);
if (disp & 4)
output_high (DISP_S2);
else
output_low (DISP_S2);

// coloca os valores
output_a ((valor[15-disp] << 4) | valor[disp]);

// passa para o dígito seguinte
disp = (disp + 1) & 7;
cont_disp = TAXA_DISP;
}
}

Comunicação Serial

A comunicação serial é feita usando as rotinas do compilador C da CSC. A operação de acerto do relógio é bem simples: envia-se um CR (enter) e o PIC responde com um prompt '>'. Em seguida deve ser enviada a data e hora no formato ddmmaahhmmss, finalizado por outro CR. Antes do CR final o valor pode ser editado através do backspace. O valor digitado é enviado ao relógio (sem consistências...).
// Caracteres ASCII
#define BS 8
#define LF 10
#define CR 13
#define ESC 27

// valor a programar no relogio (ddmmaahhmmss)
int8 horario[12];

// data/hora para o relogio
byte relogio[8];

// Trata comunicação com o PC
void TrataUart (void)
{
char c;
int i;

if (kbhit())
{
c = getc();
if (c != CR)
return;

output_high (LED);
putchar (CR);
putchar (LF);
putchar ('>');
i = c = 0;
//ddmmaahhmmss
while ((c != CR) || (i != 12))
{
c = getc();
if ((c == ESC) || (c == 0))
break;
if ((c == BS) && (i > 0))
{
putchar(BS);
putchar(' ');
putchar(BS);
i--;
}
else if ((i < 12) && (c >= '0') && (c <= '9'))
{
putchar(c);
horario[i++] = c-'0';
}
}
putchar (CR);
putchar (LF);
if (c != ESC)
{
relogio[0] = (horario[10] << 4) | horario[11];
relogio[1] = (horario[8] << 4) | horario[9];
relogio[2] = (horario[6] << 4) | horario[7];
relogio[3] = (horario[0] << 4) | horario[1];
relogio[4] = (horario[2] << 4) | horario[3];
relogio[5] = 1;
relogio[6] = (horario[4] << 4) | horario[5];
relogio[7] = 0x80;

for (i = 0; i < 8; i++)
{
putchar (hexa[relogio[i] >> 4]);
putchar (hexa[relogio[i] & 0x0F]);
putchar (' ');
}
putchar (CR);
putchar (LF);

DS1302_WriteClock (relogio);
LoadClk ();
}
output_low (LED);
}
}


Iniciação do Relógio

Após um reset ou acerto do relógio, o valor a ser apresentado no display é preenchido com o valor correspondente à data e hora no relógio. Isto requer converter uma data e hora em uma contagem de segundos. Na biblioteca padrão do C existe a rotina mktime para isto, no meu caso precisei fazer "na unha":
// Dias por mês      31 28 31  30  31  30  31  31  30  31  30
const int dias[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };

// EPOCH0 - fundação do GHC em segundos após 1/1/2000 00:00:00
#define EPOCH0 351549263L

// Coloca no display o valor do relógio não volátil
void LoadClk (void)
{
byte i, aux;
unsigned int32 time;

DS1302_ReadClock (relogio);
if ((relogio[3] & 0x3F) == 0)
{
// não programado
for (i = 0; i < 16; i++)
{
valor[i] = 0;
}
return;
}

// Zera os bits não implementados no timer
relogio[0] &= 0x7F;
relogio[1] &= 0x7F;
relogio[2] &= 0x3F;
relogio[3] &= 0x3F;
relogio[4] &= 0x1F;

// Converte valores BCD para binário
for (i = 0; i < 7; i++)
{
relogio[i] = (relogio[i] >> 4) * 10 + (relogio[i] & 0x0F);
}

// Determina o número de dias nos anos inteiros
// Na faixa 2000 a 2099 os anos divisíveis por 4 são bissextos
// Cada grupo de 4 anos tem 3*365+366 dias
time = ((unsigned int32) (relogio[6] >> 2)) * (3L*365L+366L);
aux = (relogio[6] & 3);
if (aux > 0)
{
time += 366;
if (aux > 1)
time += 365;
if (aux > 2)
time += 365;
}

// Soma os dias até a data atual
time += dias[relogio[4]-1];
if ((aux == 0) && (relogio[4] > 2))
time++; // ano bissexto
time += relogio[3]-1;

// Soma as horas
time = time*24UL + (unsigned int32) relogio[2];

// Soma os minutos
time = time*60UL + (unsigned int32) relogio[1];

// Soma os segundos
time = time*60UL + (unsigned int32) relogio[0];

// Contar a partir do EPOCH0
time -= EPOCH0;

// coloca no valor do display
disable_interrupts (INT_RTCC);
for (i = 0; i < 16; i++)
{
valor[15-i] = time % 10L;
time = time / 10L;
}
enable_interrupts (INT_RTCC);
}
Iniciação e Programa Principal

Para completar o programa fica faltando somente o Programa Principal e a iniciação do Hardware:
// Ponto de entrada e loop principal
void main (void)
{
InitHw ();
LoadClk ();
enable_interrupts(GLOBAL);
for (;;)
{
TrataUart();
}
}

// Iniciação do hardware
void InitHw (void)
{
// Bota para correr
setup_oscillator (OSC_8MHZ);

// Dispositivos não usados
setup_comparator(NC_NC);
setup_vref(FALSE);
setup_adc(ADC_OFF);

// E/S
set_tris_a (0x00); // RA0-RA7 output
set_tris_b (0xFA); // RB0 e RB2 output
set_tris_c (0xF0); // RC0-RC3 output
output_low (LED);

// Timer
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2);
setup_timer_1(T1_DISABLED);
enable_interrupts (INT_RTCC);

// Uart
while (kbhit())
getc();
}

O conjunto completo de arquivos para gerar o software, usando o ambiente MPLAB e o compilador PCM da CCS estão em Epoch.zip no SkyDrive que você acessa pelo ícone na lateral do blog.

Nenhum comentário: