Translate

Search this blog

Thursday, June 26, 2025

MCP7940M RTC library for AVR devices

The MCP7940M is a Real-Time Clock/Calendar (RTCC) with an I2C interface. This library is developed for AVR devices (tested on ATmega328PB) but it can easily be ported to other microcontroller types. The necessary I2C library is also included.

Here is some description from the datasheet:

The MCP7940M tracks time using internal counters for hours, minutes, seconds, days, months, years, and day of week. Alarms can be configured on all counters up to and including months. For usage and configuration, the MCP7940M supports I2C communications up to 400 kHz. 

The open-drain, multi-functional output can be configured to assert on an alarm match, to output a selectable frequency square wave, or as a general purpose output. The MCP7940M is designed to operate using a 32.768 kHz tuning fork crystal with external crystal load capacitors. On-chip digital trimming can be used to adjust for frequency variance caused by crystal tolerance and temperature.

Contents

 

Connecting MCP7940M

Connecting MCP7940M schematic

MCP7940M pinout

1 - X1: Quartz Crystal Input or External Oscillator Input

2 - X2: Quartz Crystal Output

3 - NC: Not Connected

4 - Vss: Ground

5 - SDA: Bidirectional Serial Data (I2C). Connect a pull-up resistor. Usually 10k.

6 - SCL: Serial Clock (I2C). Connect a pull-up resistor. Usually 10k.

7 - MFP: Open-drain multifunction pin. Connect a pull-up resistor. Usually 10k-100k. Can be configured as a GPIO controlled via I2C, or to change state whenever the alarm triggers or to output a pre-selected frequency.

8 - VCC: Power supply (1.8V to 5.5V). 

API

Configuration

The library uses the TWI0 module by default and can be changed in the header file. The MCP7940M serial address is 0xDE as defined in the datasheet. TWI speed is 100kHz by default and can be changed to 400kHz if needed.

Functions

Initialization

void MCP_Init(void)

Used to initialize the TWI (I2C) interface and to enable the oscillator bit in the RTCSEC register.

Set seconds

void MCP_SetSeconds(uint8_t seconds)

Set seconds.

seconds

Decimal value from 0 to 59.

Set minutes

void MCP_SetMinutes(uint8_t minutes)

Set minutes.

minutes

Decimal value from 0 to 59.

Set hours

void MCP_SetHours(uint8_t hours, uint8_t hr_format, uint8_t am_pm)

Set hours in a 12 or 24 format.

hours

Decimal value from 0 to 23 or 1 to 12.

hr_format

Hour format. Can be one of the available constants MCP_12H_FORMAT or MCP_24H_FORMAT.

am_pm

With 24 hour format, this can be 0. When 12 hour format is used, this indicates the AM/PM bit. Can be one of the available constants MCP_PM or MCP_AM.

Set day of the week

void MCP_SetWeekday(Day weekday)

Week day.

weekday

A value from 1 to 7. The representation is user-defined. The following enumeration is also available to be able to use name of the day instead of numbers.

typedef enum DAY {
    Monday = 1,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday,
    INCORRECTDATEFORMAT
} Day;
 

Set day of the month

void MCP_SetMonthDay(uint8_t day)

Day of the month.

day

Decimal value from 1 to 31.

Set month

void MCP_SetMonth(Month month)

Month of the year.

month

Value from 1 to 12. The following enumeration is also available to be able to use name of the month instead of numbers.

typedef enum MONTH {
    January = 1, 
    February, 
    March, 
    April, 
    May, 
    June, 
    July, 
    August, 
    September, 
    October, 
    November, 
    December 
} Month;
 

Set year

void MCP_SetYear(uint8_t year)

The year in a two digit format.

year

A value from 0 to 99 in a two digit format. For example year 2025 would be 25.

Read seconds

uint8_t MCP_ReadSeconds(void)

Returns seconds.

Read minutes

uint8_t MCP_ReadMinutes(void)

Returns minutes.

