#!/usr/bin/python
__author__ = "D.Qendri"
__copyright__ = "Copyright 2015 Sensorian"
__license__ = "GPL V3"
__version__ = "1.0"
import sys
import smbus
import time
bus = smbus.SMBus(1)
MCP79410address = 0x6f # SRAM board address
MCP79410EEPROMaddress = 0x57 # EEPROM address
EEPROM_WRITE = 0xae # DEVICE ADDR for EEPROM (writes)
EEPROM_READ = 0xaf # DEVICE ADDR for EEPROM (reads)
RTCC_WRITE = 0xde # DEVICE ADDR for RTCC MCHP (writes)
RTCC_READ = 0xdf # DEVICE ADDR for RTCC MCHP (reads)
SRAM_PTR = 0x20 # pointer of the SRAM area (RTCC)
EEPROM_SR = 0xff # STATUS REGISTER in the EEPROM
SEC = 0x00 # address of SECONDS register
MIN = 0x01 # address of MINUTES register
HOUR = 0x02 # address of HOURS register
DAY = 0x03 # address of DAY OF WK register
STAT = 0x03 # address of STATUS register
DATE = 0x04 # address of DATE register
MNTH = 0x05 # address of MONTH register
YEAR = 0x06 # address of YEAR register
CTRL = 0x07 # address of CONTROL register
CAL = 0x08 # address of CALIB register
ULID = 0x09 # address of UNLOCK ID register
ALM0SEC = 0x0a # address of ALARM0 SEC register
ALM0MIN = 0x0b # address of ALARM0 MIN register
ALM0HR = 0x0c # address of ALARM0 HOUR register
ALM0WDAY = 0x0d # address of ALARM0 CONTR register
ALM0DAT = 0x0e # address of ALARM0 DATE register
ALM0MTH = 0x0f # address of ALARM0 MONTH register
ALM1SEC = 0x11 # address of ALARM1 SEC register
ALM1MIN = 0x12 # address of ALARM1 MIN register
ALM1HR = 0x13 # address of ALARM1 HOUR register
ALM1WDAY = 0x14 # address of ALARM1 CONTR register
ALM1DAT = 0x15 # address of ALARM1 DATE register
ALM1MTH = 0x16 # address of ALARM1 MONTH register
PWRDNMIN = 0x18 # address of T_SAVER MIN(VDD->BAT)
PWRDNHOUR = 0x19 # address of T_SAVER HR (VDD->BAT)
PWRDNDATE = 0x1a # address of T_SAVER DAT(VDD->BAT)
PWRDNMTH = 0x1b # address of T_SAVER MTH(VDD->BAT)
PWRUPMIN = 0x1c # address of T_SAVER MIN(BAT->VDD)
PWRUPHOUR = 0x1d # address of T_SAVER HR (BAT->VDD)
PWRUPDATE = 0x1e # address of T_SAVER DAT(BAT->VDD)
PWRUPMTH = 0x1f # address of T_SAVER MTH(BAT->VDD)
##################GLOBAL CONSTANTS RTCC - INITIALIZATION##################
PM = 0x20 # post-meridian bit (HOUR)
HOUR_FORMAT = 0x40 # Hour format
OUT_PIN = 0x80 # = b7 (CTRL)
SQWEN = 0x40 # SQWE = b6 (CTRL)
ALM_NO = 0x00 # no alarm activated (CTRL)
ALM_0 = 0x10 # ALARM0 is activated (CTRL)
ALM_1 = 0x20 # ALARM1 is activated (CTRL)
ALM_01 = 0x30 # both alarms are activated (CTRL)
MFP_01H = 0x00 # MFP = SQVAW(01 HERZ) (CTRL)
MFP_04K = 0x01 # MFP = SQVAW(04 KHZ) (CTRL)
MFP_08K = 0x02 # MFP = SQVAW(08 KHZ) (CTRL)
MFP_32K = 0x03 # MFP = SQVAW(32 KHZ) (CTRL)
MFP_64H = 0x04 # MFP = SQVAW(64 HERZ) (CTRL)
ALMx_POL = 0x80 # polarity of MFP on alarm (ALMxCTL)
ALMxC_SEC = 0x00 # ALARM compare on SEC (ALMxCTL)
ALMxC_MIN = 0x10 # ALARM compare on MIN (ALMxCTL)
ALMxC_HR = 0x20 # ALARM compare on HOUR (ALMxCTL)
ALMxC_DAY = 0x30 # ALARM compare on DAY (ALMxCTL)
ALMxC_DAT = 0x40 # ALARM compare on DATE (ALMxCTL)
ALMxC_ALL = 0x70 # ALARM compare on all param(ALMxCTL)
ALMx_IF = 0x08 # MASK of the ALARM_IF (ALMxCTL)
OSCON = 0x20 # State of the oscillator(running or not)
VBATEN = 0x08 # Enable battery for back-up
OSCRUN = 0x20 # State of the oscillator(running or not)
PWRFAIL = 0x10
VBATEN = 0x08 # Enable battery for back-up
VBAT_DIS = 0x37 # Disable battery back-up
START_32KHZ = 0x80 # Start crystal: ST = b7 (SEC)
LP = 0x20 # Mask for the leap year bit(MONTH REG)
HOUR_12 = 0x40 # 12 hours format (HOUR)
LPYR = 0x20
##################################################################################
ALM1MSK2 = 0x40
ALM1MSK1 = 0x20
ALM1MSK0 = 0x10
ALM0MSK2 = 0x40
ALM0MSK1 = 0x20
ALM0MSK0 = 0x10
[docs]class RTCC_Struct(object):
"""RTCC time object."""
def __init__(self,sec,min,hour,weekday,date,month,year):
self.sec = sec
self.min = min
self.hour = hour
self.weekday = weekday
self.date = date
self.month = month
self.year = year
[docs]class Alarm:
"""
Class representation of Alarms for RTCC chip.
"""
ZERO = 1
ONE = 2
[docs]class PMAM_t:
"""
Class representation for PM/AM settings for RTCC chip.
"""
AMT = 0
PMT = 1
[docs]class Match:
"""
Class representation of Alarm match settings for RTCC chip.
"""
SECONDS_MATCH = 0
MINUTES_MATCH = 1
HOURS_MATCH = 2
WEEKDAY_MATCH = 3
DATE_MATCH = 4
FULL_DATE_MATCH = 5
[docs]class Polarity:
"""
Class representation of Polarity settings for RTCC chip.
"""
LOWPOL = 0
HIGHPOL = 1
[docs]class Mode:
"""
Class representation of Mode settings for RTCC chip.
"""
GPO = 0
ALARM_INTERRUPT =1
SQUARE_WAVE = 2
[docs]class MCP79410(object):
"""
Representation of an MCP79410 RTCC chip.
"""
def __init__(self):
"""
Initializes the RTCC object with the current local time.
:param none:
:returns: none
"""
self._address = MCP79410address
localtime = time.localtime(time.time())
rtc_time=RTCC_Struct(localtime.tm_sec,localtime.tm_min,localtime.tm_hour,localtime.tm_wday,localtime.tm_mday,localtime.tm_mon,(localtime.tm_year-2000))
self.SetHourFormat(24)
self.EnableVbat() #Enable the battery back-up
self.EnableOscillator() #Start RTC clock
self.SetTime(rtc_time)
[docs] def EnableOscillator(self):
"""
Enables the clock oscillator. This must be enabled in order for the RTCC to run.
:param none:
:returns: none
"""
ST_bit = self.readRegister(DAY); #Read day + OSCON bit
ST_bit = ST_bit | START_32KHZ;
self.writeRegister(SEC,ST_bit)
[docs] def DisableOscillator(self):
"""
Disables the clock oscillator.The RTCC does not operate once the oscillator is stopped.
:param none:
:returns: none
"""
ST_bit = self.readRegister(DAY); #Read day + OSCON bit
ST_bit = ST_bit & ~START_32KHZ;
self.writeRegister(SEC,ST_bit)
[docs] def IsRunning(self):
"""
Checks if the on-chip clock is running.
:returns: ClockStatus - TRUE if clock is running , FALSE otherwise.
"""
mask = self.readRegister(DAY);
if((mask & OSCRUN) == OSCRUN): #If oscillator = already running, do nothing.
return True
else:
return False
[docs] def GetTime(self):
"""
Returns a time object with the current time from the RTCC.
:param none:
:returns: current_time - This is an RTCC class object that contains the time.
"""
seconds = self.bcd2dec( self.readRegister(SEC) &(~START_32KHZ)) # mask out ST bit
minutes = self.bcd2dec( self.readRegister(MIN))
hour_t = self.readRegister(HOUR)
if((hour_t & HOUR_12) == HOUR_12):
(hour_t & 0x1F)
else:
(hour_t & 0x3F)
hours = self.bcd2dec(hour_t)
weekday = self.bcd2dec( self.readRegister(DAY) & ~(OSCRUN|PWRFAIL|VBATEN))
date = self.bcd2dec( self.readRegister(DATE))
month = self.bcd2dec( self.readRegister(MNTH) & ~(LPYR))
year = self.bcd2dec( self.readRegister(YEAR))
rtc_time=RTCC_Struct(seconds,minutes,hours,weekday,date,month,year)
return rtc_time
[docs] def SetTime(self,RTCtime):
"""
Initializes the RTCC with a specific time contained in the time structure.
:param RTCtime: RTCC class object that contains the time to be set.
:returns: none
"""
sec = self.readRegister(SEC) #Seconds
min = 0 #Minutes
hour = self.readRegister(HOUR) #Hours
weekday = self.readRegister(DAY) #Weekday
date = 0; #Date
month = self.readRegister(MNTH) #Month
year = 0 #Year
if((sec & START_32KHZ) == START_32KHZ): #Seconds register
sec = self.dec2bcd(RTCtime.sec)| START_32KHZ
else:
sec = self.dec2bcd(RTCtime.sec)
min = self.dec2bcd(RTCtime.min) #Minutes
if(( hour & HOUR_12) == HOUR_12): #Hour register
hour = self.dec2bcd(RTCtime.hour) | HOUR_12
else:
hour = self.dec2bcd(RTCtime.hour)
if(( hour & PM) == PM):
hour = hour | PM
weekday &= 0x38 #Mask 3 upper bits
weekday |= self.dec2bcd(RTCtime.weekday) #Weekday
date = self.dec2bcd(RTCtime.date) #Date
if((month & LPYR) == LPYR): #Month
month = self.dec2bcd(RTCtime.month) | LPYR
else:
month = self.dec2bcd(RTCtime.month)
year = self.dec2bcd(RTCtime.year) #Year
self.writeRegister(SEC,sec)
self.writeRegister(MIN,min)
self.writeRegister(HOUR,hour)
self.writeRegister(DAY,weekday)
self.writeRegister(DATE,date)
self.writeRegister(MNTH,month)
self.writeRegister(YEAR,year)
[docs] def SetPMAM(self, meridian):
"""
Sets the meridian mode.
:param meridian: PMAM_t object ,either PMT or AMT settings
:returns: none
"""
self.DisableOscillator() #Diable clock
PMAM_bit = self.readRegister(HOUR) #Read meridian bit
if(meridian == PMAM_t.AMT):
PMAM_bit &= ~PM #Set AM
else:
PMAM_bit |= PM #Set PM
self.writeRegister(HOUR,PMAM_bit) #Update PM/AM meridian bit
self.EnableOscillator(); #Enable clock
[docs] def GetPMAM(self):
"""
Get AM or PM for 12 hour format.
:returns: meridian - PM/AM flag
"""
bHourBuffer = self.readRegister(HOUR)
return (bHourBuffer & 0x20)
[docs] def EnableAlarm(self, alarm,config): #dest = RTCC_ALM0/RTCC_ALM1
"""
Enables the chosen alarm.
:param alarm: Alarm object , One of the two alarms, ZERO or ONE.
:param config:
:returns: none
"""
control = self.readRegister(CTRL)
if (alarm == Alarm.ZERO):
ALARMREG = (control | ALM_0)
else:
ALARMREG = (control | ALM_1)
self.writeRegister(CTRL,control) #enable alarm control bit
day = self.readRegister(control) #Set address to the alarm config/day register
AlmarmCfg = ((day & 0x07) | (config & 0xF0))
self.writeRegister(ALARMREG,AlmarmCfg)
[docs] def DisableAlarm(self, alarm): #alarm = RTCC_ALM0/RTCC_ALM1
"""
Disables one of the two alarms.
:param alarm: Alarm object , One of the two alarms, ZERO or ONE.
:returns: none
"""
temp = self.readRegister(CTRL) #read control register
if (alarm == Alarm.ZERO):
cfg = (temp & 0xEF) #disables either Alrm1 or Alrm0
else:
cfg = (temp & 0xDF)
self.writeRegister(CTRL,cfg) #update control register
[docs] def GetAlarmStatus(self,alarm):
"""
Gets the status of the alarm interrupt flag.
:param alarm: One of the two alarms, ZERO or ONE.
:returns: status - TRUE if alarm enabled , FALSE if disabled.
"""
if(alarm == Alarm.ZERO):
temp = self.readRegister(ALM0WDAY) #Read WKDAY register for ALRAM 0
else:
temp = self.readRegister(ALM1WDAY) #Read WKDAY register for ALRAM 1
if ((temp & ALMx_IF) == ALMx_IF):
return True
else:
return False
[docs] def ClearInterruptFlag(self,alarm):
"""
Selects which alarm Interrupt flag should be cleared.
:param alarm: One of the two alarms
:returns: none
"""
if (alarm == Alarm.ZERO):
temp = self.readRegister(ALM0WDAY) #Read WKDAY register for ALRAM 0
temp &= (~ALMx_IF) #Clear 4-th bit
self.writeRegister(ALM0WDAY,temp) #Enable backup battery mode
else:
temp = self.readRegister(ALM1WDAY) #Read WKDAY register for ALRAM 1
temp &= (~ALMx_IF) #Clear 4-th bit
self.writeRegister(ALM1WDAY,temp) #Enable backup battery mode
[docs] def SetAlarmTime(self,time,alarm):
"""
Sets the alarm time for one of the two alarms.
:param time: RTCC Class encapsulating time settings.
:param alarm: One of the two alarms,either alarm ZERO or ONE.
:returns: none
"""
sec = self.dec2bcd(time.sec);
min = self.dec2bcd(time.min);
hour = self.dec2bcd(time.hour);
weekday = self.dec2bcd(time.weekday);
date = self.dec2bcd(time.date);
month = self.dec2bcd(time.month);
if(alarm == Alarm.ZERO):
self.writeRegister(ALM0SEC,sec)
self.writeRegister(ALM0MIN,hour)
self.writeRegister(ALM0HR,weekday)
self.writeRegister(ALM0WDAY,date)
self.writeRegister(ALM0DAT,month)
self.writeRegister(ALM0MTH,month)
else:
self.writeRegister(ALM1SEC,sec|START_32KHZ)
self.writeRegister(ALM1MIN,min)
self.writeRegister(ALM1HR,hour)
self.writeRegister(ALM1WDAY,weekday)
self.writeRegister(ALM1DAT,date)
self.writeRegister(ALM1MTH,month)
[docs] def GetAlarmTime(self,time,alarm):
"""
Get the alarm time for one of the two alarms.
:param time: RTCC object
:param alarm: One of the two alarms
:returns: alarm time
"""
seconds = self.readRegister(ALARMREG,sec)
hours = self.readRegister(ALARMREG,hour)
AlarmDay = self.readRegister(ALARMREG,day)
AlarmDate = self.readRegister(ALARMREG,date)
alarmTime=RTCC_Struct(seconds,minutes,hours,AlarmDay,AlarmDate,0,0)
return alarmTime
[docs] def SetAlarmMFPPolarity(self,MFP_pol,alarm):
"""
Get the alarm time on the reg.
:param MFP_pol: Multi function pin polarity
:param alarm: One of the two alarms
:returns: none
"""
Polarity_bit = 0;
if(alarm == Alarm.ZERO):
Polarity_bit = self.readRegister(ALM0WDAY) #Read hour format bit
else:
Polarity_bit = self.readRegister(ALM1WDAY) #Read hour format bit
if(MFP_pol == Polarity.LOWPOL):
Polarity_bit &= ~ALMx_POL #Set MFP LOW
else:
Polarity_bit |= ALMx_POL #Set MFP HIGH
if(alarm == Alarm.ZERO):
self.writeRegister(ALM0WDAY,Polarity_bit) #Update polarity bit for Alarm 0
else:
self.writeRegister(ALM1WDAY,Polarity_bit) #Update polarity bit for Alarm 1
[docs] def SetAlarmMatch(self, match,alarm):
"""
Get the alarm time on the reg.
:param match: second, hour or mintue match from Match class
:param alarm: one of the two alarms
:returns: none
"""
AlarmRegister = 0
if(alarm == Alarm.ZERO):
AlarmRegister = ALM0WDAY
else:
AlarmRegister = ALM1WDAY
match_bits = self.readRegister(AlarmRegister)
if (match == Match.SECONDS_MATCH):
match_bits &= ~(ALM0MSK2|ALM0MSK1|ALM0MSK0)
self.writeRegister(AlarmRegister,match_bits) #Minutes match
elif (match == Match.MINUTES_MATCH):
match_bits |= ALM0MSK0
self.writeRegister(AlarmRegister,match_bits) #Minutes match
elif (match == Match.HOURS_MATCH):
match_bits |= ALM0MSK1
self.writeRegister(AlarmRegister,match_bits) #Hours match
elif (match == Match.WEEKDAY_MATCH):
match_bits |= ALM0MSK1|ALM0MSK0
self.writeRegister(AlarmRegister,match_bits) #Day of week match
elif (match == Match.DATE_MATCH):
match_bits |= ALM0MSK2
self.writeRegister(AlarmRegister,match_bits) #Date match
elif (match == Match.FULL_DATE_MATCH):
match_bits |= ALM0MSK2|ALM0MSK1|ALM0MSK0
self.writeRegister(AlarmRegister,match_bits) #Sec, Minutes Hours, Date match
else :
match_bits |= ALM0MSK0
self.writeRegister(AlarmRegister,match_bits) #Minutes match
[docs] def SetMFP_Functionality(self,mode):
"""
This function sets the MFP pin mode.
:param mode: Mode of the MFP pin.
:returns: none
"""
MFP_bits = self.readRegister(CTRL)
if(mode == Mode.GPO): #For GPO clear SQWEN, ALM0EN, ALM1EN
MFP_bits &= ~(SQWEN|ALM_0|ALM_1)
self.writeRegister(CTRL,MFP_bits)
elif (mode == Mode.ALARM_INTERRUPT): #For ALARM Interrupts clear SQWEN and set either ALM0EN or ALM1EN
MFP_bits &= SQWEN
MFP_bits |= ALM_0
self.writeRegister(CTRL,MFP_bits)
elif (mode == Mode.SQUARE_WAVE) : #For SQUARE WAVE set SQWEN
MFP_bits &= ~(ALM_0|ALM_1)
MFP_bits |= SQWEN
self.writeRegister(CTRL,MFP_bits)
else: #ALARM Interrupts
MFP_bits &= SQWEN
MFP_bits |= ALM_0
self.writeRegister(CTRL,MFP_bits)
[docs] def SetMFP_GPOStatus(self,status):
"""
Sets the MFP output logic level when the pin is configured as GPO
:param status: Polarity of MFP pin , Asserted output state of MFP is a logic low level for LOW and opposite for HIGH
:returns: none
"""
gpo_bit = self.readRegister(CTRL) #General Purpose Output mode only available when (SQWEN = 0, ALM0EN = 0, and ALM1EN = 0):
if(status == LOW):
gpo_bit = OUT_PIN #MFP signal level is logic low
self.writeRegister(CTRL,gpo_bit)
else: #MFP signal level is logic high
gpo_bit |= OUT_PIN
self.writeRegister(CTRL,gpo_bit)
[docs] def CheckPowerFailure(self):
"""
Checks if there was a power failure.
:param none:
:returns: PowerFail - TRUE if there was a power failure, FALSE otherwise.
"""
PowerFailure_bit = self.readRegister(DAY); #Read meridian bit
PowerFail = 0
if((PowerFailure_bit & PWRFAIL) == PWRFAIL):
PowerFail = 1
else:
PowerFail = 0
PowerFailure_bit &= ~PWRFAIL #Clear Power failure bit
self.writeRegister(DAY,PowerFailure_bit) #Update PM/AM meridian bit
return PowerFail
[docs] def IsVBatEnabled(self):
"""
Check if the VBAT is enabled.
:param none:
:returns: status - True is battery mode is enabled , False otherwise.
"""
temp = self.readRegister(DAY) #The 3rd bit of the RTCC_RTCC day register controls VBATEN
if((temp & VBATEN) == VBATEN):
return True;
else:
return False;
[docs] def EnableVbat(self):
"""
Enables battery backup mode.
:param none:
:returns: none
"""
temp = self.readRegister(DAY) #The 3rd bit of the RTCC_RTCC day register controls VBATEN
temp = (temp | VBATEN) #Set 3rd bit to enable backup battery mode
self.writeRegister(DAY,temp) #Enable backup battery mode
[docs] def DisableVbat(self):
"""
Disables the backup battery functionality.
:param none:
:returns: none
"""
temp = self.readRegister(DAY) #The 3rd bit of the RTCC_RTCC day register controls VBATEN
temp = (temp & VBAT_DIS) #Clear 3rd bit to disable backup battery mode
self.writeRegister(DAY,temp) #Enable backup battery mode
[docs] def GetPowerUpTime(self):
"""
Returns the time the system powered up.
:param none:
:returns: powerup_time - Power up time structure.
"""
powerup_time = RTCC_Struct(0,0,0,0,0,0,0)
powerup_time.min = self.bcd2dec( self.readRegister(PWRUPMIN))
powerup_time.hour = self.bcd2dec( self.readRegister(PWRUPHOUR))
powerup_time.date = self.bcd2dec( self.readRegister(PWRUPDATE))
powerup_time.month = self.bcd2dec( self.readRegister(PWRUPMTH))
return powerup_time
[docs] def GetPowerDownTime(self):
"""
This function returns the power-down time of the system.
:param none:
:returns: powerdown_time - Power down time structure.
"""
powerdown_time = RTCC_Struct(0,0,0,0,0,0,0)
powerdown_time.min = self.bcd2dec( self.readRegister(PWRDNMIN))
powerdown_time.hour = self.bcd2dec( self.readRegister(PWRDNHOUR))
powerdown_time.date = self.bcd2dec( self.readRegister(PWRDNDATE))
powerdown_time.month = self.bcd2dec( self.readRegister(PWRDNMTH))
return powerdown_time
##################################################################################################
[docs] def dec2bcd(self,num):
"""
Convert the value from decimal to Binary Coded Decimal (BCD).
:param num: Number to convert to BCD
:returns: bcd - BCD representation of number.
"""
return ((num/10 * 16) + (num % 10))
[docs] def bcd2dec(self,num):
"""
Convert the value from Binary Coded Decimal (BCD) to Decimal.
:param num: Number to convert to decimal.
:returns: dec - Decimal representation of number.
"""
return ((num/16 * 10) + (num % 16))
[docs] def bcdtobin(self,num):
"""
Convert the value from BCD to binary.
:param num: Number to convert to binary.
:returns: bin - Binary representation of number.
"""
return (((num) & 0x0f) + ((num) >> 4) * 10)
[docs] def writeRegister(self,rtcc_reg,dat):
"""
Write a new value on the register.
:param rtcc_reg: Address of the register.
:param dat: Byte value to be written on the register.
:returns: none
"""
bus.write_byte_data(self._address, rtcc_reg, dat)
[docs] def readRegister(self,rtcc_reg):
"""
Read the value of the register.
:param rtcc_reg: Address of the register.
:returns: Register byte content
"""
result = bus.read_byte_data(self._address, rtcc_reg) & 0xFF
return result