SAMG55 TWI interfacing MCP79411

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi

i'm quite new in this Atmel MCU world, and i've made a project based on ATSAMG55J19 MCU, programmed with Atmel Studio and ASF 3

 

Now i'm trying to add an external RTC clock, because internal SAMg55 rtc does not have a backup battery.

I have a MCP79411, connected via i2c, but there aren't any library suitable for this MCU that uses ASF TWI library. There are many Arduino implementation, but they uses Wire.h library, and i can't port it.

I made an attempt porting this simple "driver":  https://www.ccsinfo.com/forum/viewtopic.php?t=54105

 

Here is some code

static void i2c_start(void){
    static twi_options_t ext3_twi_options;

	flexcom_enable(FLEXCOM4);
	flexcom_set_opmode(FLEXCOM4, FLEXCOM_TWI);

	ext3_twi_options.master_clk = sysclk_get_cpu_hz();
	ext3_twi_options.speed = 100000;
	ext3_twi_options.smbus = 0;

	twi_master_init(TWI4, &ext3_twi_options);
}

// Init Real Time Clock
void rtc_Init(void)
{
	uint8_t seconds = 0;

	i2c_start();
	twi_write_byte(TWI4, ADDR_RTCC_WRITE);     // WR to RTC
	twi_write_byte(TWI4, ADDR_SEC);                // REG 0

	twi_write_byte(TWI4, ADDR_RTCC_READ);      // RD from RTC
	seconds = bcd2bin(i2c_read(0)); // Read current "seconds" in rtc
	//i2c_stop();
	//seconds &= 0x7F;
	seconds |= 0x80; //set to 1 bit 7 of seconds(ST) enabling oscillator

	delay_us(3);

	twi_write_byte(TWI4, ADDR_RTCC_WRITE);      // WR to RTC
	twi_write_byte(TWI4, ADDR_SEC);      // REG 0
	twi_write_byte(TWI4, bin2bcd(seconds) | 0x80);     // Start oscillator with current "seconds value

	twi_write_byte(TWI4, ADDR_RTCC_WRITE);      // WR to RTC
	twi_write_byte(TWI4, 0x07);      // Control Register
	twi_write_byte(TWI4, 0x80);      // Disable squarewave output pin
	//i2c_stop();
}

Then i tried  rtc_set_date_time(uint8_t day, uint8_t mth, uint8_t year, uint8_t dow, uint8_t hr, uint8_t min, uint8_t sec)

and 

void rtc_get_time(uint8_t &hr, uint8_t &min, uint8_t &sec)
{
	twi_write_byte(TWI4, ADDR_RTCC_WRITE);
	twi_write_byte(TWI4, 0x00);                     

	twi_write_byte(TWI4, ADDR_RTCC_READ);
	sec = bcd2bin(twi_read_byte(TWI4) & 0x7f);    //0x7f b01111111
	min = bcd2bin(twi_read_byte(TWI4) & 0x7f);    //0x7f
	hr  = bcd2bin(twi_read_byte(TWI4) & 0x3f);   //0x3f b00111111
	//i2c_stop();
}

But i always get "0" bytes.

i could not understand the correct way to open communication and read bytes from i2c. 

 

The only reference i found is  http://asf.atmel.com/docs/latest/sam.drivers.twi.twi_eeprom_example.samg53_xplained_pro/html/index.html but it seems to be a very different type of communication.

 

Thanks to everyone could help or share some helpful code sample

 

 

PS: i only use RTC module to read current time after power failure, then i'll use internal RTC. No need to write specific data in EEPROM or setting alarms.

 

This topic has a solution.
Last Edited: Mon. Feb 10, 2020 - 10:10 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

The EEPROM example (which is also an ASF3 example project for SAMG55 in Atmel Studio) is in fact not a different type of communication, you could try modifying the structs used. Compare EEPROM writing

	/* Configure the data packet to be transmitted */
	packet_tx.chip        = AT24C_ADDRESS;
	packet_tx.addr[0]     = EEPROM_MEM_ADDR >> 8;
	packet_tx.addr[1]     = EEPROM_MEM_ADDR;
	packet_tx.addr_length = EEPROM_MEM_ADDR_LENGTH;
	packet_tx.buffer      = (uint8_t *) test_data_tx;
	packet_tx.length      = TEST_DATA_LENGTH;

