rtc.c 4.17 KB
#include <os.h>
#include <bcp.h>
#include "bbint.h"

#define RTC_SLAVE_ADDR		0xd0

#define RTC_SCLK		PI_GPIO_USER0_BIT
#define RTC_SIO			PI_GPIO_USER1_BIT
#define RTC_MASK		(RTC_SCLK|RTC_SIO|((RTC_SCLK|RTC_SIO) << PI_GPIO_ENABLE_SHIFT))
#define RTC_SCLK_HI		(RTC_SCLK|(RTC_SCLK<<PI_GPIO_ENABLE_SHIFT))
#define RTC_SCLK_LO		(0|(RTC_SCLK<<PI_GPIO_ENABLE_SHIFT))
#define RTC_SO(b)		(b|(RTC_SIO<<PI_GPIO_ENABLE_SHIFT))

#define tobcd(x)		((((x)/10)<<4) | ((x)%10))
#define frbcd(x)		(((x)>>4)*10 + ((x)&0xf))

static void
write_rtc(u32 x) {
    IO_WRITE(PI_GPIO_REG, x);
    __osBbDelay(2);
}

static void
send_start(u8 write) {
    u32 i, j, mask = IO_READ(PI_GPIO_REG) & ~RTC_MASK;
    u8 byte[2];
    byte[0] = RTC_SLAVE_ADDR | (write == 0);
    byte[1] = 0; /* address */

    /* assume we are in idle state, clock and data hi */
    /* clock hi, data lo */
    write_rtc(mask | RTC_SCLK_HI | RTC_SO(0));
    /* clock lo, hold data */
    write_rtc(mask | RTC_SCLK_LO | RTC_SO(0));
    for(j = 0; j < 1+write; j++) {
	for(i = 0; i < 8; i++) {
	    u32 b = (byte[j] & (1<<(7-i))) ? RTC_SIO : 0;
	    /* clock lo, new data */
	    write_rtc(mask | RTC_SO(b) | RTC_SCLK_LO);
	    /* clock hi */
	    write_rtc(mask | RTC_SO(b) | RTC_SCLK_HI);
	    /* clock lo, hold data */
	    write_rtc(mask | RTC_SO(b) | RTC_SCLK_LO);
	}
	/* acknowledge */
	/* clock lo, new data */
	write_rtc(mask | RTC_SCLK_LO);
	/* clock hi */
	write_rtc(mask | RTC_SCLK_HI);
    }
    write_rtc(mask | RTC_SCLK_LO);
}

static void
send_stop(void) {
    u32 mask = IO_READ(PI_GPIO_REG) & ~RTC_MASK;
    /* clock lo, data lo */
    write_rtc(mask | RTC_SCLK_LO | RTC_SO(0));
    /* clock hi, data lo */
    write_rtc(mask | RTC_SCLK_HI | RTC_SO(0));
    /* clock hi, data hi */
    write_rtc(mask | RTC_SCLK_HI | RTC_SO(RTC_SIO));
}

static void
read_bytes(u8* bytes, u8 len) {
    u32 ack, i, mask = IO_READ(PI_GPIO_REG) & ~RTC_MASK;
    while(len-- > 0) {
	u32 x = 0;
	for(i = 0; i < 8; i++) {
	    /* clock low */
	    write_rtc(mask | RTC_SCLK_LO);
	    /* clock high */
	    write_rtc(mask | RTC_SCLK_HI);
	    x <<= 1;
	    x |= (IO_READ(PI_GPIO_REG) & RTC_SIO) ? 1 : 0;
	}
	*bytes++ = x;
	/* acknowledge */
	ack = (len == 0) ? RTC_SO(RTC_SIO) : RTC_SO(0);
	/* clock lo, data lo */
	write_rtc(mask | RTC_SCLK_LO | RTC_SO(0));
	/* clock hi, ack or stop */
	write_rtc(mask | RTC_SCLK_HI | ack);
    }
    send_stop();
}

static void
write_bytes(const u8* bytes, u8 len) {
    u32 i, mask = IO_READ(PI_GPIO_REG) & ~RTC_MASK;
    while(len-- > 0) {
	u32 x = *bytes++;
	for(i = 0; i < 8; i++) {
	    u32 b = (x&0x80) ? RTC_SIO : 0;
	    /* clock low */
	    write_rtc(mask | RTC_SO(b) | RTC_SCLK_LO);
	    /* clock high */
	    write_rtc(mask | RTC_SO(b) | RTC_SCLK_HI);
	    /* clock lo, hold data */
	    write_rtc(mask | RTC_SO(b) | RTC_SCLK_HI);
	    x <<= 1;
	}
	/* recv acknowledge */
	/* clock lo */
	write_rtc(mask | RTC_SCLK_LO);
	/* clock hi, hold data */
	write_rtc(mask | RTC_SCLK_HI);
	x = (IO_READ(PI_GPIO_REG) & RTC_SIO) ? 1 : 0;
#ifdef _DEBUG
	if (x && len) osSyncPrintf("premature stop?\n");
#endif
	/* clock lo */
	write_rtc(mask | RTC_SCLK_LO);
    }
    send_stop();
}

void
osBbRtcInit(void) {
    u32 mask = IO_READ(PI_GPIO_REG) & ~RTC_MASK;
    /* idle state is clock and data high */
    write_rtc(mask | RTC_SCLK_HI | RTC_SO(RTC_SIO));
}

void
osBbRtcSet(u8 year, u8 month, u8 day, u8 dow, u8 hour, u8 min, u8 sec) {
    u8 data[8];
    data[7] = 0;
    data[6] = tobcd(year); data[5] = tobcd(month); data[4] = tobcd(day);
    data[3] = tobcd(dow); data[2] = tobcd(hour); data[1] = tobcd(min);
    data[0] = tobcd(sec);
    send_start(1);
    //osSyncPrintf("w %02x %02x %02x %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
    write_bytes(data, 8);
}

void
osBbRtcGet(u8* year, u8* month, u8* day, u8* dow, u8* hour, u8* min, u8* sec) {
    u8 data[8];
    send_start(1);
    send_start(0);
    read_bytes(data, 8);
    //osSyncPrintf("r %02x %02x %02x %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
    *year = frbcd(data[6]); *month = frbcd(data[5]); *day = frbcd(data[4]);
    *dow = frbcd(data[3]); *hour = frbcd(data[2]); *min = frbcd(data[1]);
    *sec = frbcd(data[0]);
}