O módulo DS1302 é um módulo RTC mais básico e mais barato que outros encontrados e permite a contagem do tempo e da data automaticamente. Sendo assim, neste post, veremos como usar o DS1302 com o Arduino para adicionar à capacidade de obtenção de data e hora em nossos projetos. E aprenderemos um pouco sobre o que é o RTC de forma geral.
Informações básicas
O que é o módulo RTC DS1302
No contexto da eletrônica, RTC (Real-Time Clock) ou Relógio de Tempo Real é um tipo de circuito capaz de armazenar a hora e data e manter elas atualizadas em tempo real. Ou seja, a qualquer momento que eu quiser, esse circuito precisa me passar o valor correto da hora e data atual.
Para que o RTC alcance seu objetivo de manter a hora e data atualizadas a todo instante, é muito comum dele utilizar uma bateria para que a contagem da hora continue sendo feita mesmo se a fonte de alimentação principal for cortada. Por exemplo: os computadores sabem a hora e data correta graças ao RTC que fica na placa mãe (também sabem por meio da Internet). Mas, se a energia acabar ou a tomada do PC for desconectada, a bateria presente na placa mãe vai manter o RTC contando as horas. Assim, quando o PC for ligado novamente, a hora vai estar atualizada independente se o PC tem ou não conexão com a internet para puxar o horário atualizado.
Um RTC também pode englobar outras funcionalidades mais complexas como função de alarme, mas isso vai depender de caso para caso. O DS1302 é um RTC mais básico e, além da função de armazenar e controlar a data e hora, ele também tem uma memória interna, que pode ser usada para armazenar e ler dados.
E o DS1302 em si é um Circuito Integrado, mas o foco deste post é mostrar o módulo mais comum que utiliza esse CI. Veja uma foto dele abaixo, onde é possível ver o CI (do tipo PTH) e a bateria (CR2032) que mantém ele contando mesmo sem receber alimentação nos pinos VCC e GND:
Além da bateria, o RTC também pode contar com outro componente muito importante: o cristal (”cilindro” acima do CI na imagem acima). O cristal é utilizado na geração do clock interno que o RTC usa para contagem dos segundos. É muito comum dos RTCs utilizarem um cristal de 32768 Hz. Internamente ele acaba dividindo o clock até obter uma frequência próxima de 1 Hz para contar os segundos corretamente.
Imprecisão do horário
Conforme foi falado no tópico anterior, a contagem dos segundos advém de um clock que é gerado a partir de um cristal com valor específico. Qualquer imprecisão neste cristal faz com que o clock seja mais rápido ou lento que o ideal. Por conta disso, a contagem dos RTCs não é perfeita e depende bastante da precisão dos cristais.
O principal parâmetro do cristal que nós dá ideia dessa precisão é o ppm ou partes por milhão. A partir dele, conseguimos saber qual é o desvio que a frequência do cristal pode apresentar:
Desvio_f = \pm\frac{f_{cristal} \times ppm}{1000000} [Hz]
Dessa forma, um cristal de 32768 Hz com 30 ppm pode apresentar uma frequência de 32768 +- 0,98 Hz. 0,98 Hz pode parecer um desvio pequeno, mas esse efeito acumulado em um mês pode fazer o RTC contar 1 min a mais do que deveria (ou a menos). Ou seja, o RTC pode não ser recomendado para aplicações de máxima precisão.
Neste link tem um documento detalhando melhor o assunto.
Aplicações
Adiante estão alguns exemplos de uso do DS1302:
-
Despertador:
Um despertador precisa de ter noção da hora atual e um RTC pode ser muito útil pra isso.
Aqui no site tem um post que ensina a criar um despertador, mas sem RTC. A desvantagem é que a hora não é armazenada caso o dispositivo fique sem energia.
-
Aplicações onde você quer manter a funcionalidade de relógio fora do microcontrolador:
Ao invés de armazenar e contar o tempo dentro do próprio microcontrolador, um CI dedicado pode liberar o microcontrolador para focar em outras tarefas.
Alguns microcontroladores modernos já possui um circuito RTC interno pra lidar com todo o controle de data e hora automaticamente.
-
Dispositivos que precisam passar muito tempo dormindo para economizar energia:
- O RTC pode ajudar nessa tarefa, pois ele consome pouca energia e, dependendo, pode ser capaz de acordar o microcontrolador em um determinado horário, evitando que ele fique ligado quando não é necessário.
- Guardar um valor importante:
- Como o DS1302 possui uma memória interna, o microcontrolador pode armazenar algum dado importante nela.
-
Entre outros casos. O limite é a imaginação.
Características do RTC DS1302
Neste tópico, irei mostrar as características do CI DS1302 e do módulo mais comum. Não serão informações cruciais para a implementação no Arduino. Então, se desejar, pode pular para o tópico seguinte.
Características
As informações adiante foram extraídas do datasheet do DS1302.
- RTC capaz de contar:
- Segundos;
- Minutos;
- Horas;
- Dia do mês;
- Mês;
- Dia da semana;
- Ano com compensação de ano bissexto até o ano 2100.
- Possui dois pinos de alimentação: VCC1 e VCC2:
- Ambos suportam de 2,0 a 5,5V;
- A tensão típica é de 3,3V;
- VCC1: alimentação da bateria;
- VCC2: alimentação primária;
- O CI usa a alimentação que tiver tensão mais alta (tem um pequeno detalhe nesta questão que pode ser lido no datasheet).
- Tensão máxima dos pinos digitais:
- -0,3 V à (VCC + 0,3 V);
- Se VCC = 5V, então, suporta de -0,3 à 5,3 V.
- Interface Serial Síncrona:
- Pode-se dizer que é igual a comunicação SPI de 3 fios (MISO e MOSI interligados);
- Utiliza 3 fios:
- CLK ou SCLK: clock da comunicação;
- RST ou CE: pino para habilitar comunicação;
- DAT ou I/O: pino onde os dados são enviados/recebidos.
- Clock de no máximo 2 MHz em 5V.
- Consumo de corrente:
- Funcionando normalmente com alimentação primária: até 1,2 mA;
- Modo onde apenas mantém a hora/atualizada: 1 μA;
- O consumo depende da tensão de alimentação e do estado do RTC, então consulte o datasheet para maiores detalhes.
- É possível configurar o DS1302 internamente para interconectar VCC2 e VCC1 para que a alimentação principal carregue a bateria ligada em VCC1. Essa configuração é relativa ao recurso chamado de Trickle Charger. Leia os detalhes no datasheet.
- A hora pode ser configurada no formato de 24h ou de 12h am/pm.
- O DS1302 possui uma memória RAM com 31 bytes:
- É possível ler/gravar dados que são mantidos na memória desde que o DS1302 esteja alimentado de alguma forma. Se a alimentação principal e a bateria forem desligadas, os dados dessa RAM são apagados.
Circuito do módulo
Encontrei neste site uma imagem de um possível esquemático do módulo DS1302. Pelo próprio datasheet, a ligação sugerida já é bem simples e o esquemático não é muito diferente. Veja abaixo:
No caso do módulo que tenho (foto do início do post), ele não possui os resistores R1 a R3 e nem os capacitores C1 e C2.
Utilizando o DS1302 com o Arduino (com biblioteca)
Agora vejamos como usar o DS1302 com o Arduino para realizar algumas tarefas diferentes.
Circuito
A ligação do Arduino com o DS1302 é bem direta, pois você não precisa de nenhum outro componente:
O VCC vai no 5V do Arduino e os GNDs de ambos são interligados. Em relação aos demais pinos, eles podem ser ligados em quaisquer pinos digitais do Arduino. O importante é alterar a relação depois no código do Arduino. No caso da imagem acima, RST está no pino 3, DAT no pino 4 e CLK no pino 5.
Obs: Se o VCC for ligado no 3.3V do Arduino, o limite de tensão dos pinos digitais do DS1302 será de -0,3 à 3,6 V. Ou seja, você pode danificar o DS1302 se não utilizar um conversor de sinais nos pinos DAT, CLK e RST.
Baixando biblioteca
A biblioteca pode ser baixada na própria IDE do Arduino em Sketch -> Incluir Biblioteca -> Gerenciar Bibliotecas… E é só pesquisar por “DS1302” e baixar a opção que tem como autor “Rtc_by_Makuna”.
Não existe um motivo específico para utilizar esta biblioteca, mas foi uma das primeiras que testei e que achei tranquilo de usar. Fique a vontade para usar outra, mas tenha em mente que os códigos seguintes foram feitos usando a biblioteca que apresentei.
Código básico
O código abaixo e os seguintes são uma adaptação dos exemplos que tem na biblioteca. No caso, ele configura a hora do RTC no void setup e, no void loop, ele fica printando a data, hora e dia da semana a cada 5 segundos. A configuração da data e hora usa o #define __DATE__ e __TIME__ (são declarados implicitamente pela linguagem C++). Esses #defines informam ao programa a data e hora em que ele foi compilado. Então isso evita da gente ter que ficar digitando toda vez a data e hora que queremos.
Leia os comentários para entender o código.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | // Bibliotecas (os arquivos vem da mesma biblioteca) #include <ThreeWire.h> #include <RtcDS1302.h> // Pinos do RTC #define DS1302_RST 3 #define DS1302_DAT 4 #define DS1302_CLK 5 // Instância da ligação e do RTC ThreeWire ligacao(DS1302_DAT, DS1302_CLK, DS1302_RST); RtcDS1302<ThreeWire> rtc(ligacao); void setup () { // Inicia a comunicação serial Serial.begin(9600); // Inicia a comunicação com o RTC rtc.Begin(); // Desabilita a proteção contra escrita para gravar a hora e data if (rtc.GetIsWriteProtected()) { rtc.SetIsWriteProtected(false); } // Se não estava rodando, começa a funcionar if (!rtc.GetIsRunning()) { rtc.SetIsRunning(true); } // Obtém o valor da hora quando o código foi compilado RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); rtc.SetDateTime(compiled); // Para definir um formato personalizado, você pode fazer o seguinte: // O mês precisa ter 3 letras abreviando o nome do mês em inglês (no exemplo abaixo é fevereiro) //RtcDateTime teste = RtcDateTime("Feb 12 2022", "19:32:45"); //rtc.SetDateTime(teste); } void loop () { // Obtem a data e hora e imprime os valores RtcDateTime now = rtc.GetDateTime(); Serial.print(now.Day()); Serial.print("/"); Serial.print(now.Month()); Serial.print("/"); Serial.println(now.Year()); Serial.print(now.Hour()); Serial.print(":"); Serial.print(now.Minute()); Serial.print(":"); Serial.println(now.Second()); // Dia da semana (1 a 7, sendo 1 = segunda e 7 = domingo) Serial.println(now.DayOfWeek()); delay(5000); } |
Código com outros comandos
Peguei o mesmo código de antes e adicionei alguns comandos que a biblioteca disponibiliza. Por exemplo, o comando de verificar se a data e hora obtidas são válidas. Leia os comentários para entender o código.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | #include <ThreeWire.h> #include <RtcDS1302.h> #define DS1302_RST 3 #define DS1302_DAT 4 #define DS1302_CLK 5 ThreeWire ligacao(DS1302_DAT, DS1302_CLK, DS1302_RST); RtcDS1302<ThreeWire> rtc(ligacao); void setup () { // Inicia a comunicação serial Serial.begin(9600); // Inicia a comunicação com o RTC rtc.Begin(); // Desabilita a proteção contra escrita if (rtc.GetIsWriteProtected()) { rtc.SetIsWriteProtected(false); } // Se não estava rodando, começa a funcionar if (!rtc.GetIsRunning()) { rtc.SetIsRunning(true); } // Obtém o valor da hora quando o código foi compilado RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); // Se a data/hora não é valida // Pode ocorrer na primeira vez que o RTC é usado // Ou então se o RTC estiver sem a bateria ou ela estiver com tensão baixa if (!rtc.IsDateTimeValid()) { Serial.println("Data e hora inválidos!"); rtc.SetDateTime(compiled); } else { // Se a hora/data for válida, mas estiver desatualizada, atualiza RtcDateTime now = rtc.GetDateTime(); if (now < compiled) { rtc.SetDateTime(compiled); } } } void loop () { // Obtem a data e hora e imprime os valores RtcDateTime now = rtc.GetDateTime(); Serial.print(now.Day()); Serial.print("/"); Serial.print(now.Month()); Serial.print("/"); Serial.println(now.Year()); Serial.print(now.Hour()); Serial.print(":"); Serial.print(now.Minute()); Serial.print(":"); Serial.println(now.Second()); // Dia da semana (1 a 7, sendo 1 = segunda e 7 = domingo) Serial.println(now.DayOfWeek()); // É possível verificar se a data e hora são válidas // Isso pode ocorrer se o RTC estiver sem a bateria ou ela estiver com tensão baixa if (!now.IsValid()) { Serial.println("Data e hora inválidos!"); } delay(5000); } |
Leitura e escrita à memória
Por fim, o código adiante mostra como escrever e ler dados da memória RAM do DS1302. No caso, ele fica lendo a Serial e o que for escrito nela é transferido para a RAM do DS1302. E a cada 1 segundo o programa lê e printa 10 bytes da RAM.
Para testar o código você pode: abrir o monitor serial, escrever um texto de até 10 letras e aperta enter. Em seguida, o texto será printado no monitor serial a cada 1 segundo. Você pode então desligar o Arduino e ligar de novo para ver se o valor foi mantido (lembrar de colocar uma bateria no DS1302).
Leia os comentários para entender o código.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #include <ThreeWire.h> #include <RtcDS1302.h> #define DS1302_RST 3 #define DS1302_DAT 4 #define DS1302_CLK 5 ThreeWire ligacao(DS1302_DAT, DS1302_CLK, DS1302_RST); RtcDS1302<ThreeWire> rtc(ligacao); RtcDateTime now; RtcDateTime last_now; void setup () { // Inicia a comunicação serial Serial.begin(9600); // Inicia a comunicação com o RTC rtc.Begin(); // Desabilita a proteção contra escrita if (rtc.GetIsWriteProtected()) { rtc.SetIsWriteProtected(false); } // Se não estava rodando, começa a funcionar if (!rtc.GetIsRunning()) { rtc.SetIsRunning(true); } // Obtém o valor da hora quando o código foi compilado RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); rtc.SetDateTime(compiled); } void loop () { now = rtc.GetDateTime(); // Lê uma mensagem recebida no monitor serial if(Serial.available()) { char msg[10] = ""; uint8_t * msg_p = msg; uint8_t i = 0; while(Serial.available() && i < 10) { *msg_p = (char)Serial.read(); msg_p++; i++; delay(1); // Pequeno delay para dar tempo do próximo byte ser recebido } // Grava a mensagem na memória rtc.SetMemory((const uint8_t*)msg, sizeof(msg)); } // Entra aqui a cada 1 segundo if (now.Second() != last_now.Second()) { last_now = now; char msg[10] = ""; // Lê a memória RAM do DS1302 rtc.GetMemory(msg, 10); for(uint8_t i = 0; i < 10; i++) { Serial.print(msg[i]); } Serial.println(""); } } |
Valeu. Ajudou demais. Eu estava quebrando a cabeça já a vários dias. Consegui resolver o problema com as tuas dicas. Muitíssimo obrigado!
Que coisa boa, Paulo. Fico feliz por ter ajudado. Grande abraço!