Shop OBEX P1 Docs P2 Docs Learn Events
i2c Driver for DS3231 RTC in C — Parallax Forums

i2c Driver for DS3231 RTC in C

doggiedocdoggiedoc Posts: 2,241
edited 2014-08-16 06:28 in Propeller 1
Forum member JLocke was kind enough to send me a breakout board he had made for the Real Time Clock chip DS3231. I didn't find a driver for one (although I must admit I didn't look too hard) ...and since it uses i2c to communicate to the prop, I figured it would be a good chance to tinker with both. Also, I wanted to flex my C muscles a little and see if I could flesh out a 'driver' for this chip.

What I came up with seems to work just fine. Using SimpleIDE and C using the LMM Main RAM with optimization for OS size settings I was able to get functional driver that prompts you to enter the current time via Simple Terminal, and displays the Date, Time, and Temperature in celsius.

Here's the code:
Feel free to use it if you have this chip.
/*
  Test DS3231 RTC using i2c
  Paul A. Willoughby, DVM
*/


#include "simpletools.h"                      // simpletools header


#define SCL_Pin               7
#define SDA_Pin               6


#define RTC_Addr              0x68            
                                                  
#define RTC_Read              ((RTC_Addr << 1) | 0x01) 
#define RTC_Write             ((RTC_Addr << 1) & 0xFE) 


#define TEMP_MSB              0x11 
#define TEMP_LSB              0x12 




  i2c *dsBus;                    // i2c bus ID
  int secs = 0; 
  int mins = 0;
  int hrs  = 0;
  int am_pm = 0;
  float temp = 0;
  int day  = 0;
  int date = 0;
  int month = 0;
  int year = 0;




/* Function Declarations*/
int bcd_to_dec(int d);                                
int dec_to_bcd(int d) ;
void DS3231_Write(int address, int reg, int value);    
int DS3231_Read(int address, int reg); 
float getTemp();
void check_Date_Time();                                                  
void set_Date_Time();
void read_Date_Time();


int main()                                    
{  


 
  dsBus = i2c_open(dsBus, SCL_Pin, SDA_Pin,   0);             // Set up I2C bus, get bus ID
 


  
//menu for setting time  


  while(i2c_busy(dsBus, RTC_Addr));                           // ensure i2c bus not busy
  check_Date_Time();
  
  for (;;)   
  { pause(950);
       
  print("%c", CLS);
  read_Date_Time();
  switch(day){
      case 1  :
         print("Sunday, ");
         break;
      case 2  : 
         print("Monday, ");
         break;
      case 3  : 
         print("Tuesday, ");
         break;
      case 4  : 
         print("Wednesday, ");
         break;
      case 5  : 
         print("Thursday, ");
         break;
      case 6  : 
         print("Friday, ");
         break;
      case 7  : 
         print("Saturday, ");
         break; 
  }
  switch(month){
      case 1  :
         print("January ");
         break;
      case 2  : 
         print("February ");
         break;
      case 3  : 
         print("March ");
         break;
      case 4  : 
         print("April ");
         break;
      case 5  : 
         print("May ");
         break;
      case 6  : 
         print("June ");
         break;
      case 7  :
         print("July ");
         break;
      case 8  : 
         print("August ");
         break;
      case 9  : 
         print("September ");
         break;
      case 10  : 
         print("October ");
         break;
      case 11  : 
         print("November ");
         break;
      case 12  : 
         print("December ");
         break;
  }
    print("%d, 20%d \n", date, year);
    print("Time = %02d:%02d:%02d ", hrs, mins, secs);       // Display result


    switch(am_pm){
      case 0b100 :
         print("AM\n");
         break;
      case 0b101 :
         print("AM\n");
         break;
      case 0b110 : 
         print("PM\n");
         break;
      case 0b111 : 
         print("PM\n");
         break;
    }    


    print("Temp = %.2f&#730;C \n", getTemp());
    
  }        
}




void check_Date_Time()
{
  read_Date_Time();
  if (year < 1){
    print("Setting Date & Time...");
    pause(500); 
    set_Date_Time();
  }
}


