feat: 全量同步 254 个常用的 Arduino 扩展库文件

This commit is contained in:
yczpf2019
2026-01-24 16:05:38 +08:00
parent c665ba662b
commit 397b9a23a3
6878 changed files with 2732224 additions and 1 deletions

View File

@@ -0,0 +1,112 @@
#pragma once
//I2C Slave Address
const uint8_t AT24C32_ADDRESS = 0x50; // 0b0 1010 A2 A1 A0
template<class T_WIRE_METHOD> class EepromAt24c32
{
public:
EepromAt24c32(T_WIRE_METHOD& wire, uint8_t addressBits = 0b111) :
_address(AT24C32_ADDRESS | (addressBits & 0b00000111)),
_wire(wire),
_lastError(0)
{
}
void Begin()
{
_wire.begin();
}
uint8_t LastError()
{
return _lastError;
}
void SetMemory(uint16_t memoryAddress, uint8_t value)
{
SetMemory(memoryAddress, &value, 1);
}
uint8_t GetMemory(uint16_t memoryAddress)
{
uint8_t value;
GetMemory(memoryAddress, &value, 1);
return value;
}
// note: this method will write within a single page of eeprom.
// Pages are 32 bytes (5 bits), so writing past a page boundary will
// just wrap within the page of the starting memory address.
//
// xxxppppp pppaaaaa => p = page #, a = address within the page
//
// NOTE: hardware WIRE libraries often have a limit of a 32 byte send buffer. The
// effect of this is that only 30 bytes can be sent, 2 bytes for the address to write to,
// and then 30 bytes of the actual data.
uint8_t SetMemory(uint16_t memoryAddress, const uint8_t* pValue, uint8_t countBytes)
{
uint8_t countWritten = 0;
beginTransmission(memoryAddress);
while (countBytes > 0)
{
_wire.write(*pValue++);
delay(10); // per spec, memory writes
countBytes--;
countWritten++;
}
_lastError = _wire.endTransmission();
return countWritten;
}
// reading data does not wrap within pages, but due to only using
// 12 (32K) or 13 (64K) bits are used, they will wrap within the memory limits
// of the installed EEPROM
//
// NOTE: hardware WIRE libraries may have a limit of a 32 byte recieve buffer. The
// effect of this is that only 32 bytes can be read at one time.
uint8_t GetMemory(uint16_t memoryAddress, uint8_t* pValue, uint8_t countBytes)
{
// set address to read from
beginTransmission(memoryAddress);
_lastError = _wire.endTransmission();
if (_lastError != 0)
{
return 0;
}
// read the data
uint8_t countRead = 0;
countRead = _wire.requestFrom(_address, countBytes);
countBytes = countRead;
while (countBytes-- > 0)
{
*pValue++ = _wire.read();
}
return countRead;
}
private:
const uint8_t _address;
T_WIRE_METHOD& _wire;
uint8_t _lastError;
void beginTransmission(uint16_t memoryAddress)
{
_wire.beginTransmission(_address);
_wire.write(memoryAddress >> 8);
_wire.write(memoryAddress & 0xFf);
}
};

View File

@@ -0,0 +1,272 @@
#ifndef __RTCDS1302_H__
#define __RTCDS1302_H__
#include <Arduino.h>
#include "RtcDateTime.h"
#include "RtcUtility.h"
//DS1302 Register Addresses
const uint8_t DS1302_REG_TIMEDATE = 0x80;
const uint8_t DS1302_REG_TIMEDATE_BURST = 0xBE;
const uint8_t DS1302_REG_TCR = 0x90;
const uint8_t DS1302_REG_RAM_BURST = 0xFE;
const uint8_t DS1302_REG_RAMSTART = 0xc0;
const uint8_t DS1302_REG_RAMEND = 0xfd;
// ram read and write addresses are interleaved
const uint8_t DS1302RamSize = 31;
// DS1302 Trickle Charge Control Register Bits
enum DS1302TcrResistor {
DS1302TcrResistor_Disabled = 0,
DS1302TcrResistor_2KOhm = B00000001,
DS1302TcrResistor_4KOhm = B00000010,
DS1302TcrResistor_8KOhm = B00000011,
DS1302TcrResistor_MASK = B00000011,
};
enum DS1302TcrDiodes {
DS1302TcrDiodes_None = 0,
DS1302TcrDiodes_One = B00000100,
DS1302TcrDiodes_Two = B00001000,
DS1302TcrDiodes_Disabled = B00001100,
DS1302TcrDiodes_MASK = B00001100,
};
enum DS1302TcrStatus {
DS1302TcrStatus_Enabled = B10100000,
DS1302TcrStatus_Disabled = B01010000,
DS1302TcrStatus_MASK = B11110000,
};
const uint8_t DS1302Tcr_Disabled = DS1302TcrStatus_Disabled | DS1302TcrDiodes_Disabled | DS1302TcrResistor_Disabled;
// DS1302 Clock Halt Register & Bits
const uint8_t DS1302_REG_CH = 0x80; // bit in the seconds register
const uint8_t DS1302_CH = 7;
// Write Protect Register & Bits
const uint8_t DS1302_REG_WP = 0x8E;
const uint8_t DS1302_WP = 7;
template<class T_WIRE_METHOD> class RtcDS1302
{
public:
RtcDS1302(T_WIRE_METHOD& wire) :
_wire(wire)
{
}
void Begin()
{
_wire.begin();
}
void Begin(int sda, int scl)
{
_wire.begin(sda, scl);
}
bool GetIsWriteProtected()
{
uint8_t wp = getReg(DS1302_REG_WP);
return !!(wp & _BV(DS1302_WP));
}
void SetIsWriteProtected(bool isWriteProtected)
{
uint8_t wp = getReg(DS1302_REG_WP);
if (isWriteProtected)
{
wp |= _BV(DS1302_WP);
}
else
{
wp &= ~_BV(DS1302_WP);
}
setReg(DS1302_REG_WP, wp);
}
bool IsDateTimeValid()
{
return GetDateTime().IsValid();
}
bool GetIsRunning()
{
uint8_t ch = getReg(DS1302_REG_CH);
return !(ch & _BV(DS1302_CH));
}
void SetIsRunning(bool isRunning)
{
uint8_t ch = getReg(DS1302_REG_CH);
if (isRunning)
{
ch &= ~_BV(DS1302_CH);
}
else
{
ch |= _BV(DS1302_CH);
}
setReg(DS1302_REG_CH, ch);
}
uint8_t GetTrickleChargeSettings()
{
uint8_t setting = getReg(DS1302_REG_TCR);
return setting;
}
void SetTrickleChargeSettings(uint8_t setting)
{
if ((setting & DS1302TcrResistor_MASK) == DS1302TcrResistor_Disabled) {
// invalid resistor setting, set to disabled
setting = DS1302Tcr_Disabled;
goto apply;
}
if ((setting & DS1302TcrDiodes_MASK) == DS1302TcrDiodes_Disabled ||
(setting & DS1302TcrDiodes_MASK) == DS1302TcrDiodes_None) {
// invalid diode setting, set to disabled
setting = DS1302Tcr_Disabled;
goto apply;
}
if ((setting & DS1302TcrStatus_MASK) != DS1302TcrStatus_Enabled) {
// invalid status setting, set to disabled
setting = DS1302Tcr_Disabled;
goto apply;
}
apply:
setReg(DS1302_REG_TCR, setting);
}
void SetDateTime(const RtcDateTime& dt)
{
// set the date time
_wire.beginTransmission(DS1302_REG_TIMEDATE_BURST);
_wire.write(Uint8ToBcd(dt.Second()));
_wire.write(Uint8ToBcd(dt.Minute()));
_wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only
_wire.write(Uint8ToBcd(dt.Day()));
_wire.write(Uint8ToBcd(dt.Month()));
// RTC Hardware Day of Week is 1-7, 1 = Monday
// convert our Day of Week to Rtc Day of Week
uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());
_wire.write(Uint8ToBcd(rtcDow));
_wire.write(Uint8ToBcd(dt.Year() - 2000));
_wire.write(0); // no write protect, as all of this is ignored if it is protected
_wire.endTransmission();
}
RtcDateTime GetDateTime()
{
_wire.beginTransmission(DS1302_REG_TIMEDATE_BURST | THREEWIRE_READFLAG);
uint8_t second = BcdToUint8(_wire.read() & 0x7F);
uint8_t minute = BcdToUint8(_wire.read());
uint8_t hour = BcdToBin24Hour(_wire.read());
uint8_t dayOfMonth = BcdToUint8(_wire.read());
uint8_t month = BcdToUint8(_wire.read());
_wire.read(); // throwing away day of week as we calculate it
uint16_t year = BcdToUint8(_wire.read()) + 2000;
_wire.read(); // throwing away write protect flag
_wire.endTransmission();
return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
}
void SetMemory(uint8_t memoryAddress, uint8_t value)
{
// memory addresses interleaved read and write addresses
// so we need to calculate the offset
uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART;
if (address <= DS1302_REG_RAMEND)
{
setReg(address, value);
}
}
uint8_t GetMemory(uint8_t memoryAddress)
{
uint8_t value = 0;
// memory addresses interleaved read and write addresses
// so we need to calculate the offset
uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART;
if (address <= DS1302_REG_RAMEND)
{
value = getReg(address);
}
return value;
}
uint8_t SetMemory(const uint8_t* pValue, uint8_t countBytes)
{
uint8_t countWritten = 0;
_wire.beginTransmission(DS1302_REG_RAM_BURST);
while (countBytes > 0 && countWritten < DS1302RamSize)
{
_wire.write(*pValue++);
countBytes--;
countWritten++;
}
_wire.endTransmission();
return countWritten;
}
uint8_t GetMemory(uint8_t* pValue, uint8_t countBytes)
{
uint8_t countRead = 0;
_wire.beginTransmission(DS1302_REG_RAM_BURST | THREEWIRE_READFLAG);
while (countBytes > 0 && countRead < DS1302RamSize)
{
*pValue++ = _wire.read();
countRead++;
countBytes--;
}
_wire.endTransmission();
return countRead;
}
private:
T_WIRE_METHOD& _wire;
uint8_t getReg(uint8_t regAddress)
{
_wire.beginTransmission(regAddress | THREEWIRE_READFLAG);
uint8_t regValue = _wire.read();
_wire.endTransmission();
return regValue;
}
void setReg(uint8_t regAddress, uint8_t regValue)
{
_wire.beginTransmission(regAddress);
_wire.write(regValue);
_wire.endTransmission();
}
};
#endif // __RTCDS1302_H__

