MCP79411 通过 Atmel SAMG55 上的 i2c/TWI 接口连接 RTC

Posted

技术标签:

【中文标题】MCP79411 通过 Atmel SAMG55 上的 i2c/TWI 接口连接 RTC【英文标题】:MCP79411 RTC connection via i2c/TWI interface on Atmel SAMG55 【发布时间】:2020-05-16 05:27:34 【问题描述】:

我做了一个基于 ATSAMG55J19 MCU 的项目,用 Atmel Studio 和 ASF 3 编程

现在我正在尝试添加一个外部 RTC 时钟,因为内部 SAMg55 rtc 没有备用电池。 该模块将用于在断电后读取当前时间,然后我将使用内部 RTC,所以我只需要基本的通信。无需在EEPROM中写入特定数据或设置警报。

我有一个 MCP79411,通过 i2c 连接,但没有任何库适合这个使用 ASF TWI 库的 MCU。

有很多 Arduino 实现,但他们使用 Wire.h 库,我无法移植。

我尝试移植这个简单的“驱动程序”:https://www.ccsinfo.com/forum/viewtopic.php?t=54105

这是一些代码

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();

然后我尝试了 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)

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();

但我总是得到“0”字节。

我无法理解打开通信和从 i2c 读取字节的正确方法。

我找到的唯一参考是http://asf.atmel.com/docs/latest/sam.drivers.twi.twi_eeprom_example.samg53_xplained_pro/html/index.html,但它似乎是一种非常不同的交流方式。

通过 i2c 发送和接收字节的正确方法是什么?

【问题讨论】:

【参考方案1】:

我设法获取和设置数据。我发布了图书馆的草稿:

#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();
    

【讨论】:

以上是关于MCP79411 通过 Atmel SAMG55 上的 i2c/TWI 接口连接 RTC的主要内容,如果未能解决你的问题,请参考以下文章

Avr Atmel AR32UV3A0512 通过 SPI 进行 EEPROM 仿真

蓝桥杯嵌入式组别第九节:MCP4017编程设计

ARM Mcp2515添加驱动

使用 AVRDUDE 通过 CAN 对 Atmel 芯片 (AT90CAN128) 进行编程

atmel 模拟 UART 通讯

MAC认证是啥,MCP认证又是啥