2021-01-06
Módulo ZS-042, reloj RTC
El módulo ZS-042 es uno de los tantos módulos que existen para Arduino, este nos servirá para tener un reloj/calendario y así poder consultar hora y fecha con nuestro Arduino.
El módulo ZS-042
El módulo consta de dos partes, un chip ds3231 que es en realidad el reloj RTC que nos permite tener la hora/fecha y algunas características mas. Por otro lado tenemos una memoria EEPROM at24C32 de 4K bytes.
Dispone de una batería para que en caso de que desconectemos la alimentación de Arduino no se pierda la información.
El esquema es el siguiente:
Podemos observar las líneas del bus I2C que son las que permiten la comunicación con el Arduino y que comparten tanto el ds3231 como el at24C32.
Por otro lado la parte de selección de la dirección de memoria del at24C32. Dado que es posible conectar varias memorias en el mismo bus, este chip dispone de tres pines para seleccionar la parte baja de la dirección con lo que se puede direccionar desde la dirección 0x50 a 0x58.
Se puede observar en el esquema que si no modificamos nada en el módulo los pines A2, A1 y A0 estarán a nivel alto gracias a las resistencias PULL-UP, con lo que su dirección inicial es 0x58.
Para poder modificar esta dirección debemos soldar unas gotas de estaño en los pad disponibles para ello.
En el circuito también podemos observar un pequeño circuito de carga de la batería cr2032 de 3.3V de dudoso funcionamiento. En teoría dicho circuito reduce la corriente a través de una resistencia de 200 ohmios. El circuito es válido para baterías de acido/plomo, aunque dudo de su funcionamiento para pilas de Litio.
Existen dos juegos de conectores. En ambos están las señales SDA y SCL del bus I2C y la alimentación VCC/GND del circuito por lo que se pueden utilizar dicho conectores indistintamente. La diferencia entre ellos es que en uno de ellos está también disponible los pines SQW y 32KHz que mas adelante veremos lo que son.
El DS3231.
El zs042 es el módulo preferido de los aficionados a Arduino ya que el chip RTC es el ds3231, el cual ofrece una precisión muy buena y tiene corrección de temperatura.
Generalmente los chips RTC funcionan a base de un cristal de cuarzo de 32KHz y con esta señal son capaces de contar los segundos, minuto, horas, etc; pero la señal de este cuarzo se ve afectada con la temperatura no siendo exacta la frecuencia. El ds3231 es capaz de medir la temperatura y corregir la señal del cristal ofreciendo mucha precisión. En comparación, podemos ver que el otro chip ampliamente utilizado que es el ds1307 suele retrasarse mucho, cosa que a este chip no le ocurre.
Los pines que posee son:
- VCC y GND. La alimentación, esta puede ser de 2.3V a 5.5V.
- SCL y SDA. Las señales de comunicación del bus I2C.
- RST. Señal de reset.
- VBAT. Línea de alimentación para backup, ahí es donde va conectada la pila.
- 32kHz. Simplemente es una salida de reloj con dicha frecuencia, no es muy usado, pero está disponible en el conector "largo" del zs042.
- INT/SQW. Es la línea de interrupción o salida de pulsos.
La línea INT/SQW puede funcionar de dos manera diferentes.
Por un lado puede funcionar como un generador de señal que podemos programar en cuatro frecuencias: 1Hz, 1KHz, 4KHz y 8Khz. Para ello debemos configurar los bits correspondientes en los registros de control.
Por el otro, funciona como un generador de interrupción. En este casos se usa como alarma ya que la interrupción se genera cuando coincidan los registros de alarma con los de datos. Por ejemplo podemos configurarlo para que se ejecute cuando las horas, minutos y segundos coinciden con los datos para tener una alarma diaria.
Conexión con Arduino.
Dado que se usa con el bus I2C la conexión es muy simple y basta solo con conectar las líneas SCL/SDA y la alimentación.
Programación
Aquí sobre gustos colores, existen muchas librerías que son capaces de manejar el zs-042. Ni mejores ni peores, cada una tiene una funcionalidad diferente.
Mis favoritas son las de Adafruit y de Geekfactory. Te dejo un enlace a cada una de ellas.
Aunque si eres un poco maniático y no necesitas mucha complejidad, solo saber la hora y la fecha, puedes usar la librería simpleRTC que cree para un proyecto muy básico:
SimpleRTC.h
#ifndef SimpleRTC_H #define SimpleRTC_H // DATETIME es una estructura simple para mantener la información de una fecha // y hora en una sola variable. Arduino incluye una librería DateTime con sus // propias funciones que no se van a usar. struct DATETIME { unsigned char hour; // Hora unsigned char minute; // Minuto unsigned char second; // Segundo unsigned char day; // Dia unsigned char month; // Mes unsigned char year; // Año }; class SimpleRTC { public: SimpleRTC(); // Constructor. // Lectura de la hora/fecha actual. void read(struct DATETIME &time); // Escribe la hora pasando como parámetro la hora y el minuto. void writeTime(unsigned char _hour, unsigned char _minute); // Escribe la fecha pasando como parámetro el día, mes y año. void writeDate(unsigned char _day, unsigned char _month, unsigned char _year); void write(struct DATETIME t); // Escribe en el registro de alarma 1 la hora y el minuto. void writeAlarm1(unsigned char _hour, unsigned char _minute); // Lee la hora que hay en el registro de la alarma 1. void readAlarm1(unsigned char &_hour, unsigned char &_minute); }; #endif
SimpleRTC.cpp
#include "SimpleRTC.h" #include <Wire.h> // Por defecto el DS3231 tiene asignada una dirección fija y no se puede modificar. #define RTC_ADDRESS 0x68 // Funciones para convertir de BCD a binario y viceversa. unsigned char bcd2bin(unsigned char b) { return (b>>4)*10 + (b&0x0f); } unsigned char bin2bcd(unsigned char b) { return (b/10)<<4 | (b%10); } SimpleRTC::SimpleRTC() { // Inicializa el bus I2C. Wire.begin(); } void SimpleRTC::writeTime(unsigned char _hour, unsigned char _minute) { // Iniciamos la transmisión y le decimos que queremos escribir a partir // del registro 0 los segundos, minutos y horas. Wire.beginTransmission(RTC_ADDRESS); Wire.write(0); // Primera dirección Wire.write(0x00); // No ponemos los segundos así que 0. Wire.write( bin2bcd(_minute) ); // Minutos. Wire.write( bin2bcd(_hour) ); // Horas. Wire.endTransmission(); // Fin de la transmisión. } void SimpleRTC::writeDate(unsigned char _day, unsigned char _month, unsigned char _year) { // Iniciamos la transmisión, desde la dirección 4 en adelante encontramos // el día, mes y año. Wire.beginTransmission(RTC_ADDRESS); Wire.write(4); Wire.write( bin2bcd(_day) ); Wire.write( bin2bcd(_month) ); Wire.write( bin2bcd(_year) ); Wire.endTransmission(); } void SimpleRTC::read(DATETIME &time) { // La lectura empieza con una transmisión de la dirección a partir de la cual // queremos leer, en este caso es desde el principio. Wire.beginTransmission(RTC_ADDRESS); Wire.write(0); Wire.endTransmission(); // Luego solicitamos el número de bytes que se corresponden con todos los // datos que necesitamos de la fecha. Wire.requestFrom(RTC_ADDRESS,7); time.second = bcd2bin( Wire.read() & 0x7F); time.minute = bcd2bin( Wire.read() ); time.hour = bcd2bin( Wire.read() ); Wire.read(); // El día de la semana lo leemos pero no lo almacenamos. time.day = bcd2bin( Wire.read() ); time.month = bcd2bin( Wire.read() ); time.year = bcd2bin( Wire.read() ); } void SimpleRTC::write(struct DATETIME t) { writeTime(t.hour, t.minute); writeDate(t.day, t.month, t.year); } void SimpleRTC::writeAlarm1(unsigned char _hour, unsigned char _minute) { // En la dirección 08h se encuentra el minuto de la alarma y en la sucesiva // la hora, así que escribimos el registro. Wire.beginTransmission(RTC_ADDRESS); Wire.write(8); Wire.write( bin2bcd(_minute) ); Wire.write( bin2bcd(_hour) ); Wire.endTransmission(); } void SimpleRTC::readAlarm1(unsigned char &_hour, unsigned char &_minute) { // Leemos la alarma y la guardamos en los parámetros. Wire.beginTransmission(RTC_ADDRESS); Wire.write(8); Wire.endTransmission(); Wire.requestFrom(RTC_ADDRESS, 2); _minute = bcd2bin( Wire.read() ); _hour = bcd2bin( Wire.read() ); }