View File

@@ -0,0 +1,259 @@
#ifndef __RTCDS1307_H__
#define __RTCDS1307_H__
#include <Arduino.h>
#include "RtcDateTime.h"
#include "RtcUtility.h"
//I2C Slave Address
const uint8_t DS1307_ADDRESS = 0x68;
//DS1307 Register Addresses
const uint8_t DS1307_REG_TIMEDATE = 0x00;
const uint8_t DS1307_REG_STATUS = 0x00;
const uint8_t DS1307_REG_CONTROL = 0x07;
const uint8_t DS1307_REG_RAMSTART = 0x08;
const uint8_t DS1307_REG_RAMEND = 0x3f;
const uint8_t DS1307_REG_RAMSIZE = DS1307_REG_RAMEND - DS1307_REG_RAMSTART;
//DS1307 Register Data Size if not just 1
const uint8_t DS1307_REG_TIMEDATE_SIZE = 7;
// DS1307 Control Register Bits
const uint8_t DS1307_RS0 = 0;
const uint8_t DS1307_RS1 = 1;
const uint8_t DS1307_SQWE = 4;
const uint8_t DS1307_OUT = 7;
// DS1307 Status Register Bits
const uint8_t DS1307_CH = 7;
enum DS1307SquareWaveOut
{
DS1307SquareWaveOut_1Hz = 0b00010000,
DS1307SquareWaveOut_4kHz = 0b00010001,
DS1307SquareWaveOut_8kHz = 0b00010010,
DS1307SquareWaveOut_32kHz = 0b00010011,
DS1307SquareWaveOut_High = 0b10000000,
DS1307SquareWaveOut_Low = 0b00000000,
};
template<class T_WIRE_METHOD> class RtcDS1307
{
public:
RtcDS1307(T_WIRE_METHOD& wire) :
_wire(wire),
_lastError(0)
{
}
void Begin()
{
_wire.begin();
}
void Begin(int sda, int scl)
{
_wire.begin(sda, scl);
}
uint8_t LastError()
{
return _lastError;
}
bool IsDateTimeValid()
{
return GetIsRunning();
}
bool GetIsRunning()
{
uint8_t sreg = getReg(DS1307_REG_STATUS);
return (!(sreg & _BV(DS1307_CH)) && (_lastError == 0));
}
void SetIsRunning(bool isRunning)
{
uint8_t sreg = getReg(DS1307_REG_STATUS);
if (isRunning)
{
sreg &= ~_BV(DS1307_CH);
}
else
{
sreg |= _BV(DS1307_CH);
}
setReg(DS1307_REG_STATUS, sreg);
}
void SetDateTime(const RtcDateTime& dt)
{
// retain running state
uint8_t sreg = getReg(DS1307_REG_STATUS) & _BV(DS1307_CH);
// set the date time
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(DS1307_REG_TIMEDATE);
_wire.write(Uint8ToBcd(dt.Second()) | sreg);
_wire.write(Uint8ToBcd(dt.Minute()));
_wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only
// RTC Hardware Day of Week is 1-7, 1 = Monday
// convert our Day of Week to Rtc Day of Week
uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());
_wire.write(Uint8ToBcd(rtcDow));
_wire.write(Uint8ToBcd(dt.Day()));
_wire.write(Uint8ToBcd(dt.Month()));
_wire.write(Uint8ToBcd(dt.Year() - 2000));
_lastError = _wire.endTransmission();
}
RtcDateTime GetDateTime()
{
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(DS1307_REG_TIMEDATE);
_lastError = _wire.endTransmission();
if (_lastError != 0)
{
RtcDateTime(0);
}
uint8_t bytesRead = _wire.requestFrom(DS1307_ADDRESS, DS1307_REG_TIMEDATE_SIZE);
if (DS1307_REG_TIMEDATE_SIZE != bytesRead)
{
_lastError = 4;
RtcDateTime(0);
}
uint8_t second = BcdToUint8(_wire.read() & 0x7F);
uint8_t minute = BcdToUint8(_wire.read());
uint8_t hour = BcdToBin24Hour(_wire.read());
_wire.read(); // throwing away day of week as we calculate it
uint8_t dayOfMonth = BcdToUint8(_wire.read());
uint8_t month = BcdToUint8(_wire.read());
uint16_t year = BcdToUint8(_wire.read()) + 2000;
return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
}
void SetMemory(uint8_t memoryAddress, uint8_t value)
{
uint8_t address = memoryAddress + DS1307_REG_RAMSTART;
if (address <= DS1307_REG_RAMEND)
{
setReg(address, value);
}
}
uint8_t GetMemory(uint8_t memoryAddress)
{
uint8_t value = 0;
uint8_t address = memoryAddress + DS1307_REG_RAMSTART;
if (address <= DS1307_REG_RAMEND)
{
value = getReg(address);
}
return value;
}
uint8_t SetMemory(uint8_t memoryAddress, const uint8_t* pValue, uint8_t countBytes)
{
uint8_t address = memoryAddress + DS1307_REG_RAMSTART;
uint8_t countWritten = 0;
if (address <= DS1307_REG_RAMEND)
{
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(address);
while (countBytes > 0 && address <= DS1307_REG_RAMEND)
{
_wire.write(*pValue++);
address++;
countBytes--;
countWritten++;
}
_lastError = _wire.endTransmission();
}
return countWritten;
}
uint8_t GetMemory(uint8_t memoryAddress, uint8_t* pValue, uint8_t countBytes)
{
uint8_t address = memoryAddress + DS1307_REG_RAMSTART;
uint8_t countRead = 0;
if (address <= DS1307_REG_RAMEND)
{
if (countBytes > DS1307_REG_RAMSIZE)
{
countBytes = DS1307_REG_RAMSIZE;
}
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(address);
_lastError = _wire.endTransmission();
if (_lastError != 0)
{
return 0;
}
countRead = _wire.requestFrom(DS1307_ADDRESS, countBytes);
countBytes = countRead;
while (countBytes-- > 0)
{
*pValue++ = _wire.read();
}
}
return countRead;
}
void SetSquareWavePin(DS1307SquareWaveOut pinMode)
{
setReg(DS1307_REG_CONTROL, pinMode);
}
private:
T_WIRE_METHOD& _wire;
uint8_t _lastError;
uint8_t getReg(uint8_t regAddress)
{
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(regAddress);
_lastError = _wire.endTransmission();
if (_lastError != 0)
{
return 0;
}
// control register
uint8_t bytesRead = _wire.requestFrom(DS1307_ADDRESS, (uint8_t)1);
if (1 != bytesRead)
{
_lastError = 4;
return 0;
}
uint8_t regValue = _wire.read();
return regValue;
}
void setReg(uint8_t regAddress, uint8_t regValue)
{
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(regAddress);
_wire.write(regValue);
_lastError = _wire.endTransmission();
}
};
#endif // __RTCDS1307_H__

View File