Read hours

uint8_t MCP_ReadHours(uint8_t hr_format)

Returns hours in a 12 or 24 hour format.

hr_format

Hour format. Can be one of the available constants MCP_12H_FORMAT or MCP_24H_FORMAT

Read day of week

Day MCP_ReadWeekday(void)

Returns day of week from 1 to 7.

Read day of month

uint8_t MCP_ReadMonthDay(void)

Returns day of the month from 1 to 31.

Read month

Month MCP_ReadMonth(void)

Returns a number form 1 to 12 representing the month.

Read year

uint8_t MCP_ReadYear(void)

Returns the year in a two digit format. For example year 2025 will read as 25.

Check if leap year

bool MCP_IsLeapYear(void)

Returns 1 if year is a leap year.

Read oscillator status

uint8_t MCP_ReadOSCRUN(void)

Returns 1 if oscillator is enabled and running or 0 if oscillator has stopped or has been disabled. Could be used as a wrapper for every other function.

Usage:

if(MCP_ReadOSCRUN()){
    // Read time
}

Alarm

The MCP7940M has two alarm modules that can be configured individually. Most alarm functions require a pointer to one of the two available structure objects named alarm0 and alarm1.

Only one mask is possible for an alarm module so only one function type should be used.  For example doing:

MCP_setAlarmSecond();

MCP_setAlarmMinute();

The alarm will trigger only on set minute since that function was used last and configured the alarm mask. Use setAlarm() to set alarm mask for second, minute, hour, day of week, day of month and month all at the same time.

Turn alarm on/off

void MCP_alarmOnOff(ALARM_t* alarm, uint8_t state)

Turn alarm on/off using the enable bit and clear the interrupt flag. When both alarm modules are disabled, the MFP pin will be in GPIO mode, and the pin state is defined by the OUT bit.

alarm 

Pointer to an alarm object. Example: &alarm0 or &alarm1.

state 

ALARM_ON or ALARM_OFF.

Set alarm polarity

void MCP_setALMPOL(uint8_t bit)

Set Alarm Interrupt Output Polarity bit. MFP mode must be set to an interrupt type.

bit 

1 = Asserted output state of MFP is a logic high level (normal low, high when alarm on)      0 = Asserted output state of MFP is a logic low level

Set seconds alarm

void MCP_setAlarmSecond(ALARM_t* alarm, uint8_t second)

Set alarm to trigger at a given second. Triggers every minute.

Set minutes alarm

void MCP_setAlarmMinute(ALARM_t* alarm, uint8_t minute)

Set alarm to trigger at a given minute. Triggers every hour.

Set hours alarm

void MCP_setAlarmHour(ALARM_t* alarm, uint8_t hour, uint8_t hr_format, uint8_t am_pm)

Set alarm to trigger at a given hour. Triggers every day at the specified hour.

Set day of week alarm

void MCP_setAlarmWeekDay(ALARM_t* alarm, Day day)

Set alarm to trigger at a day of week. Triggers every week.

Set day of month alarm

void MCP_setAlarmMonthDay(ALARM_t* alarm, uint8_t day)

Set alarm to trigger at a day of month. Triggers every month.

Set alarm

void MCP_setAlarm(ALARM_t* alarm, uint8_t second, uint8_t minute, uint8_t hour, uint8_t hr_format, uint8_t am_pm, Day weekday, uint8_t monthday, Month month)

Set alarm to match second, minute, hour, day of week, day of month and month.

Check if alarm is on

bool MCP_isAlarmOn(ALARM_t* alarm)

Check if alarm is on by checking the interrupt flag. Returns 1 if alarm is on.

Set EXTOSC

void MCP_setEXTOSC(uint8_t bit)

Set or clear the External Oscillator Input bit (EXTOSC) in CONTROL register.

bit 

1 = Enable X1 pin to be driven by external 32.768 kHz source

0 = Disable external 32.768 kHz input

Set CRSTRIM