void set_Date_Time(int hr_10, int hr_1, int yr_10, int yr_1)
{  


  print("\nEnter year(YY): ");
  scan( " %d", &year);  
  yr_10 = year / 10;
  yr_10 <<= 4;
  yr_1 = year % 10;
  year = yr_10 | yr_1;
  
  print("\nEnter Month(1-12): ");
  scan( " %d", &month);


  print("\nEnter Day(1-7 [Sunday = 1]): ");
  scan( " %d", &day);


  print("\nEnter Date(1-31): ");
  scan( " %d", &date);


  print("\nEnter Hour(1-12): ");
  scan( " %d", &hrs);
  hr_10 = hrs / 10;
  hr_10 <<= 4;
  hr_1 = hrs % 10;
  hrs = hr_10 | hr_1;


  print("\nEnter 1 for AM or 2 for PM: ");
  scan( " %d", &am_pm);


// code for am_pm status
  switch(am_pm)
  {
     case 1:  //AM
     {
      hrs = (0x40 | (0x1F & hrs));
      am_pm = 0b100;
      break;
     }
     case 2:  //PM
     {
      hrs = (0x60 | (0x1F & hrs));
      am_pm = 0b110;
      break;


     } 
   }                 
       
      
  
  print("\nEnter Minute(1-59): ");
  scan( " %d", &mins);
  print("\nEnter Second(1-59): ");
  scan( " %d", &secs);
                                                        
  DS3231_Write(dsBus, 0x06, year);//set year 
  DS3231_Write(dsBus, 0x05, dec_to_bcd(month));//set month 
  DS3231_Write(dsBus, 0x03, dec_to_bcd(day));//set day 
  DS3231_Write(dsBus, 0x04, dec_to_bcd(date));//set date 
  DS3231_Write(dsBus, 0x02, hrs);//set hour 
  DS3231_Write(dsBus, 0x01, dec_to_bcd(mins));//set minutes 
  DS3231_Write(dsBus, 0x00, dec_to_bcd(secs));//set seconds 
  
}  


void read_Date_Time()
{
  secs = bcd_to_dec(DS3231_Read(dsBus, 0x00));
  mins = bcd_to_dec(DS3231_Read(dsBus, 0x01));
  hrs = DS3231_Read(dsBus, 0x02);
  am_pm = hrs;
  am_pm >>= 4;
  hrs = bcd_to_dec(0x1F & hrs); 
  day = bcd_to_dec(DS3231_Read(dsBus, 0x03));
  date = bcd_to_dec(DS3231_Read(dsBus, 0x04));
  month = bcd_to_dec(DS3231_Read(dsBus, 0x05));
  year = bcd_to_dec(DS3231_Read(dsBus, 0x06));


}    




int bcd_to_dec(int d)                
{                                                                                          
         return ((d & 0x0F) + (((d & 0xF0) >> 4) * 10)); 
}                                
                                                              


int dec_to_bcd(int d) 
{ 
         return (((d / 10) << 4) & 0xF0) | ((d % 10) & 0x0F); 
} 




void DS3231_Write(int address, int reg, int value)    
{  
         i2c_start(dsBus);                  
         i2c_writeByte(dsBus, RTC_Write); 
         i2c_writeByte(dsBus, reg); 
         i2c_writeByte(dsBus, value);    
         i2c_stop(dsBus); 
}  


int DS3231_Read(int address, int reg) 
{                                      
         int value = 0; 
         i2c_start(dsBus);                                                      
         i2c_writeByte(dsBus, RTC_Write); 
         i2c_writeByte(dsBus, reg); 
         i2c_start(dsBus);                                                      
         i2c_writeByte(dsBus, RTC_Read); 
         value = i2c_readByte(dsBus, 1);                      
         i2c_stop(dsBus);                  
         return value; 
} 


float getTemp()                                                  
{ 
         register float t = 0.0; 
         unsigned int lowByte = 0; 
         signed int highByte = 0; 
         lowByte = DS3231_Read(dsBus, TEMP_LSB); 
         highByte = DS3231_Read(dsBus, TEMP_MSB);  
         lowByte >>= 6;                  
         lowByte &= 0x03;      
         t = ((float)lowByte);  
         t *= 0.25;      
         t += highByte;          
         return t; 
                                      
}




