Shop OBEX P1 Docs P2 Docs Learn Events
I need some help figuring out a problem with RTC code — Parallax Forums

I need some help figuring out a problem with RTC code

Don MDon M Posts: 1,652
edited 2014-02-12 16:00 in Propeller 1
I have built a serial sniffer. It records data to an sd card. So that it doesn't hold a file open forever and risk losing data I close the file after each receive/write operation for either the TX or RX loop and open the next time with append.

But that's not my problem as that part works fine. Also in order to not have a huge file I close the file and open a new file at the top of every hour. So for example assuming the current time and date my file name for this hour would be 0211_18.TXT.

This works up until it is midnight. I get all the other date/hour file labels in order from 1 - 23 but at midnight tonight it will write it as 0212_12.TXT instead of 0212_00.TXT.


I set the RTC in 24 hour mode and if I set the time to say 23:58 and invoke the Clock.GetHour method it will return 23. If I invoke the Clock.IsPM method it will return 1. Both as expected. So I let it run and the time rolls over from 23:59:59 to 00:00:00 and it produces the correct filename: 0212_00.TXT

The kicker is if I let it run overnight it does not produce the correct date/time file name. At midnight it shows 0212_12.TXT. So through the night it would produce the file names in this order:

(starting at 10:00 PM on Feb 10th)

0210_22.TXT
0210_23.TXT (11 PM)
0211_12.TXT (Midnight)
0211_01.TXT (1 AM)
0211_02.TXT

and so on till it get to 11 AM

0211_11.TXT
0212_12.TXT (file already exists so it just "appends" to it)
0212_13.TXT

on so on...

So I don't get why I can set the clock to 23:58 (11:58 PM) and let it run and it works correctly but if I let it run from say now (18:49 {6:49 PM}) till tomorrow morning it will be wrong again.

Here's my code that's pertinent to the date/time file name methods:
  repeat

    ' other code in here...

    
    Minute := clock.GetMinutes                          ' Get minutes from RTC
    if Minute == 59                                     ' Check to see if we are in last minute of the hour
      old := 1                                          ' Set flag for last minute of hour...
    if Minute == 0                                      ' If it is top of the hour...
      if old == 1                                       ' and it was previously 59 minutes...
        sd.pclose                                       ' close any open file
        open_write                                      ' start a new file with new name
        sd.SDStr(clock.FmtDateTime)                     ' then write the new date and time to SD card
        sd.pputc(13)
        sd.pclose                                       ' and close file.
        old := 0                                        ' Reset flag

pub open_write  | l

  make_filename

  l := \sd.popen(@FileName, "r")
  if l == -1                                            ' If file does not exist then start a new file
    clock.Update
    sd.setdate(2000 + clock.GetYear, clock.GetMonth, clock.GetDay, clock.GetHour, clock.GetMinutes, clock.GetSeconds / 2)         ' Write Date / Time to SD card        
    l := \sd.popen(@FileName,"w")
    if l == -1
      ser.str(DEBUG,string("SD Error 2", 13))
    else
      ser.str(DEBUG,string("Ready... ", 13))   
  else                                                  ' Else if file already exists then just append file
    l := \sd.popen(@FileName,"a")
    if l == -1
      ser.str(DEBUG,string("SD Error 2A", 13))
    else
      ser.str(DEBUG,string("Ready (A)... ", 13))   

pub make_filename

'' Makes file name using date and hour in this format: mmdd_hh.txt

  make_nfilename(clock.GetMonth, @FileName, 0)
  make_nfilename(clock.GetDay, @FileName, 2)
  make_nfilename(clock.GetHour, @FileName, 5)

dat

FileName byte  "0000_00.TXT", 0  


And here's Roy's RTC code. It's the same one used on the Spinneret. I modified a couple items as noted.
'' S-35390A RTC Driver  Version 1.0
'' Copyright (c) 2010 Roy ELtham
'' November 13, 2010
'' See end of file for terms of use

'' This uses the basic i2c driver available on the obex site
'' 
'' This is the RTC chip used on the Spinneret Web Server.
''


'' 01-23-2011 - (Beau Schwabe added 'FmtDateTime') to PUB operations
'' 02-08-2014 - Added Update to Hours / Minutes / Seconds methods