@@ -0,0 +1,631 @@
#ifndef __RTCDS3231_H__
#define __RTCDS3231_H__
#include <Arduino.h>
#include "RtcDateTime.h"
#include "RtcTemperature.h"
#include "RtcUtility.h"
//I2C Slave Address
const uint8_t DS3231_ADDRESS = 0x68;
//DS3231 Register Addresses
const uint8_t DS3231_REG_TIMEDATE = 0x00;
const uint8_t DS3231_REG_ALARMONE = 0x07;
const uint8_t DS3231_REG_ALARMTWO = 0x0B;
const uint8_t DS3231_REG_CONTROL = 0x0E;
const uint8_t DS3231_REG_STATUS = 0x0F;
const uint8_t DS3231_REG_AGING = 0x10;
const uint8_t DS3231_REG_TEMP = 0x11;
//DS3231 Register Data Size if not just 1
const uint8_t DS3231_REG_TIMEDATE_SIZE = 7;
const uint8_t DS3231_REG_ALARMONE_SIZE = 4;
const uint8_t DS3231_REG_ALARMTWO_SIZE = 3;
const uint8_t DS3231_REG_TEMP_SIZE = 2;
// DS3231 Control Register Bits
const uint8_t DS3231_A1IE = 0;
const uint8_t DS3231_A2IE = 1;
const uint8_t DS3231_INTCN = 2;
const uint8_t DS3231_RS1 = 3;
const uint8_t DS3231_RS2 = 4;
const uint8_t DS3231_CONV = 5;
const uint8_t DS3231_BBSQW = 6;
const uint8_t DS3231_EOSC = 7;
const uint8_t DS3231_AIEMASK = (_BV(DS3231_A1IE) | _BV(DS3231_A2IE));
const uint8_t DS3231_RSMASK = (_BV(DS3231_RS1) | _BV(DS3231_RS2));
// DS3231 Status Register Bits
const uint8_t DS3231_A1F = 0;
const uint8_t DS3231_A2F = 1;
const uint8_t DS3231_BSY = 2;
const uint8_t DS3231_EN32KHZ = 3;
const uint8_t DS3231_OSF = 7;
const uint8_t DS3231_AIFMASK = (_BV(DS3231_A1F) | _BV(DS3231_A2F));
// seconds accuracy
enum DS3231AlarmOneControl
{
// bit order: A1M4 DY/DT A1M3 A1M2 A1M1
DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch = 0x00,
DS3231AlarmOneControl_OncePerSecond = 0x17,
DS3231AlarmOneControl_SecondsMatch = 0x16,
DS3231AlarmOneControl_MinutesSecondsMatch = 0x14,
DS3231AlarmOneControl_HoursMinutesSecondsMatch = 0x10,
DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch = 0x08,
};
class DS3231AlarmOne
{
public:
DS3231AlarmOne( uint8_t dayOf,
uint8_t hour,
uint8_t minute,
uint8_t second,
DS3231AlarmOneControl controlFlags) :
_flags(controlFlags),
_dayOf(dayOf),
_hour(hour),
_minute(minute),
_second(second)
{
}
uint8_t DayOf() const
{
return _dayOf;
}
uint8_t Hour() const
{
return _hour;
}
uint8_t Minute() const
{
return _minute;
}
uint8_t Second() const
{
return _second;
}
DS3231AlarmOneControl ControlFlags() const
{
return _flags;
}
bool operator == (const DS3231AlarmOne& other) const
{
return (_dayOf == other._dayOf &&
_hour == other._hour &&
_minute == other._minute &&
_second == other._second &&
_flags == other._flags);
}
bool operator != (const DS3231AlarmOne& other) const
{
return !(*this == other);
}
protected:
DS3231AlarmOneControl _flags;
uint8_t _dayOf;
uint8_t _hour;
uint8_t _minute;
uint8_t _second;
};
// minutes accuracy
enum DS3231AlarmTwoControl
{
// bit order: A2M4 DY/DT A2M3 A2M2
DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch = 0x00,
DS3231AlarmTwoControl_OncePerMinute = 0x0b,
DS3231AlarmTwoControl_MinutesMatch = 0x0a,
DS3231AlarmTwoControl_HoursMinutesMatch = 0x08,
DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch = 0x04,
};
class DS3231AlarmTwo
{
public:
DS3231AlarmTwo( uint8_t dayOf,
uint8_t hour,
uint8_t minute,
DS3231AlarmTwoControl controlFlags) :
_flags(controlFlags),
_dayOf(dayOf),
_hour(hour),
_minute(minute)
{
}
uint8_t DayOf() const
{
return _dayOf;
}
uint8_t Hour() const
{
return _hour;
}
uint8_t Minute() const
{
return _minute;
}
DS3231AlarmTwoControl ControlFlags() const
{
return _flags;
}
bool operator == (const DS3231AlarmTwo& other) const
{
return (_dayOf == other._dayOf &&
_hour == other._hour &&
_minute == other._minute &&
_flags == other._flags);
}
bool operator != (const DS3231AlarmTwo& other) const
{
return !(*this == other);
}
protected:
DS3231AlarmTwoControl _flags;
uint8_t _dayOf;
uint8_t _hour;
uint8_t _minute;
};
enum DS3231SquareWaveClock
{
DS3231SquareWaveClock_1Hz = 0b00000000,
DS3231SquareWaveClock_1kHz = 0b00001000,
DS3231SquareWaveClock_4kHz = 0b00010000,
DS3231SquareWaveClock_8kHz = 0b00011000,
};
enum DS3231SquareWavePinMode
{
DS3231SquareWavePin_ModeNone,
DS3231SquareWavePin_ModeAlarmOne,
DS3231SquareWavePin_ModeAlarmTwo,
// note: the same as DS3231SquareWavePin_ModeAlarmOne | DS3231SquareWavePin_ModeAlarmTwo
DS3231SquareWavePin_ModeAlarmBoth,
DS3231SquareWavePin_ModeClock
};
enum DS3231AlarmFlag
{
DS3231AlarmFlag_Alarm1 = 0x01,
DS3231AlarmFlag_Alarm2 = 0x02,
DS3231AlarmFlag_AlarmBoth = 0x03,
};
template<class T_WIRE_METHOD> class RtcDS3231
{
public:
RtcDS3231(T_WIRE_METHOD& wire) :
_wire(wire),
_lastError(0)
{
}
void Begin()
{
_wire.begin();
}
void Begin(int sda, int scl)
{
_wire.begin(sda, scl);
}
uint8_t LastError()
{
return _lastError;
}
bool IsDateTimeValid()
{
uint8_t status = getReg(DS3231_REG_STATUS);
return (!(status & _BV(DS3231_OSF)) && (_lastError == 0));
}
bool GetIsRunning()
{
uint8_t creg = getReg(DS3231_REG_CONTROL);
return (!(creg & _BV(DS3231_EOSC)) && (_lastError == 0));
}
void SetIsRunning(bool isRunning)
{
uint8_t creg = getReg(DS3231_REG_CONTROL);
if (isRunning)
{
creg &= ~_BV(DS3231_EOSC);
}
else
{
creg |= _BV(DS3231_EOSC);
}
setReg(DS3231_REG_CONTROL, creg);
}
void SetDateTime(const RtcDateTime& dt)
{
// clear the invalid flag
uint8_t status = getReg(DS3231_REG_STATUS);
status &= ~_BV(DS3231_OSF); // clear the flag
setReg(DS3231_REG_STATUS, status);
// set the date time
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_TIMEDATE);
_wire.write(Uint8ToBcd(dt.Second()));
_wire.write(Uint8ToBcd(dt.Minute()));
_wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only
uint8_t year = dt.Year() - 2000;
uint8_t centuryFlag = 0;
if (year >= 100)
{
year -= 100;
centuryFlag = _BV(7);
}
// RTC Hardware Day of Week is 1-7, 1 = Monday
// convert our Day of Week to Rtc Day of Week
uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());
_wire.write(Uint8ToBcd(rtcDow));
_wire.write(Uint8ToBcd(dt.Day()));
_wire.write(Uint8ToBcd(dt.Month()) | centuryFlag);
_wire.write(Uint8ToBcd(year));
_lastError = _wire.endTransmission();
}
RtcDateTime GetDateTime()
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_TIMEDATE);
_lastError = _wire.endTransmission();
if (_lastError != 0)
{
return RtcDateTime(0);
}
uint8_t bytesRead = _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_TIMEDATE_SIZE);
if (DS3231_REG_TIMEDATE_SIZE != bytesRead)
{
_lastError = 4;
return RtcDateTime(0);
}
uint8_t second = BcdToUint8(_wire.read() & 0x7F);
uint8_t minute = BcdToUint8(_wire.read());
uint8_t hour = BcdToBin24Hour(_wire.read());
_wire.read(); // throwing away day of week as we calculate it
uint8_t dayOfMonth = BcdToUint8(_wire.read());
uint8_t monthRaw = _wire.read();
uint16_t year = BcdToUint8(_wire.read()) + 2000;
if (monthRaw & _BV(7)) // century wrap flag
{
year += 100;
}
uint8_t month = BcdToUint8(monthRaw & 0x7f);
return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
}
RtcTemperature GetTemperature()
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_TEMP);
_lastError = _wire.endTransmission();
if (_lastError != 0)
{
return RtcTemperature(0);
}
// Temperature is represented as a 10-bit code with a resolution
// of 1/4th °C and is accessable as a signed 16-bit integer at
// locations 11h and 12h.
//
// | r11h | DP | r12h |
// Bit: 15 14 13 12 11 10 9 8 . 7 6 5 4 3 2 1 0 -1 -2
// s i i i i i i i . f f 0 0 0 0 0 0
//
// As it takes (8) right-shifts to register the decimal point (DP) to
// the right of the 0th bit, the overall word scaling equals 256.
//
// For example, at +/- 25.25°C, concatenated registers <r11h:r12h> =
// 256 * (+/- 25+(1/4)) = +/- 6464, or 1940h / E6C0h.
uint8_t bytesRead = _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_TEMP_SIZE);
if (DS3231_REG_TEMP_SIZE != bytesRead)
{
_lastError = 4;
return RtcTemperature(0);
}
int8_t r11h = _wire.read(); // MS byte, signed temperature
return RtcTemperature( r11h, _wire.read() ); // LS byte is r12h
}
void Enable32kHzPin(bool enable)
{
uint8_t sreg = getReg(DS3231_REG_STATUS);
if (enable == true)
{
sreg |= _BV(DS3231_EN32KHZ);
}
else
{
sreg &= ~_BV(DS3231_EN32KHZ);
}
setReg(DS3231_REG_STATUS, sreg);
}
void SetSquareWavePin(DS3231SquareWavePinMode pinMode, bool enableWhileInBatteryBackup = true)
{
uint8_t creg = getReg(DS3231_REG_CONTROL);
// clear all relevant bits to a known "off" state
creg &= ~(DS3231_AIEMASK | _BV(DS3231_BBSQW));
creg |= _BV(DS3231_INTCN); // set INTCN to disables clock SQW
if (pinMode != DS3231SquareWavePin_ModeNone)
{
if (pinMode == DS3231SquareWavePin_ModeClock)
{
creg &= ~_BV(DS3231_INTCN); // clear INTCN to enable clock SQW
}
else
{
if (pinMode & DS3231SquareWavePin_ModeAlarmOne)
{
creg |= _BV(DS3231_A1IE);
}
if (pinMode & DS3231SquareWavePin_ModeAlarmTwo)
{
creg |= _BV(DS3231_A2IE);
}
}
if (enableWhileInBatteryBackup)
{
creg |= _BV(DS3231_BBSQW); // set enable int/sqw while in battery backup flag
}
}
setReg(DS3231_REG_CONTROL, creg);
}
void SetSquareWavePinClockFrequency(DS3231SquareWaveClock freq)
{
uint8_t creg = getReg(DS3231_REG_CONTROL);
creg &= ~DS3231_RSMASK; // Set to 0
creg |= (freq & DS3231_RSMASK); // Set freq bits
setReg(DS3231_REG_CONTROL, creg);
}
void SetAlarmOne(const DS3231AlarmOne& alarm)
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_ALARMONE);
_wire.write(Uint8ToBcd(alarm.Second()) | ((alarm.ControlFlags() & 0x01) << 7));
_wire.write(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x02) << 6));
_wire.write(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x04) << 5)); // 24 hour mode only
uint8_t rtcDow = alarm.DayOf();
if (alarm.ControlFlags() == DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch)
{
rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow);
}
_wire.write(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x18) << 3));
_lastError = _wire.endTransmission();
}
void SetAlarmTwo(const DS3231AlarmTwo& alarm)
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_ALARMTWO);
_wire.write(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x01) << 7));
_wire.write(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x02) << 6)); // 24 hour mode only
// convert our Day of Week to Rtc Day of Week if needed
uint8_t rtcDow = alarm.DayOf();
if (alarm.ControlFlags() == DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch)
{
rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow);
}
_wire.write(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x0c) << 4));
_lastError = _wire.endTransmission();
}
DS3231AlarmOne GetAlarmOne()
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_ALARMONE);
_lastError = _wire.endTransmission();
if (_lastError != 0)
{
return DS3231AlarmOne(0, 0, 0, 0, DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch);
}
uint8_t bytesRead = _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_ALARMONE_SIZE);
if (DS3231_REG_ALARMONE_SIZE != bytesRead)
{
_lastError = 4;
return DS3231AlarmOne(0, 0, 0, 0, DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch);
}
uint8_t raw = _wire.read();
uint8_t flags = (raw & 0x80) >> 7;
uint8_t second = BcdToUint8(raw & 0x7F);
raw = _wire.read();
flags |= (raw & 0x80) >> 6;
uint8_t minute = BcdToUint8(raw & 0x7F);
raw = _wire.read();
flags |= (raw & 0x80) >> 5;
uint8_t hour = BcdToBin24Hour(raw & 0x7f);
raw = _wire.read();
flags |= (raw & 0xc0) >> 3;
uint8_t dayOf = BcdToUint8(raw & 0x3f);
if (flags == DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch)
{
dayOf = RtcDateTime::ConvertRtcToDow(dayOf);
}
return DS3231AlarmOne(dayOf, hour, minute, second, (DS3231AlarmOneControl)flags);
}
DS3231AlarmTwo GetAlarmTwo()
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_ALARMTWO);
_lastError = _wire.endTransmission();
if (_lastError != 0)
{
return DS3231AlarmTwo(0, 0, 0, DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch);
}
uint8_t bytesRead = _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_ALARMTWO_SIZE);
if (DS3231_REG_ALARMTWO_SIZE != bytesRead)
{
_lastError = 4;
return DS3231AlarmTwo(0, 0, 0, DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch);
}
uint8_t raw = _wire.read();
uint8_t flags = (raw & 0x80) >> 7;
uint8_t minute = BcdToUint8(raw & 0x7F);
raw = _wire.read();
flags |= (raw & 0x80) >> 6;
uint8_t hour = BcdToBin24Hour(raw & 0x7f);
raw = _wire.read();
flags |= (raw & 0xc0) >> 4;
uint8_t dayOf = BcdToUint8(raw & 0x3f);
if (flags == DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch)
{
dayOf = RtcDateTime::ConvertRtcToDow(dayOf);
}
return DS3231AlarmTwo(dayOf, hour, minute, (DS3231AlarmTwoControl)flags);
}
// Latch must be called after an alarm otherwise it will not
// trigger again
DS3231AlarmFlag LatchAlarmsTriggeredFlags()
{
uint8_t sreg = getReg(DS3231_REG_STATUS);
uint8_t alarmFlags = (sreg & DS3231_AIFMASK);
sreg &= ~DS3231_AIFMASK; // clear the flags
setReg(DS3231_REG_STATUS, sreg);
return (DS3231AlarmFlag)alarmFlags;
}
void ForceTemperatureCompensationUpdate(bool block)
{
uint8_t creg = getReg(DS3231_REG_CONTROL);
creg |= _BV(DS3231_CONV); // Write CONV bit
setReg(DS3231_REG_CONTROL, creg);
while (block && (creg & _BV(DS3231_CONV)) != 0)
{
// Block until CONV is 0
creg = getReg(DS3231_REG_CONTROL);
}
}
int8_t GetAgingOffset()
{
return getReg(DS3231_REG_AGING);
}
void SetAgingOffset(int8_t value)
{
setReg(DS3231_REG_AGING, value);
}
private:
T_WIRE_METHOD& _wire;
uint8_t _lastError;
uint8_t getReg(uint8_t regAddress)
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(regAddress);
_lastError = _wire.endTransmission();
if (_lastError != 0)
{
return 0;
}
// control register
uint8_t bytesRead = _wire.requestFrom(DS3231_ADDRESS, (uint8_t)1);
if (1 != bytesRead)
{
_lastError = 4;
return 0;
}
uint8_t regValue = _wire.read();
return regValue;
}
void setReg(uint8_t regAddress, uint8_t regValue)
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(regAddress);
_wire.write(regValue);
_lastError = _wire.endTransmission();
}
};
#endif // __RTCDS3231_H__

