Shop OBEX P1 Docs P2 Docs Learn Events
Time values in minutes and storing values in arrays — Parallax Forums

Time values in minutes and storing values in arrays

tuffstufftuffstuff Posts: 24
edited 2013-08-24 17:27 in Propeller 1
I ran into two issues recently - storing constants larger than 32 bits and how to monitor time in units of minutes. Both of these are related due to my method of monitoring time in minutes. I'm sure there is a better way to do this. Here is what I wrote to get time units in minutes:
clkfreq * 60 + cnt

My clkfreq*60 = 50,5032,704 instead of 48,000,000,000. How do I monitor time in minutes when my cycle count is larger than 32 bits?

Which brings me to the next issue I am having concerning storing a large hex string. I received the error: "Constant exceeds 32 bits". A friend of mine was saying that I need to be storing the long hex value as an array of segments (this works well for midi, which is what my project is centered around). This makes sense and in fact may be what I need to store the clock cycle count necessary for a minutes time.

I had no luck finding info on storing numbers larger than 32 bits, except in the assembly section of the manual, and I'm staying away from that for now. Also nothing about arrays either.

Any pointers (pun intended) would be useful.

Thanks,

Comments

  • GadgetmanGadgetman Posts: 2,436
    edited 2013-08-24 13:58
    Maybe you could give us an idea of exactly what you're trying to do and how you're doing it?

    Most programs I've seen that counts clock pulses tends to count up to seconds only, and keep track of those.
  • tuffstufftuffstuff Posts: 24
    edited 2013-08-24 14:42
    Yes, I am making a midi parser that reading a stream of midi data, quantizes (snaps notes to the nearest 1/4, 1/8, 1/16 timing) it on the fly and then plays back the processed midi data to a device. It's a midi recorder that does automatic quantizing. In order to do this I need to set tempo, which is usually understood in beats per minute and I need to store the very long midi sequence. Its this tempo value that I use to quantize the midi stream.

    For example
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    OBJ
      pst   : "Parallax Serial Terminal"
    
    PUB Main | bpm
    
      pst.Start(115200)
      
      bpm := 120
      
      repeat
        pst.str(string("beat "))
        waitcnt(((clkfreq*60)/(bpm))+cnt)
    

    This code will print the "beat" to the terminal every beat for 120 bpm. It seems like for timing, you are suggesting I work in smaller units. Instead of beats per minute, maybe just beats per quarter minute. To get 77 bpm, I'd have to write something like:
    bpm:= 77
    waitcnt(((clkfreq*15)/(bpm/4))+cnt)
    

    I realize too that I probably should be storing these values in another data type other than integers.

    This is a good workaround, but I'm still stuck with the issue of how to store values larger than 32bits, and how to store long series of values as a single variable like an array.
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-08-24 15:10
    Are your timing values offsets from the beginning of a song? If this is the case, could you use milliseconds as a time base instead of system ticks? 2^31 milliseconds is over 2.1 million seconds -- probably long enough to any song you want to record. :)

    I would suggest you setup a separate cog that handles the milliseconds timing for you, and allows you to start, stop, and reset it -- that way, the cognew delay is never an issue.

    Here's how you might write a background timer. It uses pointers to state and timing variables so that you can have more than one instance running.
    pri ms_timer(p_state, p_time) | mstix, t, state                 ' launch with cognew
    
      mstix := clkfreq / 1000                                       ' ticks / millisecond
    
      long[p_time] := 0                                             ' reset timing
    
      t := cnt                                                      ' sync timing
      repeat
        waitcnt(t += mstix)                                         ' wait 1ms
        state := long[p_state]                                      ' get running state
        if (state == 0)
          long[p_time] := 0                                         ' 0 = stop/reset
          repeat
          until (long[p_state] == 1)                                '  wait for run (1)
          t := cnt                                                  '  re-sync timer
    
        elseif (state == 1)                                         ' 1 = run
          ++long[p_time]                                            '  increment milliseconds
    
        elseif (state == 2)                                         ' 2 = hold
          repeat
          until (long[p_state] <> 2)                                '  wait for release
          t := cnt                                                  '  re-sync timer
    
  • tuffstufftuffstuff Posts: 24
    edited 2013-08-24 15:31
    Yes, working in smaller units other than minutes (or divisions of minutes) seems to be the answer. So instead of minutes or 1/4 minutes calculating bpm (beats per minute) in milliseconds would be like:
    bpm := 120
    waitcnt((clkfreq/1000)/(bpm/60_000)+cnt
    

    Except I would need to start using float math now, correct, or maybe there is a library already written for this?

    Using another cog for the timing is a good idea too.
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-08-24 15:59
    Using another cog for the timing is a good idea too.


    I believe so, and the same clock module can be used for recording and playback.

    While I can play my guitar reasonalby well, I'm not a musician. It seems to me that you don't want to use BPM as the measuring stick -- better to use very small, yet manageable unit; like milliseconds. At 120 BPM you have 500ms per beat; but that's for quarter notes, right? Eighth notes, then, would be 250ms apart, 16th notes, 125, 32nd notes 62.5. If you need even more resolution you might do 1/10th of a millisecond but I can imagine you'd need more.

    By knowing what speed you recorded at, you could make playback faster or slower with simple math. If I may, I think you're making this way too hard.
    waitcnt((clkfreq/1000)/(bpm/60_000)+cnt


    This is bogus as bpm/60_000 would resolve to 0, and what you really want is (clkfreq / 1000) * (60_000 / bpm). The first timer calculates ticks per millisecond, the second is the number of milliseconds per beat. And the most important point is that you NEVER want to use cnt in a waitcnt like this that is critical. You really want a synchronized loop. You can find this in the manual and see how I do it in my timer code. A synchronized loop accounts for the other work you do in between an maintains accurate timing (so long as you loop code is less than your loop delay).

    Again, it seems that there is no need using BPM for the recording process, other than knowing what it is/was so that playback can be accurately adjusted. The playback delay would be ticks_per_ms * recorded_bpm / playback_bpm.

    Finally, in any timing sensitive application you should pre-calculate time values and offsets where you can.
  • tuffstufftuffstuff Posts: 24
    edited 2013-08-24 17:14
    I appreciate the help with this. I have been using the model used in several tutorials for waiting a second - waitcount(clkfreq + cnt) - and modifying it to use the time signature that is used on so many of my other pieces of gear, BPM. But just because my other drum machines and sequencers tell me timing in BPM doesn't mean I have to (or should) use it in the backend of my device.
    Finally, in any timing sensitive application you should pre-calculate time values and offsets where you can.

    When you say this, an example might be calculating...

    cycles_per_ms = 80,000,000/1000 = 80,000

    ...and then using following code, continuing the example for playback delay:
    recordedBPM := some_variable_value
    playbackBPM := another_variable_value
    80_000* recordedBPM / playbackBPM
    

    So here the processor isn't spending time calculating the cycles per ms value, we just hardcode it in. Is this what you mean by pre-calculating time values?
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-08-24 17:27
    I have been using the model used in several tutorials for waiting a second

    Well, sorry, somebody gave you bad code -- if you want the loop timing to be constant (as you do). When reading the cnt register in your waitcnt expression you are not accounting for the (potentially variable) code timing of the rest of the loop. The only way to get what you want is to use a syncrhonize loop:
    t := cnt
      repeat
        ' do something
        waitcnt(t += delaytix)
    


    This structure ensures that your loop runs for the duration specified by delaytix -- no matter what else is happening in the loop. And by pre-calculating the value of delaytix you leave the maximum amount of time available for the loop.

    Finally, while we tend to run at 80MHz that is not always the case. I have the following at the top of all of my programs; this allows me to have constants (faster than using variables) for my timing elements, and if I want to change the crystal (some of my projects us 6.25MHz) or PLL settings, the compiler takes care of the dirty work.
    con
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000                                          ' use 5MHz crystal
    
      CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq               ' system freq as a constant
      MS_001   = CLK_FREQ / 1_000                                   ' ticks in 1ms
      US_001   = CLK_FREQ / 1_000_000                               ' ticks in 1us
    
Sign In or Register to comment.