with RTC write (when writing all seven clock registers from a buffer called buf):

	uint8_t buf[7]; // Fill this with RTC clock data for all seven registers
	/* Configure the data packet to be transmitted */
	packet_tx.chip        = RTC_ADDR;
	packet_tx.addr[0]     = 0; // RTCSEC
 	packet_tx.addr_length = 1;
	packet_tx.buffer      = buf;
	packet_tx.length      = sizeof(buf);

and very similar for reading 

	/* Configure the data packet to be received */
	packet_rx.chip        = RTC_ADDR;
	packet_rx.addr[0]     = 0; // RTCSEC
	packet_rx.addr_length = 1;
	packet_rx.buffer      = buf;
	packet_rx.length      = sizeof(buf);

RTC_ADDR should be the 7-bit address, 0x6F.

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@ Lajon

Thank you for the big hint.

7 bit address was a point.

 

I succesfully set time, adapting code with the instruction you provided. 

I used a simplified library as source for the commands:  https://os.mbed.com/users/euxton/code/microbit-RTCC-MCP7941X//file/92208e3aae5a/main.cpp/

 

My problem now is that RTC oscillator seems disabled, so i read always the same time without progress.

 

Here is my code

/// 7 bits
#define RTC_ADDR 0x6F
char config_2[8];
char tim_read[8];

//...

static void setConfig(void){
    config_2[0] = tim_read[0] | 0x80;  // bitwise OR sets Start osc bit = 1
	config_2[1] = tim_read[1];
	config_2[2] = tim_read[2];
	config_2[3] = tim_read[3];
	config_2[4] = tim_read[4];
	config_2[5] = tim_read[5];
	config_2[6] = tim_read[6];
	config_2[7] = 0x00;     // control b3 - extosc = 0
}

static void initialize(void){
	uint8_t buf[7]; // Fill this with RTC clock data for all seven registers
	// read stored time data
    config_t[0] = 0x00; //reset pointer reg to '00'

    //  Set up config to read the time and set the control bits
	//i2c.write(addr, config_t, 1); // write address 00
	packet_tx.chip        = RTC_ADDR;
	packet_tx.addr[0]     = 0; // RTCSEC
	packet_tx.addr_length = 1;
	packet_tx.buffer      = config_t;
	packet_tx.length      = 1;

	twi_master_write(TWI4, &packet_tx);

	delay_ms(250);

	//
	//i2c.read(addr, tim_read, 7); //read time ss mm hh  from r1, r2, r3
	packet_rx.chip        = RTC_ADDR;
	packet_rx.addr[0]     = 0; // RTCSEC
	packet_rx.addr_length = 1;
	packet_rx.buffer      = tim_read;
	packet_rx.length      = sizeof(tim_read);
	twi_master_read(TWI4, &packet_rx);

	delay_ms(250);

    setConfig();                 //puts RTCC data into config array from tim_read array

    // write the config data
    //i2c.write(addr, config_t, 9);  // write the config data back to the RTCC module
	packet_tx.chip        = RTC_ADDR;
	packet_tx.addr[0]     = 0; // RTCSEC
	packet_tx.addr_length = 1;
	packet_tx.buffer      = config_2;
	packet_tx.length      = sizeof(config_2);

	twi_master_write(TWI4, &packet_tx);
}