View File

@@ -0,0 +1,699 @@
#ifndef __RTCDS3234_H__
#define __RTCDS3234_H__
#include <Arduino.h>
#include <SPI.h>
#include "RtcDateTime.h"
#include "RtcTemperature.h"
#include "RtcUtility.h"
//DS3234 Register Addresses
const uint8_t DS3234_REG_WRITE_FLAG = 0x80;
const uint8_t DS3234_REG_TIMEDATE = 0x00;
const uint8_t DS3234_REG_ALARMONE = 0x07;
const uint8_t DS3234_REG_ALARMTWO = 0x0B;
const uint8_t DS3234_REG_CONTROL = 0x0E;
const uint8_t DS3234_REG_STATUS = 0x0F;
const uint8_t DS3234_REG_AGING = 0x10;
const uint8_t DS3234_REG_TEMP = 0x11;
const uint8_t DS3234_REG_RAM_ADDRESS = 0x18;
const uint8_t DS3234_REG_RAM_DATA = 0x19;
const uint8_t DS3234_RAMSTART = 0x00;
const uint8_t DS3234_RAMEND = 0xFF;
const uint8_t DS3234_RAMSIZE = DS3234_RAMEND - DS3234_RAMSTART;
// DS3234 Control Register Bits
const uint8_t DS3234_A1IE = 0;
const uint8_t DS3234_A2IE = 1;
const uint8_t DS3234_INTCN = 2;
const uint8_t DS3234_RS1 = 3;
const uint8_t DS3234_RS2 = 4;
const uint8_t DS3234_CONV = 5;
const uint8_t DS3234_BBSQW = 6;
const uint8_t DS3234_EOSC = 7;
const uint8_t DS3234_AIEMASK = (_BV(DS3234_A1IE) | _BV(DS3234_A2IE));
const uint8_t DS3234_RSMASK = (_BV(DS3234_RS1) | _BV(DS3234_RS2));
// DS3234 Status Register Bits
const uint8_t DS3234_A1F = 0;
const uint8_t DS3234_A2F = 1;
const uint8_t DS3234_BSY = 2;
const uint8_t DS3234_EN32KHZ = 3;
const uint8_t DS3234_CRATE0 = 4;
const uint8_t DS3234_CRATE1 = 5;
const uint8_t DS3234_BB32KHZ = 6;
const uint8_t DS3234_OSF = 7;
const uint8_t DS3234_AIFMASK = (_BV(DS3234_A1F) | _BV(DS3234_A2F));
const uint8_t DS3234_CRATEMASK = (_BV(DS3234_CRATE0) | _BV(DS3234_CRATE1));
// seconds accuracy
enum DS3234AlarmOneControl
{
// bit order: A1M4 DY/DT A1M3 A1M2 A1M1
DS3234AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch = 0x00,
DS3234AlarmOneControl_OncePerSecond = 0x17,
DS3234AlarmOneControl_SecondsMatch = 0x16,
DS3234AlarmOneControl_MinutesSecondsMatch = 0x14,
DS3234AlarmOneControl_HoursMinutesSecondsMatch = 0x10,
DS3234AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch = 0x08,
};
class DS3234AlarmOne
{
public:
DS3234AlarmOne( uint8_t dayOf,
uint8_t hour,
uint8_t minute,
uint8_t second,
DS3234AlarmOneControl controlFlags) :
_flags(controlFlags),
_dayOf(dayOf),
_hour(hour),
_minute(minute),
_second(second)
{
}
uint8_t DayOf() const
{
return _dayOf;
}
uint8_t Hour() const
{
return _hour;
}
uint8_t Minute() const
{
return _minute;
}
uint8_t Second() const
{
return _second;
}
DS3234AlarmOneControl ControlFlags() const
{
return _flags;
}
bool operator == (const DS3234AlarmOne& other) const
{
return (_dayOf == other._dayOf &&
_hour == other._hour &&
_minute == other._minute &&
_second == other._second &&
_flags == other._flags);
}
bool operator != (const DS3234AlarmOne& other) const
{
return !(*this == other);
}
protected:
DS3234AlarmOneControl _flags;
uint8_t _dayOf;
uint8_t _hour;
uint8_t _minute;
uint8_t _second;
};
// minutes accuracy
enum DS3234AlarmTwoControl
{
// bit order: A2M4 DY/DT A2M3 A2M2
DS3234AlarmTwoControl_HoursMinutesDayOfMonthMatch = 0x00,
DS3234AlarmTwoControl_OncePerMinute = 0x0b,
DS3234AlarmTwoControl_MinutesMatch = 0x0a,
DS3234AlarmTwoControl_HoursMinutesMatch = 0x08,
DS3234AlarmTwoControl_HoursMinutesDayOfWeekMatch = 0x04,
};
class DS3234AlarmTwo
{
public:
DS3234AlarmTwo( uint8_t dayOf,
uint8_t hour,
uint8_t minute,
DS3234AlarmTwoControl controlFlags) :
_flags(controlFlags),
_dayOf(dayOf),
_hour(hour),
_minute(minute)
{
}
uint8_t DayOf() const
{
return _dayOf;
}
uint8_t Hour() const
{
return _hour;
}
uint8_t Minute() const
{
return _minute;
}
DS3234AlarmTwoControl ControlFlags() const
{
return _flags;
}
bool operator == (const DS3234AlarmTwo& other) const
{
return (_dayOf == other._dayOf &&
_hour == other._hour &&
_minute == other._minute &&
_flags == other._flags);
}
bool operator != (const DS3234AlarmTwo& other) const
{
return !(*this == other);
}
protected:
DS3234AlarmTwoControl _flags;
uint8_t _dayOf;
uint8_t _hour;
uint8_t _minute;
};
enum DS3234SquareWaveClock
{
DS3234SquareWaveClock_1Hz = 0b00000000,
DS3234SquareWaveClock_1kHz = 0b00001000,
DS3234SquareWaveClock_4kHz = 0b00010000,
DS3234SquareWaveClock_8kHz = 0b00011000,
};
enum DS3234SquareWavePinMode
{
DS3234SquareWavePin_ModeNone,
DS3234SquareWavePin_ModeBatteryBackup,
DS3234SquareWavePin_ModeClock,
DS3234SquareWavePin_ModeAlarmOne,
DS3234SquareWavePin_ModeAlarmTwo,
DS3234SquareWavePin_ModeAlarmBoth
};
enum DS3234TempCompensationRate
{
DS3234TempCompensationRate_64Seconds,
DS3234TempCompensationRate_128Seconds,
DS3234TempCompensationRate_256Seconds,
DS3234TempCompensationRate_512Seconds,
};
enum DS3234AlarmFlag
{
DS3234AlarmFlag_Alarm1 = 0x01,
DS3234AlarmFlag_Alarm2 = 0x02,
DS3234AlarmFlag_AlarmBoth = 0x03,
};
const SPISettings c_Ds3234SpiSettings(1000000, MSBFIRST, SPI_MODE1); // CPHA must be used, so mode 1 or mode 3 are valid
template<class T_SPI_METHOD> class RtcDS3234
{
public:
RtcDS3234(T_SPI_METHOD& spi, uint8_t csPin) :
_spi(spi),
_csPin(csPin)
{
}
void Begin()
{
UnselectChip();
pinMode(_csPin, OUTPUT);
}
bool IsDateTimeValid()
{
uint8_t status = getReg(DS3234_REG_STATUS);
return !(status & _BV(DS3234_OSF));
}
bool GetIsRunning()
{
uint8_t creg = getReg(DS3234_REG_CONTROL);
return !(creg & _BV(DS3234_EOSC));
}
void SetIsRunning(bool isRunning)
{
uint8_t creg = getReg(DS3234_REG_CONTROL);
if (isRunning)
{
creg &= ~_BV(DS3234_EOSC);
}
else
{
creg |= _BV(DS3234_EOSC);
}
setReg(DS3234_REG_CONTROL, creg);
}
void SetDateTime(const RtcDateTime& dt)
{
// clear the invalid flag
uint8_t status = getReg(DS3234_REG_STATUS);
status &= ~_BV(DS3234_OSF); // clear the flag
setReg(DS3234_REG_STATUS, status);
// set the date time
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_TIMEDATE | DS3234_REG_WRITE_FLAG);
_spi.transfer(Uint8ToBcd(dt.Second()));
_spi.transfer(Uint8ToBcd(dt.Minute()));
_spi.transfer(Uint8ToBcd(dt.Hour())); // 24 hour mode only
uint8_t year = dt.Year() - 2000;
uint8_t centuryFlag = 0;
if (year >= 100)
{
year -= 100;
centuryFlag = _BV(7);
}
// RTC Hardware Day of Week is 1-7, 1 = Monday
// convert our Day of Week to Rtc Day of Week
uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());
_spi.transfer(Uint8ToBcd(rtcDow));
_spi.transfer(Uint8ToBcd(dt.Day()));
_spi.transfer(Uint8ToBcd(dt.Month()) | centuryFlag);
_spi.transfer(Uint8ToBcd(year));
UnselectChip();
_spi.endTransaction();
}
RtcDateTime GetDateTime()
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_TIMEDATE);
uint8_t second = BcdToUint8(_spi.transfer(0));
uint8_t minute = BcdToUint8(_spi.transfer(0));
uint8_t hour = BcdToBin24Hour(_spi.transfer(0));
_spi.transfer(0); // throwing away day of week as we calculate it
uint8_t dayOfMonth = BcdToUint8(_spi.transfer(0));
uint8_t monthRaw = _spi.transfer(0);
uint16_t year = BcdToUint8(_spi.transfer(0)) + 2000;
UnselectChip();
_spi.endTransaction();
if (monthRaw & _BV(7)) // century wrap flag
{
year += 100;
}
uint8_t month = BcdToUint8(monthRaw & 0x7f);
return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
}
RtcTemperature GetTemperature()
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_TEMP);
// Temperature is represented as a 10-bit code with a resolution
// of 1/4th °C and is accessable as a signed 16-bit integer at
// locations 11h and 12h.
//
// | r11h | DP | r12h |
// Bit: 15 14 13 12 11 10 9 8 . 7 6 5 4 3 2 1 0 -1 -2
// s i i i i i i i . f f 0 0 0 0 0 0
//
// As it takes (8) right-shifts to register the decimal point (DP) to
// the right of the 0th bit, the overall word scaling equals 256.
//
// For example, at +/- 25.25°C, concatenated registers <r11h:r12h> =
// 256 * (+/- 25+(1/4)) = +/- 6464, or 1940h / E6C0h.
int8_t ms = _spi.transfer(0); // MS byte, signed temperature
uint8_t ls = _spi.transfer(0); // LS byte is r12h
UnselectChip();
_spi.endTransaction();
return RtcTemperature(ms, ls); // LS byte is r12h
}
void Enable32kHzPin(bool enable)
{
uint8_t sreg = getReg(DS3234_REG_STATUS);
if (enable == true)
{
sreg |= _BV(DS3234_EN32KHZ);
}
else
{
sreg &= ~_BV(DS3234_EN32KHZ);
}
setReg(DS3234_REG_STATUS, sreg);
}
void SetSquareWavePin(DS3234SquareWavePinMode pinMode)
{
uint8_t creg = getReg(DS3234_REG_CONTROL);
// clear all relevant bits to a known "off" state
creg &= ~(DS3234_AIEMASK | _BV(DS3234_BBSQW));
creg |= _BV(DS3234_INTCN); // set INTCN to disables SQW
switch (pinMode)
{
case DS3234SquareWavePin_ModeNone:
break;
case DS3234SquareWavePin_ModeBatteryBackup:
creg |= _BV(DS3234_BBSQW); // set battery backup flag
creg &= ~_BV(DS3234_INTCN); // clear INTCN to enable SQW
break;
case DS3234SquareWavePin_ModeClock:
creg &= ~_BV(DS3234_INTCN); // clear INTCN to enable SQW
break;
case DS3234SquareWavePin_ModeAlarmOne:
creg |= _BV(DS3234_A1IE);
break;
case DS3234SquareWavePin_ModeAlarmTwo:
creg |= _BV(DS3234_A2IE);
break;
case DS3234SquareWavePin_ModeAlarmBoth:
creg |= _BV(DS3234_A1IE) | _BV(DS3234_A2IE);
break;
}
setReg(DS3234_REG_CONTROL, creg);
}
void SetSquareWavePinClockFrequency(DS3234SquareWaveClock freq)
{
uint8_t creg = getReg(DS3234_REG_CONTROL);
creg &= ~DS3234_RSMASK; // Set to 0
creg |= (freq & DS3234_RSMASK); // Set freq bits
setReg(DS3234_REG_CONTROL, creg);
}
void SetAlarmOne(const DS3234AlarmOne& alarm)
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_ALARMONE | DS3234_REG_WRITE_FLAG);
_spi.transfer(Uint8ToBcd(alarm.Second()) | ((alarm.ControlFlags() & 0x01) << 7));
_spi.transfer(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x02) << 6));
_spi.transfer(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x04) << 5)); // 24 hour mode only
uint8_t rtcDow = alarm.DayOf();
if (alarm.ControlFlags() == DS3234AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch)
{
rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow);
}
_spi.transfer(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x18) << 3));
UnselectChip();
_spi.endTransaction();
}
void SetAlarmTwo(const DS3234AlarmTwo& alarm)
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_ALARMTWO | DS3234_REG_WRITE_FLAG);
_spi.transfer(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x01) << 7));
_spi.transfer(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x02) << 6)); // 24 hour mode only
// convert our Day of Week to Rtc Day of Week if needed
uint8_t rtcDow = alarm.DayOf();
if (alarm.ControlFlags() == DS3234AlarmTwoControl_HoursMinutesDayOfWeekMatch)
{
rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow);
}
_spi.transfer(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x0c) << 4));
UnselectChip();
_spi.endTransaction();
}
DS3234AlarmOne GetAlarmOne()
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_ALARMONE);
uint8_t raw = _spi.transfer(0);
uint8_t flags = (raw & 0x80) >> 7;
uint8_t second = BcdToUint8(raw & 0x7F);
raw = _spi.transfer(0);
flags |= (raw & 0x80) >> 6;
uint8_t minute = BcdToUint8(raw & 0x7F);
raw = _spi.transfer(0);
flags |= (raw & 0x80) >> 5;
uint8_t hour = BcdToBin24Hour(raw & 0x7f);
raw = _spi.transfer(0);
flags |= (raw & 0xc0) >> 3;
uint8_t dayOf = BcdToUint8(raw & 0x3f);
UnselectChip();
_spi.endTransaction();
if (flags == DS3234AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch)
{
dayOf = RtcDateTime::ConvertRtcToDow(dayOf);
}
return DS3234AlarmOne(dayOf, hour, minute, second, (DS3234AlarmOneControl)flags);
}
DS3234AlarmTwo GetAlarmTwo()
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_ALARMTWO);
uint8_t raw = _spi.transfer(0);
uint8_t flags = (raw & 0x80) >> 7;
uint8_t minute = BcdToUint8(raw & 0x7F);
raw = _spi.transfer(0);
flags |= (raw & 0x80) >> 6;
uint8_t hour = BcdToBin24Hour(raw & 0x7f);
raw = _spi.transfer(0);
flags |= (raw & 0xc0) >> 4;
uint8_t dayOf = BcdToUint8(raw & 0x3f);
UnselectChip();
_spi.endTransaction();
if (flags == DS3234AlarmTwoControl_HoursMinutesDayOfWeekMatch)
{
dayOf = RtcDateTime::ConvertRtcToDow(dayOf);
}
return DS3234AlarmTwo(dayOf, hour, minute, (DS3234AlarmTwoControl)flags);
}
// Latch must be called after an alarm otherwise it will not
// trigger again
DS3234AlarmFlag LatchAlarmsTriggeredFlags()
{
uint8_t sreg = getReg(DS3234_REG_STATUS);
uint8_t alarmFlags = (sreg & DS3234_AIFMASK);
sreg &= ~DS3234_AIFMASK; // clear the flags
setReg(DS3234_REG_STATUS, sreg);
return (DS3234AlarmFlag)alarmFlags;
}
void SetTemperatureCompensationRate(DS3234TempCompensationRate rate)
{
uint8_t sreg = getReg(DS3234_REG_STATUS);
sreg &= ~DS3234_CRATEMASK;
sreg |= (rate << DS3234_CRATE0);
setReg(DS3234_REG_STATUS, sreg);
}
DS3234TempCompensationRate GetTemperatureCompensationRate()
{
uint8_t sreg = getReg(DS3234_REG_STATUS);
return (sreg & DS3234_CRATEMASK) >> DS3234_CRATE0;
}
void ForceTemperatureCompensationUpdate(bool block)
{
uint8_t creg = getReg(DS3234_REG_CONTROL);
creg |= _BV(DS3234_CONV); // Write CONV bit
setReg(DS3234_REG_CONTROL, creg);
while (block && (creg & _BV(DS3234_CONV)) != 0)
{
// Block until CONV is 0
creg = getReg(DS3234_REG_CONTROL);
}
}
int8_t GetAgingOffset()
{
return getReg(DS3234_REG_AGING);
}
void SetAgingOffset(int8_t value)
{
setReg(DS3234_REG_AGING, value);
}
void SetMemory(uint8_t memoryAddress, uint8_t value)
{
SetMemory(memoryAddress, &value, 1);
}
uint8_t GetMemory(uint8_t memoryAddress)
{
uint8_t value;
GetMemory(memoryAddress, &value, 1);
return value;
}
uint8_t SetMemory(uint8_t memoryAddress, const uint8_t* pValue, uint8_t countBytes)
{
uint8_t countWritten = 0;
setReg(DS3234_REG_RAM_ADDRESS, memoryAddress);
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_RAM_DATA | DS3234_REG_WRITE_FLAG);
while (countBytes > 0)
{
_spi.transfer(*pValue++);
countBytes--;
countWritten++;
}
UnselectChip();
_spi.endTransaction();
return countWritten;
}
uint8_t GetMemory(uint8_t memoryAddress, uint8_t* pValue, uint8_t countBytes)
{
// set address to read from
setReg(DS3234_REG_RAM_ADDRESS, memoryAddress);
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_RAM_DATA);
// read the data
uint8_t countRead = 0;
while (countBytes-- > 0)
{
*pValue++ = _spi.transfer(0);
countRead++;
}
UnselectChip();
_spi.endTransaction();
return countRead;
}
private:
T_SPI_METHOD& _spi;
uint8_t _csPin;
void SelectChip()
{
digitalWrite(_csPin, LOW);
}
void UnselectChip()
{
digitalWrite(_csPin, HIGH);
}
uint8_t getReg(uint8_t regAddress)
{
uint8_t regValue;
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(regAddress);
regValue = _spi.transfer(0);
UnselectChip();
_spi.endTransaction();
return regValue;
}
void setReg(uint8_t regAddress, uint8_t regValue)
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(regAddress | DS3234_REG_WRITE_FLAG);
_spi.transfer(regValue);
UnselectChip();
_spi.endTransaction();
}
};
#endif // __RTCDS3234_H__

