456 lines
12 KiB
Plaintext
456 lines
12 KiB
Plaintext
// Utility sketch to set RTC time, date, and control registers.
|
|
//
|
|
// This sketch can be used with many Maxim I2C RTC chips.
|
|
// See your chip's data sheet.
|
|
//------------------------------------------------------------------------------
|
|
#include <avr/pgmspace.h>
|
|
#include <I2cMaster.h>
|
|
#include <SoftRTClib.h>
|
|
|
|
// use software I2C if USE_SOFT_I2C is nonzero
|
|
#define USE_SOFT_I2C 1
|
|
|
|
#if USE_SOFT_I2C
|
|
// use analog pins 4, 5 for software I2C
|
|
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
|
// Is Mega
|
|
const uint8_t RTC_SCL_PIN = 59;
|
|
const uint8_t RTC_SDA_PIN = 58;
|
|
#else
|
|
// Not Mega
|
|
const uint8_t RTC_SCL_PIN = 19;
|
|
const uint8_t RTC_SDA_PIN = 18;
|
|
#endif // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
|
SoftI2cMaster i2c(RTC_SDA_PIN, RTC_SCL_PIN);
|
|
|
|
#else // USE_SOFT_I2C
|
|
// enable pull-ups on SDA/SCL
|
|
TwiMaster i2c(true);
|
|
#endif // USE_SOFT_I2C
|
|
|
|
// constants for SQW test
|
|
uint8_t const SQW_DELAY = 10;
|
|
uint8_t const SQW_INTERRUPT = 0;
|
|
uint8_t const SQW_PIN = 2;
|
|
uint8_t const GPS_PIN = 3;
|
|
uint8_t const GPS_INTERRUPT = 1;
|
|
RTC_DS1307 rtc(&i2c);
|
|
|
|
uint32_t sqwCount;
|
|
int32_t gpsCount;
|
|
int32_t gpsSqwCount;
|
|
|
|
void gpsInterrupt();
|
|
bool hexReadEcho(uint8_t*);
|
|
void hexPrint(uint8_t);
|
|
void sqwInterrupt();
|
|
//------------------------------------------------------------------------------
|
|
#define printPSTR(s) print_P(PSTR(s))
|
|
//------------------------------------------------------------------------------
|
|
void print_P(PGM_P str) {
|
|
uint8_t c;
|
|
while ((c = pgm_read_byte(str++))) Serial.write(c);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool readUint8(uint8_t base, uint8_t *v) {
|
|
uint16_t n = 0;
|
|
while (!Serial.available()) {}
|
|
while (Serial.available()) {
|
|
uint8_t c = Serial.read();
|
|
if ('0' <= c && c <= '9') {
|
|
c -= '0';
|
|
} else if ('a' <= c && c <= 'z') {
|
|
c -= 'a' - 10;
|
|
} else if ('A' <= c && c <= 'Z') {
|
|
c -= 'A' - 10;
|
|
} else {
|
|
c = base;
|
|
}
|
|
n = base * n + c;
|
|
if (c >= base || n >= 256) return false;
|
|
delay(10);
|
|
}
|
|
*v = n;
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool decReadEcho(uint8_t min, uint8_t max, uint8_t* n) {
|
|
uint8_t d;
|
|
if (!readUint8(10, &d) || d < min || d > max) {
|
|
printPSTR("Invalid\r\n");
|
|
return false;
|
|
}
|
|
Serial.println(d, DEC);
|
|
*n = d;
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void displayTime(void) {
|
|
DateTime dt;
|
|
if (!rtc.getTime(&dt)) {
|
|
printPSTR("DS1307 time error\r\n");
|
|
return;
|
|
}
|
|
dt.printDdd(&Serial);
|
|
printPSTR(", ");
|
|
dt.printDateTime(&Serial);
|
|
Serial.println();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void helpDS1307Crtl() {
|
|
printPSTR(
|
|
"07h DS1307 Control\r\n"
|
|
"+---+---+---+----+---+---+---+---+\r\n"
|
|
"|OUT| 0 | 0 |SQWE| 0 | 0 |RS1|RS2|\r\n"
|
|
"| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |\r\n"
|
|
"+---+---+---+----+---+---+---+---+\r\n"
|
|
"OUT SQWE RS1 RS2 SQW Pin\r\n"
|
|
" x 1 0 0 1 Hz\r\n"
|
|
" x 1 0 1 4096 Hz\r\n"
|
|
" x 1 1 0 8196 Hz\r\n"
|
|
" x 1 1 1 32768 Hz\r\n"
|
|
" 1 0 x x high\r\n"
|
|
" 0 0 x x low\r\n");
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void helpDS3231Ctrl() {
|
|
printPSTR(
|
|
"0Eh DS3231 Control\r\n"
|
|
"+-----+-----+----+---+---+-----+----+----+\r\n"
|
|
"|!EOSC|BBSQW|CONV|RS2|RS1|INTCN|A2IE|A1IE|\r\n"
|
|
"| 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 |\r\n"
|
|
"+-----+-----+----+---+---+-----+----+----+\r\n"
|
|
"!EOSC - set to stop osc under battery\r\n"
|
|
"BBSQW - set to enable SQW under battery\r\n"
|
|
"CONV - set to start temp conversion\r\n"
|
|
"RS2 RS1 SQW Pin\r\n"
|
|
" 0 0 1 Hz\r\n"
|
|
" 0 1 1024 Hz\r\n"
|
|
" 1 0 4096 Hz\r\n"
|
|
" 1 1 8196 Hz\r\n"
|
|
"INTCN - clear to enable SQW, set for alarm\r\n"
|
|
"A2IE - set to enable alarm 2 interrupt\r\n"
|
|
"A1IE - set to enable alarm 1 interrupt\r\n");
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void helpDS3231CrtlStatus() {
|
|
printPSTR(
|
|
"0Fh DS3231 Control/Status\r\n"
|
|
"+---+---+---+---+-------+---+---+---+\r\n"
|
|
"|OSF| 0 | 0 | 0 |EN32kHz|BSY|A2F|A1F|\r\n"
|
|
"| 1 | 0 | 0 | 0 | 1 | x | x | x |\r\n"
|
|
"+---+---+---+---+-------+---+---+---+\r\n"
|
|
"OSF - osc was stopped flag\r\n"
|
|
"EN32kHz - set to enable 32 kHz\r\n"
|
|
"BSY - busy executing TCXO functions\r\n"
|
|
"A2F - alarm 2 flag\r\n"
|
|
"A1F - alarm 1 flag\r\n");
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void DS3231Temperature() {
|
|
uint8_t r[2];
|
|
const uint8_t DS3231_TEMP_ADD = 0X11;
|
|
if (!rtc.read(DS3231_TEMP_ADD, r, 2)) {
|
|
printPSTR("Read temp failed\r\n");
|
|
return;
|
|
}
|
|
float T = ((r[0] << 8) | r[1]) / 256.0;
|
|
Serial.print(T);
|
|
printPSTR(" C\r\n");
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void dumpRegisters(void) {
|
|
uint8_t a = 0;
|
|
uint8_t h;
|
|
do {
|
|
if ((a % 8) == 0) {
|
|
if (a) Serial.println();
|
|
hexPrint(a);
|
|
Serial.write(' ');
|
|
}
|
|
Serial.write(' ');
|
|
if (!rtc.read(a, &h, 1)) break;
|
|
hexPrint(h);
|
|
} while(a++ != 0X3F);
|
|
Serial.println();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool enableSQW() {
|
|
uint8_t sqwEnable;
|
|
printPSTR("Connect RTC signal to pin ");
|
|
Serial.println(SQW_PIN, DEC);
|
|
while (Serial.read() >= 0) {}
|
|
printPSTR(
|
|
"Type 1 to enable 32 kHz SQW on a DS1307\r\n"
|
|
"Type 0 to skip SQW enable for chips like a DS3231\r\n"
|
|
"Enter (0 or 1): ");
|
|
if (!decReadEcho(0, 1, &sqwEnable)) return false;
|
|
if (sqwEnable) {
|
|
if (!rtc.setSQW(DS1307_SQW_32768_HZ)) {
|
|
printPSTR("SQW enable failed\r\n");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void gpsCal(void) {
|
|
printPSTR("Connect GPS pps to pin ");
|
|
Serial.println(GPS_PIN, DEC);
|
|
if (!enableSQW()) return;
|
|
printPSTR("\r\nType any character to stop\r\n\r\n");
|
|
while (Serial.read() >= 0);
|
|
pinMode(GPS_PIN, INPUT);
|
|
pinMode(SQW_PIN, INPUT);
|
|
// enable pullup
|
|
digitalWrite(SQW_PIN, HIGH);
|
|
digitalWrite(GPS_PIN, HIGH);
|
|
// attach interrupts
|
|
attachInterrupt(SQW_INTERRUPT, sqwInterrupt, FALLING);
|
|
attachInterrupt(GPS_INTERRUPT, gpsInterrupt, FALLING);
|
|
sqwCount = 0;
|
|
delay(100);
|
|
if (sqwCount == 0) {
|
|
printPSTR("No RTC signal on pin ");
|
|
Serial.println(SQW_PIN, DEC);
|
|
return;
|
|
}
|
|
gpsCount = -1;
|
|
while (Serial.available() == 0) {
|
|
cli();
|
|
int32_t s = gpsSqwCount;
|
|
int32_t g = gpsCount;
|
|
sei();
|
|
if (g < 1) {
|
|
printPSTR("Waiting for GPS pps on pin ");
|
|
Serial.println(GPS_PIN, DEC);
|
|
delay(1000);
|
|
} else {
|
|
float e = (float)(s - 32768 * g) / (32768 * g);
|
|
Serial.print(g);
|
|
if (e == 0.0) {
|
|
printPSTR(" RTC == GPS \r\n");
|
|
} else {
|
|
if (e > 0) {
|
|
printPSTR(" fast ");
|
|
} else if (e < 0) {
|
|
printPSTR(" slow ");
|
|
}
|
|
Serial.print(e * 24 * 3600, 3);
|
|
printPSTR(" sec/day ");
|
|
Serial.print(1000000 * e);
|
|
printPSTR(" +-");
|
|
Serial.print(0.02 + 2000000.0 / s);
|
|
printPSTR(" ppm\r\n");
|
|
}
|
|
uint32_t m = millis();
|
|
while (g == gpsCount) {
|
|
if ((millis() -m) > 2000) gpsCount = -1;
|
|
}
|
|
}
|
|
}
|
|
detachInterrupt(GPS_INTERRUPT);
|
|
detachInterrupt(SQW_INTERRUPT);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void gpsInterrupt(void) {
|
|
if (gpsCount < 0) {
|
|
sqwCount = 0;
|
|
gpsCount = 0;
|
|
return;
|
|
}
|
|
gpsCount++;
|
|
gpsSqwCount = sqwCount;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void help() {
|
|
printPSTR(
|
|
"Options are:\r\n"
|
|
"(0) Display date and time\r\n"
|
|
"(1) Set date and time\r\n"
|
|
"(2) Dump registers\r\n"
|
|
"(3) Set register\r\n"
|
|
"(4) DS3231 temperature\r\n"
|
|
"(5) SQW/32kHz pin test\r\n"
|
|
"(6) Calibrate with GPS pps\r\n"
|
|
"(7) DS1307 control help\r\n"
|
|
"(8) DS3231 control help\r\n"
|
|
"(9) DS3231 control/status help\r\n");
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void hexPrint(uint8_t v) {
|
|
Serial.print(v >> 4, HEX);
|
|
Serial.print(v & 0XF, HEX);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void hexPrintln(uint8_t v) {
|
|
hexPrint(v);
|
|
Serial.println();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool hexReadEcho(uint8_t* v) {
|
|
uint8_t h;
|
|
if (!readUint8(16, &h)) {
|
|
printPSTR("Invalid\r\n");
|
|
return false;
|
|
}
|
|
hexPrintln(h);
|
|
*v = h;
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void setDateTime(void) {
|
|
DateTime dt;
|
|
uint8_t Y, M, D, h, m, s;
|
|
uint8_t v;
|
|
printPSTR("Enter year [1,99]: ");
|
|
if (!decReadEcho(0, 99, &Y)) return;
|
|
printPSTR("Enter month [1,12]: ");
|
|
if (!decReadEcho(1, 12, &M)) return;
|
|
printPSTR("Enter day [1,31]: ");
|
|
if (!decReadEcho(1, 31, &D)) return;
|
|
printPSTR("Enter hour [0,23]: ");
|
|
if (!decReadEcho(0, 23, &h)) return;
|
|
printPSTR("Enter minute [0,59]: ");
|
|
if (!decReadEcho(0, 59, &m)) return;
|
|
printPSTR("Enter second [0,59]: ");
|
|
if (!decReadEcho(0, 59, &s)) return;
|
|
if (!dt.settime(2000 + Y, M, D, h, m, s)) {
|
|
printPSTR("Invalid date/time\r\n");
|
|
return;
|
|
}
|
|
if (!rtc.setTime(&dt)) {
|
|
printPSTR("setTime failed\r\n");
|
|
return;
|
|
}
|
|
displayTime();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void setRegister(void) {
|
|
uint8_t a;
|
|
uint8_t r;
|
|
printPSTR(
|
|
"Address/Register\n\r"
|
|
"7 DS1307 Control\n\r"
|
|
"E DS3231 Control\n\r"
|
|
"F DS3231 Control/Status\n\r"
|
|
"10 DS3231 Aging Offset\n\r"
|
|
"\n\r"
|
|
"Enter address [0,FF]: ");
|
|
if (!hexReadEcho(&a)) return;
|
|
if (!rtc.read(a, &r, 1)) {
|
|
printPSTR("read failed");
|
|
return;
|
|
}
|
|
printPSTR("Current value: ");
|
|
hexPrintln(r);
|
|
printPSTR("q to quit or new value [0,FF]: ");
|
|
while (!Serial.available());
|
|
if (Serial.peek() == 'q') {
|
|
Serial.println('q');
|
|
return;
|
|
}
|
|
if (!hexReadEcho(&r)) return;
|
|
if (!rtc.write(a, &r, 1)) {
|
|
printPSTR("write failed");
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void sqwInterrupt(void) {
|
|
sqwCount++;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void sqwTest(void) {
|
|
if (!enableSQW()) return;
|
|
// enable pullup
|
|
digitalWrite(SQW_PIN, HIGH);
|
|
printPSTR("Please wait ");
|
|
Serial.print(SQW_DELAY, DEC);
|
|
printPSTR(" seconds.\r\n");
|
|
attachInterrupt(SQW_INTERRUPT, sqwInterrupt, FALLING);
|
|
sqwCount = 0;
|
|
delay(SQW_DELAY * 1000UL);
|
|
detachInterrupt(SQW_INTERRUPT);
|
|
Serial.print(sqwCount);
|
|
printPSTR(" interrupts in ");
|
|
Serial.print(SQW_DELAY, DEC);
|
|
printPSTR(" seconds.\r\n");
|
|
if (sqwCount == 0) {
|
|
printPSTR("Is SQWE/EN32kHz set in the control register?\r\n");
|
|
printPSTR("Is the RTC connected to pin ");
|
|
Serial.print(SQW_PIN, DEC);
|
|
Serial.println('?');
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void setup(void) {
|
|
Serial.begin(9600);
|
|
|
|
Serial.println();
|
|
displayTime();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void loop(void) {
|
|
uint8_t n;
|
|
|
|
while (Serial.read() >= 0);
|
|
|
|
printPSTR("\r\nEnter option number or h for help: ");
|
|
|
|
while (!Serial.available());
|
|
n = Serial.read();
|
|
Serial.write(n);
|
|
Serial.println();
|
|
Serial.println();
|
|
|
|
switch (n) {
|
|
case 'h':
|
|
case 'H':
|
|
help();
|
|
break;
|
|
|
|
case '0':
|
|
displayTime();
|
|
break;
|
|
|
|
case '1':
|
|
setDateTime();
|
|
break;
|
|
|
|
case '2':
|
|
dumpRegisters();
|
|
break;
|
|
|
|
case '3':
|
|
setRegister();
|
|
break;
|
|
|
|
case '4':
|
|
DS3231Temperature();
|
|
break;
|
|
|
|
case '5':
|
|
sqwTest();
|
|
break;
|
|
|
|
case '6':
|
|
gpsCal();
|
|
break;
|
|
|
|
case '7':
|
|
helpDS1307Crtl();
|
|
break;
|
|
|
|
case '8':
|
|
helpDS3231Ctrl();
|
|
break;
|
|
|
|
case '9':
|
|
helpDS3231CrtlStatus();
|
|
break;
|
|
|
|
default:
|
|
printPSTR("Invalid option");
|
|
break;
|
|
}
|
|
} |