CON
    _clkmode = xtal1 + pll16x
    _clkfreq = 80_000_000
    
    SCL       = 28           ' SCL pin of the s-35390A, SDA is assumed to be
                             ' one pin higher by the i2c driver
                             
    DeviceID  = %0110_0000   ' S-35390A device id
    
    ' S-35390A commands, These are combined with DeviceID to read or write the chip registers
    Command_Status1         = %0000_0000
    Command_Status2         = %0000_0010
    Command_DateTime        = %0000_0100
    Command_Time            = %0000_0110
    Command_Alarm1          = %0000_1000
    Command_Alarm2          = %0000_1010
    Command_ClockCorrection = %0000_1100
    Command_UserData        = %0000_1110
    
    ' these are combined with the DeviceID and commands to
    ' indicate reading or writing of the data
    Read  = 1
    Write = 0

OBJ
 i2c   : "basic_i2c_driver_1"

VAR
  long Status1, Status2
  long DateTime[7]        ' year(0 to 99), month, day, day of week (0 to 6), hour, minute, seconds
  long Mode24Hour         ' 0 = 12 hour mode, 1 = 24 hour mode
  long PM                 ' 0 = AM, 1 = PM
  long AlarmOccurred[2]   ' status of the 2 alarms, 0 = did not occur, 1 = did occur

  byte DataBuffer[30]

PUB start

  i2c.Initialize(SCL)

  ' disable the alarms (int pins) 
  SetStatus2(%0000_0000)
  
  Update

PUB FmtDateTime|p,r

    Update                      ' Update RTC values
    p := string("SunMonTueWedThuFriSat")                ' changed order 10/24/2012. Mon was first.
    r := DateTime[3]            ' GetDayOfWeek
    bytemove(@DataBuffer,p+r*3,3)
    DataBuffer[3] := " "
    p := string("JanFebMarAprMayJunJulAugSepOctNovDec")
    r := DateTime[1]-1          ' GetMonth
    bytemove(@DataBuffer+4,p+r*3,3)
    DataBuffer[7] := " "
    r := DateTime[2]            ' GetDay
    DataBuffer[8] := (r/10)+48    
    DataBuffer[9] := r-((r/10)*10)+48
    DataBuffer[10] := ","

    DataBuffer[11] := " "
    DataBuffer[12] := "2"
    DataBuffer[13] := "0"
    r := DateTime[0]            ' GetYear
    DataBuffer[14] := (r/10)+48
    DataBuffer[15] := r-((r/10)*10)+48
    DataBuffer[16] := " "
    r := DateTime[4]            ' GetHour
    DataBuffer[17] := (r/10)+48    
    DataBuffer[18] := r-((r/10)*10)+48
    DataBuffer[19] := ":"
    r := DateTime[5]            ' GetMinutes
    DataBuffer[20] := (r/10)+48    
    DataBuffer[21] := r-((r/10)*10)+48
    DataBuffer[22] := ":"
    r := DateTime[6]            ' GetSeconds
    DataBuffer[23] := (r/10)+48    
    DataBuffer[24] := r-((r/10)*10)+48
    DataBuffer[25] := " "
    'If Mode24Hour == 0                                 ' This routine doesn't work right.
    '   If PM == 0
    '      DataBuffer[26]:="A"
    '   else
    '      DataBuffer[26]:="P"
    '   DataBuffer[27]:="M"
    'else      
    DataBuffer[28]:=0         
    return @DataBuffer
  
PUB GetYear
  Update                        ' Added 2/8/2014 so correct time is shown when requested 
  return DateTime[0]
  
PUB GetMonth
  Update                        ' Added 2/8/2014 so correct time is shown when requested 
  return DateTime[1]
  
PUB GetDay
  Update                        ' Added 2/8/2014 so correct time is shown when requested 
  return DateTime[2]
  
PUB GetDayOfWeek
  Update                        ' Added 2/8/2014 so correct time is shown when requested 
  return DateTime[3]
  
PUB GetHour
  Update                        ' Added 2/8/2014 so correct time is shown when requested
  return DateTime[4]
  
PUB GetMinutes
  Update                        ' Added 2/8/2014 so correct time is shown when requested    
  return DateTime[5]
  
PUB GetSeconds
  Update                        ' Added 2/8/2014 so correct time is shown when requested    
  return DateTime[6]
  
PUB Is24HourMode
  Update                        ' Added 2/8/2014 so correct time is shown when requested  
  return Mode24Hour
  
PUB IsPM
  Update                        ' Added 2/8/2014 so correct time is shown when requested  
  return PM


'' This functions tells you if the indicated alarm occurred since the last time you called
'' this function. These flags are potentially set by calls to GetStatus1, Update, SetTime, or SetDateTime.
'' The flag for the indicated alarm is cleared by this function so it can be triggered again later.
'' whichAlarm should be 0 or 1
PUB DidAlarmOccur(whichAlarm)

  ' clamp incoming value
  whichAlarm := 0 #> whichAlarm <# 1
  result := AlarmOccurred[whichAlarm]
  AlarmOccurred[whichAlarm] := 0