View File

@@ -0,0 +1,211 @@
#include <Arduino.h>
#include "RtcDateTime.h"
const uint8_t c_daysInMonth[] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };
RtcDateTime::RtcDateTime(uint32_t secondsFrom2000)
{
_initWithSecondsFrom2000<uint32_t>(secondsFrom2000);
}
bool RtcDateTime::IsValid() const
{
// this just tests the most basic validity of the value ranges
// and valid leap years
// It does not check any time zone or daylight savings time
if ((_month > 0 && _month < 13) &&
(_dayOfMonth > 0 && _dayOfMonth < 32) &&
(_hour < 24) &&
(_minute < 60) &&
(_second < 60))
{
// days in a month tests
//
if (_month == 2)
{
if (_dayOfMonth > 29)
{
return false;
}
else if (_dayOfMonth > 28)
{
// leap day
// check year to make sure its a leap year
uint16_t year = Year();
if ((year % 4) != 0)
{
return false;
}
if ((year % 100) == 0 &&
(year % 400) != 0)
{
return false;
}
}
}
else if (_dayOfMonth == 31)
{
if ((((_month - 1) % 7) % 2) == 1)
{
return false;
}
}
return true;
}
return false;
}
uint8_t StringToUint8(const char* pString)
{
uint8_t value = 0;
// skip leading 0 and spaces
while ('0' == *pString || *pString == ' ')
{
pString++;
}
// calculate number until we hit non-numeral char
while ('0' <= *pString && *pString <= '9')
{
value *= 10;
value += *pString - '0';
pString++;
}
return value;
}
RtcDateTime::RtcDateTime(const char* date, const char* time)
{
// sample input: date = "Dec 06 2009", time = "12:34:56"
_yearFrom2000 = StringToUint8(date + 9);
// Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
switch (date[0])
{
case 'J':
if ( date[1] == 'a' )
_month = 1;
else if ( date[2] == 'n' )
_month = 6;
else
_month = 7;
break;
case 'F':
_month = 2;
break;
case 'A':
_month = date[1] == 'p' ? 4 : 8;
break;
case 'M':
_month = date[2] == 'r' ? 3 : 5;
break;
case 'S':
_month = 9;
break;
case 'O':
_month = 10;
break;
case 'N':
_month = 11;
break;
case 'D':
_month = 12;
break;
}
_dayOfMonth = StringToUint8(date + 4);
_hour = StringToUint8(time);
_minute = StringToUint8(time + 3);
_second = StringToUint8(time + 6);
}
template <typename T> T DaysSinceFirstOfYear2000(uint16_t year, uint8_t month, uint8_t dayOfMonth)
{
T days = dayOfMonth;
for (uint8_t indexMonth = 1; indexMonth < month; ++indexMonth)
{
days += pgm_read_byte(c_daysInMonth + indexMonth - 1);
}
if (month > 2 && year % 4 == 0)
{
days++;
}
return days + 365 * year + (year + 3) / 4 - 1;
}
template <typename T> T SecondsIn(T days, uint8_t hours, uint8_t minutes, uint8_t seconds)
{
return ((days * 24L + hours) * 60 + minutes) * 60 + seconds;
}
uint8_t RtcDateTime::DayOfWeek() const
{
uint16_t days = DaysSinceFirstOfYear2000<uint16_t>(_yearFrom2000, _month, _dayOfMonth);
return (days + 6) % 7; // Jan 1, 2000 is a Saturday, i.e. returns 6
}
// 32-bit time; as seconds since 1/1/2000
uint32_t RtcDateTime::TotalSeconds() const
{
uint16_t days = DaysSinceFirstOfYear2000<uint16_t>(_yearFrom2000, _month, _dayOfMonth);
return SecondsIn<uint32_t>(days, _hour, _minute, _second);
}
// 64-bit time; as seconds since 1/1/2000
uint64_t RtcDateTime::TotalSeconds64() const
{
uint32_t days = DaysSinceFirstOfYear2000<uint32_t>(_yearFrom2000, _month, _dayOfMonth);
return SecondsIn<uint64_t>(days, _hour, _minute, _second);
}
// total days since 1/1/2000
uint16_t RtcDateTime::TotalDays() const
{
return DaysSinceFirstOfYear2000<uint16_t>(_yearFrom2000, _month, _dayOfMonth);
}
void RtcDateTime::InitWithIso8601(const char* date)
{
// sample input: date = "Sat, 06 Dec 2009 12:34:56 GMT"
_yearFrom2000 = StringToUint8(date + 13);
// Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
switch (date[8])
{
case 'J':
if (date[1 + 8] == 'a')
_month = 1;
else if (date[2 + 8] == 'n')
_month = 6;
else
_month = 7;
break;
case 'F':
_month = 2;
break;
case 'A':
_month = date[1 + 8] == 'p' ? 4 : 8;
break;
case 'M':
_month = date[2 + 8] == 'r' ? 3 : 5;
break;
case 'S':
_month = 9;
break;
case 'O':
_month = 10;
break;
case 'N':
_month = 11;
break;
case 'D':
_month = 12;
break;
}
_dayOfMonth = StringToUint8(date + 5);
_hour = StringToUint8(date + 17);
_minute = StringToUint8(date + 20);
_second = StringToUint8(date + 23);
}

