|
|
View previous topic :: View next topic |
Author |
Message |
temtronic
Joined: 01 Jul 2010 Posts: 9205 Location: Greensville,Ontario
|
Basic DS3231 driver |
Posted: Tue Apr 13, 2021 4:49 am |
|
|
Below is a basic driver for the DS3231 RTC chip, essentially modiifed DS1307 code, so not all features, but it does set/get time info.
Code: |
////////////////////////////////////////////////////////////////////////////////
/// jay_rtc3231 from jay_DS1307.C ///
/// Driver for Real Time Clock ///
// ds3231_init() - Enable oscillator without clearing the seconds register -///
// enable square wave output at 1Hz
// ds3231_set_date_time(year,mth,day,dow,hrs,min,sec) Set the date/time ///
// ds3231_get_date_time(year,mth,day,dow,hrs,min,sec) Get the date/time
// ds3231_get_date(year,mth,day,dow) Get the date ///
//
// ds3231_get_time(hrs,min,sec) Get the time ///
//setup RTC pins
#ifndef RTC_SDA
#define RTC_SDA PIN_B1
#define RTC_SCL PIN_B2
#endif
#use i2c(master, sda=RTC_SDA, scl=RTC_SCL)
BYTE bin2bcd(BYTE binary_value);
BYTE bcd2bin(BYTE bcd_value);
void ds3231_init(void)
{
BYTE seconds = 0;
i2c_start();
i2c_write(0xD0); // WR to RTC
i2c_write(0x00); // REG 0
i2c_start();
i2c_write(0xD1); // RD from RTC
seconds = bcd2bin(i2c_read(0)); // Read current "seconds" in DS1307
i2c_stop();
seconds &= 0x7F;
delay_us(3);
i2c_start();
i2c_write(0xD0); // WR to RTC
i2c_write(0x00); // REG 0
i2c_write(bin2bcd(seconds)); // Start oscillator with current "seconds value
i2c_start();
i2c_write(0xD0); // WR to RTC
i2c_write(0x0E); // point to Control Register
i2c_write(0b01000000); // Enable squarewave output pin
i2c_stop();
}
void ds3231_set_date_time(BYTE rtcyer, BYTE rtcmth, BYTE rtcday, BYTE rtcdow, BYTE rtchrs, BYTE rtcmin, BYTE rtcsec)
{
rtcsec &= 0x7F;
rtchrs &= 0x3F;
i2c_start();
i2c_write(0xD0); // I2C write address
i2c_write(0x00); // Start at REG 0 - Seconds
i2c_write(bin2bcd(rtcsec)); // REG 0
i2c_write(bin2bcd(rtcmin)); // REG 1
i2c_write(bin2bcd(rtchrs)); // REG 2
i2c_write(bin2bcd(rtcdow)); // REG 3
i2c_write(bin2bcd(rtcday)); // REG 4
i2c_write(bin2bcd(rtcmth)); // REG 5
i2c_write(bin2bcd(rtcyer)); // REG 6
i2c_stop();
}
void ds3231_get_time(BYTE &rtchrs, BYTE &rtcmin, BYTE &rtcsec)
{
i2c_start();
i2c_write(0xD0);
i2c_write(0x00); // Start at REG 0 - Seconds
i2c_start();
i2c_write(0xD1);
rtcsec = bcd2bin(i2c_read() & 0x7f);
rtcmin = bcd2bin(i2c_read() & 0x7f);
rtchrs = bcd2bin(i2c_read(0) & 0x3f);
i2c_stop();
}
void ds3231_get_date(BYTE &rtcyer, BYTE &rtcmth, BYTE &rtcday, BYTE &rtcdow)
{
i2c_start();
i2c_write(0xD0);
i2c_write(0x03); // Start at REG 3 - Day of week
i2c_start();
i2c_write(0xD1);
rtcdow = bcd2bin(i2c_read() & 0x7f); // REG 3
rtcday = bcd2bin(i2c_read() & 0x3f); // REG 4
rtcmth = bcd2bin(i2c_read() & 0x1f); // REG 5
rtcyer = bcd2bin(i2c_read(0)); // REG 6
i2c_stop();
}
void ds3231_get_date_time(BYTE &rtcyer, BYTE &rtcmth, BYTE &rtcday, BYTE &rtcdow, BYTE &rtchrs, BYTE &rtcmin, BYTE &rtcsec)
{
i2c_start();
i2c_write(0xD0); // WRITE cmd
i2c_write(0x00); // Start at REG 0 - Seconds
i2c_start();
i2c_write(0xD1); // READ cmd
rtcsec = bcd2bin(i2c_read() & 0x7f); // REG 0
rtcmin = bcd2bin(i2c_read() & 0x7f); // REG 1
rtchrs = bcd2bin(i2c_read() & 0x3f); // REG 2
rtcdow = bcd2bin(i2c_read() & 0x7f); // REG 3
rtcday = bcd2bin(i2c_read() & 0x3f); // REG 4
rtcmth = bcd2bin(i2c_read() & 0x1f); // REG 5
rtcyer = bcd2bin(i2c_read(0)); // REG 6
i2c_stop();
}
BYTE bin2bcd(BYTE binary_value)
{
BYTE temp;
BYTE retval;
temp = binary_value;
retval = 0;
while(1)
{
// Get the tens digit by doing multiple subtraction
// of 10 from the binary value.
if(temp >= 10)
{
temp -= 10;
retval += 0x10;
}
else // Get the ones digit by adding the remainder.
{
retval += temp;
break;
}
}
return(retval);
}
// Input range - 00 to 99.
BYTE bcd2bin(BYTE bcd_value)
{
BYTE temp;
temp = bcd_value;
// Shifting upper digit right by 1 is same as multiplying by 8.
temp >>= 1;
// Isolate the bits for the upper digit.
temp &= 0x78;
// Now return: (Tens * 8) + (Tens * 2) + Ones
return(temp + (temp >> 2) + (bcd_value & 0x0f));
}
//read DS3231 temerature sensor
void ds3231_get_temp(BYTE &rtctms, BYTE &rtctls)
{
i2c_start();
i2c_write(0xD0);
i2c_write(0x11); // Start at temperature MSB
i2c_start();
i2c_write(0xD1);
rtctms = i2c_read();
rtctls = i2c_read(0);
i2c_stop();
}
// returns chip temperature
// Temperature is stored in hundredths C (output value of "3125" equals 31.25 °C).
unsigned int16 Get_RTC_Temperature()
{
char t_msb, t_lsb;
unsigned int16 c_temp;
I2C_START();
I2C_WRITE(0xd0);
I2C_WRITE(0x11);
I2C_START();
I2C_WRITE(0xd1);
t_msb = I2C_READ(1);
t_lsb = I2C_READ(0);
I2C_STOP();
c_temp = (unsigned int16)t_msb << 2 | t_lsb >> 6;
if(t_msb & 0x80)
c_temp |= 0xFC00;
return c_temp * 25;
}
void ds3231_set_RAM(BYTE &rtcrm7,BYTE &rtcrm8,BYTE &rtcrm9,BYTE &rtcrmA,BYTE &rtcrmB,BYTE &rtcrmC,BYTE &rtcrmD)
{
i2c_start();
i2c_write(0xD0); // I2C write address
i2c_write(0x07); // Start at REG 7 'alarm data'
i2c_write(rtcrm7); // REG 7
i2c_write(rtcrm8); // REG 8
i2c_write(rtcrm9); // REG 9
i2c_write(rtcrmA); // REG A
i2c_write(rtcrmB); // REG B
i2c_write(rtcrmC); // REG C
i2c_write(rtcrmD); // REG D
i2c_stop();
}
void ds3231_get_RAM(BYTE &rtcrm7,BYTE &rtcrm8,BYTE &rtcrm9,BYTE &rtcrmA,BYTE &rtcrmB,BYTE &rtcrmC,BYTE &rtcrmD)
{
i2c_start();
i2c_write(0xD0);
i2c_write(0x07); // Start at REG 7 - 'alarm data'
i2c_start();
i2c_write(0xD1);
rtcrm7 = i2c_read(); // read all 7 bytes of RTC RAM
rtcrm8 = i2c_read(); // aka alarm bits
rtcrm9 = i2c_read(); // and bytes
rtcrmA = i2c_read();
rtcrmB = i2c_read();
rtcrmC = i2c_read();
rtcrmD = i2c_read(0);
i2c_stop();
}
|
|
|
|
robleso7473
Joined: 25 Mar 2009 Posts: 47
|
Thanks for posting code |
Posted: Tue May 25, 2021 1:54 pm |
|
|
Thanks Temtronic, saved me some time implementing this.
Big O |
|
|
rudy
Joined: 27 Apr 2008 Posts: 167
|
|
Posted: Mon Nov 13, 2023 8:14 am |
|
|
Hi temtronic. Don't know why, there is no way to read DS3231 with accuracy time after a power shutt down. Always comes with two or more minuts off.
The battery is ok, with 3V, nothing seems to solve this problem, can you take a look at my routine please?
The RTC_TEST_OSC() routine runs only once at the very first startup.
Code: | int BCD2BIN(int d)
{
int T_M_P;
T_M_P=d;
T_M_P>>=1;
T_M_P&=0X78;
return (T_M_P+(T_M_P>>2)+(d&0X0F));
//return ((d&0x0F)+(((d&0xF0)>>4)*10));
}
int BIN2BCD(int d)
{
int T_M_P, RETVAL;
T_M_P=d;
RETVAL=0X00;
While(1)
{
if(T_M_P>=10)
{
T_M_P-=10;
RETVAL+=0X10;
}
else
{
RETVAL+=T_M_P;
break;
}
}
return(retval);
//return (((d/10)<<4)&0xF0)|((d%10)&0x0F);
}
void RTC_TEST_OSC()
{
I2C_START();
I2C_WRITE(0xD0); // WR to RTC
I2C_WRITE(0x0E); // REG 0x0E - control
I2C_START();
I2C_WRITE(0xD1); // RD from RTC
SEGUNDOS=I2C_READ(0); // Read REG 0x0E
I2C_STOP();
if(SEGUNDOS)
{
I2C_START();
I2C_WRITE(0xD0); // WR to RTC
I2C_WRITE(0x0E); // REG 0x0E
I2C_WRITE(0x00); // REG 0x0E - Start oscillator + 1HZ SQW
I2C_STOP();
}
I2C_START();
I2C_WRITE(0xD0); // WR to RTC
I2C_WRITE(0x02); // REG 0x02 - 12HS/24HS CHECK
I2C_STOP();
I2C_START();
I2C_WRITE(0xD1); // RD from RTC
SEGUNDOS=I2C_READ(0); // Read REG 0x02
I2C_STOP();
SEGUNDOS&=0X40;
if(SEGUNDOS==0X40) //REG 0X02 = 0b01000000 - SETA 24h
{
I2C_START();
I2C_WRITE(0xD0); // WR to RTC
I2C_WRITE(0x02); // REG 0x02 - 12HS/24HS CHECK
I2C_WRITE(0x00); // REG 0x02 - SETA 24h
I2C_STOP();
}
}
void RTC_GETTIME()
{
I2C_START();
I2C_WRITE(0xD0);
I2C_WRITE(0x00); //REG 0 - Seconds
I2C_START();
I2C_WRITE(0xD1);
SEGUNDOS=BCD2BIN(I2C_READ());
MINUTOS=BCD2BIN(I2C_READ());
HORAS=BCD2BIN(I2C_READ(0) & 0x3f);
I2C_STOP();
}
void RTC_GETDATE()
{
I2C_START();
I2C_WRITE(0xD0);
I2C_WRITE(0x03); //REG 0x03 DDS
I2C_START();
I2C_WRITE(0xD1);
DDS=(0x07 & I2C_READ()); //pega DDS REG 0X03
DIA=BCD2BIN(0x3F&I2C_READ()); //pega dia REG 0X04
MES=BCD2BIN(0x1F&I2C_READ()); //pega mês REG 0X05
ANO=BCD2BIN(I2C_READ(0)); //pega ano REG 0X06
I2C_STOP();
RTC_GET_DATE=0;
}
void RTC_SET_TIME()
{
I2C_START();
I2C_WRITE(0xD0); // I2C write address
I2C_WRITE(0x00); // Start at REG 0 - Seconds
I2C_WRITE(0); // REG 0
I2C_WRITE(BIN2BCD(MINUTOS)); // REG 1
I2C_WRITE(BIN2BCD((HORAS)&0x3F)); // REG 2
I2C_STOP();
}
void RTC_SET_DATE()
{
I2C_START();
I2C_WRITE(0xD0); // I2C write address
I2C_WRITE(0x03); // Start at REG 3 - dow
I2C_WRITE(BIN2BCD(DDS)); // REG 3 - dow
I2C_WRITE(BIN2BCD((DIA))); // REG 4 - dia
I2C_WRITE(BIN2BCD((MES))); // REG 5 - mes
I2C_WRITE(BIN2BCD(ANO)); // REG 6 - ano
I2C_STOP();
} |
Thanks! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19459
|
|
Posted: Thu Nov 16, 2023 8:25 am |
|
|
Just a thought Rudy. The power off might be generating a clock to
the I2C, resulting in the chip getting confused. Possibly worth adding
I2C reset code.
Basically read the SDA line, and if it is not high, manually send a number
of clocks on the SCL line. Normally 9. This makes the bus release. Then
turn on the I2C driver. |
|
|
rudy
Joined: 27 Apr 2008 Posts: 167
|
|
Posted: Thu Nov 16, 2023 8:41 am |
|
|
Thanks, You mean first of all instructions, send a I2C_atasrt() and I2C stop()? This issue really gets me odd; I can’t have any clue of what is going wrong, made a lot of tests and nothing seems to solve this matter.
Somehow, I think it may be related to the hardware, because I used this module some time ago, in another project, and none of these problems appears.
Will give a try as you teach, let’s see what happens. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19459
|
|
Posted: Sat Nov 25, 2023 8:16 am |
|
|
No.
You have to do this manually. Can't use the I2C instructions.
Have a look at this thread:
[url]
https://www.ccsinfo.com/forum/viewtopic.php?t=60157&highlight=hung+i2c+bus
[/url]
You use the NO_INIT option on the I2C so you can manually access the line
before you enable the bus. You check that the data line is high. If it is not
you manually issue 100KHz clocks to clear it, then wake up the bus. |
|
|
rudy
Joined: 27 Apr 2008 Posts: 167
|
|
Posted: Sun Nov 26, 2023 4:37 am |
|
|
Thanks Ttelmah
I will give it a try.
Regards. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|