'' state should be 0 for 12 hour mode or 1 for 24 hour mode
PUB Set24HourMode(state)

  ' clamp incoming value
  Mode24Hour := 0 #> state <# 1

  ' read the current Status1 value
  Status1 := GetStatus1

  ' update the 12/14 bit based on state
  if Mode24Hour == 1
    Status1 |= %0100_0000       ' or in the bit
    ' fix up our stored hour
    if PM == 1 AND DateTime[4] < 12
      DateTime[4] += 12
  else
    Status1 &= %1011_1111       ' mask off the bit
    ' fix up our stored hour
    if PM == 1
      DateTime[4] -= 12
      ' in 12 hour mode change hour 0 to 12
      if DateTime[4] == 0
        DateTime[4] := 12

  SetStatus1(Status1)

    
'' The hour is expected in 24 hour mode, it will be converted into 12 hour mode if you have set that mode.
'' I did this to save having another parameter passed in for the AM/PM flag which would be ignored in
'' 24 hour mode.
'' dayOfWeek is a value from 0 to 6, you can set it to whatever you want for whatever day, and the clock will just 
'' increment it each day, wrapping at 6 back to 0. You probably want to use 0 to either Sunday or Monday.
'' It only matters if you set an alarm with a dayOfWeek enabled.
PUB SetDateTime(month, day, year, dayOfWeek, hour, minutes, seconds) | index
  DateTime[0] := ConvertToBCD(year)
  DateTime[1] := ConvertToBCD(month)
  DateTime[2] := ConvertToBCD(day)
  DateTime[3] := ConvertToBCD(dayOfWeek)
  
  ' adjust hour based on 12/24 hour mode
  if Mode24Hour == 0 AND hour > 11
    hour -= 12
    DateTime[4] := ConvertToBCD(hour) | %0100_0000
  else
    DateTime[4] := ConvertToBCD(hour)
    
  DateTime[5] := ConvertToBCD(minutes)
  DateTime[6] := ConvertToBCD(seconds)

  ' write out the full date and time to the chip
  i2c.Start(SCL)
  i2c.Write(SCL, DeviceID | Command_DateTime | Write)
  repeat index from 0 to 6 
    i2c.Write(SCL, DateTime[index] >< 8)
  i2c.Stop(SCL)

  ' reread the date & time to fix our stored values
  Update

'' The hour is expected in 24 hour mode, it will be converted into 12 hour mode if you have set that mode.
'' I did this to save having another parameter passed in for the AM/PM flag which would be ignored in
'' 24 hour mode.
PUB SetTime(hour, minutes, seconds) | index

  ' adjust hour based on 24/12 hour mode
  if Is24HourMode == 0 AND hour > 11
    hour -= 12
    DateTime[4] := ConvertToBCD(hour) | %0100_0000 ' or in the am/pm flag (high = pm)
  else
    DateTime[4] := ConvertToBCD(hour)
    
  DateTime[5] := ConvertToBCD(minutes)
  DateTime[6] := ConvertToBCD(seconds)

  ' write out the time to the chip
  i2c.Start(SCL)
  i2c.Write(SCL, DeviceID | Command_Time | Write)
  repeat index from 4 to 6 
    i2c.Write(SCL, DateTime[index] >< 8)
  i2c.Stop(SCL)
  
  ' reread the date & time to fix our stored values
  Update
  