static void write_time(void){
	// re-calculate mins
	mins = 8;		//ORE 10:08
	mins = mins%60;
	ch_mins = 16*(mins/10) + mins%10;
	MSN = msn(ch_mins);
	LSN = lsn(ch_mins);
	tim_read[1] = ch_mins;
	inc_mins = 0;
	//write the data back to RTCC
	setConfig();

	//i2c.write(addr, config_t, 9);

	/* Configure the data packet to be transmitted */
	packet_tx.chip        = RTC_ADDR;
	packet_tx.addr[0]     = 0; // RTCSEC
	packet_tx.addr_length = 1;
	packet_tx.buffer      = config_2;
	packet_tx.length      = sizeof(config_2);

	twi_master_write(TWI4, &packet_tx);

	//Display and set hours
	//hrs = bcd2dec(tim_read[2]);

	// re-calculate hrs
	hrs = 10;		//ORE 10:08
	hrs = hrs%24;
	ch_hrs = 16*(hrs/10) + hrs%10;
	MSN = msn(ch_hrs);
	LSN = lsn(ch_hrs);

	tim_read[2] = ch_hrs;
	inc_hr = 0;
	//write the data back to RTCC
	setConfig();

	/* Configure the data packet to be transmitted */
	packet_tx.chip        = RTC_ADDR;
	packet_tx.addr[0]     = 0; // RTCSEC
	packet_tx.addr_length = 1;
	packet_tx.buffer      = config_2;
	packet_tx.length      = sizeof(config_2);

	twi_master_write(TWI4, &packet_tx);
}

static void read_time(void){
	config_t[0] = 0x00; //reset pointer reg to '00'
	// First Get the time
	//i2c.write(addr, config_t, 1); // write address 00
	packet_tx.chip        = RTC_ADDR;
	packet_tx.addr[0]     = 0; // RTCSEC
	packet_tx.addr_length = 1;
	packet_tx.buffer      = config_t;
	packet_tx.length      = 1;

	twi_master_write(TWI4, &packet_tx);
	delay_ms(250);

	uint8_t buf[7]; // Fill this with RTC clock data for all seven registers
	/* Configure the data packet to be received */
	packet_rx.chip        = RTC_ADDR;
	packet_rx.addr[0]     = 0; // RTCSEC
	packet_rx.addr_length = 1;
	packet_rx.buffer      = buf;
	packet_rx.length      = sizeof(buf);
	twi_master_read(TWI4, &packet_rx);

	for(uint8_t i = 0; i < sizeof(buf); i++){
		tim_read[i] = buf[i];
	}
}

void prova_read_ore(void){  //called from MAIN

	initialize();
	delay_ms(1000);
	//write_time();     //commented to see if time is permanent
	delay_ms(1000);
	read_time();

	while(1){
		printf("Reading time\n");
		printf("%d:%d:%d\n", bcd2dec(tim_read[2]), bcd2dec(tim_read[1]), bcd2dec(tim_read[0]));
		delay_ms(1000);
		read_time();
	}
} 

It seems that the first byte enables or not the oscillator, but it is not effective. Maybe there is a different way to send that single byte?

Last Edited: Mon. Feb 3, 2020 - 05:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You have this in read_time

    config_t[0] = 0x00; //reset pointer reg to '00'
    // First Get the time
    //i2c.write(addr, config_t, 1); // write address 00
    packet_tx.chip        = RTC_ADDR;
    packet_tx.addr[0]     = 0; // RTCSEC
    packet_tx.addr_length = 1;
    packet_tx.buffer      = config_t;
    packet_tx.length      = 1;

    twi_master_write(TWI4, &packet_tx);
    delay_ms(250);

It's overwriting RTCSEC with 0 so the RTC is no longer running. Just remove this.

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you again! 

Also in the same way, i checked byte 3 to enable backup battery:

config_2[3] = tim_read[3] | VBATEN;

 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I post some draft code if someone will search for this "library" in future.

 

/*
 * external_rtc.c
 *
 * Created: 27/01/2020 15:42:07
 *  Author: Edoardo
 */ 

#include "asf.h"
#include "conf_board_3in4out.h"
#include "external_rtc.h"
#include <time.h>
//#include <time_utils.h>
twi_packet_t packet_tx, packet_rx;

// helper functions to manipulate BCD and binary to integers
static int bcd2dec(char r_char)
{
	MSN = (r_char & 0xF0)/16;
	LSN = r_char & 0x0F;
	return(10*MSN + LSN);
}
static char msn(char tim)
{
	return (tim & 0xF0)/16;
}
static char lsn(char tim)
{
	return (tim & 0x0F);
}