void MCP_setCRSTRIM(uint8_t bit)

Set or clear the Coarse Trim Mode Enable bit (CRSTRIM) in CONTROL register.  Coarse Trim mode results in the MCP7940M applying digital trimming every 64 Hz clock cycle.

From what I understand, this mode is for calibration only.

"With Coarse Trim mode enabled, the TRIMVAL<6:0> value has a drastic effect on timing.   Leaving the mode enabled during normal operation will likely result in inaccurate time.

By monitoring the MFP output frequency while in this mode, the user can easily observe the TRIMVAL<6:0>  value affecting the clock timing." 

bit 

1 = Enable Coarse Trim mode. If SQWEN = 1, MFP will output trimmed 64 Hz nominal clock signal. When SQWEN = 1, the only output frequency will be 64Hz.

0 = Disable Coarse Trim mode

Digital trimming

void MCP_setOSCTRIM(uint8_t sign, uint8_t trimval)

Apply digital trimming.

sign

Trim sign bit. Can be one of following constants: ADD_CLOCKSSUBTRACT_CLOCKS.

ADD_CLOCKS: 1 = Add clocks to correct for slow time

SUBTRACT_CLOCKS: 0 = Subtract clocks to correct for fast time

trimval

Oscillator Trim Value bits. Number obtained from datasheet formula. It will be shifted right by 1 to convert it to power of 2. When CRSTRIM = 1, divide the number by 128.

When CRSTRIM = 0:

1111111 = Add or subtract 254 clock cycles every minute

1111110 = Add or subtract 252 clock cycles every minute

0000010 = Add or subtract 4 clock cycles every minute

0000001 = Add or subtract 2 clock cycles every minute

0000000 = Disable digital trimming

When CRSTRIM = 1:

1111111 = Add or subtract 254 clock cycles 128 times per second

1111110 = Add or subtract 252 clock cycles 128 times per second

0000010 = Add or subtract 4 clock cycles 128 times per second

0000001 = Add or subtract 2 clock cycles 128 times per second

0000000 = Disable digital trimming

Configure MFP

void MCP_configMFP(uint8_t mode)

Configure the MFP (Multi Function Pin) output. Alarm interrupt modes are activated using the  functions for setting the alarm.

mode

Available constants:

MFP_MODE_GPIO:  0 - General Purpose Output (GPIO)

MFP_MODE_SQRWAVE: 4 - Square Wave Clock Output

Set MFP

void MCP_setMFPin(uint8_t pin_state)

Set the MFP (Multi Function Pin) output as high or low. Available only when in GPIO mode. Switching frequency is limited by the I2C speed.

Set MFP frequency

void MCP_setMFPfreq(uint8_t freq)

Set the MFP (Multi Function Pin) output frequency.  Available only when in MFP_MODE_SQRWAVE mode set using MCP_configMFP().

freq

Available constants:

MFP_SQWFS_1HZ: 1 Hz

MFP_SQWFS_4096HZ: 4096 Hz

MFP_SQWFS_8192HZ: 8192 Hz

MFP_SQWFS_32768HZ: 32768 Hz

Write SRAM

uint8_t MCP_writeSRAM(uint8_t addr, uint8_t data)

Write 1 byte to RTC SRAM. Memory is volatile.

addr

RAM address from 0 to 63 (64 bytes total). It will be mapped to RTC SRAM memory starting from 0x20 and ending at 0x5F.

data

One byte of data.

return

Number of bytes written. 0 means address out of range.

Read SRAM

uint8_t MCP_readSRAM(uint8_t addr)

Read 1 byte from RTC SRAM.

addr

RAM address from 0 to 63 (64 bytes total). It will be mapped to RTC SRAM memory starting from 0x20 and ending at 0x5F.

return

The byte read from RTC memory. 0 could mean address out of range or data is 0. 

Code example & tutorial

#include "MCP7940M.h"