'' Sets the indicated alarm. The hour value is expected to be in 24 hour mode (like SetTime)
'' if dayOfWeek, hour, or minute are negative values then the alarm will not use that portion
'' of the setting.  For example, SetAlarm(0, -1, 10, 30) will have the alarm go off every morning
'' at 10:30am, SetAlarm(0, -1, -1, 15) will have the alarm go off 15 minutes after every hour of
'' every day, SetAlarm(0, -1, -1, -1) will disable the alarm.  
'' dayOfWeek is a value from 0 to 6, and corresponds with whatever you set in SetDateTime. If, when you called
'' SetDateTime, you set dayOfWeek to be 0 for Sunday, then a value of 3 here would mean Wednesday.
PUB SetAlarm(alarmIndex, dayOfWeek, hour, minutes) | Alarm[3], index

  ' Clamp to valid range
  alarmIndex := 0 #> alarmIndex <# 1

  ' Set the indicated alarm into alarm mode  
  Status2 := GetStatus2
  if alarmIndex == 0
    Status2 := Status2 & %0001_1111  ' clear the alarm 1 state flags
    Status2 := %0010_0000 | Status2  ' set the alarm 1 state flags to be in alarm mode
  else
    Status2 := Status2 & %1111_0001  ' clear the alarm 2 state flags
    Status2 := %0000_0010 | Status2  ' set the alarm 2 state flags to be in alarm mode
  SetStatus2(Status2)

  ' setup what we are going to write to the alarm registers based on the input params
  '  
  if dayOfWeek > 0
    Alarm[0] := ConvertToBCD(dayOfWeek) | %1000_0000
  else
    Alarm[0] := 0

  if hour > 0
    if Is24HourMode == 0 AND hour > 11
      hour -= 12
      Alarm[1] := ConvertToBCD(hour) | %0100_0000 ' or in the am/pm flag (high = pm)
    else
      Alarm[1] := ConvertToBCD(hour) | %1000_0000
  else
    Alarm[1] := 0

  if minutes > 0
    Alarm[2] := ConvertToBCD(minutes) | %1000_0000
  else
    Alarm[2] := 0

  ' write out the cooked alarm info to the chip
  i2c.Start(SCL)
  if alarmIndex == 0
    i2c.Write(SCL, DeviceID | Command_Alarm1 | Write)
  else
    i2c.Write(SCL, DeviceID | Command_Alarm2 | Write)
  repeat index from 0 to 2 
    i2c.Write(SCL, Alarm[index] >< 8)
  i2c.Stop(SCL)