#define RTC_ADDR 0x6F		// 7 bits
char config_t[10];
char config_2[8];
char tim_read[8];

#define  ADDR_EEPROM_WRITE 0xae       //  DEVICE ADDR for EEPROM (writes)
#define  ADDR_EEPROM_READ  0xaf       //  DEVICE ADDR for EEPROM (reads)
#define  ADDR_RTCC_WRITE   0xde       //  DEVICE ADDR for RTCC MCHP  (writes)
#define  ADDR_RTCC_READ    0xdf

#define  ADDR_UNIQUE_ID    0xf2

#define  SRAM_PTR          0x20       //  pointer of the SRAM area (RTCC)
#define  ADDR_EEPROM_SR    0xff       //  STATUS REGISTER in the  EEPROM

#define  ADDR_SEC          0x00       //  address of SECONDS      register
#define  ADDR_MIN          0x01       //  address of MINUTES      register
#define  ADDR_HOUR         0x02       //  address of HOURS        register
#define  ADDR_DAY          0x03       //  address of DAY OF WEEK  register
#define  ADDR_STAT         0x03       //  address of STATUS       register
#define  ADDR_DATE         0x04       //  address of DATE         register
#define  ADDR_MNTH         0x05       //  address of MONTH        register
#define  ADDR_YEAR         0x06       //  address of YEAR         register
#define  ADDR_CTRL         0x07       //  address of CONTROL      register
#define  ADDR_CAL          0x08       //  address of CALIB        register
#define  ADDR_ULID         0x09       //  address of UNLOCK ID    register

#define  ADDR_ALM0SEC      0x0a       //  address of ALARMO SEC   register
#define  ADDR_ALM0MIN      0x0b       //  address of ALARMO MIN   register
#define  ADDR_ALM0HR       0x0c       //  address of ALARMO HOUR  register
#define  ADDR_ALM0CTL      0x0d       //  address of ALARM0 CONTR register
#define  ADDR_ALM0DAT      0x0e       //  address of ALARMO DATE  register
#define  ADDR_ALM0MTH      0x0f       //  address of ALARMO MONTH register

#define  ADDR_ALM1SEC      0x11       //  address of ALARM1 SEC   register
#define  ADDR_ALM1MIN      0x12       //  address of ALARM1 MIN   register
#define  ADDR_ALM1HR       0x13       //  address of ALARM1 HOUR  register
#define  ADDR_ALM1CTL      0x14       //  address of ALARM1 CONTR register
#define  ADDR_ALM1DAT      0x15       //  address of ALARM1 DATE  register
#define  ADDR_ALM1MTH      0x16       //  address of ALARM1 MONTH register

#define  ADDR_SAVtoBAT_MIN 0x18       //  address of T_SAVER MIN(VDD->BAT)
#define  ADDR_SAVtoBAT_HR  0x19       //  address of T_SAVER HR (VDD->BAT)
#define  ADDR_SAVtoBAT_DAT 0x1a       //  address of T_SAVER DAT(VDD->BAT)
#define  ADDR_SAVtoBAT_MTH 0x1b       //  address of T_SAVER MTH(VDD->BAT)

#define  ADDR_SAVtoVDD_MIN 0x1c       //  address of T_SAVER MIN(BAT->VDD)
#define  ADDR_SAVtoVDD_HR  0x1d       //  address of T_SAVER HR (BAT->VDD)
#define  ADDR_SAVtoVDD_DAT 0x1e       //  address of T_SAVER DAT(BAT->VDD)
#define  ADDR_SAVtoVDD_MTH 0x1f       //  address of T_SAVER MTH(BAT->VDD)

