View previous topic :: View next topic |
Author |
Message |
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
Programming registers in the DS3231 RTC.... |
Posted: Mon Jul 16, 2018 1:20 pm |
|
|
Hi All,
I've been using the DS3231 RTC with success for a number of projects. On a new clock project, I've decided to add some additional functionality that allows for automatic DST correction, and the ability to sync time to an external GPS. I've written two routines that I use to manipulate individual registers of the RTC.
This routine seems to work fine for adjusting just the HOURS register:
Code: |
void SetRTCHours(void){
//This subroutine sets the hours register of the RTC. This routine is called whenever DST begins/ends and it is necessary
//to adjust the hours register by +/- 1 hour.
fprintf(Diag, "\n\rUpdating RTC hours register to: %d\n\r", DS3231SN_regs[HOURS_REG]);
//Here we convert the new value to BCD...
DS3231SN_regs[HOURS_REG] = BINtoBCD(DS3231SN_regs[HOURS_REG]);
// write 1 byte of BCD data to DS3231
i2c_start();
i2c_write(WRITE_CNTRL);
// start at hours register....
i2c_write(HOURS_REG);
i2c_write(DS3231SN_regs[HOURS_REG]);
i2c_write(CONTROL_INIT_VAL);
i2c_stop();
}
|
But, this routine for adjusting both MINUTES and SECONDS does not work. It seems to zero the HOURS register no matter what.
Code: |
void SyncRTC(int8 SyncMinutes, int8 SyncSeconds){
//This subroutine 'syncs' the minutes and seconds registers of the RTC with the GPS derived time. The new minutes and seconds
//are passed into this routine, and are used to update the minutes and seconds registers of the RTC.
fprintf(Diag, "\n\rSync Minutes: %02u / Sync Seconds: %02u\n\r", SyncMinutes, SyncSeconds);
//Here we convert the new values to BCD...
DS3231SN_regs[MINUTES_REG] = BINtoBCD(SyncMinutes);
DS3231SN_regs[SECONDS_REG] = BINtoBCD(SyncSeconds);
// write 2 bytes of BCD data to DS3231
i2c_start();
i2c_write(WRITE_CNTRL);
// start at seconds register....
i2c_write(SECONDS_REG);
i2c_write(DS3231SN_regs[SECONDS_REG]);
i2c_write(DS3231SN_regs[MINUTES_REG]);
i2c_write(CONTROL_INIT_VAL);
i2c_stop();
}
|
It's probably something silly, but I can't see it....
Jack |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Mon Jul 16, 2018 1:34 pm |
|
|
You don't give us much to go on...
Show your register defines, and what CONTROL_INIT_VAL is defined as.
However I suspect you are not understanding how the time is meant to be set. If you set the seconds register, you _must_ set all the other time registers:
Quote: |
The countdown chain is reset whenever the seconds register is written. Write transfers occur on the acknowledge from the DS3231. Once the countdown chain is reset, to avoid rollover issues the remaining time and date registers must be written within 1 second.
|
|
|
|
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
|
Posted: Mon Jul 16, 2018 1:51 pm |
|
|
Hi Ttelmah,
Here are my defines for the DS3231:
Code: |
// Dallas DS3231SN control bytes
#define WRITE_CNTRL 0xd0
#define READ_CNTRL 0xd1
// Dallas DS3231SN RTC register offsets
#define SECONDS_REG 0x00
#define MINUTES_REG 0x01
#define HOURS_REG 0x02
#define DAY_OF_WEEK_REG 0x03
#define DATE_REG 0x04
#define MONTH_REG 0x05
#define YEAR_REG 0x06
#define ALARM1_SECONDS_REG 0x07
#define ALARM1_MINUTES_REG 0x08
#define ALARM1_HOURS_REG 0x09
#define ALARM1_DAY/DATE_REG 0x0A
#define ALARM2_MINUTES_REG 0x0B
#define ALARM2_HOURS_REG 0x0C
#define ALARM2_DAY/DATE_REG 0x0D
#define CONTROL_REG 0x0E
#define STATUS_REG 0x0F
#define AGING_REG 0x10
#define TEMPMSB_REG 0x11
#define TEMPLSB_REG 0x12
#define DATE_TIME_BYTE_COUNT 19
#define DS3231SN_NVRAM_START_ADDR 8
#define CONTROL_INIT_VAL 0x80
|
I guess I need to dig into the DS3231 datasheet a bit more, but it seems like you are saying it's not OK to simply write to select registers in the DS3231, and NOT update all registers when writing?
Jack |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9241 Location: Greensville,Ontario
|
|
Posted: Mon Jul 16, 2018 2:00 pm |
|
|
Hay Jack, I sent you a PM with my driver(bodged version of the 1307) and I update everything,seemed simpler and it works... really the time involved(no pun) to update everything really isn't a lot.
Jay |
|
|
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
|
Posted: Mon Jul 16, 2018 7:28 pm |
|
|
Hi Jay,
Thanks, but my issue is not really using the 'normal' operations of the DS-3231 - I have a robust driver for that - it's for a couple of odd operations I'd like to perform. I know writing all the data doesn't take too long, it's just that my GPS data is in UTC format, and my RTC data is in LCL format, so I'd need to do a conversion for a full write of the RTC data, which can get quite messy.
I'd like to just write the values I need to write, and let the others be.
Maybe I'll read the RTC first to put the data in temporary variables, update the ones I want, and write it all back? Seems like a lot of work for a simple thing.....
Jack |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Mon Jul 16, 2018 10:40 pm |
|
|
You can set any register you want, _except_ the seconds register. If you write to this you must reset all the timing registers. |
|
|
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
|
Posted: Mon Jul 30, 2018 2:10 pm |
|
|
Hi All,
OK, so if I'm going to 'Sync' my RTC (local time) to the GPS (UTC time) then I'm going to have to be able to convert between the two. Below is some code I've written to do this. I've tested it a lot and it seems to work correctly, but there are so many different time scenarios that I doubt I'll be able to test them all! I'd appreciate it if you guys can have a look and offer any additions or improvements!
Code: |
//Here we adjust the UTC time to local time based on the user-defined UTC offset....
//Here we correct the hours. No need to correct minutes or seconds.
Hours += UTCOffset;
if (Hours < 0){
Hours +=24;
Day--;
}
if (Hours > 23){
Hours -=24;
Day++;
}
//Here we correct the day if we need to go back a day!
if (Day < 1)
{
if ((Month == 5) || (Month == 7) || (Month == 10) || (Month == 12)) //Previous month has 30 days
Day = 30;
else if (Month == 3) //Month after February
if ((Year % 4) != 0) //Non leap year!
Day = 28;
else
Day = 29;
else if ((Month == 1) || (Month == 2) || (Month == 4) || (Month == 6) || (Month == 8) ||(Month == 9) || (Month == 11)) //Previous month has 31 days
Day = 31;
Month--;
}
//Here we correct the day if we need to go forward a day!
if ((Day > 31) && ((Month == 1) || (Month == 3) || (Month == 5) || (Month == 7) || (Month == 8) || (Month == 10) || (Month == 12))) //Months with 31 days
{
Day = 1; Month++;
}
else if ((Day > 30) && ((Month == 4) || (Month == 6) || (Month == 9) || (Month == 11))) //Months with 30 days
{
Day = 1; Month++;
}
else if ((Day > 29) && (Month == 2)) //Feb. with 29 days, ie. leap year!
{
Day = 1; Month++;
}
else if((Day > 28) && (Month == 2) && ((Year % 4) != 0)) //Feb. with 28 days, ie. no leap year!
{
Day = 1; Month++;
}
//Here we correct the Month and Year!!
if (Month < 1){
Month +=12;
Year--;
}
if (Month > 12){
Month -=12;
Year++;
}
|
Thanks,
Jack |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9241 Location: Greensville,Ontario
|
|
Posted: Mon Jul 30, 2018 3:25 pm |
|
|
hmm.. would it not be easier to just take the UTC data from the GPS and stuff into the RTC, once you've adjusted for the time zone part?
just thinking out loud here....
Jay |
|
|
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
|
Posted: Mon Jul 30, 2018 6:21 pm |
|
|
Hi Jay,
If you think about it, it's a much bigger/more complicated problem than you are imagining. For example, when the 'adjustment' occurs, it's possible that the adjusted time/date is a different day, month, or year than the original depending on when it occurs.
Example: Right now, it's 12:18AM UTC on July 31st, but locally, it's 8:18PM on EDT on July 30th. A 'proper' conversion routine needs to deal with all that!
Jack |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Tue Jul 31, 2018 12:15 am |
|
|
When running RTC's, I've always had them run in UTC, and work internally in 'epoch seconds'. Then you only need to handle converting this too/from your local time for input, and display. That is simply done by adding a time offset, before converting to the display/storage format. I usually hold the offset as a 1/4 hour value. So in your case it'd be -16. You convert UTC to the 'epoch seconds' value (working from whatever origin you decide to use), then apply the offset (so 60*15*-16), then convert epoch seconds to the display format. The time.c library contains all the routines to do this. |
|
|
|