Search code examples
i2ccortex-mmicrochipatmelstudio

MCP79411 RTC connection via i2c/TWI interface on Atmel SAMG55


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. The module will be used to read current time after power failure, then i'll use internal RTC, so i need only basic communication. No need to write specific data in EEPROM or setting alarms.

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.

What is the correct way to send and receive that bytes via i2c?


Solution

  • I managed to get and set data. I post a draft of library:

    #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_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_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  START_32KHZ       0x80       //  start crystal: ST = b7 (ADDR_SEC)
    #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();
        }
    }