#define  START_32KHZ       0x80       //  start crystal: ST = b7 (ADDR_SEC)
#define  LP                0x20       //  mask for the leap year bit(MONTH REG)
#define  HOUR_12           0x40       //  12 hours format   (ADDR_HOUR)
#define  PM                0x20       //  post-meridian bit (ADDR_HOUR)
#define  OUT_PIN           0x80       //  = b7 (ADDR_CTRL)
#define  SQWE              0x40       //  SQWE = b6 (ADDR_CTRL)
#define  ALM_NO            0x00       //  no alarm activated        (ADDR_CTRL)
#define  ALM_0             0x10       //  ALARM0 is       activated (ADDR_CTRL)
#define  ALM_1             0x20       //  ALARM1 is       activated (ADDR_CTRL)
#define  ALM_01            0x30       //  both alarms are activated (ADDR_CTRL)
#define  MFP_01H           0x00       //  MFP = SQVAW(01 HERZ)      (ADDR_CTRL)
#define  MFP_04K           0x01       //  MFP = SQVAW(04 KHZ)       (ADDR_CTRL)
#define  MFP_08K           0x02       //  MFP = SQVAW(08 KHZ)       (ADDR_CTRL)
#define  MFP_32K           0x03       //  MFP = SQVAW(32 KHZ)       (ADDR_CTRL)
#define  MFP_64H           0x04       //  MFP = SQVAW(64 HERZ)      (ADDR_CTRL)
#define  ALMx_POL          0x80       //  polarity of MFP on alarm  (ADDR_ALMxCTL)
#define  ALMxC_SEC         0x00       //  ALARM compare on SEC      (ADDR_ALMxCTL)
#define  ALMxC_MIN         0x10       //  ALARM compare on MIN      (ADDR_ALMxCTL)
#define  ALMxC_HR          0x20       //  ALARM compare on HOUR     (ADDR_ALMxCTL)
#define  ALMxC_DAY         0x30       //  ALARM compare on DAY      (ADDR_ALMxCTL)
#define  ALMxC_DAT         0x40       //  ALARM compare on DATE     (ADDR_ALMxCTL)
#define  ALMxC_ALL         0x70       //  ALARM compare on all param(ADDR_ALMxCTL)
#define  ALMx_IF           0x08       //  MASK of the ALARM_IF      (ADDR_ALMxCTL)
#define  OSCON             0x20       //  state of the oscillator(running or not)
#define  VBATEN            0x08       //  enable battery for back-up

static uint8_t bin2bcd(uint8_t binary_value)
{
	uint8_t temp;
	uint8_t retval;

	temp = binary_value;
	retval = 0;

	if(temp >= 10)
	{
		temp -= 10;
		retval += 0x10;
	}
	else
	{
		retval += temp;
		//break;
	}

	return(retval);
}

static uint8_t bcd2bin(uint8_t bcd_value)
{
	uint8_t temp;

	temp = bcd_value;
	temp >>= 1;
	temp &= 0x78;
	return(temp + (temp >> 2) + (bcd_value & 0x0f));
}

static void setConfig(void){
	config_2[0] = tim_read[0] | START_32KHZ;  // bitwise OR sets Start osc bit = 1
	config_2[1] = tim_read[1];
	config_2[2] = tim_read[2];

	//0x03h – Contains the BCD day. The range is 1-7.
	//Bit 3 is the VBATEN bit. If this bit is set, the
	//internal circuitry is connected to the VBAT pin
	//when VCC fails. If this bit is ‘0’ then the VBAT pin is
	//disconnected and the only current drain on the
	//external battery is the VBAT pin leakage.
	config_2[3] = tim_read[3] | VBATEN;

	config_2[4] = tim_read[4];
	config_2[5] = tim_read[5];
	config_2[6] = tim_read[6];
	config_2[7] = 0x00;     // control b3 - extosc = 0
}