'' These 2 functions allow you to read and write a byte of data to the clock chip that is saved across
'' power cycling the Spinneret. It will be saved as long as the SuperCap keeps the RTC chip going (days).
PUB GetUserData : result
  i2c.Start(SCL)
  i2c.Write(SCL, DeviceID | Command_UserData | Read)
  result := i2c.Read(SCL, i2c#NAK)
  i2c.Stop(SCL)
  return result

PUB SetUserData(value)
  i2c.Start(SCL)
  i2c.Write(SCL, DeviceID | Command_UserData | Write)
  i2c.Write(SCL, value)
  i2c.Stop(SCL)


PUB GetStatus1 : result
  i2c.Start(SCL)
  i2c.Write(SCL, DeviceID | Command_Status1 | Read)
  result := i2c.Read(SCL, i2c#NAK)
  i2c.Stop(SCL)

  ' check to see if either alarm occurred
  ' we need to do this any time Status1 is read since reading it clears the alarm flags 
  if result & %0000_1000 <> 0
    AlarmOccurred[0] := 1
  if result & %0000_0100 <> 0
    AlarmOccurred[1] := 1

  ' Clear off the alarm flags and the reset bit (high bit).
  ' We clear the reset bit because we don't want to make it easy to accidentally reset the
  ' chip. Which could happen if we read Status1 and then use that value modified to write
  ' back to Status1.
  result &= %01110011
  
  return result

PUB SetStatus1(value)
  i2c.Start(SCL)
  i2c.Write(SCL, DeviceID | Command_Status1 | Write)
  i2c.Write(SCL, value)
  i2c.Stop(SCL)

PUB GetStatus2 : result

  i2c.Start(SCL)
  i2c.Write(SCL, DeviceID | Command_Status2 | Read)
  result := i2c.Read(SCL, i2c#NAK)
  i2c.Stop(SCL)

  ' Clear off the low bit, this si the test bit and should always be zero.
  result &= %1111_1110
  
  return result

PUB SetStatus2(value)

  ' Clear off the low bit, this si the test bit and should always be zero.
  value &= %1111_1110
  
  i2c.Start(SCL)
  i2c.Write(SCL, DeviceID | Command_Status2 | Write)
  i2c.Write(SCL, value)
  i2c.Stop(SCL)


'' Read the full date and time from the chip. Also, updates our PM flag appropriately, and
'' updates the cached Status variables as well as the AlarmOccurred variable.
PUB Update | index, temp
  
  i2c.Start(SCL)
  i2c.Write(SCL, DeviceID | Command_DateTime | Read)
  
  ' read first 6 bytes
  repeat index from 0 to 5 
    temp := i2c.Read(SCL, i2c#ACK) >< 8

    ' the am/pm flag is valid in both 12 and 24 hour mode
    if index == 4 ' index 4 is the hour
      if temp & %0100_0000 ' check the am/pm flag bit
        PM := 1
        temp &= %1011_1111 ' mask off the am/pm flag bit
      else
        PM := 0            ' it's AM to clear the PM flag
        
    DateTime[index] := ConvertFromBCD(temp)
    
  ' read last byte   
  DateTime[6] := ConvertFromBCD(i2c.Read(SCL, i2c#NAK) >< 8)
  
  i2c.Stop(SCL)

  ' in 12 hour mode change hour 0 to 12
  if Mode24Hour == 0 AND DateTime[4] == 0
    DateTime[4] := 12

  ' update cached status variables by reading the chip status
  ' this will also update the Alarm1_Occurred and Alarm2_Occurred variables
  Status1 := GetStatus1
  Status2 := GetStatus2  

  
'' read the detailed information about clock correction in the S-35390A datasheet
'' before using these functions
PUB SetClockCorrection(value)
  i2c.Start(SCL)
  i2c.Write(SCL, DeviceID | Command_ClockCorrection | Write)
  i2c.Write(SCL, value)
  i2c.Stop(SCL)

PUB GetClockCorrection : result
  i2c.Start(SCL)
  i2c.Write(SCL, DeviceID | Command_ClockCorrection | Read)
  result := i2c.Read(SCL, i2c#NAK)
  i2c.Stop(SCL)
  return result


PRI ConvertToBCD(value) : result
  ' convert a long to Binary Coded Decimal
  result := ((value / 10) * 16) + (value // 10) 
  return result

PRI ConvertFromBCD(value) : result
  ' convert from Binary Coded Decimal to a long
  result := ((value / 16) * 10) + (value // 16) 
  return result
  
{{
                            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.
}}


Any help trying to figure this out would be greatly appreciated.

Thanks
Don

Comments

  • kwinnkwinn Posts: 8,697
    edited 2014-02-11 18:10
    Try putting a short (1 - 10 sec) delay after the "if Minute == 0" statement.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-02-11 18:14
    I haven't used this particular RTC code but it kind seems like there's something going on with the 24 hour mode setting.
    Don M wrote: »
    I set the RTC in 24 hour mode

    Are you doing this with by calling Set24HourMode?
    Clock.Set24HourMode(1)
    
    Don M wrote: »
    If I invoke the Clock.IsPM method it will return 1.

    What do you get when you call Clock.Is24HourMode?

    Sorry if you've already checked these things. Just throwing out wild guesses.
  • kwinnkwinn Posts: 8,697
    edited 2014-02-11 18:19
    If that doesn't work perhaps you should close the current file and start a new one when the hour changes, and make sure when it is at 23 that it changes to 00 rather than 12.

    This problem may be due to how the RTC changes the hour in 24hr mode. I had one RTC chip that went from 23:59:59 to 12:00:00, then very quickly to 00:00:00. Only reason I caught it was that time data was latched and sent to the PC using the 1PPS pulse from the RTC.
  • Don MDon M Posts: 1,652
    edited 2014-02-11 18:28
    kwinn wrote: »
    If that doesn't work perhaps you should close the current file and start a new one when the hour changes

    My code does that now. I thought about putting in some code to check but I shouldn't have to.

    @Duane - I'll look into your suggestions.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-02-11 18:34
    Don M wrote: »
    @Duane - I'll look into your suggestions.

    Keep in mind they're just wild guesses.
  • Don MDon M Posts: 1,652
    edited 2014-02-12 15:25
    Duane Degn wrote: »
    I haven't used this particular RTC code but it kind seems like there's something going on with the 24 hour mode setting.

    Are you doing this with by calling Set24HourMode?
    Clock.Set24HourMode(1)
    

    Yes.
    Duane Degn wrote: »
    What do you get when you call Clock.Is24HourMode?

    Sorry if you've already checked these things. Just throwing out wild guesses.

    This is interesting... I get 0. So I'll have to see why. The current time even shows as 22:24 so I don't know why it displays the time that way but thinks its not in the 24 Hr mode.

    Like you say there must be a bug in the clock object code.
  • Don MDon M Posts: 1,652
    edited 2014-02-12 16:00
    This part of the clock object code may be where the problem is. I'll have to dig through it to try and understand what's happening here...
    PUB Set24HourMode(state)
    
      ' clamp incoming value
      Mode24Hour := 0 #> state <# 1
    
      ' read the current Status1 value
      Status1 := GetStatus1
    
      ' update the 12/24 bit based on state
      if Mode24Hour == 1
        Status1 |= %0100_0000       ' or in the bit
        ' fix up our stored hour
        if PM == 1 AND DateTime[4] < 12
          DateTime[4] += 12
      else
        Status1 &= %1011_1111       ' mask off the bit
        ' fix up our stored hour
        if PM == 1
          DateTime[4] -= 12
          ' in 12 hour mode change hour 0 to 12
          if DateTime[4] == 0
            DateTime[4] := 12
    
      SetStatus1(Status1)
    
    
Sign In or Register to comment.