Shop OBEX P1 Docs P2 Docs Learn Events
Code help "keeping time" — Parallax Forums

Code help "keeping time"

BitsBits Posts: 414
edited 2013-01-05 08:45 in Propeller 1
I am having a problem with time. After one hour my timing begins to loos a second and keeps loosing as time increases.
Pub Master_Loop | mSec ,static


  Timex := 0
  Static := Clkfreq / 100    
  mSec := Cnt + Static                          
              
  Repeat                
      IF (CNT - mSec => 0)   
          mSec := mSec + static            
          Get_mSeconds                                                                                                                                     
          IF (TimeX++ == 100)                             
                                TimeX := 0
                                Get_Seconds

          

Any ideas?

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2013-01-03 12:01
    You're having problems with rollover where CNT or mSec become negative as the time becomes greater than 2^30. Rather than "IF (CNT - mSec => 0)", I'd do something like:
    Static := Clkfreq / 100
    lastTime := CNT
    time10ms := timeSeconds := 0
    repeat
       if (CNT - lastTime) => Static
          lastTime += Static
          if ++time10ms == 100
             time10ms := 0
             timeSeconds++
    
  • BitsBits Posts: 414
    edited 2013-01-03 12:12
    Thanks Mike, but it fails horribly and only after a minute. I was sure that negative and positive were handled in my function up there. Could it be that one of my functions are taking to long?
  • Dave HeinDave Hein Posts: 6,347
    edited 2013-01-03 12:24
    I think the original code is OK except that TimeX should be pre-incremented instead of post-incremented, or you should compare with 99 instead of 100. I believe the original code incrementes TimeX 101 times before it is reset to zero.
  • Mike GreenMike Green Posts: 23,101
    edited 2013-01-03 12:59
    Thanks, Dave. I made the same mistake (see corrected code in Post #2).
  • JonnyMacJonnyMac Posts: 9,108
    edited 2013-01-03 19:28
    I did this on an industrial application about a year ago. The normal sensor scan was every second but I broke that down into smaller chucks to deal with incoming MODBUS messages as quickly as possible.
    var
    
      long  millis
      long  seconds
      long  minutes
      long  hours
    
    
    pub main | t
    
      t := cnt
      repeat
      
        ' main loop code / calls
    
        waitcnt(t += (clkfreq >> 2))
        if ((millis += 250) == 1000)
          millis := 0
          if (++seconds == 60)
            secs := 0
            if (++minutes == 60)
              minutes := 0
              if (++hours == 24)
                hours := 0
    
  • Mark_TMark_T Posts: 1,981
    edited 2013-01-03 19:39
    Both methods of comparing times are correct in Spin as all values are implicitly signed and both take the difference of two timestamps before comparison:
          IF (CNT - mSec => 0)   
    
       if (CNT - lastTime) => Static 
    
    

    If timestamps were treated as unsigned then the first comparison would always return true (this is the case on the Arduino for instance).

    The gotcha to be avoided is comparing two timestamps. Always compare the difference of timestamps with a time interval (and
    be aware of signs where necessary). The interval must be less than 2^31 (or 2^32 for unsigned long in C)
       if (CNT > target_time)   ' broken comparison, fails at wraparound.
        target_time += Static
    
    
  • BitsBits Posts: 414
    edited 2013-01-04 09:12
    Wow this is so fustrating,

    Mike your new code works the same as my original code in that they both loose time after a hour. Am I missing something simple? Perhaps my functions are taking to long and I need to speed up the count by doing this.

    Using mikes example
    [b]Static := Clkfreq / 1000[/b]
    lastTime := CNT
    time10ms := timeSeconds := 0
    repeat
       if (CNT - lastTime) => Static
          lastTime += Static
          if ++time10ms == [b]1000[/b]
             time10ms := 0
             timeSeconds++ 
    

    I am going to test this now but as I test this can you think of anything that I may be forgetting?
  • Dave HeinDave Hein Posts: 6,347
    edited 2013-01-04 09:30
    Bits, what is your clock frequency? If it's not divisible by 100 or 1000 the value of static will not be accurate. How are you driving the Prop's clock? Can you post all your code?
  • BitsBits Posts: 414
    edited 2013-01-04 09:58
    Dave
    CON
      _clkmode = xtal1 + pll16x                            
      _xinfreq = 5_000_000
    

    I would post my code as soon as I can.
    And the example above still counts off after the same hour of time.
  • lonesocklonesock Posts: 917
    edited 2013-01-04 10:06
    I usually do something like this:
    pub RefreshSeconds | add_s
      add_s := (cnt - seconds_cnt) / clkfreq
      seconds += add_s
      seconds_cnt += add_s * clkfreq
      return seconds
    
    Both 'seconds' and 'seconds_cnt' need to be globals. But as long as you call this function more frequently than 1/2 the counter roll-over period, it keeps time well.

    Jonathan
  • JonnyMacJonnyMac Posts: 9,108
    edited 2013-01-04 10:12
    Bits:

    This seems to be working -- give it a go (I lifted the logic from the bit timing of FullDuplexSerial).
    var
    
      long  seconds
    
    
    pub main | period, et1, et2
    
      term.start(RX1, TX1, %0000, 115_200)
      pause(10)
    
      term.str(string(CLS, "Hello, World!", CR, CR))
    
      period := clkfreq                                             ' set 
      
      et1 := cnt + period                                           ' sync timer
      repeat
    
        { loop code here }
    
    
        ' update running time
      
        et2 := cnt                                                  ' capture elapsed ticks
        if ((et1 - et2) < 0)                                        ' has full period elapsed?
          ++seconds                                                 '  yes, increment
          et1 += period                                             '  and re-sync timer
    
          ' show running clock for testing (remove later)
    
          dec2(seconds / 3600)
          term.tx(":")
    
          dec2((seconds // 3600) / 60)
          term.tx(":")
    
          dec2(seconds // 60)
          term.tx(CR) 
    
    
    pub dec2(value)
    
      if (value < 10)
        term.tx("0")
      else
        term.hex(value / 10, 1)                                     ' faster than dec for 1 digit
    
      term.hex(value // 10, 1)
    


    Found and fixed a "gotcha" in the re-sync line


    Update: I started the program and a stopwatch on my phone at the same time, then went out and ran errands for a couple hours. On my return the two were in perfect sync. I'm now going to use this code in my book! (how to create an RTC when you're out of cogs).
  • JonnyMacJonnyMac Posts: 9,108
    edited 2013-01-05 08:45
    I've attached a slightly more fleshed out version that updates RTC registers.
Sign In or Register to comment.