int main(void){
    uint8_t seconds = 0;
    uint8_t minutes = 0;
    uint8_t hours = 0;
    uint8_t weekday = 0;
    uint8_t monthday = 0;
    uint8_t month = 0;
    uint8_t year = 0;
    bool sysinit = false;
	
    // Initialize the TWI (I2C) interface and enable the RTC oscillator.
    MCP_Init();

    // Set time. Values could be taken from a user input interface.
    MCP_SetSeconds(45);
    MCP_SetMinutes(10);
    MCP_SetHours(13, MCP_24H_FORMAT, 0);
    MCP_SetWeekday(Monday);
    MCP_SetMonthDay(26);
    MCP_SetMonth(June);
    MCP_SetYear(25);

    // SENARIO 1: Configure alarm: set MFP to change state when the alarm trigger.
    // Could drive an active type buzzer or trigger a pin interrupt
    // to wake a microcontroller from sleep mode.
    MCP_setALMPOL(1); // MFP normal low, hight when alarm is on
		
    // Set alarm to trigger at 7:10:30 (HH:MM:SS) 24 hour format
    // every Monday day 8 of the month in June.
    // To trigger an alarm, all values must match but most of the time
    // we want the alarm every day, so the application should update these every day
    // to trigger for the next day.
    MCP_setAlarm(&alarm0, 30, 10, 7, MCP_24H_FORMAT, 0, Monday, 8, June);
		
    // If minutes are irrelevant, an alarm can be set to trigger each day
    // at a certain hour using another register mask.
    MCP_setAlarmHour(&alarm0, 7, MCP_24H_FORMAT, 0); // every day at 7 in 24 hour format
		
    // SCENARIO 2: MFP pin is set to output a certain frequency
    // MFP mode set to square wave
    MCP_configMFP(MFP_MODE_SQRWAVE);
		
    // Frequency set to 4096 Hz. Can be used to measure and trim the crystal or
    // to control a passive buzzer for an alarm while a microcontroller pin
    // would modulate the transistor base to create beeps.
    MCP_setMFPfreq(MFP_SQWFS_4096HZ);
		
    // SCENARIO 3: use the MFP pin as a GPIO
    MCP_configMFP(MFP_MODE_GPIO);
    MCP_setMFPin(0); // set MFP low
    MCP_setMFPin(1); // set MFP high
		
		
    while(1){
        // Here we can ask the RTC for time values but
	// it should be done with a time delay considering the while loop
	// is very fast and the smaller time value is 1 second.
	// Another interrupt could be used to compound a variable and form
	// a time interval of roughly 1 second and then put the MCU to sleep.
	// Minutes, hours... could be updated every 60 seconds.
	// Example:
	//if(1 second elapsed){
	    // Check if RTC oscillator is working
	    if(MCP_ReadOSCRUN()){
	        // Read time
		seconds = MCP_ReadSeconds();
					
		// Update every minute when seconds reach 0 which is the next value
		// after 59 indicating the minute will change. If the minute will change
		// the hour could change and also the rest of time values.
		// sysinit variable is used to update the time in the first minute.
		if((sysinit == false) || (seconds == 0)){
		    sysinit = true;
		    minutes = MCP_ReadMinutes();
		    hours = MCP_ReadHours(MCP_24H_FORMAT);
		    weekday = MCP_ReadWeekday();
		    monthday = MCP_ReadMonthDay();
		    month = MCP_ReadMonth();
		    year = MCP_ReadYear();
		}
					
		// Check if alarm is on by checking the interrupt flag in the RTC register
		if(MCP_isAlarmOn(&alarm0)){
		    // Alarm is on. If seconds are greater than 5 the alarm will be turned off.
		    if(seconds > 5) MCP_alarmOnOff(&alarm0, ALARM_OFF);
		}
	    }
				
	//}
    }
		
    return 1;
}

Links

v1.0 MCP7940M library Folder includes:
MCP7940M, twi
Changelog
v1.0 (2025-06-25)
Public release under GNU GPL v3 license.

No comments:

Post a Comment