CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Basic DS3231 driver

 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
temtronic



Joined: 01 Jul 2010
Posts: 9081
Location: Greensville,Ontario

View user's profile Send private message

Basic DS3231 driver
PostPosted: Tue Apr 13, 2021 4:49 am     Reply with quote

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

View user's profile Send private message

Thanks for posting code
PostPosted: Tue May 25, 2021 1:54 pm     Reply with quote

Thanks Temtronic, saved me some time implementing this.
Big O
rudy



Joined: 27 Apr 2008
Posts: 167

View user's profile Send private message Send e-mail MSN Messenger

PostPosted: Mon Nov 13, 2023 8:14 am     Reply with quote

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: 19195

View user's profile Send private message

PostPosted: Thu Nov 16, 2023 8:25 am     Reply with quote

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

View user's profile Send private message Send e-mail MSN Messenger

PostPosted: Thu Nov 16, 2023 8:41 am     Reply with quote

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: 19195

View user's profile Send private message

PostPosted: Sat Nov 25, 2023 8:16 am     Reply with quote

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

View user's profile Send private message Send e-mail MSN Messenger

PostPosted: Sun Nov 26, 2023 4:37 am     Reply with quote

Thanks Ttelmah

I will give it a try.

Regards.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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