View File

@@ -0,0 +1,185 @@
#ifndef __RTCDATETIME_H__
#define __RTCDATETIME_H__
// ESP32 complains if not included
#if defined(ARDUINO_ARCH_ESP32)
#include <inttypes.h>
#endif
enum DayOfWeek
{
DayOfWeek_Sunday = 0,
DayOfWeek_Monday,
DayOfWeek_Tuesday,
DayOfWeek_Wednesday,
DayOfWeek_Thursday,
DayOfWeek_Friday,
DayOfWeek_Saturday,
};
const uint16_t c_OriginYear = 2000;
const uint32_t c_Epoch32OfOriginYear = 946684800;
extern const uint8_t c_daysInMonth[] PROGMEM;
class RtcDateTime
{
public:
RtcDateTime(uint32_t secondsFrom2000 = 0);
RtcDateTime(uint16_t year,
uint8_t month,
uint8_t dayOfMonth,
uint8_t hour,
uint8_t minute,
uint8_t second) :
_yearFrom2000((year >= c_OriginYear) ? year - c_OriginYear : year),
_month(month),
_dayOfMonth(dayOfMonth),
_hour(hour),
_minute(minute),
_second(second)
{
}
// RtcDateTime compileDateTime(__DATE__, __TIME__);
RtcDateTime(const char* date, const char* time);
bool IsValid() const;
uint16_t Year() const
{
return c_OriginYear + _yearFrom2000;
}
uint8_t Month() const
{
return _month;
}
uint8_t Day() const
{
return _dayOfMonth;
}
uint8_t Hour() const
{
return _hour;
}
uint8_t Minute() const
{
return _minute;
}
uint8_t Second() const
{
return _second;
}
// 0 = Sunday, 1 = Monday, ... 6 = Saturday
uint8_t DayOfWeek() const;
// 32-bit time; as seconds since 1/1/2000
uint32_t TotalSeconds() const;
// 64-bit time; as seconds since 1/1/2000
uint64_t TotalSeconds64() const;
// total days since 1/1/2000
uint16_t TotalDays() const;
// add seconds
void operator += (uint32_t seconds)
{
RtcDateTime after = RtcDateTime( TotalSeconds() + seconds );
*this = after;
}
// remove seconds
void operator -= (uint32_t seconds)
{
RtcDateTime before = RtcDateTime( TotalSeconds() - seconds );
*this = before;
}
// allows for comparisons to just work (==, <, >, <=, >=, !=)
operator uint32_t() const
{
return TotalSeconds();
}
// Epoch32 support
uint32_t Epoch32Time() const
{
return TotalSeconds() + c_Epoch32OfOriginYear;
}
void InitWithEpoch32Time(uint32_t time)
{
_initWithSecondsFrom2000<uint32_t>(time - c_Epoch32OfOriginYear);
}
// Epoch64 support
uint64_t Epoch64Time() const
{
return TotalSeconds64() + c_Epoch32OfOriginYear;
}
void InitWithEpoch64Time(uint64_t time)
{
_initWithSecondsFrom2000<uint64_t>(time - c_Epoch32OfOriginYear);
}
void InitWithIso8601(const char* date);
// convert our Day of Week to Rtc Day of Week
// RTC Hardware Day of Week is 1-7, 1 = Monday
static uint8_t ConvertDowToRtc(uint8_t dow)
{
if (dow == 0)
{
dow = 7;
}
return dow;
}
// convert Rtc Day of Week to our Day of Week
static uint8_t ConvertRtcToDow(uint8_t rtcDow)
{
return (rtcDow % 7);
}
protected:
uint8_t _yearFrom2000;
uint8_t _month;
uint8_t _dayOfMonth;
uint8_t _hour;
uint8_t _minute;
uint8_t _second;
template <typename T> void _initWithSecondsFrom2000(T secondsFrom2000)
{
_second = secondsFrom2000 % 60;
T timeFrom2000 = secondsFrom2000 / 60;
_minute = timeFrom2000 % 60;
timeFrom2000 /= 60;
_hour = timeFrom2000 % 24;
T days = timeFrom2000 / 24;
T leapDays;
for (_yearFrom2000 = 0;; ++_yearFrom2000)
{
leapDays = (_yearFrom2000 % 4 == 0) ? 1 : 0;
if (days < 365U + leapDays)
break;
days -= 365 + leapDays;
}
for (_month = 1;; ++_month)
{
uint8_t daysPerMonth = pgm_read_byte(c_daysInMonth + _month - 1);
if (leapDays && _month == 2)
daysPerMonth++;
if (days < daysPerMonth)
break;
days -= daysPerMonth;
}
_dayOfMonth = days + 1;
}
};
#endif // __RTCDATETIME_H__