/*
 +--------------------------------------------------------------------
 | TERMS OF USE: MIT License
 +--------------------------------------------------------------------
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files
 (the "Software"), to deal in the Software without restriction,
 including without limitation the rights to use, copy, modify, merge,
 publish, distribute, sublicense, and/or sell copies of the Software,
 and to permit persons to whom the Software is furnished to do so,
 subject to the following conditions:
 
 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 +--------------------------------------------------------------------
 */





DS3231_board.jpg


RTC_output.png



Paul

Comments

  • jazzedjazzed Posts: 11,803
    edited 2014-08-13 20:48
    Good work Doc.

    This post talks about how to integrate with the propeller-gcc time library features: http://forums.parallax.com/showthread.php/156864-Real-Time-Clock?p=1285444&viewfull=1#post1285444

    The details are a little sketchy, but it gives an idea of what can be done in the standard C context. All you need to do is replace the GbI2C functions.
  • RaymanRayman Posts: 14,658
    edited 2014-08-14 03:01
    This is good to know... When I was thinking about how to do this for PSM3 the other day, I googled and found this:

    http://propgcc.googlecode.com/hg/doc/Library.html#RTC

    Which says you need to compute unix timestamp, which is a lot of code when your rtc doesn't give it to you...

    Where is the code that computes the unix timestamp given date, hour, etc.?
  • ersmithersmith Posts: 6,053
    edited 2014-08-14 06:56
    Rayman wrote: »
    This is good to know... When I was thinking about how to do this for PSM3 the other day, I googled and found this:

    http://propgcc.googlecode.com/hg/doc/Library.html#RTC

    Which says you need to compute unix timestamp, which is a lot of code when your rtc doesn't give it to you...

    Where is the code that computes the unix timestamp given date, hour, etc.?

    Look at the mktime() function; it takes a structure containing date, hour, etc. and returns a time_t.
  • RaymanRayman Posts: 14,658
    edited 2014-08-14 07:12
    Do I still need to:

    "replace the built-in drivers by assigning the global _rtc_gettime and _rtc_settime pointers to your own gettime() and settime() routines" ?

    Or just the Gbi2c ?
  • RS_JimRS_Jim Posts: 1,766
    edited 2014-08-14 07:33
    Doc,
    Did you succeed in programming the 12/24hour bit and the am/pm bit?
    Jim
  • jazzedjazzed Posts: 11,803
    edited 2014-08-14 07:45
    Rayman wrote: »
    Do I still need to:

    "replace the built-in drivers by assigning the global _rtc_gettime and _rtc_settime pointers to your own gettime() and settime() routines" ?

    Or just the Gbi2c ?


    Function RTC_init() in the link I gave sets the drivers, so you need to call that at startup.
    Looks like mytime is a global integer and the declaration was omitted.
  • RaymanRayman Posts: 14,658
    edited 2014-08-14 07:53
    Ok, I see that now, thanks.
  • doggiedocdoggiedoc Posts: 2,241
    edited 2014-08-14 12:29
    Thanks Jazzed - I will take a look a that link. I suppose 'driver' may have been too strong of word - but perhaps it is a starting point. :)

    @Rayman - thanks for that link, that will help. I'm hoping to flesh this out to a more useful work.

    @RS_Jim - here's where I set the 12/24 bit:
    print("\nEnter 1 for AM or 2 for PM: ");
      scan( " %d", &am_pm);
    
    
    // code for am_pm status
      switch(am_pm)
      {
         case 1:  //AM
         {
          hrs = (0x40 | (0x1F & hrs));
          am_pm = 0b100;
          break;
         }
         case 2:  //PM
         {
          hrs = (0x60 | (0x1F & hrs));
          am_pm = 0b110;
          break;
    
    
         } 
       }                 
    
    I'm setting bit 6 with AM and PM user selection - if PM I'm also setting bit 5 then 'OR' with variable for hrs before writing to the register.
  • doggiedocdoggiedoc Posts: 2,241
    edited 2014-08-14 12:31
    @RS_Jim - one more note: I didn't give option for 24 hour clock at this point.
  • RS_JimRS_Jim Posts: 1,766
    edited 2014-08-16 06:28
    Doc,
    Thanks, I will bookmark your code and be able to study it in 2 weeks when I have a little alone time!
    Jim
Sign In or Register to comment.