Main Content

Circuitos_1_1

Estando perto do fim do ano, resolvemos construir um circuito para controlar um relógio de tempo Real (RTC). Um RTC caracteriza-se por ser uma fonte de tempo autónoma que trabalha de forma independente do resto do circuito. Para implementar este circuito recorreu-se ao DS1307, este IC comunica com o exterior através do protocolo I2C.

Foi usado um CSEduino para programar a hora e para posteriormente a apresentar.
O DS1307 para funcionar precisa de um cristal oscilador (de 32.768 KHz) e de uma fonte de energia de 3.3V de backup (tipicamente uma pilha tipo CR) para quando o IC não se encontra alimentado.

A comunicação I2C é feita através dos dois pinos SCA e SCL do IC (estes devem ter resistências ligadas aos 5V para garantir que têm um Pull-UP nas linhas. Isto acontece porque o IC tem transístores que funcionam em modo open-drain.

O Integrado DS1307 tem uma memória de 56 bytes do tipo NVSRAM. Esta tem posições fixas onde se encontra a data/hora atual:

  • Endereço 00h - Segundos (usa os bits 0-6) e está guardado em formato BCD (Bit 0-3 para as unidades (0-9), bit 4-6 para as dezenas (0-5)). O bit 7 é usado para o CH - Clock Halt - permite parar o relógio.
  • Endereço 01h - Minutos (usa os bits 0-6) e está guardado em formato BCD (Bit 0-3 para as unidades (0-9), bit 4-6 para as dezenas (0-5))
  • Endereço 02h - Horas
  • Bit 6 para indicar se é 12 ou 24.
    • Em modo 24 horas - Bit 4-5 para as dezenas (0-2)
    • Em modo 12 horas - bit 4 para as dezenas (0 ou 1) e bit 5 para AM ou PM.
  • Bit 0-3 - para as unidades (0-9)
  • Endereço 03h - Dia da Semana (usa os bits 0-3) (0-7)
  • Endereço 04h - Dia do mês (usa os bits 0-5) e está guardado em formato BCD (Bit 0-3 para as unidades (0-9), bit 4-5 para as dezenas (0-3))
  • Endereço 05h - Mês (usa os bits 0-4) e está guardado em formato BCD (Bit 0-3 para as unidades (0-9), bit 4 para as dezenas (0-1))
  • Endereço 06h - Dia do mês (usa os bits 0-7) e está guardado em formato BCD (Bit 0-3 para as unidades (0-9), bit 4-7 para as dezenas
     (0-9))
  • Endereço 07h - Bit 7: OUT, Bit 4: SQWE, Bit 1: RS1 e Bit 0: RS0. Este endereço é usado para controlar a operação do pino SQW/OUT.
  • Endereço 08h a 3Fh - RAM

Esquemático

Circuitos_3_Schematics

Componentes (BOM)

  • 1x IC DS1307 (U1)
  • 1x LEDs 5mm Vermelho (D1)
  • 1x Resistência de 220 Ohms (R3)
  • 2x Resistências de 2.2K Ohms (R1, R2)
  • 1x Cristal Oscilador de 32.768kHz (Y1)
  • 1x Bateria de 3.3V - CR2032

Associamos ao projeto um display LCD com o integrado HD44780 (ver o circuito do LCD) para apresentar a data. O CSEduino (ver informação adicional na página) foi acrescentado para escrever e ler a informação do RTC e apresentá-la no Display LCD.

Pin-out dos IC/Componentes

Circuitos_4_Pinout

Código

O sketch deve ser carregado num Arduino/CSEDuino através do seu IDE (foi usada a versão 1.6.6).
O código apresentado usa as seguintes bibliotecas externas:

#include <Time.h>
#include <Wire.h>
#include <DS1307RTC.h> //a basic DS1307 library
#include <LiquidCrystal_I2C.h>

// LiquidCrystal_I2C lcd(0x27);  // Set the LCD I2C address

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

const int sqw_pin = 2;

void setup() {
  lcd.begin(16, 2);
  lcd.home();
  lcd.print("CSEduino LCD i2C");
  pinMode(sqw_pin, INPUT);
  Serial.begin(9600);
  setSyncProvider(RTC.get); //this is the function to get the time from the RTC
  lcd.setCursor ( 0, 1 );        // go to the next line
  if (timeStatus() != timeSet) {
    lcd.print("Unable to sync");
    delay ( 5000 );
  }  else {
    lcd.print("RTC system OK");
    attachInterrupt(digitalPinToInterrupt(sqw_pin), setDisplay, FALLING);
    RTC.sqwOutput(1);
  }
  lcd.clear();
}

volatile byte display = 1;

void setDisplay() {
  if (display == 0) {
    display = 1;
  }
}

void loop() {
  if (Serial.available()) {
    display = -1;  //disable display updates
    time_t t = processSyncMessage();
    if (t > 0) {
      RTC.set(t); //set the RTC and the system time to the received value setTime(t);
    }
    display = 1;
  }
  if (display > 0) {
    digitalClockDisplay();
    display = 0;
  }
}

void digitalClockDisplay() {
  if (display) {
    //digital clock display of the time
    lcd.home();
    lcd.print(day());
    lcd.print("-");
    lcd.print(month());
    lcd.print("-");
    lcd.print(year());
    lcd.print("  ");
    lcd.setCursor ( 0, 1 );        // go to the next line
    printDigits(hour(), true);
    printDigits(minute(), true);
    printDigits(second(), false);
  }
}
//utility function for digital clock display: prints preceding colon and leading 0
void printDigits(int digits, boolean sep) {

  if (digits < 10)
    lcd.print('0');
  lcd.print(digits);
  if (sep)
    lcd.print(":");
}

//code to process time sync messages from the serial port
#define TIME_MSG_LEN 11 //time sync to PC is HEADER followed by Unit time_t as ten ascii digits
#define TIME_HEADER 'T' //Header tag for serial time sync message

time_t processSyncMessage() {
  //return the time if a valid sync message is received on the serial port
  //time message consists of a header and ten ascii digits
  while (Serial.available() >= TIME_MSG_LEN) {
    char c = Serial.read();
    Serial.print(c);
    if (c == TIME_HEADER) {
      time_t pctime = 0;
      for (int i = 0; i < TIME_MSG_LEN - 1; i++) {
        c = Serial.read();
        if (c >= '0' && c <= '9') {
          pctime = (10 * pctime) + (c - '0'); //convert digits to a number
        }
      }
      return pctime;
    }
  }
  return 0;
}
// Sketch uses 8,970 bytes (27%) of program storage space. Maximum is 32,256 bytes.
// Global variables use 549 bytes (26%) of dynamic memory, leaving 1,499 bytes for local variables. Maximum is 2,048 bytes.

O código precisa por uma entrada no formato “T9999999999” via porta série que fará o Setup do RTC. O número a seguir à letra “T” deverá ter 10 dígitos e representar o número de segundos desde 1 Janeiro de 1970.

Para se obter esse número em Linux basta correr o comando: date +%s.

Em Windows para obter esse numero deverão criar um ficheiro com a extensão vbs e colocar-lhe o seguinte conteúdo: wscript.echo(datediff("s",#1970/1/1#,now())).

Caso ambas a soluções sejam complicadas demais podem ir ao seguinte link e ver o valor.

É também configurado o registo do SQW para que o mesmo pulse a 1Hz ou seja 1 vez por segundo. Este input está ligado ao pino 2 do CSEduino que tem uma interrupção associada a este pino, atualizando a data no LCD apenas quando é o valor do pino passa a 0.