x
MCP79412.cpp
- Committer:
- JackB
- Date:
- 2018-07-23
- Revision:
- 0:04311b121ac4
File content as of revision 0:04311b121ac4:
#include "MCP79412.h" MCP79412::MCP79412(PinName sda, PinName scl) : _i2c(sda, scl) { _address_RTC = MCP79412_RTC_ADDR << 1; squareWave(SQWAVE_1_HZ); // setTimeZone(1); // dayLightSaving = true; setI2Cfrequency(400000); } void MCP79412::setI2Cfrequency(int freq) { _i2c.frequency(freq); } // get a uint8_t containing just the requested bits // pass the register address to read, a mask to apply to the register and // an uint* for the output // you can test this value directly as true/false for specific bit mask // of use a mask of 0xff to just return the whole register uint8_t // returns true to indicate success /** Get flag * @param reg : register address * @param mask : flag mask * @return The register content */ bool MCP79412::getFlag(char reg, char mask, char *flag) { char buf[1]; buf[0] = reg; int w = _i2c.write(_address_RTC, buf, 1); int r = _i2c.read(_address_RTC, buf, 1); _error = ((w != 0) || (r != 0)); // return only requested flag *flag = (buf[0] & mask); return flag == 0 ? false : true; } // set/clear bits in a uint8_t register, or replace the uint8_t altogether // pass the register address to modify, a uint8_t to replace the existing // value with or containing the bits to set/clear and one of // MCP79412_SET/MCP79412_CLEAR/MCP79412_REPLACE // returns true to indicate success /** Set flag * @param reg : register address * @param bits : bits to set or reset * @param mode : MCP79412_REPLACE, MCP79412_SET, MCP79412_CLEAR * @return none */ void MCP79412::setFlag(char reg, char bits, char mode) { char buf[2]; buf[0] = reg; // get status register int w = _i2c.write(_address_RTC, buf, 1); int r = _i2c.read(_address_RTC, buf+1, 1); // clear the flag if (mode == MCP79412_REPLACE) buf[1] = bits; else if (mode == MCP79412_SET) buf[1] |= bits; else buf[1] &= ~bits; int w2 = _i2c.write(_address_RTC, buf, 2); _error = ((w != 0) || (r != 0) || (w2 != 0)); } // read a register int MCP79412::readRegister(char reg) { // int w = _i2c.write(_address_RTC, ®, 1); // char rtn; // int r = _i2c.read(_address_RTC, &rtn, 1); // _error = ((w != 0) || (r != 0)); // return rtn; char buf[1]; buf[0] = reg; int w = _i2c.write(_address_RTC, buf, 1); int r = _i2c.read(_address_RTC, buf, 1); _error = ((w != 0) || (r != 0)); return(buf[0]); } // read registers void MCP79412::readRegisters(char reg, char *outbuf, char length) { char buf[1]; buf[0] = reg; int w = _i2c.write(_address_RTC, buf, 1); int r = _i2c.read(_address_RTC, outbuf, length); _error = ((w != 0) || (r != 0)); } // write a register void MCP79412::writeRegister(int reg, char uint8_t) { char buf[2]; buf[0] = reg; buf[1] = uint8_t; int w = _i2c.write(_address_RTC, buf, 2); _error = (w != 0); } // write registers void MCP79412::writeRegisters(int reg, char *inbuf, char length) { char buf[32]; buf[0] = reg; for (int i = 1; i <= length; i++) { buf[i] = inbuf[i-1]; } int w = _i2c.write(_address_RTC, buf, length+1); _error = (w != 0); } // Function to read the mac address from the eeprom void MCP79412::getMacAddress(char *mac_address) { char buf[1]; buf[0] = MAC_LOCATION; int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 1); int r = _i2c.read(MCP79412_EEPROM_ADDR, mac_address, 6); _error = ((w != 0) || (r != 0)); } // Unlock the unique id area and write in the mac address void MCP79412::writeMacAddress(char *mac_address) { char buf[7]; unlockUniqueID(); buf[0] = MAC_LOCATION; for (int i = 1; i <= 6; i++) { buf[i] = mac_address[i-1]; } int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 7); _error = (w != 0); } // Unlock the unique id area ready for writing void MCP79412::unlockUniqueID() { // Write 0x55 to the memory location 0x09 char buf[2]; buf[0] = UNLOCK_ID_REG; buf[1] = UNLOCK_ID_CODE1; int w1 = _i2c.write(MCP79412_RTC_ADDR, buf, 2); // Write 0xAA to the memory location 0x09 buf[0] = UNLOCK_ID_REG; buf[1] = UNLOCK_ID_CODE2; int w2 = _i2c.write(MCP79412_RTC_ADDR, buf, 2); _error = ((w1 != 0) || (w2 != 0)); } // Set the date/time, set to 24hr and enable the clock // (assumes you're passing in valid numbers) void MCP79412::setRtcDateTime( uint8_t second, // 0-59 uint8_t minute, // 0-59 uint8_t hour, // 1-23 uint8_t dayOfWeek, // 1-7 uint8_t dayOfMonth, // 1-31 uint8_t month, // 1-12 uint8_t year) // 0-99 { char buf[8]; buf[0] = RTC_LOCATION; buf[1] = decToBcd(second) & 0x7f; // set seconds and disable clock (01111111, Bit 7, ST = 0) buf[2] = decToBcd(minute) & 0x7f; // set minutes (01111111) buf[3] = decToBcd(hour) & 0x3f; // set hours and to 24hr format (00111111, Bit 6 = 0) buf[4] = _BV(VBATEN) | (decToBcd(dayOfWeek) & 0x07); // set the day and enable battery backup (00000111)|(00001000, Bit 3 = 1) buf[5] = decToBcd(dayOfMonth) & 0x3f; // set the date in month (00111111) buf[6] = decToBcd(month) & 0x1f; // set the month (00011111) buf[7] = decToBcd(year); // set the year (11111111) int w1 = _i2c.write(MCP79412_RTC_ADDR, buf, 8); // Start Clock: buf[0] = RTC_LOCATION; buf[1] = _BV(ST) | decToBcd(second); // set seconds and enable clock (10000000) int w2 = _i2c.write(MCP79412_RTC_ADDR, buf, 2); _error = ((w1 != 0) || (w2 != 0)); } // Get the date/time void MCP79412::getRtcDateTime( uint8_t *second, uint8_t *minute, uint8_t *hour, uint8_t *dayOfWeek, uint8_t *dayOfMonth, uint8_t *month, uint8_t *year) { char buf[8]; buf[0] = RTC_LOCATION; int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); int r = _i2c.read(MCP79412_RTC_ADDR, buf, 7); _error = ((w != 0) || (r != 0)); // A few of these need masks because certain bits are control bits *second = bcdToDec(buf[0] & 0x7f); // 01111111 0-59 *minute = bcdToDec(buf[1] & 0x7f); // 01111111 0-59 *hour = bcdToDec(buf[2] & 0x3f); // 00111111 1-23 *dayOfWeek = bcdToDec(buf[3] & 0x07); // 00000111 1-7 *dayOfMonth = bcdToDec(buf[4] & 0x3f); // 00111111 1-31 *month = bcdToDec(buf[5] & 0x1f); // 00011111 1-12 *year = bcdToDec(buf[6]); // 11111111 0-99 } bool MCP79412::checkTimeLost(void) { char buf[8]; uint8_t second, minute, hour, dayOfWeek, dayOfMonth, month, year; buf[0] = RTC_LOCATION; int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); int r = _i2c.read(MCP79412_RTC_ADDR, buf, 7); _error = ((w != 0) || (r != 0)); // A few of these need masks because certain bits are control bits second = bcdToDec(buf[0] & 0x7f); // 01111111 0-59 minute = bcdToDec(buf[1] & 0x7f); // 01111111 0-59 hour = bcdToDec(buf[2] & 0x3f); // 00111111 1-23 dayOfWeek = bcdToDec(buf[3] & 0x07); // 00000111 1-7 dayOfMonth = bcdToDec(buf[4] & 0x3f); // 00111111 1-31 month = bcdToDec(buf[5] & 0x1f); // 00011111 1-12 year = bcdToDec(buf[6]); // 11111111 0-99 return (year <= 15) ? true : false; } // Enable the clock without changing the date/time void MCP79412::enableClock() { // Get the current seconds value as the enable/disable bit is in the same // byte of memory as the seconds value: char buf[2]; buf[0] = RTC_LOCATION; int w1 = _i2c.write(MCP79412_RTC_ADDR, buf, 1); int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1); int second = bcdToDec(buf[0] & 0x7f); // 01111111 // Start Clock: buf[0] = RTC_LOCATION; buf[1] = _BV(ST) | decToBcd(second); // set seconds and enable clock (10000000, Bit 7, ST = 1) int w2 = _i2c.write(MCP79412_RTC_ADDR, buf, 2); _error = ((w1 != 0) || (r != 0) || (w2 != 0)); } // Disable the clock without changing the date/time void MCP79412::disableClock() { // Get the current seconds value as the enable/disable bit is in the same // uint8_t of memory as the seconds value: char buf[2]; buf[0] = RTC_LOCATION; int w1 = _i2c.write(MCP79412_RTC_ADDR, buf, 1); int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1); int second = bcdToDec(buf[0] & 0x7f); // 01111111 // Stop Clock: buf[0] = RTC_LOCATION; buf[1] = decToBcd(second); // set seconds and disable clock (01111111, Bit 7, ST = 0) int w2 = _i2c.write(MCP79412_RTC_ADDR, buf, 2); _error = ((w1 != 0) || (r != 0) || (w2 != 0)); } // Enable the battery void MCP79412::enableBattery() { // Get the current day value as the enable/disable bit is in the same // uint8_t of memory as the seconds value: char buf[2]; buf[0] = DAY_REG; int w1 = _i2c.write(MCP79412_RTC_ADDR, buf, 1); int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1); int day = bcdToDec(buf[0] & 0x07); // 00000111 // Start Clock: buf[0] = DAY_REG; buf[1] = _BV(VBATEN) | decToBcd(day); // set day and enable battery (00001000) int w2 = _i2c.write(MCP79412_RTC_ADDR, buf, 2); _error = ((w1 != 0) || (r != 0) || (w2 != 0)); } // Write a single byte of data to RAM void MCP79412::writeRamByte(uint8_t location, uint8_t data) { writeRamBytes(location, &data, 1); // char buf[2]; // if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR)) // { // buf[0] = location; // buf[1] = data; // int w = _i2c.write(MCP79412_RTC_ADDR, buf, 2); // _error = (w != 0); // } } // Write multiple bytes of data to RAM uint8_t MCP79412::writeRamBytes(uint8_t location, uint8_t *data, uint8_t length) { uint8_t bytesWritten = 0; char buf[1]; buf[0] = location; int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); _error = (w != 0); for (uint8_t i = 0; i < length; i++) { buf[0] = data[i]; int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); // Returns 0 on success (ack), non-0 on failure (nack) bytesWritten++; if (_error == false) { _error = (w != 0); } } return bytesWritten; } // Read a single byte of data from RAM uint8_t MCP79412::readRamByte(uint8_t location) { uint8_t data; readRamBytes(location, &data, 1); return data; // char buf[2]; // if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR)) // { // buf[0] = location; // int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); // int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1); // // _error = ((w != 0) || (r != 0)); // // return buf[0]; // } // return 0; } // Read multiple bytes of data from RAM uint8_t MCP79412::readRamBytes(uint8_t location, uint8_t *data, uint8_t length) { uint8_t bytesRead = 0; char buf[1]; buf[0] = location; int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); _error = (w != 0); for (uint8_t i = 0; i < length; i++) { int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1); bytesRead++; data[i] = buf[0]; if (_error == false) { _error = (r != 0); } } return bytesRead; } // 64 Bytes SRAM, Battery Backed // Write a single byte of data to SRAM void MCP79412::writeSramByte(uint8_t location, uint8_t data) { writeSramBytes(location, &data, 1); // char buf[2]; // if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR)) // { // buf[0] = location; // buf[1] = data; // int w = _i2c.write(MCP79412_RTC_ADDR, buf, 2); // _error = (w != 0); // } } // 64 Bytes SRAM, Battery Backed // Write multiple bytes of data to SRAM uint8_t MCP79412::writeSramBytes(uint8_t location, uint8_t *data, uint8_t length) { uint8_t bytesWritten = 0; char buf[1]; buf[0] = location; int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); _error = (w != 0); for (uint8_t i = 0; i < length; i++) { if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR)) { buf[0] = data[i]; int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); // Returns 0 on success (ack), non-0 on failure (nack) bytesWritten++; if (_error == false) { _error = (w != 0); } } location++; } return bytesWritten; } // 64 Bytes SRAM, Battery Backed // Read a single byte of data from SRAM uint8_t MCP79412::readSramByte(uint8_t location) { uint8_t data; readSramBytes(location, &data, 1); return data; // char buf[2]; // if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR)) // { // buf[0] = location; // int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); // int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1); // // _error = ((w != 0) || (r != 0)); // // return buf[0]; // } // return 0; } // 64 Bytes SRAM, Battery Backed // Read multiple bytes of data from SRAM uint8_t MCP79412::readSramBytes(uint8_t location, uint8_t *data, uint8_t length) { uint8_t bytesRead = 0; char buf[1]; buf[0] = location; int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); _error = (w != 0); for (uint8_t i = 0; i < length; i++) { if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR)) { int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1); bytesRead++; data[i] = buf[0]; if (_error == false) { _error = (r != 0); } } location++; } return bytesRead; } // 128 Bytes EEPROM // Write a single byte of data to EEPROM void MCP79412::writeEepromByte(uint8_t location, uint8_t data) { writeEepromBytes(location, &data, 1); // char buf[2]; // unlockUniqueID(); // buf[0] = location & (EEPROM_SIZE - 1); // buf[1] = data; // int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 2); // _error = (w != 0); } // 128 Bytes EEPROM // Unlock the unique id area and write multiple of bytes to EEPROM uint8_t MCP79412::writeEepromBytes(uint8_t location, uint8_t *data, uint8_t length) { uint8_t bytesWritten = 0; char buf[1]; unlockUniqueID(); buf[0] = location & (EEPROM_SIZE - 1); // location & 0x7f int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 1); _error = (w != 0); for (uint8_t i = 0; i < length; i++) { if (location < EEPROM_SIZE) { buf[0] = data[i]; int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 1); // Returns 0 on success (ack), non-0 on failure (nack) bytesWritten++; if (_error == false) { _error = (w != 0); } } location++; } return bytesWritten; } // 128 Bytes EEPROM // Read a single byte of data from EEPROM uint8_t MCP79412::readEepromByte(uint8_t location) { uint8_t data; readEepromBytes(location, &data, 1); return data; // char buf[2]; // if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR)) // { // buf[0] = location; // int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); // int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1); // // _error = ((w != 0) || (r != 0)); // // return buf[0]; // } // return 0; } // 128 Bytes EEPROM // Read multiple bytes of data from EEPROM uint8_t MCP79412::readEepromBytes(uint8_t location, uint8_t *data, uint8_t length) { uint8_t bytesRead = 0; char buf[1]; buf[0] = location & (EEPROM_SIZE - 1); // location & 0x7f int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 1); _error = (w != 0); for (uint8_t i = 0; i < length; i++) { if (location < EEPROM_SIZE) { int r = _i2c.read(MCP79412_EEPROM_ADDR, buf, 1); bytesRead++; data[i] = buf[0]; if (_error == false) { _error = (r != 0); } } location++; } return bytesRead; } /*----------------------------------------------------------------------* * Read the calibration register. * * The calibration value is not a twos-complement number. The MSB is * * the sign bit, and the 7 LSBs are an unsigned number, so we convert * * it and return it to the caller as a regular twos-complement integer. * *----------------------------------------------------------------------*/ int MCP79412::calibRead(void) { uint8_t val = readRamByte(CALIB_REG); if ( val & 0x80 ) { return -(val & 0x7F); } return val; } /*----------------------------------------------------------------------* * Write the calibration register. * * Calibration value must be between -127 and 127, others result * * in no action. See note above on the format of the calibration value. * *----------------------------------------------------------------------*/ void MCP79412::calibWrite(int value) { uint8_t calibVal; if (value >= -127 && value <= 127) { calibVal = abs(value); if (value < 0) { calibVal += 128; } writeRamByte(CALIB_REG, calibVal); } } /*----------------------------------------------------------------------* * Read the unique ID. * * User or factory programmable, Protected area * * For the MCP79411 (EUI-48), the first two bytes will contain 0xFF. * * Caller must provide an 8-byte array to contain the results. * *----------------------------------------------------------------------*/ void MCP79412::readUniqueId(char *uniqueID) { char buf[1]; buf[0] = UNIQUE_ID_ADDR; int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 1); int r = _i2c.read(MCP79412_EEPROM_ADDR, uniqueID, UNIQUE_ID_SIZE); _error = ((w != 0) || (r != 0)); } /*----------------------------------------------------------------------------* * Returns an EUI-64 ID. For an MCP79411, the EUI-48 ID is converted to * * EUI-64. For an MCP79412, calling this function is equivalent to * * calling readUniqueId(). For an MCP79412, if the RTC type is known, calling * * readUniqueId() will be a bit more efficient. * * Caller must provide an 8-byte array to contain the results. * *----------------------------------------------------------------------------*/ void MCP79412::getEUI64(char *uniqueID) { char rtcID[8]; readUniqueId(rtcID); if ((rtcID[0] == 0xFF) && (rtcID[1] == 0xFF)) { rtcID[0] = rtcID[2]; rtcID[1] = rtcID[3]; rtcID[2] = rtcID[4]; rtcID[3] = 0xFF; rtcID[4] = 0xFE; } for (uint8_t i = 0; i < UNIQUE_ID_SIZE; i++) { uniqueID[i] = rtcID[i]; } } /*----------------------------------------------------------------------* * Check to see if a power failure has occurred. If so, returns TRUE * * as the function value, and returns the power down and power up * * timestamps. After returning the time stamps, the RTC's timestamp * * registers are cleared and the VBAT bit which indicates a power * * failure is reset. * * * * Note that the power down and power up timestamp registers do not * * contain values for seconds or for the year. The returned time stamps * * will therefore contain the current year from the RTC. However, there * * is a chance that a power outage spans from one year to the next. * * If we find the power down timestamp to be later (larger) than the * * power up timestamp, we will assume this has happened, and well * * subtract one year from the power down timestamp. * * * * Still, there is an assumption that the timestamps are being read * * in the same year as that when the power up occurred. * * * * Finally, note that once the RTC records a power outage, it must be * * cleared before another will be recorded. * *----------------------------------------------------------------------*/ bool MCP79412::powerFail(time_t *powerDown, time_t *powerUp) { uint8_t day, yr; //copies of the RTC Day and Year registers struct tm dn, up; //power down and power up times char buf[8]; readRamBytes(DAY_REG, &day, 1); readRamBytes(YEAR_REG, &yr, 1); yr = y2kYearToTm(bcdToDec(yr)); if ( day & _BV(VBAT) ) { buf[0] = PWRDWN_TS_REG; int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); int r = _i2c.read(MCP79412_RTC_ADDR, buf, 8); //read both timestamp registers, 8 bytes total dn.tm_sec = 0; dn.tm_min = bcdToDec(buf[0]); dn.tm_hour = bcdToDec(buf[1] & ~_BV(HR1224)); //assumes 24hr clock dn.tm_mday = bcdToDec(buf[2]); dn.tm_mon = bcdToDec(buf[3] & 0x1F); //mask off the day, we don't need it dn.tm_year = yr; //assume current year up.tm_sec = 0; up.tm_min = bcdToDec(buf[4]); up.tm_hour = bcdToDec(buf[5] & ~_BV(HR1224)); //assumes 24hr clock up.tm_mday = bcdToDec(buf[6]); up.tm_mon = bcdToDec(buf[7] & 0x1F); //mask off the day, we don't need it up.tm_year = yr; //assume current year *powerDown = mktime(&dn); *powerUp = mktime(&up); //clear the VBAT bit, which causes the RTC hardware to clear the timestamps too. //I suppose there is a risk here that the day has changed since we read it, //but the Day of Week is actually redundant data and the makeTime() function //does not use it. This could be an issue if someone is reading the RTC //registers directly, but as this library is meant to be used with the Time library, //and also because we don't provide a method to read the RTC clock/calendar //registers directly, we won't lose any sleep about it at this point unless //some issue is actually brought to our attention ;-) day &= ~_BV(VBAT); writeRamBytes(DAY_REG, &day , 1); //adjust the powerDown timestamp if needed (see notes above) if (*powerDown > *powerUp) { --dn.tm_year; *powerDown = mktime(&dn); } return true; } return false; } /*----------------------------------------------------------------------* * Enable or disable the square wave output. * *----------------------------------------------------------------------*/ void MCP79412::squareWave(Sqwave freq) { uint8_t ctrlReg; readRamBytes(CTRL_REG, &ctrlReg, 1); if (freq > 3) { ctrlReg &= ~_BV(SQWE); } else { ctrlReg = (ctrlReg & 0xF8) | _BV(SQWE) | freq; } writeRamByte(CTRL_REG, ctrlReg); } /*----------------------------------------------------------------------* * Set an alarm time. Sets the alarm registers only, does not enable * * the alarm. See enableAlarm(). * *----------------------------------------------------------------------*/ void MCP79412::setAlarm(uint8_t alarmNumber, time_t alarmTime) { struct tm *t; uint8_t day; // Need to preserve bits in the day (of week) register alarmNumber &= 0x01; // Ensure a valid alarm number readRamBytes(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG) , &day, 1); t = localtime(&alarmTime); // Put the time_t into the tm structure char buf[7]; buf[0] = ALM0_REG + alarmNumber * (ALM1_REG - ALM0_REG); buf[1] = dec2bcd(t->tm_sec); buf[2] = dec2bcd(t->tm_min); buf[3] = dec2bcd(t->tm_hour); // Sets 24 hour format (Bit 6 == 0) buf[4] = (day & 0xF8) + t->tm_wday; buf[5] = dec2bcd(t->tm_mday); buf[6] = dec2bcd(t->tm_mon); int w = _i2c.write(MCP79412_RTC_ADDR, buf, 7); _error = (w != 0); } /*----------------------------------------------------------------------* * Enable or disable an alarm, and set the trigger criteria, * * e.g. match only seconds, only minutes, entire time and date, etc. * *----------------------------------------------------------------------*/ void MCP79412::enableAlarm(uint8_t alarmNumber, uint8_t alarmType) { uint8_t day; //alarm day register has config & flag bits uint8_t ctrl; //control register has alarm enable bits alarmNumber &= 0x01; //ensure a valid alarm number readRamBytes(CTRL_REG, &ctrl, 1); if (alarmType < ALM_DISABLE) { readRamBytes(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1); day = ( day & 0x87 ) | alarmType << 4; //reset interrupt flag, OR in the config bits writeRamByte(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), day); ctrl |= _BV(ALM0 + alarmNumber); //enable the alarm } else { ctrl &= ~(_BV(ALM0 + alarmNumber)); //disable the alarm } writeRamByte(CTRL_REG, ctrl); } /*----------------------------------------------------------------------* * Returns true or false depending on whether the given alarm has been * * triggered, and resets the alarm "interrupt" flag. This is not a real * * interrupt, just a bit that's set when an alarm is triggered. * *----------------------------------------------------------------------*/ bool MCP79412::alarm(uint8_t alarmNumber) { uint8_t day; //alarm day register has config & flag bits alarmNumber &= 0x01; //ensure a valid alarm number readRamBytes( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1); if (day & _BV(ALMIF)) { day &= ~_BV(ALMIF); //turn off the alarm "interrupt" flag writeRamByte(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), day); return true; } return false; } /*----------------------------------------------------------------------* * Sets the logic level on the MFP when it's not being used as a * * square wave or alarm output. The default is HIGH. * *----------------------------------------------------------------------*/ void MCP79412::out(bool level) { uint8_t ctrlReg; readRamBytes(CTRL_REG, &ctrlReg, 1); if (level) ctrlReg |= _BV(OUT); else ctrlReg &= ~_BV(OUT); writeRamByte(CTRL_REG, ctrlReg); } /*----------------------------------------------------------------------* * Specifies the logic level on the Multi-Function Pin (MFP) when an * * alarm is triggered. The default is LOW. When both alarms are * * active, the two are ORed together to determine the level of the MFP. * * With alarm polarity set to LOW (the default), this causes the MFP * * to go low only when BOTH alarms are triggered. With alarm polarity * * set to HIGH, the MFP will go high when EITHER alarm is triggered. * * * * Note that the state of the MFP is independent of the alarm * * "interrupt" flags, and the alarm() function will indicate when an * * alarm is triggered regardless of the polarity. * *----------------------------------------------------------------------*/ void MCP79412::alarmPolarity(bool polarity) { uint8_t alm0Day; readRamBytes(ALM0_DAY, &alm0Day, 1); if (polarity) alm0Day |= _BV(OUT); else alm0Day &= ~_BV(OUT); writeRamByte(ALM0_DAY, alm0Day); } /*----------------------------------------------------------------------* * Check to see if the RTC's oscillator is started (ST bit in seconds * * register). Returns true if started. * *----------------------------------------------------------------------*/ bool MCP79412::isRunning(void) { char buf[1]; buf[0] = (uint8_t)TIME_REG; int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1); _error = ((w != 0) || (r != 0)); return buf[0] & _BV(ST); } /*----------------------------------------------------------------------* * Set or clear the VBATEN bit. Setting the bit powers the clock and * * SRAM from the backup battery when Vcc falls. Note that setting the * * time via set() or write() sets the VBATEN bit. * *----------------------------------------------------------------------*/ void MCP79412::vbaten(bool enable) { uint8_t day; readRamBytes(DAY_REG, &day, 1); if (enable) day |= _BV(VBATEN); else day &= ~_BV(VBATEN); writeRamByte(DAY_REG, day); return; } //bool MCP79412::getSummerTime(void) //{ // getDateTime(&t); // // time_t secondsEpoch = mktime(&t); // seconds since the Epoch // t = *localtime(&secondsEpoch); // strftime(buffer, 32, "%j", localtime(&secondsEpoch)); // int dayOfYearC = atoi(buffer); // // strftime(buffer, 32, "%Y", localtime(&secondsEpoch)); // int year = atoi(buffer); // // int index = (year - 2011) * 5; // if (index < 0) // index = 0; // if (index > 440) // (2099 - 2011) * 5 = 440 // index = 440; // // int monthS = atoi(SummerTime[index+1]); // int dayS = atoi(SummerTime[index+2]); // // t.tm_mon = monthS - 1; // adjust for tm structure required values // t.tm_mday = dayS; // secondsEpoch = mktime(&t); // seconds since the Epoch // t = *localtime(&secondsEpoch); // strftime(buffer, 32, "%j", localtime(&secondsEpoch)); // int dayOfYearS = atoi(buffer); // // int monthE = atoi(SummerTime[index+3]); // int dayE = atoi(SummerTime[index+4]); // // t.tm_mon = monthE - 1; // adjust for tm structure required values // t.tm_mday = dayE; // secondsEpoch = mktime(&t); // seconds since the Epoch // t = *localtime(&secondsEpoch); // strftime(buffer, 32, "%j", localtime(&secondsEpoch)); // int dayOfYearE = atoi(buffer); // // return ((dayOfYearC >= dayOfYearS) && (dayOfYearC < dayOfYearE)) ? true : false; //} // //int MCP79412::dayOfYearC(void) //{ // getDateTime(&t); // // time_t secondsEpoch = mktime(&t); // seconds since the Epoch // strftime(buffer, 32, "%j", localtime(&secondsEpoch)); // return atoi(buffer); //} // //char * MCP79412::getSunRise(void) //{ // return (char*) SunRise[dayOfYearC()]; //} // //char * MCP79412::getSunSet(void) //{ // return (char*) SunSet[dayOfYearC()]; //} // //char * MCP79412::getDayLength(void) //{ // return (char*) DayLength[dayOfYearC()]; //} // //int MCP79412::getSunRiseMinute(void) //{ // int doy = dayOfYearC(); // int h = atoi(substr((char*)SunRise[doy], 0, 2)); // int m = atoi(substr((char*)SunRise[doy], 3, 2)); // return h * 60 + m; //} // //int MCP79412::getSunSetMinute(void) //{ // int doy = dayOfYearC(); // int h = atoi(substr((char*)SunSet[doy], 0, 2)); // int m = atoi(substr((char*)SunSet[doy], 3, 2)); // return h * 60 + m; //} // //bool MCP79412::checkSunRise(void) //{ // int dayOfWeek, mday, month, year, hours, minutes, seconds; // readDateTime(&dayOfWeek, &mday, &month, &year, &hours, &minutes, &seconds); // // int absMinute = hours * 60 + minutes; // int SunRiseMinute = getSunRiseMinute(); // int SunSetMinute = getSunSetMinute(); // // return ((absMinute >= SunRiseMinute) && (absMinute < SunSetMinute)) ? true : false; //} void MCP79412::substr(char *s, char *d, int pos, int len) { char *t; s = s+pos; t = s+len; while (s != t) { *d=*s; s++; d++; } *d='\0'; } char * MCP79412::substr(char *s, int pos, int len) { char *t; char *d; d = buffer; s = s+pos; t = s+len; while (s != t) { *d=*s; s++; d++; } *d='\0'; return buffer; } struct tm MCP79412::setSystemDateTime( uint8_t second, // 0-59 uint8_t minute, // 0-59 uint8_t hour, // 1-23 uint8_t dayOfMonth, // 1-31 uint8_t month, // 1-12 uint8_t year) // 0-99 { uint8_t dayOfWeek = 1; // Will be determined // Convert to unix time structure t->tm_sec = second; // 0-59 t->tm_min = minute; // 0-59 t->tm_hour = hour; // 0-23 t->tm_wday = dayOfWeek - 1; // 0-6 (0 = Sunday) t->tm_mday = dayOfMonth; // 1-31 t->tm_mon = month - 1; // 0-11 t->tm_year = year + 100; // 100-199 year since 1900 t->tm_isdst = 0; // printf("-> Debug %d %02d-%02d-%03d %02d:%02d:%02d\n", t->tm_wday, t->tm_mday, t->tm_mon, t->tm_year, t->tm_hour, t->tm_min, t->tm_sec); secondsEpoch = mktime(t); // seconds since the Epoch set_time(secondsEpoch); // Get weekday 0-6, Sunday as 0 for RTC t = localtime(&secondsEpoch); // printf("-> Debug %d %02d-%02d-%03d %02d:%02d:%02d\n", t->tm_wday, t->tm_mday, t->tm_mon, t->tm_year, t->tm_hour, t->tm_min, t->tm_sec); return *t; } void MCP79412::getSystemDateTime( uint8_t *second, // 0-59 uint8_t *minute, // 0-59 uint8_t *hour, // 0-23 uint8_t *dayOfWeek, // 1-7 (1 = Sunday) uint8_t *dayOfMonth, // 1-31 uint8_t *month, // 1-12 uint8_t *year) // 0-99 year since 2000 { // struct tm *t; // time_t secondsEpoch; // Get system DateTime secondsEpoch = time(NULL); t = localtime(&secondsEpoch); // time/date data *second = (t->tm_sec); // 0-59 *minute = (t->tm_min); // 0-59 *hour = (t->tm_hour); // 0-23 *dayOfWeek = (t->tm_wday + 1); // 1-7 (1 = Sunday) *dayOfMonth = (t->tm_mday); // 1-31 *month = (t->tm_mon + 1); // 1-12 *year = (t->tm_year - 100); // 0-99 year since 2000 } void MCP79412::setRtcToSystemDateTime(void) { // Get system DateTime secondsEpoch = time(NULL); t = localtime(&secondsEpoch); // Convert from unix time structure uint8_t second = (t->tm_sec); // 0-59 uint8_t minute = (t->tm_min); // 0-59 uint8_t hour = (t->tm_hour); // 0-23 uint8_t dayOfWeek = (t->tm_wday + 1); // 1-7 (1 = Sunday) uint8_t dayOfMonth = (t->tm_mday); // 1-31 uint8_t month = (t->tm_mon + 1); // 1-12 uint8_t year = (t->tm_year - 100); // 0-99 year since 2000 // Set RTC DateTime setRtcDateTime(second, minute, hour, dayOfWeek, dayOfMonth, month, year); } void MCP79412::setSystemToRtcDateTime(void) { // Get RTC DateTime getRtcDateTime(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); // Convert to unix time structure t->tm_sec = second; // 0-59 t->tm_min = minute; // 0-59 t->tm_hour = hour; // 0-23 t->tm_wday = dayOfWeek - 1; // 0-6 (0 = Sunday) t->tm_mday = dayOfMonth; // 1-31 t->tm_mon = month - 1; // 0-11 t->tm_year = year + 100; // 100-199 year since 1900 t->tm_isdst = 0; // Set system DateTime secondsEpoch = mktime(t); // seconds since the Epoch set_time(secondsEpoch); } void MCP79412::setRtcFromTm(struct tm *t) { // // Get system DateTime // secondsEpoch = time(NULL); // t = localtime(&secondsEpoch); // Convert from unix time structure uint8_t second = (t->tm_sec); // 0-59 uint8_t minute = (t->tm_min); // 0-59 uint8_t hour = (t->tm_hour); // 0-23 uint8_t dayOfWeek = (t->tm_wday + 1); // 1-7 (1 = Sunday) uint8_t dayOfMonth = (t->tm_mday); // 1-31 uint8_t month = (t->tm_mon + 1); // 1-12 uint8_t year = (t->tm_year - 100); // 0-99 year since 2000 // printf("setRtcFromTm %d %02d-%02d-%03d %02d:%02d:%02d\n", dayOfWeek, dayOfMonth, month, year, hour, minute, second); // Set RTC DateTime setRtcDateTime(second, minute, hour, dayOfWeek, dayOfMonth, month, year); } struct tm MCP79412::getTmFromRtc(void) { // Get RTC DateTime getRtcDateTime(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); // Convert to unix time structure t->tm_sec = second; // 0-59 t->tm_min = minute; // 0-59 t->tm_hour = hour; // 0-23 t->tm_wday = dayOfWeek - 1; // 0-6 (0 = Sunday) t->tm_mday = dayOfMonth; // 1-31 t->tm_mon = month - 1; // 0-11 t->tm_year = year + 100; // 100-199 year since 1900 t->tm_isdst = 0; return *t; // // Set system DateTime // secondsEpoch = mktime(t); // seconds since the Epoch // set_time(secondsEpoch); } time_t MCP79412::getSecondsEpoch(void) { secondsEpoch = time(NULL); return secondsEpoch; } void MCP79412::setSecondsEpoch(time_t t) { secondsEpoch = t; set_time(secondsEpoch); } void MCP79412::getRtcDateTimeAsTm(void) { // Get RTC DateTime getRtcDateTime(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); // Convert to unix time structure t->tm_sec = second; // 0-59 t->tm_min = minute; // 0-59 t->tm_hour = hour; // 0-23 t->tm_wday = dayOfWeek - 1; // 0-6 (0 = Sunday) t->tm_mday = dayOfMonth; // 1-31 t->tm_mon = month - 1; // 0-11 t->tm_year = year + 100; // 100-199 year since 1900 t->tm_isdst = 0; // Set system DateTime secondsEpoch = mktime(t); // seconds since the Epoch // time_t t = time(secondsEpoch); // set_time(secondsEpoch); } time_t MCP79412::convertDateTimeToTimestamp( uint8_t second, // 0-59 uint8_t minute, // 0-59 uint8_t hour, // 0-23 uint8_t dayOfMonth, // 1-31 uint8_t month, // 1-12 uint8_t year) // 0-99 year since 2000 { // setup time structure for Wed, 28 Oct 2009 11:35:37 struct tm t; t.tm_sec = second; // 0-59 t.tm_min = minute; // 0-59 t.tm_hour = hour; // 0-23 t.tm_mday = dayOfMonth; // 1-31 t.tm_mon = month - 1; // 0-11 t.tm_year = year + 100; // 100-199 year since 1900 // printf("Debug %d %02d-%02d-%03d %02d:%02d:%02d\n", t.tm_wday, t.tm_mday, t.tm_mon, t.tm_year, t.tm_hour, t.tm_min, t.tm_sec); // convert to timestamp and display (1256729737) time_t seconds = mktime(&t); // printf("Time as seconds since January 1, 1970 = %d\n", seconds); // char buffer[32]; // strftime(buffer, 32, "%a %d-%m-%Y %H:%M:%S\n", localtime(&seconds)); // printf("Time: %s", buffer); // Get weekday Sunday as 0 (0-6) for RTC struct tm *t2; t2 = localtime(&seconds); // printf("Debug %d %02d-%02d-%03d %02d:%02d:%02d\n", t.tm_wday, t2->tm_mday, t2->tm_mon, t2->tm_year, t2->tm_hour, t2->tm_min, t2->tm_sec); // printf("Weekday %d\n", t2->tm_wday); return seconds; } uint8_t MCP79412::getWeekdayFromDate(uint8_t dayOfMonth, uint8_t month, uint8_t year) // year 0-99 { // setup time structure for Wed, 28 Oct 2009 11:35:37 struct tm t; t.tm_sec = 0; // 0-59 t.tm_min = 0; // 0-59 t.tm_hour = 0; // 0-23 t.tm_mday = dayOfMonth; // 1-31 t.tm_mon = month - 1; // 0-11 t.tm_year = year + 100; // 100-199 year since 1900 // printf("Debug %d %02d:%02d:%02d %02d-%02d-%02d\n", t.tm_wday, t.tm_mday, t.tm_mon, t.tm_year, t.tm_hour, t.tm_min, t.tm_sec); // convert to timestamp and display (1256729737) time_t seconds = mktime(&t); // printf("Time as seconds since January 1, 1970 = %d\n", seconds); // char buffer[32]; // strftime(buffer, 32, "%a %d-%m-%Y %H:%M:%S\n", localtime(&seconds)); // printf("Time: %s", buffer); // Get weekday Sunday as 0 (0-6) for RTC struct tm *t2; t2 = localtime(&seconds); // printf("Debug %d %02d-%02d-%03d %02d:%02d:%02d\n", t2->tm_wday, t2->tm_mday, t2->tm_mon, t2->tm_year, t2->tm_hour, t2->tm_min, t2->tm_sec); // printf("Weekday %d\n", t2->tm_wday); return t2->tm_wday; } //double MCP79412::clamp(double v) //{ // const double t = v < 0.0f ? 0.0f : v; // return t > 1.0f ? 1.0f : t; //} //bool MCP79412::checkTimeLost(void) //{ //// return (atoi(getFormatedDateTime("%Y")) <= 2015) ? true : false; // return false; //} // ///*----------------------------------------------------------------------* // * Read the current time from the RTC and return it in a tmElements_t * // * structure. Returns false if RTC not present (I2C I/O error). * // *----------------------------------------------------------------------*/ //boolean MCP79412RTC::read(tmElements_t &tm) //{ // i2cBeginTransmission(RTC_ADDR); // i2cWrite((uint8_t)TIME_REG); // if (i2cEndTransmission() != 0) { // return false; // } // else { // //request 7 uint8_ts (secs, min, hr, dow, date, mth, yr) // i2cRequestFrom(RTC_ADDR, tmNbrFields); // tm.Second = bcd2dec(i2cRead() & ~_BV(ST)); // tm.Minute = bcd2dec(i2cRead()); // tm.Hour = bcd2dec(i2cRead() & ~_BV(HR1224)); //assumes 24hr clock // tm.Wday = i2cRead() & ~(_BV(OSCON) | _BV(VBAT) | _BV(VBATEN)); //mask off OSCON, VBAT, VBATEN bits // tm.Day = bcd2dec(i2cRead()); // tm.Month = bcd2dec(i2cRead() & ~_BV(LP)); //mask off the leap year bit // tm.Year = y2kYearToTm(bcd2dec(i2cRead())); // return true; // } //} // ///*----------------------------------------------------------------------* // * Set the RTC's time from a tmElements_t structure. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::write(tmElements_t &tm) //{ // i2cBeginTransmission(RTC_ADDR); // i2cWrite((uint8_t)TIME_REG); // i2cWrite((uint8_t)0x00); //stops the oscillator (Bit 7, ST == 0) // i2cWrite(dec2bcd(tm.Minute)); // i2cWrite(dec2bcd(tm.Hour)); //sets 24 hour format (Bit 6 == 0) // i2cWrite(tm.Wday | _BV(VBATEN)); //enable battery backup operation // i2cWrite(dec2bcd(tm.Day)); // i2cWrite(dec2bcd(tm.Month)); // i2cWrite(dec2bcd(tmYearToY2k(tm.Year))); // i2cEndTransmission(); // // i2cBeginTransmission(RTC_ADDR); // i2cWrite((uint8_t)TIME_REG); // i2cWrite(dec2bcd(tm.Second) | _BV(ST)); //set the seconds and start the oscillator (Bit 7, ST == 1) // i2cEndTransmission(); //} // ///*----------------------------------------------------------------------* // * Write a single uint8_t to RTC RAM. * // * Valid address range is 0x00 - 0x5F, no checking. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::ramWrite(uint8_t addr, uint8_t value) //{ // ramWrite(addr, &value, 1); //} // ///*----------------------------------------------------------------------* // * Write multiple uint8_ts to RTC RAM. * // * Valid address range is 0x00 - 0x5F, no checking. * // * Number of uint8_ts (nuint8_ts) must be between 1 and 31 (Wire library * // * limitation). * // *----------------------------------------------------------------------*/ //void MCP79412RTC::ramWrite(uint8_t addr, uint8_t *values, uint8_t nuint8_ts) //{ // i2cBeginTransmission(RTC_ADDR); // i2cWrite(addr); // for (uint8_t i=0; i<nuint8_ts; i++) i2cWrite(values[i]); // i2cEndTransmission(); //} // ///*----------------------------------------------------------------------* // * Read a single uint8_t from RTC RAM. * // * Valid address range is 0x00 - 0x5F, no checking. * // *----------------------------------------------------------------------*/ //uint8_t MCP79412RTC::ramRead(uint8_t addr) //{ // uint8_t value; // // ramRead(addr, &value, 1); // return value; //} // ///*----------------------------------------------------------------------* // * Read multiple uint8_ts from RTC RAM. * // * Valid address range is 0x00 - 0x5F, no checking. * // * Number of uint8_ts (nuint8_ts) must be between 1 and 32 (Wire library * // * limitation). * // *----------------------------------------------------------------------*/ //void MCP79412RTC::ramRead(uint8_t addr, uint8_t *values, uint8_t nuint8_ts) //{ // i2cBeginTransmission(RTC_ADDR); // i2cWrite(addr); // i2cEndTransmission(); // i2cRequestFrom( (uint8_t)RTC_ADDR, nuint8_ts ); // for (uint8_t i=0; i<nuint8_ts; i++) values[i] = i2cRead(); //} // ///*----------------------------------------------------------------------* // * Write a single uint8_t to Static RAM. * // * Address (addr) is constrained to the range (0, 63). * // *----------------------------------------------------------------------*/ //void MCP79412RTC::sramWrite(uint8_t addr, uint8_t value) //{ // ramWrite( (addr & (SRAM_SIZE - 1) ) + SRAM_START_ADDR, &value, 1 ); //} // ///*----------------------------------------------------------------------* // * Write multiple uint8_ts to Static RAM. * // * Address (addr) is constrained to the range (0, 63). * // * Number of uint8_ts (nuint8_ts) must be between 1 and 31 (Wire library * // * limitation). * // * Invalid values for nuint8_ts, or combinations of addr and nuint8_ts * // * that would result in addressing past the last uint8_t of SRAM will * // * result in no action. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::sramWrite(uint8_t addr, uint8_t *values, uint8_t nuint8_ts) //{ //#if defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // if (nuint8_ts >= 1 && (addr + nuint8_ts) <= SRAM_SIZE) { //#else // if (nuint8_ts >= 1 && nuint8_ts <= (BUFFER_LENGTH - 1) && (addr + nuint8_ts) <= SRAM_SIZE) { //#endif // ramWrite( (addr & (SRAM_SIZE - 1) ) + SRAM_START_ADDR, values, nuint8_ts ); // } //} // ///*----------------------------------------------------------------------* // * Read a single uint8_t from Static RAM. * // * Address (addr) is constrained to the range (0, 63). * // *----------------------------------------------------------------------*/ //uint8_t MCP79412RTC::sramRead(uint8_t addr) //{ // uint8_t value; // // ramRead( (addr & (SRAM_SIZE - 1) ) + SRAM_START_ADDR, &value, 1 ); // return value; //} // ///*----------------------------------------------------------------------* // * Read multiple uint8_ts from Static RAM. * // * Address (addr) is constrained to the range (0, 63). * // * Number of uint8_ts (nuint8_ts) must be between 1 and 32 (Wire library * // * limitation). * // * Invalid values for nuint8_ts, or combinations of addr and * // * nuint8_ts that would result in addressing past the last uint8_t of SRAM * // * result in no action. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::sramRead(uint8_t addr, uint8_t *values, uint8_t nuint8_ts) //{ //#if defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // if (nuint8_ts >= 1 && (addr + nuint8_ts) <= SRAM_SIZE) { //#else // if (nuint8_ts >= 1 && nuint8_ts <= BUFFER_LENGTH && (addr + nuint8_ts) <= SRAM_SIZE) { //#endif // ramRead((addr & (SRAM_SIZE - 1) ) + SRAM_START_ADDR, values, nuint8_ts); // } //} // ///*----------------------------------------------------------------------* // * Write a single uint8_t to EEPROM. * // * Address (addr) is constrained to the range (0, 127). * // * Can't leverage page write function because a write can't start * // * mid-page. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::eepromWrite(uint8_t addr, uint8_t value) //{ // i2cBeginTransmission(MCP79412_EEPROM_ADDR); // i2cWrite( addr & (EEPROM_SIZE - 1) ); // i2cWrite(value); // i2cEndTransmission(); // eepromWait(); //} // ///*----------------------------------------------------------------------* // * Write a page (or less) to EEPROM. An EEPROM page is 8 uint8_ts. * // * Address (addr) should be a page start address (0, 8, ..., 120), but * // * is ruthlessly coerced into a valid value. * // * Number of uint8_ts (nuint8_ts) must be between 1 and 8, other values * // * result in no action. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::eepromWrite(uint8_t addr, uint8_t *values, uint8_t nuint8_ts) //{ // if (nuint8_ts >= 1 && nuint8_ts <= EEPROM_PAGE_SIZE) { // i2cBeginTransmission(MCP79412_EEPROM_ADDR); // i2cWrite( addr & ~(EEPROM_PAGE_SIZE - 1) & (EEPROM_SIZE - 1) ); // for (uint8_t i=0; i<nuint8_ts; i++) i2cWrite(values[i]); // i2cEndTransmission(); // eepromWait(); // } //} // ///*----------------------------------------------------------------------* // * Read a single uint8_t from EEPROM. * // * Address (addr) is constrained to the range (0, 127). * // *----------------------------------------------------------------------*/ //uint8_t MCP79412RTC::eepromRead(uint8_t addr) //{ // uint8_t value; // // eepromRead( addr & (EEPROM_SIZE - 1), &value, 1 ); // return value; //} // ///*----------------------------------------------------------------------* // * Read multiple uint8_ts from EEPROM. * // * Address (addr) is constrained to the range (0, 127). * // * Number of uint8_ts (nuint8_ts) must be between 1 and 32 (Wire library * // * limitation). * // * Invalid values for addr or nuint8_ts, or combinations of addr and * // * nuint8_ts that would result in addressing past the last uint8_t of EEPROM * // * result in no action. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::eepromRead(uint8_t addr, uint8_t *values, uint8_t nuint8_ts) //{ //#if defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // if (nuint8_ts >= 1 && (addr + nuint8_ts) <= EEPROM_SIZE) { //#else // if (nuint8_ts >= 1 && nuint8_ts <= BUFFER_LENGTH && (addr + nuint8_ts) <= EEPROM_SIZE) { //#endif // i2cBeginTransmission(MCP79412_EEPROM_ADDR); // i2cWrite( addr & (EEPROM_SIZE - 1) ); // i2cEndTransmission(); // i2cRequestFrom( (uint8_t)MCP79412_EEPROM_ADDR, nuint8_ts ); // for (uint8_t i=0; i<nuint8_ts; i++) values[i] = i2cRead(); // } //} // ///*----------------------------------------------------------------------* // * Wait for EEPROM write to complete. * // *----------------------------------------------------------------------*/ //uint8_t MCP79412RTC::eepromWait(void) //{ // uint8_t waitCount = 0; // uint8_t txStatus; // // do // { // ++waitCount; // i2cBeginTransmission(MCP79412_EEPROM_ADDR); // i2cWrite((uint8_t)0); // txStatus = i2cEndTransmission(); // // } while (txStatus != 0); // // return waitCount; //} // ///*----------------------------------------------------------------------* // * Read the calibration register. * // * The calibration value is not a twos-complement number. The MSB is * // * the sign bit, and the 7 LSBs are an unsigned number, so we convert * // * it and return it to the caller as a regular twos-complement integer. * // *----------------------------------------------------------------------*/ //int MCP79412RTC::calibRead(void) //{ // uint8_t val = ramRead(CALIB_REG); // // if ( val & 0x80 ) return -(val & 0x7F); // else return val; //} // ///*----------------------------------------------------------------------* // * Write the calibration register. * // * Calibration value must be between -127 and 127, others result * // * in no action. See note above on the format of the calibration value. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::calibWrite(int value) //{ // uint8_t calibVal; // // if (value >= -127 && value <= 127) { // calibVal = abs(value); // if (value < 0) calibVal += 128; // ramWrite(CALIB_REG, calibVal); // } //} // ///*----------------------------------------------------------------------* // * Read the unique ID. * // * For the MCP79411 (EUI-48), the first two uint8_ts will contain 0xFF. * // * Caller must provide an 8-uint8_t array to contain the results. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::idRead(uint8_t *uniqueID) //{ // i2cBeginTransmission(MCP79412_EEPROM_ADDR); // i2cWrite(UNIQUE_ID_ADDR); // i2cEndTransmission(); // i2cRequestFrom( MCP79412_EEPROM_ADDR, UNIQUE_ID_SIZE ); // for (uint8_t i=0; i<UNIQUE_ID_SIZE; i++) uniqueID[i] = i2cRead(); //} // ///*----------------------------------------------------------------------* // * Returns an EUI-64 ID. For an MCP79411, the EUI-48 ID is converted to * // * EUI-64. For an MCP79412, calling this function is equivalent to * // * calling idRead(). For an MCP79412, if the RTC type is known, calling * // * idRead() will be a bit more efficient. * // * Caller must provide an 8-uint8_t array to contain the results. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::getEUI64(uint8_t *uniqueID) //{ // uint8_t rtcID[8]; // // idRead(rtcID); // if (rtcID[0] == 0xFF && rtcID[1] == 0xFF) { // rtcID[0] = rtcID[2]; // rtcID[1] = rtcID[3]; // rtcID[2] = rtcID[4]; // rtcID[3] = 0xFF; // rtcID[4] = 0xFE; // } // for (uint8_t i=0; i<UNIQUE_ID_SIZE; i++) uniqueID[i] = rtcID[i]; //} // ///*----------------------------------------------------------------------* // * Check to see if a power failure has occurred. If so, returns TRUE * // * as the function value, and returns the power down and power up * // * timestamps. After returning the time stamps, the RTC's timestamp * // * registers are cleared and the VBAT bit which indicates a power * // * failure is reset. * // * * // * Note that the power down and power up timestamp registers do not * // * contain values for seconds or for the year. The returned time stamps * // * will therefore contain the current year from the RTC. However, there * // * is a chance that a power outage spans from one year to the next. * // * If we find the power down timestamp to be later (larger) than the * // * power up timestamp, we will assume this has happened, and well * // * subtract one year from the power down timestamp. * // * * // * Still, there is an assumption that the timestamps are being read * // * in the same year as that when the power up occurred. * // * * // * Finally, note that once the RTC records a power outage, it must be * // * cleared before another will be recorded. * // *----------------------------------------------------------------------*/ //boolean MCP79412RTC::powerFail(time_t *powerDown, time_t *powerUp) //{ // uint8_t day, yr; //copies of the RTC Day and Year registers // tmElements_t dn, up; //power down and power up times // // ramRead(DAY_REG, &day, 1); // ramRead(YEAR_REG, &yr, 1); // yr = y2kYearToTm(bcd2dec(yr)); // if ( day & _BV(VBAT) ) { // i2cBeginTransmission(RTC_ADDR); // i2cWrite(PWRDWN_TS_REG); // i2cEndTransmission(); // // i2cRequestFrom(RTC_ADDR, TIMESTAMP_SIZE); //read both timestamp registers, 8 uint8_ts total // dn.Second = 0; // dn.Minute = bcd2dec(i2cRead()); // dn.Hour = bcd2dec(i2cRead() & ~_BV(HR1224)); //assumes 24hr clock // dn.Day = bcd2dec(i2cRead()); // dn.Month = bcd2dec(i2cRead() & 0x1F); //mask off the day, we don't need it // dn.Year = yr; //assume current year // up.Second = 0; // up.Minute = bcd2dec(i2cRead()); // up.Hour = bcd2dec(i2cRead() & ~_BV(HR1224)); //assumes 24hr clock // up.Day = bcd2dec(i2cRead()); // up.Month = bcd2dec(i2cRead() & 0x1F); //mask off the day, we don't need it // up.Year = yr; //assume current year // // *powerDown = makeTime(dn); // *powerUp = makeTime(up); // // //clear the VBAT bit, which causes the RTC hardware to clear the timestamps too. // //I suppose there is a risk here that the day has changed since we read it, // //but the Day of Week is actually redundant data and the makeTime() function // //does not use it. This could be an issue if someone is reading the RTC // //registers directly, but as this library is meant to be used with the Time library, // //and also because we don't provide a method to read the RTC clock/calendar // //registers directly, we won't lose any sleep about it at this point unless // //some issue is actually brought to our attention ;-) // day &= ~_BV(VBAT); // ramWrite(DAY_REG, &day , 1); // // //adjust the powerDown timestamp if needed (see notes above) // if (*powerDown > *powerUp) { // --dn.Year; // *powerDown = makeTime(dn); // } // return true; // } // else // return false; //} // ///*----------------------------------------------------------------------* // * Enable or disable the square wave output. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::squareWave(uint8_t freq) //{ // uint8_t ctrlReg; // // ramRead(CTRL_REG, &ctrlReg, 1); // if (freq > 3) { // ctrlReg &= ~_BV(SQWE); // } // else { // ctrlReg = (ctrlReg & 0xF8) | _BV(SQWE) | freq; // } // ramWrite(CTRL_REG, &ctrlReg, 1); //} // ///*----------------------------------------------------------------------* // * Set an alarm time. Sets the alarm registers only, does not enable * // * the alarm. See enableAlarm(). * // *----------------------------------------------------------------------*/ //void MCP79412RTC::setAlarm(uint8_t alarmNumber, time_t alarmTime) //{ // tmElements_t tm; // uint8_t day; //need to preserve bits in the day (of week) register // // alarmNumber &= 0x01; //ensure a valid alarm number // ramRead( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG) , &day, 1); // breakTime(alarmTime, tm); // i2cBeginTransmission(RTC_ADDR); // i2cWrite( ALM0_REG + alarmNumber * (ALM1_REG - ALM0_REG) ); // i2cWrite(dec2bcd(tm.Second)); // i2cWrite(dec2bcd(tm.Minute)); // i2cWrite(dec2bcd(tm.Hour)); //sets 24 hour format (Bit 6 == 0) // i2cWrite( (day & 0xF8) + tm.Wday ); // i2cWrite(dec2bcd(tm.Day)); // i2cWrite(dec2bcd(tm.Month)); // i2cEndTransmission(); //} // ///*----------------------------------------------------------------------* // * Enable or disable an alarm, and set the trigger criteria, * // * e.g. match only seconds, only minutes, entire time and date, etc. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::enableAlarm(uint8_t alarmNumber, uint8_t alarmType) //{ // uint8_t day; //alarm day register has config & flag bits // uint8_t ctrl; //control register has alarm enable bits // // alarmNumber &= 0x01; //ensure a valid alarm number // ramRead(CTRL_REG, &ctrl, 1); // if (alarmType < ALM_DISABLE) { // ramRead(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1); // day = ( day & 0x87 ) | alarmType << 4; //reset interrupt flag, OR in the config bits // ramWrite(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1); // ctrl |= _BV(ALM0 + alarmNumber); //enable the alarm // } // else { // ctrl &= ~(_BV(ALM0 + alarmNumber)); //disable the alarm // } // ramWrite(CTRL_REG, &ctrl, 1); //} // ///*----------------------------------------------------------------------* // * Returns true or false depending on whether the given alarm has been * // * triggered, and resets the alarm "interrupt" flag. This is not a real * // * interrupt, just a bit that's set when an alarm is triggered. * // *----------------------------------------------------------------------*/ //boolean MCP79412RTC::alarm(uint8_t alarmNumber) //{ // uint8_t day; //alarm day register has config & flag bits // // alarmNumber &= 0x01; //ensure a valid alarm number // ramRead( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1); // if (day & _BV(ALMIF)) { // day &= ~_BV(ALMIF); //turn off the alarm "interrupt" flag // ramWrite( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1); // return true; // } // else // return false; //} // ///*----------------------------------------------------------------------* // * Sets the logic level on the MFP when it's not being used as a * // * square wave or alarm output. The default is HIGH. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::out(boolean level) //{ // uint8_t ctrlReg; // // ramRead(CTRL_REG, &ctrlReg, 1); // if (level) // ctrlReg |= _BV(OUT); // else // ctrlReg &= ~_BV(OUT); // ramWrite(CTRL_REG, &ctrlReg, 1); //} // ///*----------------------------------------------------------------------* // * Specifies the logic level on the Multi-Function Pin (MFP) when an * // * alarm is triggered. The default is LOW. When both alarms are * // * active, the two are ORed together to determine the level of the MFP. * // * With alarm polarity set to LOW (the default), this causes the MFP * // * to go low only when BOTH alarms are triggered. With alarm polarity * // * set to HIGH, the MFP will go high when EITHER alarm is triggered. * // * * // * Note that the state of the MFP is independent of the alarm * // * "interrupt" flags, and the alarm() function will indicate when an * // * alarm is triggered regardless of the polarity. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::alarmPolarity(boolean polarity) //{ // uint8_t alm0Day; // // ramRead(ALM0_DAY, &alm0Day, 1); // if (polarity) // alm0Day |= _BV(OUT); // else // alm0Day &= ~_BV(OUT); // ramWrite(ALM0_DAY, &alm0Day, 1); //} // ///*----------------------------------------------------------------------* // * Check to see if the RTC's oscillator is started (ST bit in seconds * // * register). Returns true if started. * // *----------------------------------------------------------------------*/ //boolean MCP79412RTC::isRunning(void) //{ // i2cBeginTransmission(RTC_ADDR); // i2cWrite((uint8_t)TIME_REG); // i2cEndTransmission(); // // //request just the seconds register // i2cRequestFrom(RTC_ADDR, 1); // return i2cRead() & _BV(ST); //} // ///*----------------------------------------------------------------------* // * Set or clear the VBATEN bit. Setting the bit powers the clock and * // * SRAM from the backup battery when Vcc falls. Note that setting the * // * time via set() or write() sets the VBATEN bit. * // *----------------------------------------------------------------------*/ //void MCP79412RTC::vbaten(boolean enable) //{ // uint8_t day; // // ramRead(DAY_REG, &day, 1); // if (enable) // day |= _BV(VBATEN); // else // day &= ~_BV(VBATEN); // // ramWrite(DAY_REG, &day, 1); // return; //} ///*----------------------------------------------------------------------* // * Decimal-to-BCD conversion * // *----------------------------------------------------------------------*/ //uint8_t MCP79412RTC::dec2bcd(uint8_t n) //{ // return n + 6 * (n / 10); //} // ///*----------------------------------------------------------------------* // * BCD-to-Decimal conversion * // *----------------------------------------------------------------------*/ //uint8_t __attribute__ ((noinline)) MCP79412RTC::bcd2dec(uint8_t n) //{ // return n - 6 * (n >> 4); //} // BCD to decimal conversion int MCP79412::bcd2dec(int bcd) { return(((bcd & 0xF0) >> 4) * 10 + (bcd & 0x0F)); } // decimal to BCD conversion int MCP79412::dec2bcd(int dec) { return((dec / 10) * 16 + (dec % 10)); } // Convert normal decimal numbers to binary coded decimal: int MCP79412::decToBcd(int val) { return ( (val/10*16) + (val%10) ); } // Convert binary coded decimal to normal decimal numbers: int MCP79412::bcdToDec(int val) { return ( (val/16*10) + (val%16) ); }