Shop OBEX P1 Docs P2 Docs Learn Events
the best way to code a clock method? — Parallax Forums

the best way to code a clock method?

varnonvarnon Posts: 184
edited 2012-06-21 21:29 in Propeller 1
Hey guys.


So I've been working on some code to make it easy for myself and my colleagues to conduct experiments. I've been using it for about six months and its working pretty well. Now it's time for me to proofread, fix up some things and seeing how well it works for my colleagues. One big thing we need for experiments is something to keep track of time. I would like to do this in software so that no one has to worry about finding the right RTC or other part.


This is the code I have been using. Another method launches this on a new cog where it runs forever.
The value "clock" is incremented every millisecond, and is referred to by other code. The clock should run for something like 24 days before it resets. I've tested it up to 7 hours and it is still perfectly accurate. Probably no one will ever have the clock run for more than an hour, but I like to have the option.

Here is the code:
PRI ExpClock | systime


systime:=cnt                                                            ' Finds current value of system counter  
repeat
    waitcnt(systime += clkfreq/1000)                                    ' Waits one millisecond. A synchonized pause.
    clock:=clock+1                                                      ' After a millisecond passes, add 1 to the clock
    if clock>2147483000                                                 ' If the clock needs reseting
      clock:=0                                                          ' Reset the clock


My questions:

First, I don't really get signed longs in spin.
Is there a way to use a long in an unsigned form (0 to 4,294,967,296) instead of the signed form (- 2,147,483,648 to +2,147,483,648) ? It honestly wouldn't really matter anyway, nobody is going to run the thing for more than a few hours, but it would still be nice to use the full potential for the long. (My apologies if I am completely misunderstanding signed vs unsigned.)

Second, Is there a better way to set this up in general?
I've thought about it a bit, and I've been using it for six months... but I have to wonder if there a whole cog is really needed for this. Again, I don't think anyone will ever need all 8 cogs, but if there is a better way I would like to consider it.

Any thoughts are much appreciated.

Comments

  • jmgjmg Posts: 15,183
    edited 2012-06-19 15:30
    varnon wrote: »
    ... but I have to wonder if there a whole cog is really needed for this. Again, I don't think anyone will ever need all 8 cogs, but if there is a better way I would like to consider it.

    You cannot use half a cog ;) - but you could get this cog to do more, if you want.
    The WAIT line represent spare processing resource, so provided you can 'still get it done' inside the 1ms budget here, you can add something else to do in this under-used COG.
  • cavelambcavelamb Posts: 720
    edited 2012-06-19 16:35
    I think it's pretty slick, Varnon. I love straight forward simple code.

    Like jmg said, you can do more in this cog.
    Maybe move the division operation outside the repeat loop (clkfreq/1000)
    to provide more available time inside it?
    IF there is something else to do there?
    Like break it down into seconds, hours, etc.

    It certainly looks like a good starting point for a soft clock.
  • Cluso99Cluso99 Posts: 18,069
    edited 2012-06-19 17:09
    Take a look at the two main SD drivers - Kye's SD-MMC_FATEngine and mw-rawb_spi. These both have version with software RTCs embedded inthe SD card pasm drivers.
  • varnonvarnon Posts: 184
    edited 2012-06-21 12:15
    Thanks for considering the topic guys.

    Cluso, I knew somebody would have an alternate idea. I really appreciate it. I use an SD card driver anyway, so it would be a great way to save a cog.
    For what ever reason my method is more accurate, especially when polling the time repeatedly at smaller intervals. MW-rawb_spi starts producing weird results if you poll in the order of every 10 milliseconds. Although I probably won't need super high accuracy, I think it is worth sacrificing a cog.

    Thanks for the thoughts.
  • AribaAriba Posts: 2,690
    edited 2012-06-21 14:55
    I don't see a good reason to reset the clock after ~24 days. This is simpler and counts with the full 32 bits:
    PRI ExpClock | systime
    
    systime:=cnt                             ' Finds current value of system counter  
    repeat
        waitcnt(systime += clkfreq/1000)     ' Waits one millisecond. A synchonized pause.
        clock++                              ' After a millisecond passes, add 1 to the clock
    
    Yes, Spin will interprete it as a negative value if the highest bit is set. But there are some logical instructions which works with unsigned longs, like & and >> which can be useful here:
    ms := ExpClock                   'will be negative after ~24 days 
      ms := ExpClock & $7FFFFFFF       'will do the same as your code
      ms2 := ExpClock>>1               '2ms resolution but up to ~49 days
      ms10 := ExpClock>>1 / 5          '10ms resolution up to ~49 days
    

    ... but I have to wonder if there a whole cog is really needed for this
    If you have a free pin you can do it also with the two counters. One counter outputs a frequency of 1 kHz at a pin, the other counts the positive edges at the same pin and increments therfore every 1 ms. You can use one of the EEPROM pins (P28) for that, if you don't have other I2C devices connected at P28, P29.

    Andy
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-06-21 21:14
    I don't think you need to use a cog for this.

    If you have a cog that's under used already you could have a method to check if a certain interval has passed.

    Here's my soft clock method.
    PRI CheckClock | {localTime, oneSecond,} daysInMonth
      if (cnt - clockTime) => oneSecond
        
        clockTime += oneSecond
             
        if ++seconds > 59
          seconds := 0      
          if ++minutes > 59
            minutes := 0
            
            if ++mHours > 23          
              mHours := 0
              if ++date > daysInMonth
                date := 1
                if ++months > 12
                  months := 1
                  years++            
                daysInMonth := lookup(months: 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
                if months == 2
                  if ((years // 4 == 0) and (years // 100 <> 0)) or (years // 400 == 0)
                    daysInMonth := 29
                  else
                    daysInMonth := 28
              if ++dayOfWeek > 7
                dayOfWeek := 1
            amHours := mHours // 12
            if amHours == 0
              amHours := 12          
          if displayMode == _ClockDisplay
            Display(_MinutesPlace)
          if displayMode == _ClockDisplay
            Display(_HoursPlace)
        if displayMode == _ClockDisplay
          Display(_SecondsPlace)
                
    
    

    "oneSecond" had been previously defined as clkfreq. If you could use a smaller interval of time if you could call this method frequently enough.

    This method is from my 7-segment clock project, so there's additional code in the method to display the time.

    The main aspect of this method is the contition check "if cnt - previousTime > interval". This lets the cog do other tasks instead of spending most of its time waiting with "waitcnt".

    I'm pretty sure parts of the above method have been copied from some other source.

    Edit: I realize I've never left my clock running more than a few hours at a time. I just found (and fixed) a bug with the way it was keeping track of hours. I'm not sure if there are other bugs in the code. The main point if the above code is to illustrate the way to check for the passage of a set interval of time.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-06-21 21:29
    varnon wrote:
    Is there a way to use a long in an unsigned form (0 to 4,294,967,296) instead of the signed form (- 2,147,483,648 to +2,147,483,648) ?
    Yes, x ^ $8000_0000 > y ^ $8000_0000 is the unsigned equivalent of x > y. It works over the entire range of values from 0 to 4_294_967_295.

    -Phil
Sign In or Register to comment.