View File

@@ -0,0 +1,153 @@
#ifndef __RTCTEMPERATURE_H__
#define __RTCTEMPERATURE_H__
class RtcTemperature
{
public:
// Constructor
// a) Merge RTC registers into signed scaled temperature (x256),
// then bind to RTC resolution.
// | r11h | DP | r12h |
// Bit: 15 14 13 12 11 10 9 8 . 7 6 5 4 3 2 1 0 -1 -2
// s i i i i i i i . f f 0 0 0 0 0 0
//
// b) Rescale to (x4) by right-shifting (6) bits
// | | DP | |
// Bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 . 1 0 -1 -2
// s s s s s s s i i i i i i i f f 0 0
RtcTemperature(int8_t highByteDegreesC, uint8_t lowByteDegreesC)
{
int16_t scaledDegC = ((highByteDegreesC << 8) | (lowByteDegreesC & 0xC0)) >> 6;
_centiDegC = scaledDegC * 100 / 4;
}
RtcTemperature(int16_t centiDegC = 0) :
_centiDegC(centiDegC)
{
}
// Float temperature Celsius
float AsFloatDegC()
{
return (float)_centiDegC / 100.0f;
}
// Float temperature Fahrenheit
float AsFloatDegF()
{
return AsFloatDegC() * 1.8f + 32.0f;
}
// centi degrees (1/100th of a degree),
int16_t AsCentiDegC()
{
return _centiDegC;
}
void Print(Stream& target, uint8_t decimals = 2, char decimal = '.')
{
int16_t decimalDivisor = 1;
int16_t integerPart;
int16_t decimalPart;
{
int16_t rounded = abs(_centiDegC);
// round up as needed
if (decimals == 0)
{
rounded += 50;
}
else if (decimals == 1)
{
rounded += 5;
decimalDivisor = 10;
}
integerPart = rounded / 100;
decimalPart = (rounded % 100) / decimalDivisor;
}
// test for zero before printing negative sign to not print-0.00
if (_centiDegC < 0 && (integerPart != 0 || decimalPart != 0))
{
target.print('-');
}
// print integer part
target.print(integerPart);
// print decimal part
if (decimals != 0)
{
target.print(decimal);
if (decimalPart != 0)
{
target.print(decimalPart);
}
else
{
// append zeros as requested
while (decimals > 0)
{
target.print('0');
decimals--;
}
}
}
}
bool operator==(const RtcTemperature& other) const
{
return (_centiDegC == other._centiDegC);
};
bool operator>(const RtcTemperature& other) const
{
return (_centiDegC > other._centiDegC);
};
bool operator<(const RtcTemperature& other) const
{
return (_centiDegC < other._centiDegC);
};
bool operator>=(const RtcTemperature& other) const
{
return (_centiDegC >= other._centiDegC);
};
bool operator<=(const RtcTemperature& other) const
{
return (_centiDegC <= other._centiDegC);
};
bool operator!=(const RtcTemperature& other) const
{
return (_centiDegC != other._centiDegC);
};
RtcTemperature operator-(const RtcTemperature& right)
{
RtcTemperature result;
result._centiDegC = (_centiDegC - right._centiDegC);
return result;
}
RtcTemperature operator+(const RtcTemperature& right)
{
RtcTemperature result;
result._centiDegC = (_centiDegC + right._centiDegC);
return result;
}
protected:
int16_t _centiDegC; // 1/100th of a degree temperature (100 x degC)
};
#endif // __RTCTEMPERATURE_H__

