Accedere ai registri dell’MCP7940N usando Arduino
Molti tutorial sull’utilizzo di un RTCC (Real Time Clock and Calendar) con Arduino riguardano l’integrato DS1307 in quanto la sua diffusione è data dalla disponibilità di diverse librerie, dal costo contenuto e dalla sua integrazione in numerose shield. Nonostante ciò sul mercato esistono molti altri dispositivi interessanti che svolgono funzioni aggiuntive e hanno precisioni molto più elevate riguardo al conteggio del tempo.
Mi sono trovato a realizzare dei progetti che come specifica richiedevano una alimentazione di 3,3V, dato che il DS1307 lavora tra i 4,5V e i 5,5V ho dovuto ripiegare su un altro rtcc. Quello che ho scelto è l’MCP7940N prodotto da Microchip. Nonostante non sia disponibile la versione PDIP ho scelto questo integrato perché oltre a lavorare con tensione di alimentazione di 3,3V ha alcune altre funzioni interessanti come la possibilità di impostare due allarmi e la possibilità di calibrare l’orologio per poter aumentare la sua precisione nel conteggio del tempo.
Nella tabella seguente ho messo in comparazione le funzioni del DS1307 col MCP7940N
La gestione dei registri di questo integrato è molto semplice; è molto simile a quella del DS1307 e possiamo usare gran parte del codice usato in questo precedente articolo.
DS1307 | MCP7940N | |
Tensione di alimentazione | da 4.5V a 5.5V | da 1.8V a 5.5V |
Consumo (Alimentazione a batteria) | <500nA | <700nA |
Calibrazione RTC | NO | SI(digitale) |
SRAM | 56Bytes | 64Bytes |
Allarme programmabile | NO | SI (2) |
Uscita | Onda quadra programmabile |
Onda quadra programmabile. Uscita allarme |
Passiamo alla pratica realizzando il seguente circuito elettrico. Lo schema è identico a quello utilizzato per il DS13017.
Il circuito utilizza il bus I2C per comunicare con l’integrato MCP7940N.
Andiamo a controllare il datasheet dell’ MCP7940N per vedere quali sono i registri che permettono il settaggio della data e dell’orario:
i registri che vanno dall’indirizzo 00h all’indirizzo 06h sono quelli che utilizzeremmo per impostare l’ora e la data. La modalità di scrittura di questi registri deve avvenire utilizzando la codifica BCD. Da notare che oltre al dato di tempo e di tata, ci sono alcuni bit che permettono di attivare alcune funzioni, ad esempio il registro 00h setta i secondi (dal bit0 al bit6) ma permette anche di avviare l’oscillatore (bit7 ST).
E’ possibile settare il bit7 manualmente, con la funzione bitSet(), oppure direttamente quando impostiamo il valore dei secondi servendoci di una maschera di bit. Vediamo subito con un esempio cosa dobbiamo fare.
Supponiamo di impostare il registro a 38 secondi. La prima cosa da fare è convertire il numero 38 in codice BCD utilizzando questa funzione:
// Converte un valore decimale in valore BCD byte dec2bcd(byte val) { return ( ((val / 10) * 16) + (val % 10)); }
il valore decimale 38 equivale al valore BCD 0b00111000.
Il valore decimale 38 viene convertito in valore BCD semplicemente convertendo le unità in codice binario e le decine in codice binario, infatti l’unità 8 equivale al valore binario 0b1000 mentre la decina 3 equivale al valore binario 0b0011. I due valori binari vengono concatenati per ottenere il valore BCD infatti 0b0011 concatenato con 0b1000 forma il valore BCD 0b00111000 (38 decimale)
Come possiamo osservare il bit7 del valore BCD è a valore logico basso. Per far partire l’oscillatore dovremmo impostare questo bit a valore logico alto. Questo può essere fatto utilizzando la funzione bitSet() o più semplicemente mascherando il valore BCD dei secondi con un valore binario che imposti il bit7 a livello alto, lasciando inalterato il valore BCD dei secondi.
Quest’ultima soluzione viene eseguita usando l’operatore | (bitwise or). Il valore della maschera deve tenere il bit7 a livello logico alto e lasciare invariato il valore dei secondi. Il valore della maschera da utilizzare sarà quindi 0b10000000 (0x80). In questo modo mettendo il valore 0b10000000 in or col valore BCD dei secondi 0b00111000 otterremmo il valore 0b10111000:
byte Maschera = 0x80; byte valore_registro_00h = Secondi | Maschera;
Ora sappiamo come generare i dati in valore BCD e come mascherarli per attivare le funzioni aggiuntive dei registri quindi di seguito il codice che permette di impostare i registri dall’indirizzo 00h all’indirizzo 06h, con l’ora e la data:
//includo la libreria per la //comunicazione su bus I2C #include <Wire.h> void setup() { //inizializzo la libreria Wire.begin(); //attivo la comunicazione con il MCP7940N //l'indirizzo dell'RTC è 0x6F Wire.beginTransmission(0x6F); Wire.write(0x00); //specifico il tempo e la data Wire.write(dec2bcd(0) | 0x80); //Secondi e ST a livello logico alto Wire.write(dec2bcd(35)); //minuti Wire.write(dec2bcd(20) | 0x20); // ore e formato 24h Wire.write(dec2bcd(4) | 0x08); //giorno della settimana e VBAT on Wire.write(dec2bcd(3)); //Giorno Wire.write(dec2bcd(11)); //mese Wire.write(dec2bcd(16)); //anno Wire.endTransmission(); } void loop() { } //converte il numero decimale in numero BCD byte bcd2dec(byte num) { return (((num / 16) * 10) + (num % 16)); }
Leggere orario e data dall’MCP7940N
Dopo aver impostato data e ora e settato alcuni parametri (ST e VBATEN) il circuito RTCC MCP7940N autonomamente gestirà orario e data senza l’ausilio di istruzioni aggiuntive. A questo punto sarà sufficiente eseguire la lettura dei sette registri, da 00h a 06h, per ottenere le informazioni relative a data e ora correnti.
Il codice seguente mostra come eseguire la lettura dei registri e come elaborare i dati, in quanto i registri restituiscono i valori in formato BCD comprensivi dei bit di settagio (ST e VBATEN):
#include <Wire.h> //struttura timeelements tmElements_t tm; void setup() { //inizializzo la seriale Serial.begin(9600); delay (500); //inizializzo la libreria Wire.begin(); } void loop() { //leggere registri MCP7940N //inizzializza la trasmissione partendo //dall'indirizzo 0x00 Wire.beginTransmission(0x6F); Wire.write(0x00); Wire.endTransmission(); //richiedo 7 byte dal dispositivo con //indirizzo 0x6F Wire.requestFrom(0x6F, 7); //regupero i 7 byte relativi ai //corrispondenti registri tm.Second = bcd2dec(Wire.read() & 0x7f); //secondi tm.Minute = bcd2dec(Wire.read()); //minuti tm.Hour = bcd2dec(Wire.read() & 0x3f); //ore tm.Wday = bcd2dec(Wire.read() & 0x07); //giorno settimana tm.Day = bcd2dec(Wire.read()); //giorno tm.Month = bcd2dec(Wire.read() & 0x1F ); //mese tm.Year = bcd2dec(Wire.read()); //anno Serial.print("Orario corrente: "); Serial.print(tm.Hour); Serial.print(":"); Serial.print(tm.Minute ); Serial.print(":"); Serial.println(tm.Second); Serial.print("Giorno della settimana: "); Serial.println(tm.Wday); Serial.print("Data corrente: "); Serial.print(tm.Day); Serial.print("/"); Serial.print(tm.Month); Serial.print("/"); Serial.println(tm.Year); } //Converti numero BCD in numero Decimale byte bcd2dec(byte num) { return (((num / 16) * 10) + (num % 16)); }
Nel codice possiamo osservare come vengono mascherati i dati letti tramite un l’operatore & (bitwise and). Sapendo che i registri contengono bit che permettono di settare o resettare delle funzioni aggiuntive dell’MCP7940N, dobbiamo necessariamente escluderli dalla lettura per ottenere solamente il dato relativo al tempo.
Nel caso del registro 00h abbiamo che, oltre al dato dei secondi, è presente anche il bit7 (ST) che deve essere escluso quando convertiamo il valore BCD in decimale. Tramite l’operatore & e la maschera 0x7f (0b01111111) otteniamo un comportamento di questo tipo; supponiamo che il dato letto dal registro sia 0b10111000, mascherando questo dato con il valore 0b01111111 otteniamo 0b00111000.
In conclusione, risulta molto semplice scrivere e leggere dai registri dell’MCP7940N, dobbiamo solo stare attenti nell’utilizzo delle maschere di bit che ci permettano, in fase di scrittura, di inserire i dati di data e tempo insieme ai settaggi che abilitano alcune funzionalità, mentre, in fase di lettura, di ottenere i dati di tempo filtrando i settaggi delle funzionalità.