static void initialize(void){
	uint8_t buf[7]; // Fill this with RTC clock data for all seven registers
	// read stored time data
	config_t[0] = 0x00; //reset pointer reg to '00'

	//  Set up config to read the time and set the control bits
	//i2c.write(addr, config_t, 1); // write address 00
	packet_tx.chip        = RTC_ADDR;
	packet_tx.addr[0]     = 0; // RTCSEC
	packet_tx.addr_length = 1;
	packet_tx.buffer      = config_t;
	packet_tx.length      = 1;

	twi_master_write(TWI4, &packet_tx);

	delay_ms(250);		

	//
	//i2c.read(addr, tim_read, 7); //read time ss mm hh  from r1, r2, r3
	packet_rx.chip        = RTC_ADDR;
	packet_rx.addr[0]     = 0; // RTCSEC
	packet_rx.addr_length = 1;
	packet_rx.buffer      = tim_read;
	packet_rx.length      = sizeof(tim_read);
	twi_master_read(TWI4, &packet_rx);

	delay_ms(250);

	setConfig();                 //puts RTCC data into config array from tim_read array

	// write the config data
	//i2c.write(addr, config_t, 9);  // write the config data back to the RTCC module
	packet_tx.chip        = RTC_ADDR;
	packet_tx.addr[0]     = 0; // RTCSEC
	packet_tx.addr_length = 1;
	packet_tx.buffer      = config_2;
	packet_tx.length      = sizeof(config_2);

	twi_master_write(TWI4, &packet_tx);
}

static void write_time(void){
	// re-calculate mins
	mins = 8;		//ORE 10:08
	mins = mins%60;
	ch_mins = 16*(mins/10) + mins%10;
	MSN = msn(ch_mins);
	LSN = lsn(ch_mins);
	tim_read[1] = ch_mins;
	inc_mins = 0;
	//write the data back to RTCC
	setConfig();

	//i2c.write(addr, config_t, 9);

	/* Configure the data packet to be transmitted */
	packet_tx.chip        = RTC_ADDR;
	packet_tx.addr[0]     = 0; // RTCSEC
	packet_tx.addr_length = 1;
	packet_tx.buffer      = config_2;
	packet_tx.length      = sizeof(config_2);

	twi_master_write(TWI4, &packet_tx);

	//Display and set hours
	//hrs = bcd2dec(tim_read[2]);

	// re-calculate hrs
	hrs = 10;		//ORE 10:08
	hrs = hrs%24;
	ch_hrs = 16*(hrs/10) + hrs%10;
	MSN = msn(ch_hrs);
	LSN = lsn(ch_hrs);

	tim_read[2] = ch_hrs;
	inc_hr = 0;
	//write the data back to RTCC
	setConfig();

	/* Configure the data packet to be transmitted */
	packet_tx.chip        = RTC_ADDR;
	packet_tx.addr[0]     = 0; // RTCSEC
	packet_tx.addr_length = 1;
	packet_tx.buffer      = config_2;
	packet_tx.length      = sizeof(config_2);

	twi_master_write(TWI4, &packet_tx);
}

static void read_time(void){
	//config_t[0] = 0x00; //reset pointer reg to '00'
	//// First Get the time
	////i2c.write(addr, config_t, 1); // write address 00
	//packet_tx.chip        = RTC_ADDR;
	//packet_tx.addr[0]     = 0; // RTCSEC
	//packet_tx.addr_length = 1;
	//packet_tx.buffer      = config_t;
	//packet_tx.length      = 1;
//
	//twi_master_write(TWI4, &packet_tx);
	delay_ms(250);

	uint8_t buf[7]; // Fill this with RTC clock data for all seven registers
	/* Configure the data packet to be received */
	packet_rx.chip        = RTC_ADDR;
	packet_rx.addr[0]     = 0; // RTCSEC
	packet_rx.addr_length = 1;
	packet_rx.buffer      = buf;
	packet_rx.length      = sizeof(buf);
	twi_master_read(TWI4, &packet_rx);

	for(uint8_t i = 0; i < sizeof(buf); i++){
		tim_read[i] = buf[i];
	}
}

void example_print_time(void){
	//initialize();
	delay_ms(1000);
	//write_time();     //commented to see if time is permanent
	delay_ms(1000);
	read_time();

	while(1){
		printf("Reading time\n");
		printf("%d:%d:%d\n", bcd2dec(tim_read[2]), bcd2dec(tim_read[1]), bcd2dec(tim_read[0] ^ START_32KHZ));
		delay_ms(1000);
		read_time();
	}
}