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
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
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:
- NewliquidCrystal. Pode ser descarregado localmente deste link.
- Time. Pode ser descarregado localmente deste link
- DS1307RTC. Pode ser descarregado localmente deste link
#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.