View File

@@ -0,0 +1,34 @@
#include <Arduino.h>
#include "RtcUtility.h"
uint8_t BcdToUint8(uint8_t val)
{
return val - 6 * (val >> 4);
}
uint8_t Uint8ToBcd(uint8_t val)
{
return val + 6 * (val / 10);
}
uint8_t BcdToBin24Hour(uint8_t bcdHour)
{
uint8_t hour;
if (bcdHour & 0x40)
{
// 12 hour mode, convert to 24
bool isPm = ((bcdHour & 0x20) != 0);
hour = BcdToUint8(bcdHour & 0x1f);
if (isPm)
{
hour += 12;
}
}
else
{
hour = BcdToUint8(bcdHour);
}
return hour;
}

View File

@@ -0,0 +1,35 @@
#ifndef __RTCUTILITY_H__
#define __RTCUTILITY_H__
// ESP32 complains if not included
#if defined(ARDUINO_ARCH_ESP32)
#include <inttypes.h>
#endif
// Arduino has no standard for attributing methods used for ISRs
// even though some platforms require it, so to simplify the problem
// for end users, this provides a standard ISR_ATTR
#if !defined(ISR_ATTR)
#if defined(ARDUINO_ARCH_ESP8266)
#define ISR_ATTR ICACHE_RAM_ATTR
#elif defined(ARDUINO_ARCH_ESP32)
#define ISR_ATTR ICACHE_RAM_ATTR
#else
#define ISR_ATTR
#endif
#endif // !defined(ISR_ATTR)
// for some reason, the DUE board support does not define this, even though other non AVR archs do
#ifndef _BV
#define _BV(b) (1UL << (b))
#endif
extern uint8_t BcdToUint8(uint8_t val);
extern uint8_t Uint8ToBcd(uint8_t val);
extern uint8_t BcdToBin24Hour(uint8_t bcdHour);
#endif // __RTCUTILITY_H__

View File

@@ -0,0 +1,99 @@
#pragma once
//ThreeWire command Read/Write flag
const uint8_t THREEWIRE_READFLAG = 0x01;
class ThreeWire
{
public:
ThreeWire(uint8_t ioPin, uint8_t clkPin, uint8_t cePin) :
_ioPin(ioPin),
_clkPin(clkPin),
_cePin(cePin)
{
}
void begin() {
resetPins();
}
void end() {
resetPins();
}
void beginTransmission(uint8_t command) {
digitalWrite(_cePin, LOW); // default, not enabled
pinMode(_cePin, OUTPUT);
digitalWrite(_clkPin, LOW); // default, clock low
pinMode(_clkPin, OUTPUT);
pinMode(_ioPin, OUTPUT);
digitalWrite(_cePin, HIGH); // start the session
delayMicroseconds(4); // tCC = 4us
write(command, (command & THREEWIRE_READFLAG) == THREEWIRE_READFLAG);
}
void endTransmission() {
digitalWrite(_cePin, LOW);
delayMicroseconds(4); // tCWH = 4us
}
void write(uint8_t value, bool isDataRequestCommand = false) {
for (uint8_t bit = 0; bit < 8; bit++) {
digitalWrite(_ioPin, value & 0x01);
delayMicroseconds(1); // tDC = 200ns
// clock up, data is read by DS1302
digitalWrite(_clkPin, HIGH);
delayMicroseconds(1); // tCH = 1000ns, tCDH = 800ns
// for the last bit before a read
// Set IO line for input before the clock down
if (bit == 7 && isDataRequestCommand) {
pinMode(_ioPin, INPUT);
}
digitalWrite(_clkPin, LOW);
delayMicroseconds(1); // tCL=1000ns, tCDD=800ns
value >>= 1;
}
}
uint8_t read() {
uint8_t value = 0;
for (uint8_t bit = 0; bit < 8; bit++) {
// first bit is present on io pin, so only clock the other
// bits
value |= (digitalRead(_ioPin) << bit);
// Clock up, prepare for next
digitalWrite(_clkPin, HIGH);
delayMicroseconds(1);
// Clock down, value is ready after some time.
digitalWrite(_clkPin, LOW);
delayMicroseconds(1); // tCL=1000ns, tCDD=800ns
}
return value;
}
private:
const uint8_t _ioPin;
const uint8_t _clkPin;
const uint8_t _cePin;
void resetPins() {
// just making sure they are in a default low power use state
// as required state is set when transmissions are started
// three wire devices have internal pull downs so they will be low
pinMode(_clkPin, INPUT);
pinMode(_ioPin, INPUT);
pinMode(_cePin, INPUT);
}
};