Shop OBEX P1 Docs P2 Docs Learn Events
Is there a way to make a Millisecond Counter WITHOUT using a cog? — Parallax Forums

Is there a way to make a Millisecond Counter WITHOUT using a cog?

DavidMDavidM Posts: 630
edited 2011-03-12 21:17 in Propeller 1
Hi,

I have a Millisecond Timer Object that used throughout my current project, and to do this I use a COG, that simply uses the system counter. This millisecond counter simply updates a Global variable passed to it via a pointer in from MAIN LOOP (Cog 0)

Q1) Is there a way to make a Millisecond Counter WITHOUT using a cog?

As I need more cogs for other tasks.

I was looking at the other features of the counters (i.e CNTB, FRQA,FRQB, PHSA, PHSB ) but I don't understand what they do. Can any of these be used?

Thanks

Dave M

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2011-03-10 15:59
    Counters can certainly be used, problem is that 1ms (1kHz) isn't exactly a good choice when it comes to dividing the system clock. Also, I assume that the time is used by several cogs. In this case you'll still need s/w support to transfer the counter value(s) to hub RAM (to make them public).
  • DavidMDavidM Posts: 630
    edited 2011-03-10 16:23
    Hi Kuroneko,

    Thanks for the reply.

    So you say it CAN be done, But dividing the counter to get 1000 hz is not a good choice?

    So How does this work?

    Dave M
  • lonesocklonesock Posts: 917
    edited 2011-03-10 16:35
    Something like this may work for you.
    ''  Timing
    DAT
    mstimer_lock  long      0
    mstimer_ms    long      0
    mstimer_last  long      0
    PUB ms_register
      ms_unregister
      mstimer_last := cnt
      mstimer_lock := locknew + 1
    PUB ms_unregister
      if mstimer_lock
        lockclr( mstimer_lock~ - 1 )
    PUB ms | delta_ms
      repeat until lockset( mstimer_lock-1 )
      delta_ms := (cnt - mstimer_last) / (clkfreq / 1000)
      mstimer_ms += delta_ms
      mstimer_last += delta_ms * (clkfreq / 1000)
      lockret( mstimer_lock-1 )
      return mstimer_ms
    
    Notes:
    * if your clock rate isn't an exact multiple of 1000, you'd probably want to handle the integer math better
    * you need to make sure you call this more frequently than the counter wrapping
    * the lock lets you call this from multiple cogs
    * you need to call ms_register before using the ms function
    * the variables are in DAT instead of VAR, so only a single copy of this object will ever run.

    Jonathan
  • kuronekokuroneko Posts: 3,623
    edited 2011-03-10 16:37
    I was trying to say that you can use counters for timing stuff in general. But as you noticed 1kHz isn't exactly binary friendly. Quick and dirty (assuming 80MHz): divide by 8 which gives you 0.1us resolution which is fed into a second chained counter (unfortunately eating a pin). Anyway, that's why I asked how you plan to get the data to hub RAM. In this case the actual resolution wouldn't really matter as you could scale the time before writing it. For example even if your resolution is 0.2ms you could do some arithmetic to get human readable time :)

    What are your requirements on accuracy and unique-ness (i.e. how long until roll-over is acceptable)? And do you really need 1ms at this level, i.e. would any increasing timestamp do (which could then be adjusted later)?
  • groggorygroggory Posts: 205
    edited 2011-03-10 16:48
    kuroneko wrote: »
    What are your requirements on accuracy and unique-ness (i.e. how long until roll-over is acceptable)? And do you really need 1ms at this level, i.e. would any increasing timestamp do (which could then be adjusted later)?

    Exactly. I have a similar problem and those questions are exactly what you need to answer in order to narrow down the huge number of ways you can tackle this problem.
  • DavidMDavidM Posts: 630
    edited 2011-03-10 17:02
    HI Kuroneko,

    you said "(unfortunately eating a pin)",So to use these counters you need a spare pin? I actually don't have any spare pins in my project! They are all used up on my current PCB.

    Thanks anyway!

    Dave M
  • kuronekokuroneko Posts: 3,623
    edited 2011-03-10 17:11
    Yes, unfortunately you have to go outside in order to chain counters. Using a single counter - while workable - suffers from relatively fast roll-over (same as using cnt but you can reset it when required).
  • RaymanRayman Posts: 14,880
    edited 2011-03-10 19:49
    I've found ways of integrating a RTC into cogs meant for other things, such as display.
    Do you have any cogs doing ordinary things?
  • DavidMDavidM Posts: 630
    edited 2011-03-10 21:37
    Yes, I have a cog that used just to control a Buzzer, I am planning to integrate this into my timer COG, as All I need to do is turn a pin on or off, So this should not effect the main timing,

    What is your RTC idea?

    Thanks

    Dave M
  • groggorygroggory Posts: 205
    edited 2011-03-11 02:21
    DavidM wrote: »
    Yes, I have a cog that used just to control a Buzzer, I am planning to integrate this into my timer COG, as All I need to do is turn a pin on or off, So this should not effect the main timing,

    What is your RTC idea?

    Thanks

    Dave M

    It always interests me to see how people use their cogs and configure their projects.

    Would you be willing to sketch out your cog use and roughly outline the devices hooked up to the prop?
  • DavidMDavidM Posts: 630
    edited 2011-03-11 03:34
    groggory wrote: »
    It always interests me to see how people use their cogs and configure their projects.

    Would you be willing to sketch out your cog use and roughly outline the devices hooked up to the prop?

    HI ,
    Funny you mention this, I have a mud map I made the other day showing my OBJECTS vs COGS vs I/O, I need to "REORGANISE" My code to free up some cogs for my RS485 Protocol I am halfway through!

    I will see if I can "Draw " up a COG "Mud Map" soon,


    Dave M
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-11 06:11
    DavidM wrote:
    I actually don't have any spare pins in my project!
    As long as SDA remains high, you can do anything you want with SCL.

    -Phil
  • StefanL38StefanL38 Posts: 2,292
    edited 2011-03-11 09:36
    if you have a loop that can run very faster than a millisecond you might can scale down it to one millisecond

    things that should be repeated at a slower rate can be started by a second "timer-variable" like shown in the example-code
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      
    VAR
      long timer1
      long eTime1
    
    
    OBJ
      debug : "FullDuplexSerial"
    
    
    PUB Main
      'the FIRST PUB-Method inside a spinfile is ALWAYS the startpoint where the program starts to run    
      debug.start(31, 30, 0, 115200)
    
      timer1 := cnt 'make inital snapshot of systemcounter 
    
      repeat
        'do whatever you like inside this loop
        '
        'the method elapsed_time_msec calculates the time that has gone since
        'variable timer1 has set to the (former) value of systemcounter
        eTime1 := elapsed_time_msec(timer1) 
    
        if eTime1 => 1000
          debug.str(string("snapshot of systemcounter: timer1="))
          debug.dec(timer1)
          debug.str(string("  elapsed time="))
          debug.dec(eTime1)
          debug.tx(13)
          debug.tx(13)
          'make new snapshot of systemcouner to set new startvalue
          timer1 := cnt  
    
          
    PUB elapsed_time_msec(p_TimeVar) | RightNow, ClockTicks
    
      RightNow := cnt
    
      if RightNow < p_TimeVar                          
        ClockTicks := ||($FFFFFFFF - RightNow + p_TimeVar)  
      else
        ClockTicks := ||(RightNow - p_TimeVar)
    
      result := ClockTicks / (clkfreq / 1_000) 'calculate milliseconds
      ' SPIN treats longs as SIGNED longs meaning bit 32 represents the sign "+-"
      ' the systemcounter thinks of the 32bits as UNsigned meaning bit 32 is
      ' just another bit of the number and NOT a sign "+-" 
      ' if one or both values are negative it could happen
      ' that the result is negative too
      ' the command "||" calculates the absolute value of the SIGNED longs
    
      'if the systemcounter has wrapped around since snapshot of
      'systemcounter (value of parameter p_TimeVar),
      'this method calculates the timedifference in the right way
      
      'wrap-around example with easy numbers: counter maxvalue 1000
      'p_TimeVar containing value 980
      'time goes on counter wraps around from 1000 to 0
      'time goes on 
      'RightNow containing 300. This means 20 ticks until maximum 1000 and 300 ticks
      'since wrapping from 1000 to 0 in summary 320 ticks of time has gone
      'this is calculated by MaxCounter - p_TimeVar + RightNow
      'in numbers              1000     -   980     +   300     = 320
      'the real systemcounter has 32bits max value 2^32 -1 = 4294967295
      'hexadecimal $FFFFFFFF
     
    'end of PUB elapsed_time_msec(p_TimeVar) | RightNow, ClockTicks
    

    best regards

    Stefan
  • kwinnkwinn Posts: 8,697
    edited 2011-03-11 12:40
    David, you can use any spin main program loop that executes in less than 1mS to provide a 1mS clock provided that slowing that loop to execute every mS does not create a problem.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-11 12:43
    The point of my most recent post here is that, if you're not using the EEPROM interface after boot, you can use the SCL line to daisy-chain two counters in the manner that Kuroneko suggested. Hence, no need for any "babysitter code" to keep the plates spinning and the balls in the air. In fact, each cog can have its own millisecond (approx.) counter slaved off of the same master NCO, using that pin as an input.

    -Phil
  • max72max72 Posts: 1,155
    edited 2011-03-11 14:23
    I asked something similar, but for seconds counting, here
    http://forums.parallax.com/showthread.php?129913-Using-counters-for-long-term-seconds-time-keeping
    The same considerations apply to your case, even if you must count faster.
    If you have a fast loop somewhere you can check the cnt, without needing to use the waitcnt.
    This counts seconds.
      previous:=cnt+clkfreq
      repeat
         if (cnt-previous)>0
             seconds:=seconds+1
             previous:=previous+clkfreq
         your loop code here
    

    Massimo
  • groggorygroggory Posts: 205
    edited 2011-03-11 16:35
    The point of my most recent post here is that, if you're not using the EEPROM interface after boot, you can use the SCL line to daisy-chain two counters in the manner that Kuroneko suggested. Hence, no need for any "babysitter code" to keep the plates spinning and the balls in the air. In fact, each cog can have its own millisecond (approx.) counter slaved off of the same master NCO, using that pin as an input.

    -Phil

    Let me bring my example into this just a bit...

    If I have an external 32768 Hz clock source that feeds into p26 ... let's just say. And I have a cog that reads this clock internally in order to do some of it's timing routines using it's cog's counter module. If another cog wanted to access that counter value also, I don't think it can directly read the other cog's counter module. You could have that cog update a variable in hub RAM with that variable, but that takes some overhead. You could instantiate both cogs at approximately the same time and start reading that counter at the same time so they would have independent, but NEARLY identical clock values.

    Is there a way to 'reset' a counter in the propeller's counter module?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-11 17:04
    A cog can only access its own counters. However, you can synchronize slave counters in all cogs that need them, by starting them at a prescribed, future cnt value after all the cogs have started. Another way to synchronize them is to couple your external oscillator to A26 with a series resistor and to hold that pin low until all cogs have been started, then to reset A26 to an input state.

    Any counter can be reset by setting its phsx register to zero.

    -Phil
  • DavidMDavidM Posts: 630
    edited 2011-03-11 19:50
    HI,

    My eprom is being accessed throughout the running of the main loop at various times ( reading and writing Variables as well as large chunks of DATA), So the SDL Pin is not available.

    I run an LCD of the i@C line wi=which is updated all of the time, So basically I don't have a spare pin.

    My Main loops runs at about 10hz, and is over 1200 lines of code, it does a lot, so all my TIME CRITICAL Processing is done in the other cogs.

    I guess my project uses ALL the PINS , ALL the COGS, and now almost ALL the RAM!

    Another thing is that my MILLISECOND Timer is working VERY WELL and is used in MANY places and is the most important part of my project, So I really don't want to "Play Around" with it.

    Anyhow I have worked out a way to remove my BUZZER COG and merge its functionality with the MILLISECOND Timer code which has ample "Cycle time" for switching on/off a buzzer.

    Thanks for all the replies.

    I gather that these other counter features were NOT really designed for BASIC TIMING, It would have been nice to have some simple timers for each cog, Does the PROP 2 have something like this?

    OR It would be nice if we could SPLIT a cog, In 2 or more "INTERVALS", then we could theoretically have more cogs, Remember sometimes we need the Advantage of a cog running independently but does not always has to run at FULL SPEED if you know what I mean.

    Dave M
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-11 20:00
    DavidM wrote:
    I gather that these other counter features were NOT really designed for BASIC TIMING, It would have been nice to have some simple timers for each cog, Does the PROP 2 have something like this?
    AFAIK, there will be an "upper count register" for counters in the Prop II, in order to make divide-by-n counting easier. I'm not sure what's planned for daisy-chaining, though.
    DavidM wrote:
    OR It would be nice if we could SPLIT a cog, In 2 or more "INTERVALS", then we could theoretically have more cogs, Remember sometimes we need the Advantage of a cog running independently but does not always has to run at FULL SPEED if you know what I mean.
    Hyperthreading like you describe was discussed, but I think Chip discarded the idea due to pipeline complexities. I believe there might be some sort of cooperative threading mechanism available, though.

    -Phil
  • AriAri Posts: 63
    edited 2011-03-11 22:46
    this is a perfect example of why every time critical project should start with a good external clock and voltage ref.

    if you eat a few i/o's from the get go....so what....at least you know where you stand up front....

    I would personally ditch the internal clock (or crystal ref.) and never rely on the inadequate Vref on chip.....

    every good design should start with ROCK SOLID voltage regulation and a clock with the ability to poll NTP (or at the very least not drift a significant amount)

    I think there is no way around the problem of poor clock implementation in the OP's project....just consider it a hard lesson learned....
  • DavidMDavidM Posts: 630
    edited 2011-03-12 01:48
    Hey Ari, Ill stick with my custom prop timer, it's doing the job its been asked to ! that is, millisecond accuracy.

    Works perfect for me!

    Dave M
  • AriAri Posts: 63
    edited 2011-03-12 16:30
    DavidM wrote: »
    Hey Ari, Ill stick with my custom prop timer, it's doing the job its been asked to ! that is, millisecond accuracy.

    Works perfect for me!

    Dave M


    I was presenting you with a way to not use a cog for timing.....which is what you asked for....take it or leave it David....whatever you are doing it obviously isn't working for you, or it's less than ideal....so how can you say it's a problem in your original post....and then say it's perfect later on?

    Oh wait David....I remember you form the other thread....my bad, I know exactly what the problem is....
  • DavidMDavidM Posts: 630
    edited 2011-03-12 17:35
    I did not say that my timer was NOT working, I was asking is there another way ( using the prop) to do timing that I may not be ware of.
    I only wanted to know this because I need more cogs, When I realised that I could get rid of another cog, and put that functionality in the existing timer cog, there's no need for other outside components. And besides , My PCB is already designed and out in the "field" so to speak, I am working on an upgrade that requires 2 more cogs.

    Dave M
  • kwinnkwinn Posts: 8,697
    edited 2011-03-12 21:04
    DavidM, there is a way to split a cog to run multiple PASM programs from hub ram. You can use a small program in the cog to load and execute PASM instructions from the hub. There was a thread about it a while back but I cannot find it. Each program will run much slower than if it were running in a cog, but it would still be much faster than spin.
  • AriAri Posts: 63
    edited 2011-03-12 21:17
    DavidM wrote: »
    I did not say that my timer was NOT working, I was asking is there another way ( using the prop) to do timing that I may not be ware of.
    I only wanted to know this because I need more cogs, When I realised that I could get rid of another cog, and put that functionality in the existing timer cog, there's no need for other outside components. And besides , My PCB is already designed and out in the "field" so to speak, I am working on an upgrade that requires 2 more cogs.

    Dave M


    Dave,

    I think the only way to do this, would be to break up your timer into a set of objects.....then nest those objects within other tasks (or "call" them)....I described this in Humanoidio's topic....and "coined" the term program bouncing....I have no demonstration of this code for you (I would gladly make it public if I did), but I have no doubts that it can be done....as I am doing something similar to control full color gamut LED's....it is possible to nest a broken up object (into multiple callable objects) within diverse functions....I imagine the same could be done with your timer...just as long as it was first in line....

    I still think an external clock ref is the way to go, but I understand your situation.....

    this issue is, of course, where to store your "chunks" and how to time them (especially for a linear function).....to put this more simply (it's not simple) just split your timer up among cogs....this could streamline some code, and possibly make extra resources available to you (maybe)....

    not sure if this is helpful, or just confusing....good luck
Sign In or Register to comment.