BriComp
Well-known member
Hi,
I hope someone can help me.
EDIT: The code is independent of Teensy model and is demonstrated with Arduino 1.8.19.
I have attached software below which demonstrates the problem. When I compile the software I get the following two errors:
The code and libraries that produce the error are as below. It's HHMRadioC.h that creates the problem.
I have removed most of the code from the HHMRadio.cpp as the error is still demonstrated with out it.
Demonstrate_Error.ino
HHMRadioC.h
HHMRadioC.cpp
RTC_RV_3028_C7.h
RTC_RV_3028_C7.cpp
I hope someone can help me.
EDIT: The code is independent of Teensy model and is demonstrated with Arduino 1.8.19.
I have attached software below which demonstrates the problem. When I compile the software I get the following two errors:
Code:
ld.exe: HHMRadioC\\HHMRadioC.cpp.o:c:\arduino programs\libraries\hhmradioc\hhmradioc.h:101: multiple definition of receivePacket; Demonstrate_Error.cpp.o:c:\arduino programs\libraries\hhmradioc\hhmradioc.h:101: first defined here
ld.exe: HHMRadioC\\HHMRadioC.cpp.o:c:\arduino programs\libraries\hhmradioc\hhmradioc.h:100: multiple definition of transmitPacket; Demonstrate_Error.cpp.o:c:\arduino programs\libraries\hhmradioc\hhmradioc.h:100: first defined here
collect2.exe*: error: ld returned 1 exit status
Error linking for board Teensy 4.0 (teensy40)
Build failed for project 'Demonstrate_Error'
The code and libraries that produce the error are as below. It's HHMRadioC.h that creates the problem.
I have removed most of the code from the HHMRadio.cpp as the error is still demonstrated with out it.
Demonstrate_Error.ino
Code:
#include <Arduino.h>
#include <HHMRadioC.h>
#ifndef excludeRtc
#include <TimeLib.h> // https://github.com/PaulStoffregen/Time
#include <RTC_RV_3028_C7.h> // https://github.com/constiko/RV-3028_C7-Arduino_Library
RV3028 rtc;
#endif
HHMRadioC radio;
void GetCompileTime() {
int h, m, s, d, mo, y;
h = ((int)__TIME__[0] - (int)'0') * 10 + ((int)__TIME__[1] - (int)'0');
m = ((int)__TIME__[3] - (int)'0') * 10 + ((int)__TIME__[4] - (int)'0');
s = ((int)__TIME__[6] - (int)'0') * 10 + ((int)__TIME__[7] - (int)'0');
if (__DATE__[0] == 'J') {
if (__DATE__[1] == 'a') {
mo = 1;
}
else if (__DATE__[2] == 'n') {
mo = 6;
}
else mo = 7;
}
else if (__DATE__[0] == 'F') {
mo = 2;
}
else if (__DATE__[0] == 'M') {
if (__DATE__[2] == 'r')
mo = 3;
else
mo = 5;
}
else if (__DATE__[0] == 'A') {
mo = 8;
}
else if (__DATE__[0] == 'S') {
mo = 9;
}
else if (__DATE__[0] == 'O') {
mo = 10;
}
else if (__DATE__[0] == 'N') {
mo = 11;
}
else if (__DATE__[0] == 'D') {
mo = 12;
}
if (__DATE__[4] != ' ') d = ((int)__DATE__[4] - (int)'0') * 10; else d = 0;
d += ((int)__DATE__[5] - (int)'0');
y = ((int)__DATE__[7] - (int)'0') * 1000 + ((int)__DATE__[8] - (int)'0') * 100 + ((int)__DATE__[9] - (int)'0') * 10 + ((int)__DATE__[10] - (int)'0');;
setTime(h, m, s, d, mo, y); // Set Teensy Time
}
static time_t getUnixTime() { return rtc.getUNIX(); }
uint8_t setRtcError = 0;
bool SetUpTime(bool trickleCharge = false) {
bool isOk = true;
Wire1.begin();
if (rtc.begin(Wire1) == false) {
Serial.println("Something went wrong, check wiring");
return false;
}
else
{
Serial.println("RTC online!");
}
if (rtc.timeUpdateRequired) {
Serial.println("Setting time from Teensy Compile Time");
if (!SetRtcDateTime()) {
Serial.print("Unable to set RTC Date / Time. Status Byte");
Serial.println(setRtcError, HEX);
}
}
setSyncProvider(getUnixTime);
isOk = (timeStatus() == timeSet);
if (!isOk)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
return isOk;
};
bool SetRtcDateTime() {
GetCompileTime();
#ifdef debugSrdt
Serial.print("now() = "); Serial.println(now());
Serial.print("hour() = "); Serial.println(hour());
Serial.print("minute() = "); Serial.println(minute());
Serial.print("day() = "); Serial.println(day());
Serial.print("month() = "); Serial.println(month());
Serial.print("year() = "); Serial.println(year());
#endif
if (!rtc.setUNIX( (uint32_t) now() )) { Serial.println("Unable to rtc.SetUNIX(now())"); setRtcError = 1; }
if (!rtc.setHours( hour() )) { Serial.println("Unable to rtc.setHours(hour())"); setRtcError |= 0b10; }
if (!rtc.setMinutes( minute() )) { Serial.println("Unable to rtc.minutes(minute())"); setRtcError |= 0b100; }
if (!rtc.setSeconds( second() )) { Serial.println("Unable to rtc.seconds(second())"); setRtcError |= 0b100; }
if (!rtc.setDate( day() )) { Serial.println("Unable to rtc.day(day())"); setRtcError |= 0b1000; }
if (!rtc.setMonth( month() )) { Serial.println("Unable to rtc.month(month())"); setRtcError |= 0b10000; }
if (!rtc.setYear( year() )) { Serial.println("Unable to rtc.year(year())"); setRtcError |= 0b100000; }
return (setRtcError == 0);
}
#define smstDBz
bool SendMessageSetTime(uint8_t toWhome) {
transmitPacket.data.command = bSetLocalTime;
#ifdef smstDB
Serial.print("Cmdz-"); Serial.print(transmitPacket.data.command, HEX); Serial.println(" ");
#endif
radio.generateCommandMessage(bSetLocalTime);
return radio.sendMessage(toWhome);
}
#define smrtDBz
bool SendMessageReportTime(uint8_t toWhome) {
// transmitPacket.data.command = bSendLocalTime;
// Serial.print("Cmdz-"); Serial.print(transmitPacket.data.command, HEX); Serial.println(" ");
radio.generateCommandMessage(bSendLocalTime);
#ifdef smrtDB
Serial.print("Cmdz-"); Serial.print(transmitPacket.data.command, HEX); Serial.println(" ");
#endif
return radio.sendMessage(toWhome);
}
#define ptDB
void PrintTime( time_t t ){
TimeElements tm;
#ifdef ptDB
Serial.print("PrintTime: t "); Serial.println(t, HEX);
#endif
breakTime(t, tm);
Serial.print(tm.Day); Serial.print("/");
Serial.print(tm.Month); Serial.print("/");
Serial.print(tm.Year); Serial.print(" ");
Serial.print(tm.Hour); Serial.print(":");
Serial.print(tm.Minute); Serial.print(":");
Serial.print(tm.Second); Serial.print(" ");
}
void setup() {
Serial.begin(9600);
while (!Serial && millis() < 3000);
Serial1.begin(9600);
TargetRadioTypes radioSort = thermostatRadio; // boilerRadio, thermostatRadio, masterRadio
RadioLocationIdTYPE radioLocationId = christopherBedroomId;
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
radio.init(radioSort);
if (!radio.setupRadio(radioLocationId, MODE_NORMAL, OPT_WAKEUP500, false))
Serial.println("Unable to");
else
Serial.println("Done");
Serial.println(" Initialise radio");
Serial.println("Finished SetUp");
digitalWrite(LED_BUILTIN, LOW);
// Serial.print("Target Address "); Serial.print()
}
void loop(){
Serial.print("Getting time from Target: MyTime: "); PrintTime(now());
Serial.print("Sending message requesting time ");
if (SendMessageReportTime(tBoilerTestHarnessId)) Serial.println("OK"); else Serial.println("Doh");
if (radio.gotRadioMessage(1000)) {
// memcpy(&ReceivePacket, &receivePacket, sizeof(receivePacket));
// ReceivePacket = receivePacket;
Serial.print( receivePacket.data.command, HEX); Serial.print(" ");
Serial.print( receivePacket.data.header.ckSum, HEX); Serial.print(" ");
Serial.print( receivePacket.data.header.senderId, HEX); Serial.print(" ");
Serial.println( receivePacket.data.header.time, HEX);
Serial.print(" Received Time: "); Serial.print(receivePacket.data.header.time,HEX);
Serial.print(" TargetTime: "); PrintTime(receivePacket.data.header.time);
}else
{
Serial.print("No Reply from Target Radio");
}
Serial.println();
delay(3000);
}
HHMRadioC.h
Code:
#pragma once
#ifndef HHMRadioC_H
#define HHMRadioC_H
/**************************************************************
* *
* Converted from HHMRadio.h *
* Instead of Master / Slave has Sender and Receiver *
* *
***************************************************************/
#include <Arduino.h>
#include <HHM_EBYTE_E220.h>
#include "C:\Arduino Programs\HomeHeatingManagement\HHMDefines.h"
// connect to any of the Teensy Serial ports
/*#define RadioSerial Serial1
#define PIN_M0 2
#define PIN_M1 3
#define PIN_AX 4
#define auxPin PIN_AX
*/
#define Radio_MODE_TYPE MODE_TYPE
//addrType masterAddr, slaveAddr;
//#define addresses addrE220
enum ReceiveOrTransmitType {
receive,
transmit
};
enum TargetRadioTypes {
boilerRadio, thermostatRadio, masterRadio
};
/*
* Error code generated by this library when incorrect number of characters returned by Nextion
*/
const uint8_t radioInvalidNumCharsSent = 0x3F;
#pragma pack(push,1)
/***************************************************************************
* Message Construction Types *
***************************************************************************/
/* The following now in HHMDefines.h
struct addrType {
byte H;
byte L;
byte Chan;
};
*/
typedef struct {
uint16_t ckSum;
uint8_t senderId;
time_t time;
} HeaderType;
typedef struct {
uint8_t command;
HeaderType header;
union {
uint8_t valveChannel;
uint16_t intTemp; // int(temperature * 100 + 0.5)
uint16_t t2;
nextionEepromDataDayType eepromHwData;
uint16_t largestData;
};
} RootDataType;
const uint8_t cSumPos = 1;
const uint8_t cSumPosPlusOne = 2;
const uint8_t headerSize = sizeof(HeaderType); // minus size of command
//const int sizeOfMasterToSlaveCommand = sizeof(MasterToSlaveCommandType);
const int sizeOfMinMsgDta = sizeof(HeaderType) + sizeof( byte );
typedef struct {
radioAddrType addr;
union {
RootDataType data;
uint8_t dataArray[sizeof(RootDataType)];
};
} TransmitPacketType;
typedef struct ReceivePacketType {
union {
RootDataType data;
uint8_t dataArray[sizeof(RootDataType)];
};
} ReceivePacketType;
TransmitPacketType transmitPacket;
ReceivePacketType receivePacket;
#pragma pack(pop)
class HHMRadioC {
public:
const uint8_t instructionSuccess = 1;
uint8_t errorCode = instructionSuccess;
// void AddError(uint8_t a, uint8_t b) {}
bool waitForRadioToComeAlive();
void init(TargetRadioTypes target);
bool setupRadio(uint8_t myId, MODE_TYPE modeAfterSetup, byte wakeUp, bool sleep);
/*Could be masterAddr
OR slaveId
OR sender
OR myId
*/
void generateCommandMessage(byte cmnd);
bool sendMessage(uint8_t whoTo);
bool messageReply();
bool receiveMessage();
bool radioDataReady();
bool gotRadioMessage(uint32_t timeout = 0);
bool needToRespondToMessage();
bool sendAck = true;
void clearIpBuffer();
private:
bool needsResponse = true;
uint32_t airCharDataRate;
};
#endif
HHMRadioC.cpp
Code:
#include "Arduino.h"
#include <HHMRadioC.h>
#define radioDb
static uint8_t auxPin;
Stream* _s;
uint32_t waitTilRadioTxBufferEmpty;
void AddError(uint8_t a, uint8_t b) {}
bool HHMRadioC::waitForRadioToComeAlive() {
elapsedMillis rTime = 0;
while ((digitalReadFast(auxPin) == LOW) && (rTime <= 4000)) {}
return (rTime >= 4000);
}
void HHMRadioC::init(TargetRadioTypes target) {
}
#define srDB
bool HHMRadioC::setupRadio(uint8_t myId, MODE_TYPE modeAfterSetup, byte wakeUp, /*uint8_t pwrStatus, */bool sleep) {
return true;// !fault;
}
#define cmdlDBz
uint8_t CalcMsgDataLen( uint8_t comd ) {
uint8_t len = 0;
return len;
};
/* THIS WAS RUBBISH ---- FIXED IT */
#define cmcDBz
uint16_t CalcMsgChecksum( ReceiveOrTransmitType tsmtOrRcv ) {
uint16_t cSum = 0;
return cSum;
}
void HHMRadioC::generateCommandMessage(byte cmnd) {
transmitPacket.data.command = cmnd;
}
#define mrDB
bool HHMRadioC::messageReply() {
return true;
};
#define smDB
bool HHMRadioC::sendMessage(uint8_t whoTo) {
// transmitPacket.addr = radioAddr[whoTo];
#ifdef smDB
#endif
return HHMRadioC::messageReply();
};
#define rmDBz
bool HHMRadioC::receiveMessage() {
bool okState = false;
return okState;
}
bool HHMRadioC::radioDataReady() {
return true;// (transceiver.available() >= sizeOfMinMsgDta);
}
bool HHMRadioC::gotRadioMessage(uint32_t timeout) {
bool gotData = false;
return gotData;
};
bool HHMRadioC::needToRespondToMessage() {
return false;// needsResponse;
}
void HHMRadioC::clearIpBuffer() {
// transceiver.ClearBuffer();
};
RTC_RV_3028_C7.h
Code:
/******************************************************************************
RV-3028-C7.h
RV-3028-C7 Arduino Library
Constantin Koch
July 25, 2019
https://github.com/constiko/RV-3028_C7-Arduino_Library
Resources:
Uses Wire.h for I2C operation
Development environment specifics:
Arduino IDE 1.8.9
This code is released under the [MIT License](http://opensource.org/licenses/MIT).
Please review the LICENSE.md file included with this example. If you have any questions
or concerns with licensing, please contact constantinkoch@outlook.com.
Distributed as-is; no warranty is given.
******************************************************************************/
#pragma once
#if (ARDUINO >= 100)
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <Wire.h>
//The 7-bit I2C ADDRESS of the RV3028
#define RV3028_ADDR (uint8_t)0x52
//REGISTERS
//Clock registers
#define RV3028_SECONDS 0x00
#define RV3028_MINUTES 0x01
#define RV3028_HOURS 0x02
//Calendar registers
#define RV3028_WEEKDAY 0x03
#define RV3028_DATE 0x04
#define RV3028_MONTHS 0x05
#define RV3028_YEARS 0x06
//Alarm registers
#define RV3028_MINUTES_ALM 0x07
#define RV3028_HOURS_ALM 0x08
#define RV3028_DATE_ALM 0x09
//Periodic Countdown Timer registers
#define RV3028_TIMERVAL_0 0x0A
#define RV3028_TIMERVAL_1 0x0B
#define RV3028_TIMERSTAT_0 0x0C
#define RV3028_TIMERSTAT_1 0x0D
//Configuration registers
#define RV3028_STATUS 0x0E
#define RV3028_CTRL1 0x0F
#define RV3028_CTRL2 0x10
#define RV3028_GPBITS 0x11
#define RV3028_INT_MASK 0x12
//Eventcontrol/Timestamp registers
#define RV3028_EVENTCTRL 0x13
#define RV3028_COUNT_TS 0x14
#define RV3028_SECONDS_TS 0x15
#define RV3028_MINUTES_TS 0x16
#define RV3028_HOURS_TS 0x17
#define RV3028_DATE_TS 0x18
#define RV3028_MONTH_TS 0x19
#define RV3028_YEAR_TS 0x1A
//Unix Time registers
#define RV3028_UNIX_TIME0 0x1B
#define RV3028_UNIX_TIME1 0x1C
#define RV3028_UNIX_TIME2 0x1D
#define RV3028_UNIX_TIME3 0x1E
//RAM registers
#define RV3028_USER_RAM1 0x1F
#define RV3028_USER_RAM2 0x20
//Password registers
#define RV3028_PASSWORD0 0x21
#define RV3028_PASSWORD1 0x22
#define RV3028_PASSWORD2 0x23
#define RV3028_PASSWORD3 0x24
//EEPROM Memory Control registers
#define RV3028_EEPROM_ADDR 0x25
#define RV3028_EEPROM_DATA 0x26
#define RV3028_EEPROM_CMD 0x27
//ID register
#define RV3028_ID 0x28
//EEPROM Registers
#define EEPROM_Clkout_Register 0x35
#define RV3028_EEOffset_8_1 0x36 //bits 8 to 1 of EEOffset. Bit 0 is bit 7 of register 0x37
#define EEPROM_Backup_Register 0x37
//BITS IN IMPORTANT REGISTERS
//Bits in Status Register
#define STATUS_EEBUSY 7
#define STATUS_CLKF 6
#define STATUS_BSF 5
#define STATUS_UF 4
#define STATUS_TF 3
#define STATUS_AF 2
#define STATUS_EVF 1
#define STATUS_PORF 0
//Bits in Control1 Register
#define CTRL1_TRPT 7
#define CTRL1_WADA 5//Bit 6 not implemented
#define CTRL1_USEL 4
#define CTRL1_EERD 3
#define CTRL1_TE 2
#define CTRL1_TD1 1
#define CTRL1_TD0 0
//Bits in Control2 Register
#define CTRL2_TSE 7
#define CTRL2_CLKIE 6
#define CTRL2_UIE 5
#define CTRL2_TIE 4
#define CTRL2_AIE 3
#define CTRL2_EIE 2
#define CTRL2_12_24 1
#define CTRL2_RESET 0
//Bits in Hours register
#define HOURS_AM_PM 5
//Bits in Alarm registers
#define MINUTESALM_AE_M 7
#define HOURSALM_AE_H 7
#define DATE_AE_WD 7
//Commands for EEPROM Command Register (0x27)
#define EEPROMCMD_First 0x00
#define EEPROMCMD_Update 0x11
#define EEPROMCMD_Refresh 0x12
#define EEPROMCMD_WriteSingle 0x21
#define EEPROMCMD_ReadSingle 0x22
//Bits in EEPROM Backup Register
#define EEPROMBackup_TCE_BIT 5 //Trickle Charge Enable Bit
#define EEPROMBackup_FEDE_BIT 4 //Fast Edge Detection Enable Bit (for Backup Switchover Mode)
#define EEPROMBackup_BSM_SHIFT 2 //Backup Switchover Mode shift
#define EEPROMBackup_TCR_SHIFT 0 //Trickle Charge Resistor shift
#define EEPROMBackup_BSM_CLEAR 0b11110011 //Backup Switchover Mode clear
#define EEPROMBackup_TCR_CLEAR 0b11111100 //Trickle Charge Resistor clear
#define TCR_3K 0b00 //Trickle Charge Resistor 3kOhm
#define TCR_5K 0b01 //Trickle Charge Resistor 5kOhm
#define TCR_9K 0b10 //Trickle Charge Resistor 9kOhm
#define TCR_15K 0b11 //Trickle Charge Resistor 15kOhm
// Clock output register (0x35)
#define EEPROMClkout_CLKOE_BIT 7 //1 = CLKOUT pin is enabled. – Default value on delivery
#define EEPROMClkout_CLKSY_BIT 6
// Bits 5 and 4 not implemented
#define EEPROMClkout_PORIE 3 //0 = No interrupt, or canceled, signal on INT pin at POR. – Default value on delivery
//1 = An interrupt signal on INT pin at POR. Retained until the PORF flag is cleared to 0 (no automatic cancellation).
#define EEPROMClkout_FREQ_SHIFT 0 //frequency shift
#define FD_CLKOUT_32k 0b000 //32.768 kHz – Default value on delivery
#define FD_CLKOUT_8192 0b001 //8192 Hz
#define FD_CLKOUT_1024 0b010 //1024 Hz
#define FD_CLKOUT_64 0b011 //64 Hz
#define FD_CLKOUT_32 0b100 //32 Hz
#define FD_CLKOUT_1 0b101 //1 Hz
#define FD_CLKOUT_TIMER 0b110 //Predefined periodic countdown timer interrupt
#define FD_CLKOUT_LOW 0b111 //CLKOUT = LOW
#define IMT_MASK_CEIE 3 //Clock output when Event Interrupt bit.
#define IMT_MASK_CAIE 2 //Clock output when Alarm Interrupt bit.
#define IMT_MASK_CTIE 1 //Clock output when Periodic Countdown Timer Interrupt bit.
#define IMT_MASK_CUIE 0 //Clock output when Periodic Time Update Interrupt bit.
#define TIME_ARRAY_LENGTH 7 // Total number of writable values in device
enum time_order {
TIME_SECONDS, // 0
TIME_MINUTES, // 1
TIME_HOURS, // 2
TIME_WEEKDAY, // 3
TIME_DATE, // 4
TIME_MONTH, // 5
TIME_YEAR, // 6
};
class RV3028
{
public:
RV3028(void);
bool begin(TwoWire &wirePort = Wire, bool set_24Hour = true, bool disable_TrickleCharge = true, bool set_LevelSwitchingMode = true, bool reset_Status = true);
bool setTime(uint8_t sec, uint8_t min, uint8_t hour, uint8_t weekday, uint8_t date, uint8_t month, uint16_t year);
bool setTime(uint8_t * time, uint8_t len);
bool setSeconds(uint8_t value);
bool setMinutes(uint8_t value);
bool setHours(uint8_t value);
bool setWeekday(uint8_t value);
bool setDate(uint8_t value);
bool setMonth(uint8_t value);
bool setYear(uint16_t value);
bool setToCompilerTime(); //Uses the hours, mins, etc from compile time to set RTC
bool updateTime(); //Update the local array with the RTC registers
char* stringDateUSA(); //Return date in mm-dd-yyyy
char* stringDate(); //Return date in dd-mm-yyyy
char* stringTime(); //Return time hh:mm:ss with AM/PM if in 12 hour mode
char* stringTimeStamp(); //Return timeStamp in ISO 8601 format yyyy-mm-ddThh:mm:ss
uint8_t getSeconds();
uint8_t getMinutes();
uint8_t getHours();
uint8_t getWeekday();
uint8_t getDate();
uint8_t getMonth();
uint16_t getYear();
bool is12Hour(); //Returns true if 12hour bit is set
bool isPM(); //Returns true if is12Hour and PM bit is set
void set12Hour();
void set24Hour();
bool setUNIX(uint32_t value);//Set the UNIX Time (Real Time and UNIX Time are INDEPENDENT!)
uint32_t getUNIX();
void enableAlarmInterrupt(uint8_t min, uint8_t hour, uint8_t date_or_weekday, bool setWeekdayAlarm_not_Date, uint8_t mode, bool enable_clock_output = false);
void enableAlarmInterrupt();
void disableAlarmInterrupt();
bool readAlarmInterruptFlag();
void clearAlarmInterruptFlag();
void setTimer(bool timer_repeat, uint16_t timer_frequency, uint16_t timer_value, bool setInterrupt, bool start_timer, bool enable_clock_output = false);
void enableTimer();
void disableTimer();
void enableTimerInterrupt();
void disableTimerInterrupt();
bool readTimerInterruptFlag();
void clearTimerInterruptFlag();
void enablePeriodicUpdateInterrupt(bool every_second, bool enable_clock_output = false);
void disablePeriodicUpdateInterrupt();
bool readPeriodicUpdateInterruptFlag();
void clearPeriodicUpdateInterruptFlag();
void enableTrickleCharge(uint8_t tcr = TCR_15K); //Trickle Charge Resistor default 15k
void disableTrickleCharge();
bool setBackupSwitchoverMode(uint8_t val);
void enableClockOut(uint8_t freq);
void enableInterruptControlledClockout(uint8_t freq);
void disableClockOut();
bool readClockOutputInterruptFlag();
void clearClockOutputInterruptFlag();
uint8_t status(); //Returns the status byte
void clearInterrupts();
//Values in RTC are stored in Binary Coded Decimal. These functions convert to/from Decimal
uint8_t BCDtoDEC(uint8_t val);
uint8_t DECtoBCD(uint8_t val);
uint8_t readRegister(uint8_t addr);
bool writeRegister(uint8_t addr, uint8_t val);
bool readMultipleRegisters(uint8_t addr, uint8_t * dest, uint8_t len);
bool writeMultipleRegisters(uint8_t addr, uint8_t * values, uint8_t len);
bool writeConfigEEPROM_RAMmirror(uint8_t eepromaddr, uint8_t val);
uint8_t readConfigEEPROM_RAMmirror(uint8_t eepromaddr);
bool writeUserEEPROM(uint8_t eepromaddr, uint8_t val);
uint8_t readUserEEPROM(uint8_t eepromaddr);
bool waitforEEPROM();
void reset();
void setBit(uint8_t reg_addr, uint8_t bit_num);
void clearBit(uint8_t reg_addr, uint8_t bit_num);
bool readBit(uint8_t reg_addr, uint8_t bit_num);
bool timeUpdateRequired = true;
private:
uint8_t _time[TIME_ARRAY_LENGTH];
TwoWire *_i2cPort;
};
//POSSIBLE ENHANCEMENTS :
//ENHANCEMENT: Battery Interrupt / check battery voltage
//ENHANCEMENT: External Event Interrupt
RTC_RV_3028_C7.cpp
Code:
/******************************************************************************
RV-3028-C7.h
RV-3028-C7 Arduino Library
Constantin Koch
July 25, 2019
https://github.com/constiko/RV-3028_C7-Arduino_Library
Development environment specifics:
Arduino IDE 1.8.9
This code is released under the [MIT License](http://opensource.org/licenses/MIT).
Please review the LICENSE.md file included with this example. If you have any questions
or concerns with licensing, please contact constantinkoch@outlook.com.
Distributed as-is; no warranty is given.
******************************************************************************/
#include "RTC_RV_3028_C7.h"
//****************************************************************************//
//
// Settings and configuration
//
//****************************************************************************//
// Parse the __DATE__ predefined macro to generate date defaults:
// __Date__ Format: MMM DD YYYY (First D may be a space if <10)
// <MONTH>
#define BUILD_MONTH_JAN ((__DATE__[0] == 'J') && (__DATE__[1] == 'a')) ? 1 : 0
#define BUILD_MONTH_FEB (__DATE__[0] == 'F') ? 2 : 0
#define BUILD_MONTH_MAR ((__DATE__[0] == 'M') && (__DATE__[1] == 'a') && (__DATE__[2] == 'r')) ? 3 : 0
#define BUILD_MONTH_APR ((__DATE__[0] == 'A') && (__DATE__[1] == 'p')) ? 4 : 0
#define BUILD_MONTH_MAY ((__DATE__[0] == 'M') && (__DATE__[1] == 'a') && (__DATE__[2] == 'y')) ? 5 : 0
#define BUILD_MONTH_JUN ((__DATE__[0] == 'J') && (__DATE__[1] == 'u') && (__DATE__[2] == 'n')) ? 6 : 0
#define BUILD_MONTH_JUL ((__DATE__[0] == 'J') && (__DATE__[1] == 'u') && (__DATE__[2] == 'l')) ? 7 : 0
#define BUILD_MONTH_AUG ((__DATE__[0] == 'A') && (__DATE__[1] == 'u')) ? 8 : 0
#define BUILD_MONTH_SEP (__DATE__[0] == 'S') ? 9 : 0
#define BUILD_MONTH_OCT (__DATE__[0] == 'O') ? 10 : 0
#define BUILD_MONTH_NOV (__DATE__[0] == 'N') ? 11 : 0
#define BUILD_MONTH_DEC (__DATE__[0] == 'D') ? 12 : 0
#define BUILD_MONTH BUILD_MONTH_JAN | BUILD_MONTH_FEB | BUILD_MONTH_MAR | \
BUILD_MONTH_APR | BUILD_MONTH_MAY | BUILD_MONTH_JUN | \
BUILD_MONTH_JUL | BUILD_MONTH_AUG | BUILD_MONTH_SEP | \
BUILD_MONTH_OCT | BUILD_MONTH_NOV | BUILD_MONTH_DEC
// <DATE>
#define BUILD_DATE_0 ((__DATE__[4] == ' ') ? 0 : (__DATE__[4] - 0x30))
#define BUILD_DATE_1 (__DATE__[5] - 0x30)
#define BUILD_DATE ((BUILD_DATE_0 * 10) + BUILD_DATE_1)
// <YEAR>
#define BUILD_YEAR (((__DATE__[7] - 0x30) * 1000) + ((__DATE__[8] - 0x30) * 100) + \
((__DATE__[9] - 0x30) * 10) + ((__DATE__[10] - 0x30) * 1))
// Parse the __TIME__ predefined macro to generate time defaults:
// __TIME__ Format: HH:MM:SS (First number of each is padded by 0 if <10)
// <HOUR>
#define BUILD_HOUR_0 ((__TIME__[0] == ' ') ? 0 : (__TIME__[0] - 0x30))
#define BUILD_HOUR_1 (__TIME__[1] - 0x30)
#define BUILD_HOUR ((BUILD_HOUR_0 * 10) + BUILD_HOUR_1)
// <MINUTE>
#define BUILD_MINUTE_0 ((__TIME__[3] == ' ') ? 0 : (__TIME__[3] - 0x30))
#define BUILD_MINUTE_1 (__TIME__[4] - 0x30)
#define BUILD_MINUTE ((BUILD_MINUTE_0 * 10) + BUILD_MINUTE_1)
// <SECOND>
#define BUILD_SECOND_0 ((__TIME__[6] == ' ') ? 0 : (__TIME__[6] - 0x30))
#define BUILD_SECOND_1 (__TIME__[7] - 0x30)
#define BUILD_SECOND ((BUILD_SECOND_0 * 10) + BUILD_SECOND_1)
RV3028::RV3028(void)
{
}
bool RV3028::begin(TwoWire &wirePort, bool set_24Hour, bool disable_TrickleCharge, bool set_LevelSwitchingMode, bool reset_Status)
{
//We require caller to begin their I2C port, with the speed of their choice
//external to the library
//_i2cPort->begin();
_i2cPort = &wirePort;
delay(1);
if (set_24Hour) { set24Hour(); delay(1); }
if (disable_TrickleCharge) { disableTrickleCharge(); delay(1); }
timeUpdateRequired = readBit(RV3028_STATUS, STATUS_PORF);
return((set_LevelSwitchingMode ? setBackupSwitchoverMode(3) : true) && (reset_Status ? writeRegister(RV3028_STATUS, 0x00) : true));
}
bool RV3028::setTime(uint8_t sec, uint8_t min, uint8_t hour, uint8_t weekday, uint8_t date, uint8_t month, uint16_t year)
{
_time[TIME_SECONDS] = DECtoBCD(sec);
_time[TIME_MINUTES] = DECtoBCD(min);
_time[TIME_HOURS] = DECtoBCD(hour);
_time[TIME_WEEKDAY] = DECtoBCD(weekday);
_time[TIME_DATE] = DECtoBCD(date);
_time[TIME_MONTH] = DECtoBCD(month);
_time[TIME_YEAR] = DECtoBCD(year - 2000);
bool status = false;
if (is12Hour())
{
set24Hour();
status = setTime(_time, TIME_ARRAY_LENGTH);
set12Hour();
}
else
{
status = setTime(_time, TIME_ARRAY_LENGTH);
}
return status;
}
// setTime -- Set time and date/day registers of RV3028 (using data array)
bool RV3028::setTime(uint8_t * time, uint8_t len)
{
if (len != TIME_ARRAY_LENGTH)
return false;
return writeMultipleRegisters(RV3028_SECONDS, time, len);
}
bool RV3028::setSeconds(uint8_t value)
{
_time[TIME_SECONDS] = DECtoBCD(value);
return setTime(_time, TIME_ARRAY_LENGTH);
}
bool RV3028::setMinutes(uint8_t value)
{
_time[TIME_MINUTES] = DECtoBCD(value);
return setTime(_time, TIME_ARRAY_LENGTH);
}
bool RV3028::setHours(uint8_t value)
{
_time[TIME_HOURS] = DECtoBCD(value);
return setTime(_time, TIME_ARRAY_LENGTH);
}
bool RV3028::setWeekday(uint8_t value)
{
_time[TIME_WEEKDAY] = DECtoBCD(value);
return setTime(_time, TIME_ARRAY_LENGTH);
}
bool RV3028::setDate(uint8_t value)
{
_time[TIME_DATE] = DECtoBCD(value);
return setTime(_time, TIME_ARRAY_LENGTH);
}
bool RV3028::setMonth(uint8_t value)
{
_time[TIME_MONTH] = DECtoBCD(value);
return setTime(_time, TIME_ARRAY_LENGTH);
}
bool RV3028::setYear(uint16_t value)
{
_time[TIME_YEAR] = DECtoBCD(value - 2000);
return setTime(_time, TIME_ARRAY_LENGTH);
}
//Takes the time from the last build and uses it as the current time
//Works very well as an arduino sketch
bool RV3028::setToCompilerTime()
{
_time[TIME_SECONDS] = DECtoBCD(BUILD_SECOND);
_time[TIME_MINUTES] = DECtoBCD(BUILD_MINUTE);
_time[TIME_HOURS] = DECtoBCD(BUILD_HOUR);
//Build_Hour is 0-23, convert to 1-12 if needed
if (is12Hour())
{
uint8_t hour = BUILD_HOUR;
bool pm = false;
if (hour == 0)
hour += 12;
else if (hour == 12)
pm = true;
else if (hour > 12)
{
hour -= 12;
pm = true;
}
_time[TIME_HOURS] = DECtoBCD(hour); //Load the modified hours
if (pm == true) _time[TIME_HOURS] |= (1 << HOURS_AM_PM); //Set AM/PM bit if needed
}
// Calculate weekday (from here: http://stackoverflow.com/a/21235587)
// 0 = Sunday, 6 = Saturday
uint16_t d = BUILD_DATE;
uint16_t m = BUILD_MONTH;
uint16_t y = BUILD_YEAR;
uint16_t weekday = (d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 - y / 100 + y / 400) % 7 + 1;
_time[TIME_WEEKDAY] = DECtoBCD(weekday);
_time[TIME_DATE] = DECtoBCD(BUILD_DATE);
_time[TIME_MONTH] = DECtoBCD(BUILD_MONTH);
_time[TIME_YEAR] = DECtoBCD(BUILD_YEAR - 2000); //! Not Y2K (or Y2.1K)-proof :(
return setTime(_time, TIME_ARRAY_LENGTH);
}
//Move the hours, mins, sec, etc registers from RV-3028-C7 into the _time array
//Needs to be called before printing time or date
//We do not protect the GPx registers. They will be overwritten. The user has plenty of RAM if they need it.
bool RV3028::updateTime()
{
if (readMultipleRegisters(RV3028_SECONDS, _time, TIME_ARRAY_LENGTH) == false)
return(false); //Something went wrong
if (is12Hour()) _time[TIME_HOURS] &= ~(1 << HOURS_AM_PM); //Remove this bit from value
return true;
}
//Returns a pointer to array of chars that are the date in mm/dd/yyyy format because they're weird
char* RV3028::stringDateUSA()
{
static char date[14]; //Max of mm/dd/yyyy with \0 terminator
sprintf(date, "%02hhu/%02hhu/20%02hhu", BCDtoDEC(_time[TIME_MONTH]), BCDtoDEC(_time[TIME_DATE]), BCDtoDEC(_time[TIME_YEAR]));
return(date);
}
//Returns a pointer to array of chars that are the date in dd/mm/yyyy format
char* RV3028::stringDate()
{
static char date[14]; //Max of dd/mm/yyyy with \0 terminator
sprintf(date, "%02hhu/%02hhu/20%02hhu", BCDtoDEC(_time[TIME_DATE]), BCDtoDEC(_time[TIME_MONTH]), BCDtoDEC(_time[TIME_YEAR]));
return(date);
}
//Returns a pointer to array of chars that represents the time in hh:mm:ss format
//Adds AM/PM if in 12 hour mode
char* RV3028::stringTime()
{
static char time[14]; //Max of hh:mm:ssXM with \0 terminator
if (is12Hour() == true)
{
char half = 'A';
if (isPM()) half = 'P';
sprintf(time, "%02hhu:%02hhu:%02hhu%cM", BCDtoDEC(_time[TIME_HOURS]), BCDtoDEC(_time[TIME_MINUTES]), BCDtoDEC(_time[TIME_SECONDS]), half);
}
else
sprintf(time, "%02hhu:%02hhu:%02hhu", BCDtoDEC(_time[TIME_HOURS]), BCDtoDEC(_time[TIME_MINUTES]), BCDtoDEC(_time[TIME_SECONDS]));
return(time);
}
char* RV3028::stringTimeStamp()
{
static char timeStamp[29]; //Max of yyyy-mm-ddThh:mm:ss.ss with \0 terminator
if (is12Hour() == true)
{
char half = 'A';
if (isPM()) half = 'P';
sprintf(timeStamp, "20%02hhu-%02hhu-%02hhu %02hhu:%02hhu:%02hhu%cM", BCDtoDEC(_time[TIME_YEAR]), BCDtoDEC(_time[TIME_MONTH]), BCDtoDEC(_time[TIME_DATE]), BCDtoDEC(_time[TIME_HOURS]), BCDtoDEC(_time[TIME_MINUTES]), BCDtoDEC(_time[TIME_SECONDS]), half);
}
else
sprintf(timeStamp, "20%02hhu-%02hhu-%02hhu %02hhu:%02hhu:%02hhu", BCDtoDEC(_time[TIME_YEAR]), BCDtoDEC(_time[TIME_MONTH]), BCDtoDEC(_time[TIME_DATE]), BCDtoDEC(_time[TIME_HOURS]), BCDtoDEC(_time[TIME_MINUTES]), BCDtoDEC(_time[TIME_SECONDS]));
return(timeStamp);
}
uint8_t RV3028::getSeconds()
{
return BCDtoDEC(_time[TIME_SECONDS]);
}
uint8_t RV3028::getMinutes()
{
return BCDtoDEC(_time[TIME_MINUTES]);
}
uint8_t RV3028::getHours()
{
return BCDtoDEC(_time[TIME_HOURS]);
}
uint8_t RV3028::getWeekday()
{
return BCDtoDEC(_time[TIME_WEEKDAY]);
}
uint8_t RV3028::getDate()
{
return BCDtoDEC(_time[TIME_DATE]);
}
uint8_t RV3028::getMonth()
{
return BCDtoDEC(_time[TIME_MONTH]);
}
uint16_t RV3028::getYear()
{
return BCDtoDEC(_time[TIME_YEAR]) + 2000;
}
//Returns true if RTC has been configured for 12 hour mode
bool RV3028::is12Hour()
{
uint8_t controlRegister2 = readRegister(RV3028_CTRL2);
return(controlRegister2 & (1 << CTRL2_12_24));
}
//Returns true if RTC has PM bit set and 12Hour bit set
bool RV3028::isPM()
{
uint8_t hourRegister = readRegister(RV3028_HOURS);
if (is12Hour() && (hourRegister & (1 << HOURS_AM_PM)))
return(true);
return(false);
}
//Configure RTC to output 1-12 hours
//Converts any current hour setting to 12 hour
void RV3028::set12Hour()
{
//Do we need to change anything?
if (is12Hour() == false)
{
uint8_t hour = BCDtoDEC(readRegister(RV3028_HOURS)); //Get the current hour in the RTC
//Set the 12/24 hour bit
uint8_t setting = readRegister(RV3028_CTRL2);
setting |= (1 << CTRL2_12_24);
writeRegister(RV3028_CTRL2, setting);
//Take the current hours and convert to 12, complete with AM/PM bit
bool pm = false;
if (hour == 0)
hour += 12;
else if (hour == 12)
pm = true;
else if (hour > 12)
{
hour -= 12;
pm = true;
}
hour = DECtoBCD(hour); //Convert to BCD
if (pm == true) hour |= (1 << HOURS_AM_PM); //Set AM/PM bit if needed
writeRegister(RV3028_HOURS, hour); //Record this to hours register
}
}
//Configure RTC to output 0-23 hours
//Converts any current hour setting to 24 hour
void RV3028::set24Hour()
{
//Do we need to change anything?
if (is12Hour() == true)
{
//Not sure what changing the CTRL2 register will do to hour register so let's get a copy
uint8_t hour = readRegister(RV3028_HOURS); //Get the current 12 hour formatted time in BCD
bool pm = false;
if (hour & (1 << HOURS_AM_PM)) //Is the AM/PM bit set?
{
pm = true;
hour &= ~(1 << HOURS_AM_PM); //Clear the bit
}
//Change to 24 hour mode
uint8_t setting = readRegister(RV3028_CTRL2);
setting &= ~(1 << CTRL2_12_24); //Clear the 12/24 hr bit
writeRegister(RV3028_CTRL2, setting);
//Given a BCD hour in the 1-12 range, make it 24
hour = BCDtoDEC(hour); //Convert core of register to DEC
if (pm == true) hour += 12; //2PM becomes 14
if (hour == 12) hour = 0; //12AM stays 12, but should really be 0
if (hour == 24) hour = 12; //12PM becomes 24, but should really be 12
hour = DECtoBCD(hour); //Convert to BCD
writeRegister(RV3028_HOURS, hour); //Record this to hours register
}
}
//ATTENTION: Real Time and UNIX Time are INDEPENDENT!
bool RV3028::setUNIX(uint32_t value)
{
uint8_t unix_reg[4];
unix_reg[0] = value;
unix_reg[1] = value >> 8;
unix_reg[2] = value >> 16;
unix_reg[3] = value >> 24;
return writeMultipleRegisters(RV3028_UNIX_TIME0, unix_reg, 4);
}
//ATTENTION: Real Time and UNIX Time are INDEPENDENT!
uint32_t RV3028::getUNIX()
{
uint8_t unix_reg[4];
readMultipleRegisters(RV3028_UNIX_TIME0, unix_reg, 4);
return ((uint32_t)unix_reg[3] << 24) | ((uint32_t)unix_reg[2] << 16) | ((uint32_t)unix_reg[1] << 8) | unix_reg[0];
}
/*********************************
Set the alarm mode in the following way:
0: When minutes, hours and weekday/date match (once per weekday/date)
1: When hours and weekday/date match (once per weekday/date)
2: When minutes and weekday/date match (once per hour per weekday/date)
3: When weekday/date match (once per weekday/date)
4: When hours and minutes match (once per day)
5: When hours match (once per day)
6: When minutes match (once per hour)
7: All disabled � Default value
If you want to set a weekday alarm (setWeekdayAlarm_not_Date = true), set 'date_or_weekday' from 0 (Sunday) to 6 (Saturday)
********************************/
void RV3028::enableAlarmInterrupt(uint8_t min, uint8_t hour, uint8_t date_or_weekday, bool setWeekdayAlarm_not_Date, uint8_t mode, bool enable_clock_output)
{
//disable Alarm Interrupt to prevent accidental interrupts during configuration
disableAlarmInterrupt();
clearAlarmInterruptFlag();
//ENHANCEMENT: Add Alarm in 12 hour mode
set24Hour();
//Set WADA bit (Weekday/Date Alarm)
if (setWeekdayAlarm_not_Date)
clearBit(RV3028_CTRL1, CTRL1_WADA);
else
setBit(RV3028_CTRL1, CTRL1_WADA);
//Write alarm settings in registers 0x07 to 0x09
uint8_t alarmTime[3];
alarmTime[0] = DECtoBCD(min); //minutes
alarmTime[1] = DECtoBCD(hour); //hours
alarmTime[2] = DECtoBCD(date_or_weekday); //date or weekday
//shift alarm enable bits
if (mode > 0b111) mode = 0b111; //0 to 7 is valid
if (mode & 0b001)
alarmTime[0] |= 1 << MINUTESALM_AE_M;
if (mode & 0b010)
alarmTime[1] |= 1 << HOURSALM_AE_H;
if (mode & 0b100)
alarmTime[2] |= 1 << DATE_AE_WD;
//Write registers
writeMultipleRegisters(RV3028_MINUTES_ALM, alarmTime, 3);
//enable Alarm Interrupt
enableAlarmInterrupt();
//Clock output?
if (enable_clock_output)
setBit(RV3028_INT_MASK, IMT_MASK_CAIE);
else
clearBit(RV3028_INT_MASK, IMT_MASK_CAIE);
}
void RV3028::enableAlarmInterrupt()
{
setBit(RV3028_CTRL2, CTRL2_AIE);
}
//Only disables the interrupt (not the alarm flag)
void RV3028::disableAlarmInterrupt()
{
clearBit(RV3028_CTRL2, CTRL2_AIE);
}
bool RV3028::readAlarmInterruptFlag()
{
return readBit(RV3028_STATUS, STATUS_AF);
}
void RV3028::clearAlarmInterruptFlag()
{
clearBit(RV3028_STATUS, STATUS_AF);
}
/*********************************
Countdown Timer Interrupt
********************************/
void RV3028::setTimer(bool timer_repeat, uint16_t timer_frequency, uint16_t timer_value, bool set_interrupt, bool start_timer, bool enable_clock_output)
{
disableTimer();
disableTimerInterrupt();
clearTimerInterruptFlag();
writeRegister(RV3028_TIMERVAL_0, timer_value & 0xff);
writeRegister(RV3028_TIMERVAL_1, timer_value >> 8);
uint8_t ctrl1_val = readRegister(RV3028_CTRL1);
if (timer_repeat)
{
ctrl1_val |= 1 << CTRL1_TRPT;
}
else
{
ctrl1_val &= ~(1 << CTRL1_TRPT);
}
switch (timer_frequency)
{
case 4096: // 4096Hz (default) // up to 122us error on first time
ctrl1_val &= ~3; // Clear both the bits
break;
case 64: // 64Hz // up to 7.813ms error on first time
ctrl1_val &= ~3; // Clear both the bits
ctrl1_val |= 1;
break;
case 1: // 1Hz // up to 7.813ms error on first time
ctrl1_val &= ~3; // Clear both the bits
ctrl1_val |= 2;
break;
case 60000: // 1/60Hz // up to 7.813ms error on first time
ctrl1_val |= 3; // Set both bits
break;
}
if (set_interrupt)
{
enableTimerInterrupt();
}
if (start_timer)
{
ctrl1_val |= (1 << CTRL1_TE);
}
writeRegister(RV3028_CTRL1, ctrl1_val);
//Clock output?
if (enable_clock_output)
setBit(RV3028_INT_MASK, IMT_MASK_CTIE);
else
clearBit(RV3028_INT_MASK, IMT_MASK_CTIE);
}
void RV3028::enableTimerInterrupt()
{
setBit(RV3028_CTRL2, CTRL2_TIE);
}
void RV3028::disableTimerInterrupt()
{
clearBit(RV3028_CTRL2, CTRL2_TIE);
}
bool RV3028::readTimerInterruptFlag()
{
return readBit(RV3028_STATUS, STATUS_TF);
}
void RV3028::clearTimerInterruptFlag()
{
clearBit(RV3028_STATUS, STATUS_TF);
}
void RV3028::enableTimer()
{
setBit(RV3028_CTRL1, CTRL1_TE);
}
void RV3028::disableTimer()
{
clearBit(RV3028_CTRL1, CTRL1_TE);
}
/*********************************
Periodic Time Update Interrupt
********************************/
void RV3028::enablePeriodicUpdateInterrupt(bool every_second, bool enable_clock_output)
{
disablePeriodicUpdateInterrupt();
clearPeriodicUpdateInterruptFlag();
if (every_second)
{
clearBit(RV3028_CTRL1, CTRL1_USEL);
}
else
{ // every minute
setBit(RV3028_CTRL1, CTRL1_USEL);
}
setBit(RV3028_CTRL2, CTRL2_UIE);
//Clock output?
if (enable_clock_output)
setBit(RV3028_INT_MASK, IMT_MASK_CUIE);
else
clearBit(RV3028_INT_MASK, IMT_MASK_CUIE);
}
void RV3028::disablePeriodicUpdateInterrupt()
{
clearBit(RV3028_CTRL2, CTRL2_UIE);
}
bool RV3028::readPeriodicUpdateInterruptFlag()
{
return readBit(RV3028_STATUS, STATUS_UF);
}
void RV3028::clearPeriodicUpdateInterruptFlag()
{
clearBit(RV3028_STATUS, STATUS_UF);
}
/*********************************
Enable the Trickle Charger and set the Trickle Charge series resistor (default is 15k)
TCR_3K = 3kOhm
TCR_5K = 5kOhm
TCR_9K = 9kOhm
TCR_15K = 15kOhm
*********************************/
void RV3028::enableTrickleCharge(uint8_t tcr)
{
if (tcr > 3) return;
//Read EEPROM Backup Register (0x37)
uint8_t EEPROMBackup = readConfigEEPROM_RAMmirror(EEPROM_Backup_Register);
//Set TCR Bits (Trickle Charge Resistor)
EEPROMBackup &= EEPROMBackup_TCR_CLEAR; //Clear TCR Bits
EEPROMBackup |= tcr << EEPROMBackup_TCR_SHIFT; //Shift values into EEPROM Backup Register
//Write 1 to TCE Bit
EEPROMBackup |= 1 << EEPROMBackup_TCE_BIT;
//Write EEPROM Backup Register
writeConfigEEPROM_RAMmirror(EEPROM_Backup_Register, EEPROMBackup);
}
void RV3028::disableTrickleCharge()
{
//Read EEPROM Backup Register (0x37)
uint8_t EEPROMBackup = readConfigEEPROM_RAMmirror(EEPROM_Backup_Register);
//Write 0 to TCE Bit
EEPROMBackup &= ~(1 << EEPROMBackup_TCE_BIT);
//Write EEPROM Backup Register
writeConfigEEPROM_RAMmirror(EEPROM_Backup_Register, EEPROMBackup);
}
/*********************************
0 = Switchover disabled
1 = Direct Switching Mode
2 = Standby Mode
3 = Level Switching Mode
*********************************/
bool RV3028::setBackupSwitchoverMode(uint8_t val)
{
if (val > 3)return false;
bool success = true;
//Read EEPROM Backup Register (0x37)
uint8_t EEPROMBackup = readConfigEEPROM_RAMmirror(EEPROM_Backup_Register);
if (EEPROMBackup == 0xFF) success = false;
//Ensure FEDE Bit is set to 1
EEPROMBackup |= 1 << EEPROMBackup_FEDE_BIT;
//Set BSM Bits (Backup Switchover Mode)
EEPROMBackup &= EEPROMBackup_BSM_CLEAR; //Clear BSM Bits of EEPROM Backup Register
EEPROMBackup |= val << EEPROMBackup_BSM_SHIFT; //Shift values into EEPROM Backup Register
//Write EEPROM Backup Register
if (!writeConfigEEPROM_RAMmirror(EEPROM_Backup_Register, EEPROMBackup)) success = false;
return success;
}
/*********************************
Clock Out functions
********************************/
void RV3028::enableClockOut(uint8_t freq)
{
if (freq > 7) return; // check out of bounds
//Read EEPROM CLKOUT Register (0x35)
uint8_t EEPROMClkout = readConfigEEPROM_RAMmirror(EEPROM_Clkout_Register);
//Ensure CLKOE Bit is set to 1
EEPROMClkout |= 1 << EEPROMClkout_CLKOE_BIT;
//Shift values into EEPROM Backup Register
EEPROMClkout |= freq << EEPROMClkout_FREQ_SHIFT;
//Write EEPROM Backup Register
writeConfigEEPROM_RAMmirror(EEPROM_Clkout_Register, EEPROMClkout);
}
void RV3028::enableInterruptControlledClockout(uint8_t freq)
{
if (freq > 7) return; // check out of bounds
//Read EEPROM CLKOUT Register (0x35)
uint8_t EEPROMClkout = readConfigEEPROM_RAMmirror(EEPROM_Clkout_Register);
//Shift values into EEPROM Backup Register
EEPROMClkout |= freq << EEPROMClkout_FREQ_SHIFT;
//Write EEPROM Backup Register
writeConfigEEPROM_RAMmirror(EEPROM_Clkout_Register, EEPROMClkout);
//Set CLKIE Bit
setBit(RV3028_CTRL2, CTRL2_CLKIE);
}
void RV3028::disableClockOut()
{
//Read EEPROM CLKOUT Register (0x35)
uint8_t EEPROMClkout = readConfigEEPROM_RAMmirror(EEPROM_Clkout_Register);
//Clear CLKOE Bit
EEPROMClkout &= ~(1 << EEPROMClkout_CLKOE_BIT);
//Write EEPROM CLKOUT Register
writeConfigEEPROM_RAMmirror(EEPROM_Clkout_Register, EEPROMClkout);
//Clear CLKIE Bit
clearBit(RV3028_CTRL2, CTRL2_CLKIE);
}
bool RV3028::readClockOutputInterruptFlag()
{
return readBit(RV3028_STATUS, STATUS_CLKF);
}
void RV3028::clearClockOutputInterruptFlag()
{
clearBit(RV3028_STATUS, STATUS_CLKF);
}
//Returns the status byte
uint8_t RV3028::status(void)
{
return(readRegister(RV3028_STATUS));
}
void RV3028::clearInterrupts() //Read the status register to clear the current interrupt flags
{
writeRegister(RV3028_STATUS, 0);
}
/*********************************
FOR INTERNAL USE
********************************/
uint8_t RV3028::BCDtoDEC(uint8_t val)
{
return ((val / 0x10) * 10) + (val % 0x10);
}
// BCDtoDEC -- convert decimal to binary-coded decimal (BCD)
uint8_t RV3028::DECtoBCD(uint8_t val)
{
return ((val / 10) * 0x10) + (val % 10);
}
uint8_t RV3028::readRegister(uint8_t addr)
{
_i2cPort->beginTransmission(RV3028_ADDR);
_i2cPort->write(addr);
_i2cPort->endTransmission();
_i2cPort->requestFrom(RV3028_ADDR, (uint8_t)1);
if (_i2cPort->available()) {
return _i2cPort->read();
}
else {
return (0xFF); //Error
}
}
bool RV3028::writeRegister(uint8_t addr, uint8_t val)
{
_i2cPort->beginTransmission(RV3028_ADDR);
_i2cPort->write(addr);
_i2cPort->write(val);
if (_i2cPort->endTransmission() != 0)
return (false); //Error: Sensor did not ack
return(true);
}
bool RV3028::readMultipleRegisters(uint8_t addr, uint8_t * dest, uint8_t len)
{
_i2cPort->beginTransmission(RV3028_ADDR);
_i2cPort->write(addr);
if (_i2cPort->endTransmission() != 0)
return (false); //Error: Sensor did not ack
_i2cPort->requestFrom(RV3028_ADDR, len);
for (uint8_t i = 0; i < len; i++)
{
dest[i] = _i2cPort->read();
}
return(true);
}
bool RV3028::writeMultipleRegisters(uint8_t addr, uint8_t * values, uint8_t len)
{
_i2cPort->beginTransmission(RV3028_ADDR);
_i2cPort->write(addr);
for (uint8_t i = 0; i < len; i++)
{
_i2cPort->write(values[i]);
}
if (_i2cPort->endTransmission() != 0)
return (false); //Error: Sensor did not ack
return(true);
}
bool RV3028::writeConfigEEPROM_RAMmirror(uint8_t eepromaddr, uint8_t val)
{
bool success = waitforEEPROM();
//Disable auto refresh by writing 1 to EERD control bit in CTRL1 register
uint8_t ctrl1 = readRegister(RV3028_CTRL1);
ctrl1 |= 1 << CTRL1_EERD;
if (!writeRegister(RV3028_CTRL1, ctrl1)) success = false;
//Write Configuration RAM Register
writeRegister(eepromaddr, val);
//Update EEPROM (All Configuration RAM -> EEPROM)
writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_First);
writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_Update);
if (!waitforEEPROM()) success = false;
//Reenable auto refresh by writing 0 to EERD control bit in CTRL1 register
ctrl1 = readRegister(RV3028_CTRL1);
if (ctrl1 == 0x00)success = false;
ctrl1 &= ~(1 << CTRL1_EERD);
writeRegister(RV3028_CTRL1, ctrl1);
if (!waitforEEPROM()) success = false;
return success;
}
uint8_t RV3028::readConfigEEPROM_RAMmirror(uint8_t eepromaddr)
{
bool success = waitforEEPROM();
//Disable auto refresh by writing 1 to EERD control bit in CTRL1 register
uint8_t ctrl1 = readRegister(RV3028_CTRL1);
ctrl1 |= 1 << CTRL1_EERD;
if (!writeRegister(RV3028_CTRL1, ctrl1)) success = false;
//Read EEPROM Register
writeRegister(RV3028_EEPROM_ADDR, eepromaddr);
writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_First);
writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_ReadSingle);
if (!waitforEEPROM()) success = false;
uint8_t eepromdata = readRegister(RV3028_EEPROM_DATA);
if (!waitforEEPROM()) success = false;
//Reenable auto refresh by writing 0 to EERD control bit in CTRL1 register
ctrl1 = readRegister(RV3028_CTRL1);
if (ctrl1 == 0x00)success = false;
ctrl1 &= ~(1 << CTRL1_EERD);
writeRegister(RV3028_CTRL1, ctrl1);
if (!success) return 0xFF;
return eepromdata;
}
bool RV3028::writeUserEEPROM(uint8_t eepromaddr, uint8_t val)
{
bool success = waitforEEPROM();
//Disable auto refresh by writing 1 to EERD control bit in CTRL1 register
uint8_t ctrl1 = readRegister(RV3028_CTRL1);
ctrl1 |= 1 << CTRL1_EERD;
if (!writeRegister(RV3028_CTRL1, ctrl1)) success = false;
//Write addr to EEADDR
writeRegister(0x25, eepromaddr);
//Write value to EEDATA
writeRegister(0x26, val);
//Update EEPROM (All Configuration RAM -> EEPROM)
writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_First);
writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_WriteSingle);
if (!waitforEEPROM()) success = false;
//Reenable auto refresh by writing 0 to EERD control bit in CTRL1 register
ctrl1 = readRegister(RV3028_CTRL1);
if (ctrl1 == 0x00)success = false;
ctrl1 &= ~(1 << CTRL1_EERD);
writeRegister(RV3028_CTRL1, ctrl1);
if (!waitforEEPROM()) success = false;
return success;
}
uint8_t RV3028::readUserEEPROM(uint8_t eepromaddr)
{
bool success = waitforEEPROM();
//Disable auto refresh by writing 1 to EERD control bit in CTRL1 register
uint8_t ctrl1 = readRegister(RV3028_CTRL1);
ctrl1 |= 1 << CTRL1_EERD;
if (!writeRegister(RV3028_CTRL1, ctrl1)) success = false;
//Read EEPROM Register
writeRegister(RV3028_EEPROM_ADDR, eepromaddr);
writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_First);
writeRegister(RV3028_EEPROM_CMD, EEPROMCMD_ReadSingle);
if (!waitforEEPROM()) success = false;
uint8_t eepromdata = readRegister(RV3028_EEPROM_DATA);
if (!waitforEEPROM()) success = false;
//Reenable auto refresh by writing 0 to EERD control bit in CTRL1 register
ctrl1 = readRegister(RV3028_CTRL1);
if (ctrl1 == 0x00)success = false;
ctrl1 &= ~(1 << CTRL1_EERD);
writeRegister(RV3028_CTRL1, ctrl1);
if (!success) return 0xFF;
return eepromdata;
}
//True if success, false if timeout occured
bool RV3028::waitforEEPROM()
{
unsigned long timeout = millis() + 500;
while ((readRegister(RV3028_STATUS) & 1 << STATUS_EEBUSY) && millis() < timeout);
return millis() < timeout;
}
void RV3028::reset()
{
setBit(RV3028_CTRL2, CTRL2_RESET);
}
void RV3028::setBit(uint8_t reg_addr, uint8_t bit_num)
{
uint8_t value = readRegister(reg_addr);
value |= (1 << bit_num); //Set the bit
writeRegister(reg_addr, value);
}
void RV3028::clearBit(uint8_t reg_addr, uint8_t bit_num)
{
uint8_t value = readRegister(reg_addr);
value &= ~(1 << bit_num); //Clear the bit
writeRegister(reg_addr, value);
}
bool RV3028::readBit(uint8_t reg_addr, uint8_t bit_num)
{
uint8_t value = readRegister(reg_addr);
value &= (1 << bit_num);
return value;
}
Last edited: