i2c Driver for DS3231 RTC in C
doggiedoc
Posts: 2,241
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.
Paul
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˚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. +-------------------------------------------------------------------- */
Paul
Comments
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.
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.
"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 ?
Did you succeed in programming the 12/24hour bit and the am/pm bit?
Jim
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.
@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: 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.
Thanks, I will bookmark your code and be able to study it in 2 weeks when I have a little alone time!
Jim