Shop OBEX P1 Docs P2 Docs Learn Events
help with fractional timing issue — Parallax Forums

help with fractional timing issue

radialrandyradialrandy Posts: 78
edited 2010-09-10 21:06 in Propeller 1
in the program below im having problem with entering an rpm value that gets a fractional result of the dt := clkfreq/((rpm*100)/750). If the calculation gets a fractional result then I notice the square wave out put is not perfect. When I measure the counts between low to high transition I get some variance. but a good clean calculation has exact counts. is there a way to make the calculation round up or down?


Pub squarewave | dt, T, rpm
rpm := 1500
dira[15]~~
outa[15]~
dt := clkfreq/((rpm*100)/750)
T := cnt
repeat
T += dt
waitcnt(T)
!outa[15]

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2010-09-05 23:02
    Pub squarewave | dt, T, rpm
      rpm := 1500
      dira[15]~~
      outa[15]~
      dt := clkfreq/((rpm*100)/750)            
      T := cnt
      repeat
        T += dt
        waitcnt(T)   
        !outa[15]
    

    Can you elaborate on the variance? Are you saying the clock cycle count is slightly different for each period or that the timing (measured externally) is not related to the frequency you want?

    Also, what is your clock setup (_clkmode/_xinfreq)?
  • radialrandyradialrandy Posts: 78
    edited 2010-09-06 07:00
    Heres the whole program. the squarewave part is just a test signal to test the pulse measureing program. I lauch the squarewave in another cog and jumper the output pin to the signal in pin for the main program. the current setting of the rpm value in the squarewave program gives me a varience in counts between the first 2 and second 2 high to low transitions. it bounces at
    rpm1 81392
    rpm2 81376
    but if i enter good values in rpm that dont get a fractional result from the dt := calculation then I get perfect counts in rpm1 and rpm2





    CON

    _xinfreq = 5_000_000
    _clkmode = xtal1 + pll16x '80MHz

    tb = 1
    shaft = 2


    OBJ

    Debug : "FullDuplexSerialPlus"

    VAR
    long a, b, c, rpm1, rpm2, rpmdiff
    long stack[30]

    PUB main


    cognew(squarewave, @stack[20]) 'starts my test signal
    repeat
    waitpeq(|<tb,|<tb,0) 'waits for high on pin 1
    waitpne(|<tb,|<tb,0) 'waits for low on pin 1

    repeat

    waitpne(|<shaft,|<shaft,0)
    waitcnt(1000+cnt) 'debounce
    waitpeq(|<shaft,|<shaft,0)
    waitcnt(1000+cnt)
    a := cnt 'sets a to cnt for first low to high
    waitpne(|<shaft,|<shaft,0)
    waitcnt(1000+cnt)
    waitpeq(|<shaft,|<shaft,0)
    waitcnt(1000+cnt)
    b := cnt 'sets b to cnt for 2nd high to low
    rpm1 := b-a 'calculates the counts between first 2 low to high transitions
    a := b 'makes a = b and waits for the third count
    waitpne(|<shaft,|<shaft,0)
    waitcnt(1000+cnt)
    waitpeq(|<shaft,|<shaft,0)
    waitcnt(1000+cnt)
    b := cnt 'sets b to cnt for the third low to high
    c := b-a 'calculates the counts between the 2nd and 3rd low to high transitions
    rpm2 := b-a 'measures the difference between the 2 count measurements
    rpmdiff := rpm2 - rpm1

    Dbug



    Pub Dbug

    Debug.start(31, 30, 0, 57600)
    Waitcnt(clkfreq/4+ cnt)
    Debug.tx(Debug#CLS)
    Debug.str(String(13,"a= "))
    debug.dec (a)
    Debug.str(String(13,"b= "))
    Debug.dec (b)
    Debug.str(String(13,"c= "))
    debug.dec (c)
    Debug.str(String(13,"rpm1= "))
    debug.dec (rpm1)
    Debug.str(String(13,"rpm2= "))
    debug.dec (rpm2)
    Debug.str(String(13,"rpmdiff= "))
    debug.dec (rpmdiff)

    waitcnt(clkfreq/4+cnt)

    Pub squarewave | dt, T, rpm
    rpm := 14750 'putting values in rpm that get a fractional calculation in the dt formula
    dira[15]~~ 'somehow makes the count values of the above program vary.
    outa[15]~
    dt := clkfreq/((rpm*10)/75)
    T := cnt
    repeat
    T += dt
    waitcnt(T)
    !outa[15]
  • kuronekokuroneko Posts: 3,623
    edited 2010-09-06 07:48
    Heres the whole program. the squarewave part is just a test signal to test the pulse measureing program. I lauch the squarewave in another cog and jumper the output pin to the signal in pin for the main program. the current setting of the rpm value in the squarewave program gives me a varience in counts between the first 2 and second 2 high to low transitions. it bounces at
    rpm1 81392
    rpm2 81376
    but if i enter good values in rpm that dont get a fractional result from the dt := calculation then I get perfect counts in rpm1 and rpm2

    The difference/variance I see is +/-16 which looks suspiciously like hub window aliasing. All variable accesses in SPIN (e.g. a := cnt) are somehow locked to 16n system clocks (which is because variable a lives in hub RAM and is accessed from within the SPIN cog). The external pulse signal is effectively free running, waitpxx has single cycle resolution (i.e. conditions are checked every system cycle). So certain pulse widths are likely to be misaligned and over time drift into the next hub window. This doesn't necessarily mean that the pulse period varies, it's just that the edges are timed with lower granularity.

    That's my observation. If you need higher accuracy you may have to get involved with PASM to remove the aliasing. Not sure how much of an issue this is for the intended purpose.

    HTH

    BTW, when you post code you should embed in [noparse]
    
    [/noparse] tags.                        
  • radialrandyradialrandy Posts: 78
    edited 2010-09-06 08:58
    Hey thanks for the responce. Unfortunately most of it is over my head. lol. I did away with the squarewave part and just feed the input with a signal from an external signal generator. I get variations of +-16,32,48 when trying to measure pulses. Where does the extra counts come from? How can I get accurate results? Am i doing it the wrong way? I'm trying to get accurate measurements between every positive transistion whether its a set frequency of a varying frequency.
  • Mike GreenMike Green Posts: 23,101
    edited 2010-09-06 09:14
    In assembly language, the programs in the cogs work independently of each other as long as they don't reference the shared (hub) memory and they can do timing to a resolution of 12.5ns (with an 80MHz system clock). When they reference hub memory, the hub memory controller gives them one access slot every 16 system clocks and the cogs are forced to wait until their slot comes up which can cause delays up to 16 clocks. That's the multiples of 16 resolution you're seeing. In Spin, the Spin interpreter is the actual program running in the cog and this program is repeatedly referencing hub memory for the byte codes of the program, for the stack contents, and for access to any variables used, all of which are stored in hub memory. Most of these references cause these "multiples of 16" delays which is what you're seeing.

    You can get timing resolutions to the resolution of the system clock in Spin by using the cog counters. Download and read the documentation in the application note on these (see AN001).
  • radialrandyradialrandy Posts: 78
    edited 2010-09-06 09:17
    Ok i stripped it down even further. now im just measureing the counts between 1 low to high and the next low to high. I'm still getting the variations on single measurements. I dont understand how the count cant be accurate. This seems like it should be so simple. This frusterates me!




    CON
    
        _xinfreq = 5_000_000                     
        _clkmode = xtal1 + pll1x                           '80MHz
    
        tb = 1
        shaft = 2
        
        
    OBJ
       
      Debug   : "FullDuplexSerialPlus"
      
    VAR
      long a, b, rpm1
    
    
    PUB main 
       
     repeat
       waitpeq(|<tb,|<tb,0)                                 'waits for high on pin 1
       waitpne(|<tb,|<tb,0)                                 'waits for low on pin 1
       
       repeat
         
         waitpne(|<shaft,|<shaft,0)
         waitcnt(1000+cnt)                                        'debounce
         waitpeq(|<shaft,|<shaft,0)
         waitcnt(1000+cnt)
         a := cnt                                            'sets a to cnt for first low to high
         waitpne(|<shaft,|<shaft,0)
         waitcnt(1000+cnt)
         waitpeq(|<shaft,|<shaft,0)
         waitcnt(1000+cnt)                                   
         b := cnt                                            'sets b to cnt for 2nd high to low 
         rpm1 := b-a                                         'calculates the counts between first 2 low to high transitions
        
        
        Dbug
     
    
      
    Pub Dbug
      
          Debug.start(31, 30, 0, 57600)
          Waitcnt(clkfreq/4+ cnt)
          Debug.tx(Debug#CLS)      
          Debug.str(String(13,"a= "))
          debug.dec (a)
          Debug.str(String(13,"b= "))
          Debug.dec (b)
          Debug.str(String(13,"rpm1= "))
          debug.dec (rpm1)
          waitcnt(clkfreq/4+cnt)
    
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-09-06 09:37
    radialrandy,

    Let's put your timing errors in perspective, shall we? Your code hints that you will be measuring the RPMs of a physical shaft. The errors you're seeing are +/-0.2us. Say you've got a really high speed shaft, turning at 50,000RPM. That's 1200us per revolution. So your potential measurement error would be 0.2/1200 = .0167%. It will be even better at low speeds. Is it good enough for your application?

    As Mike and Kuroneko pointed out, the 16-count bobble is due to the fact that you're using Spin, which is interpreted from the hub. Hub accesses add the timing uncertainty that you're seeing. If you were to write the code in PASM, that uncertainty would be reduced considerably. You might also be able to reduce it in Spin by measuring the period required by 16 pulses, say, and dividing the result by 16.

    -Phil
  • radialrandyradialrandy Posts: 78
    edited 2010-09-06 09:49
    radialrandy,

    Let's put your timing errors in perspective, shall we? Your code hints that you will be measuring the RPMs of a physical shaft. The errors you're seeing are +/-0.2us. Say you've got a really high speed shaft, turning at 50,000RPM. That's 1200us per revolution. So your potential measurement error would be 0.2/1200 = .0167%. It will be even better at low speeds. Is it good enough for your application?

    As Mike and Kuroneko pointed out, the 16-count bobble is due to the fact that you're using Spin, which is interpreted from the hub. Hub accesses add the timing uncertainty that you're seeing. If you were to write the code in PASM, that uncertainty would be reduced considerably. You might also be able to reduce it in Spin by measuring the period required by 16 pulses, say, and dividing the result by 16.

    -Phil


    Here's what I'm trying to do. I'm measureing time between every low to high pulse on a shaft. Then I'm calulating what rpm each pulse would be. from one pulse to the next im calculating the rpm per second rate on increase or decrease. So if im measuring a 15000 rpm shaft speed that has 4 magnets on the shaft and the rpm is rock solid at 15000. I should get a 0 rpm per second rate of increase or decrease. But with the +-16,32 variations then I get over 3000rpm per second increase or decrease even though the speed is 15000.

    I'm fairly new at this and chose the propellor to learn. Am I wasting my time trying to learn the spin language? Should I just try to learn the assembly language?

    thanks for helping me
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-09-06 10:25
    I think your calculations are off. With four magnets spinning at 15,000 RPM, thats 60,000 pulses per minute, or 1,000 per second (i.e. 1 ms/pulse). One millisecond is 80,000 CNT cycles at 80MHz. A bobble of only 16 or 32 counts is not going to give you an error of 3000 RPM -- more like 6 RPM, worst case.

    As I've already said, you can reduce this error further by averaging over several pulse periods.

    -Phil
  • radialrandyradialrandy Posts: 78
    edited 2010-09-06 11:03
    my math is right. At least I think it is. I'm calculating rpm per second increase not rpm.
    80000 counts = 15000 rpm
    79984 counts = 14997 rpm differnce of -16 counts
    a difference of 3 rpm in .0009998 seconds

    1/.0009998 = 1000.2 in a second scale
    3rpm * 1000.2= 3000.06 rpm per second increase

    the error is not as bad at lower rpms but it gets worse the higher the rpm gets. so a +-16 counts is throwing me way off.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-09-06 11:18
    I see. Measuring acceleration like that at each pulse interval is way too sensitive to small variations and will inevitably lead to large errors -- even if the bobble were only +/- 1. Why not measure the period of 100 (or more) pulses instead? It will be much less sensitive to noise, and your acceleration figure will be more stable as a result.

    -Phil
  • radialrandyradialrandy Posts: 78
    edited 2010-09-06 13:41
    Man this dang hub thing is killing me on my simple little task. So is it correct to say that I cant get an accurate count using Spin?
    I've put over 20 hours into trying to make this work and everything I try still has the +-16,32 count errors. I have no idea how to use assembly language. I understand Spin but assembly language looks 1000X harder. I dont know where to start.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-09-06 14:15
    So is it correct to say that I cant get an accurate count using Spin?
    No, that would be incorrect. Although a +/-16-count bobble will be observed for each reading due to hub access uncertainties, that's not to say you can't reduce that error by taking readings over more pulses to get an average, as I've suggested twice now. It's not Spin that's causing you problems per se; it's that your method of measurement is too sensitive to to small amounts of sampling jitter. And this sensitivity can be reduced to manageable levels by sampling over more pulses.

    -Phil
  • radialrandyradialrandy Posts: 78
    edited 2010-09-06 15:51
    No, that would be incorrect. Although a +/-16-count bobble will be observed for each reading due to hub access uncertainties, that's not to say you can't reduce that error by taking readings over more pulses to get an average, as I've suggested twice now. It's not Spin that's causing you problems per se; it's that your method of measurement is too sensitive to to small amounts of sampling jitter. And this sensitivity can be reduced to manageable levels by sampling over more pulses.

    -Phil

    Hey Phil
    For the use than I'm needing this calculation I NEED to measure down to this accuracy (high pulse to high pulse). Measuring over many pulses will not work for this. To me this calculation seems like it should be simple for a microcontroller of this caliber. But its not looking that way yet. I though calculations like this would be a peice of cake for this microcontroller and thats why I chose it to try to learn. Does this count fluxuation not effect big complex programs? I'm so frusterated. I'm just begining and I already want to quit and go back to analog circuits:(
  • Mike GreenMike Green Posts: 23,101
    edited 2010-09-06 16:11
    As mentioned before, there are counters in each cog that will count system clock cycles and can be used for accurate timing. You will need an external edge triggered flipflop triggered by the positive (or negative) edge of your pulse. You'd connect the Q output to one I/O pin and the /Q output to another pin. You'd set up one counter to count system clocks when an I/O pin is high and the other to count system clocks when an I/O pin is low so they'd time alternate positive edge to positive edge times. Your Spin program would just read and reset one counter while the other counter is active, then alternate. You'd get the times to the nearest 12.5ns.
  • radialrandyradialrandy Posts: 78
    edited 2010-09-06 16:47
    Mike Green wrote: »
    As mentioned before, there are counters in each cog that will count system clock cycles and can be used for accurate timing. You will need an external edge triggered flipflop triggered by the positive (or negative) edge of your pulse. You'd connect the Q output to one I/O pin and the /Q output to another pin. You'd set up one counter to count system clocks when an I/O pin is high and the other to count system clocks when an I/O pin is low so they'd time alternate positive edge to positive edge times. Your Spin program would just read and reset one counter while the other counter is active, then alternate. You'd get the times to the nearest 12.5ns.

    I'm on the same page with ya now! I can see how that would work perfect.
    I was trying to think of how to use those counter.
    Thanks alot Mike!
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-09-06 23:17
    I'm glad Mike has shown you a way of getting the measurement precision that you wanted. But I'm still a little concerned with your methodology. In any system having a timing resolution of n, you will experience timing errors on the order of +/- n. By reducing the error from 16 clocks to 1, you will still have an error of 188 RPM/sec (by your calculations). Is that small enough? Also, you are using a magnetic encoder, which is an inherently analog device before thresholding. Any shaft vibrations will cause the pulse-to-pulse timing to jitter a little. And then there's the relative spacing of the four magnets. How accurately are they distributed on the shaft? You may discover that the additional (and undeniable) precision afforded by a smaller timing uncertainty will not lead to the accuracy you expect, due simply to physical phenomena unrelated to the timing method. I would be very surprised to see any mechanical system showing repeatability down to 12.5ns.

    But try it and see what happens. We're here to help if it doesn't quite pan out the way you expect. And don't give up on the Propeller. It's more than capable of handling your app, once the mechanical limitations are determined.

    -Phil
  • radialrandyradialrandy Posts: 78
    edited 2010-09-08 18:38
    Ok still not working with the external flip flop. Worse now. Now I get erros of 16,32,46,64 randomly. the program below should measure 1 full cycle. acount is inverted Q and bcount is Q. flipflopset sets Q to high. Im really not liking this chip! Out of all the people that use this chip for great things Im the one who wants to do something so simple and cant:(
    CON
    
        _xinfreq = 5_000_000                     
        _clkmode = xtal1 + pll16x                           '80MHz
    
        start = 1
        acount = 2
        bcount = 3
        flipflopset = 4
    OBJ
       
      Debug   : "FullDuplexSerialPlus"                          
      
    VAR
      long  a
    
    PUB main                                       
     repeat
      ctra[30..26] := ctrb[30..26] := %11111                           'clock count
      ctra[5..0] := 2 
      ctrb[5..0] := 3                                
      phsa~
      phsb~
      frqa := frqb := 0
      dira[flipflopset] := 1
      outa[flipflopset] := 0
      
      waitpeq(|<start, |<start, 0)
      waitpne(|<start, |<start, 0)
      outa[flipflopset] := 1
      
      waitpeq(|<acount, |<acount, 0)
      waitpne(|<acount, |<acount, 0)
      phsa~
      frqa := 1
      waitpeq(|<acount, |<acount, 0)
      waitpne(|<acount, |<acount, 0)
      a := phsa
      phsa~
      Dbug
     
      
     
    Pub Dbug
      
          Debug.start(31, 30, 0, 57600)
          Waitcnt(clkfreq*1 + cnt)
          Debug.str(String(13,"a= "))
          debug.dec (a)
    
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-09-08 21:40
    I don't normally just hand someone some code, but I hate to see you suffer, and I have a feeling you need to see some success to prove the Prop's capabilities. So I've written a little PASM routine that computes the period between leading pulse edges. The program is self-contained, in that the main cog uses a counter to provide the stimulus on the tach pin. Run it this way first first to make sure you're happy with its precision. Then comment out the dira statement, couple your tach output to the input pin, and try it again.
    [font=Parallax,courier,monospaced]
    CON
    
       _clkmode     = xtal1 + pll16x
       _xinfreq     = 5_000_000
    
       TACH_PIN     = 0
       TACH_FRQ     = 60_000
    
       HOME         = 1
       CLREOL       = 11
    
    VAR
    
      long  period
    
    OBJ
    
      sio   : "FullDuplexSerial"
    
    PUB  Start
    
      ctra := %00100 << 26 | TACH_PIN
      frqa := frqval(TACH_FRQ)
      dira[TACH_PIN]~~                  'Comment out this statement to connect an external clock source.
      cognew(@period_meas, @period)
      sio.start(31, 30, 0, 9600)
      repeat
        sio.tx(HOME)
        sio.dec(period)
        sio.tx(CLREOL)
    
    PRI frqval(a) : f     'Compute FRQx value from frequency.
      repeat 32
         a <<= 1
         f <<= 1
         if a => clkfreq
            a -= clkfreq
            f++
    
    DAT
    
    period_meas   waitpne   tach_mask,tach_mask     'Wait for input to go low.
                  call      #debounce               'Wait for debounce interval.
                  waitpeq   tach_mask,tach_mask     'Wait for input to go high.
                  mov       end,cnt                 'Read the time.
                  call      #debounce               'Wait for debounce interval.
                  mov       acc,end                 'Compute period := end - begin.
                  sub       acc,begin
                  wrlong    acc,par                 'Write period to hub variable.
                  mov       begin,end               'Period end is new begin.
                  jmp       #period_meas            'Lather, rinse, repeat.
    
    debounce      mov       acc,cnt                 'Read the counter.
                  add       acc,#511                'Add a little time.                                                
                  waitcnt   acc,#0                  'Wait for it.
    debounce_ret  ret                               'Over and out.
    
    tach_mask     long      1 << TACH_PIN
    
    acc           res       1
    begin         res       1
    end           res       1               
                      
    [/font]
    
    -Phil
  • kuronekokuroneko Posts: 3,623
    edited 2010-09-08 22:36
    PUB main                                       
     repeat
      ctra[30..26] := ctrb[30..26] := [COLOR="Red"]%11111[/COLOR]                           'clock count
      ctra[5..0] := 2 
      ctrb[5..0] := 3                                
    

    FWIW, doing this gets you back to square one. A LOGIC:always counter shows the same side effects as using cnt. What you want - based on Mike's suggestion - is LOGIC:A (%11010) or LOGIC:!A (%10101).
  • radialrandyradialrandy Posts: 78
    edited 2010-09-10 15:34
    I don't normally just hand someone some code, but I hate to see you suffer, and I have a feeling you need to see some success to prove the Prop's capabilities. So I've written a little PASM routine that computes the period between leading pulse edges. The program is self-contained, in that the main cog uses a counter to provide the stimulus on the tach pin. Run it this way first first to make sure you're happy with its precision. Then comment out the dira statement, couple your tach output to the input pin, and try it again.

    -Phil

    Hey Phil
    I tried this and set it up for the external signal. This setup is WAY worse than my original program. It has alot more cnt variance. I appreciate all your help but I think I've lost faith in this chip doing the things I want to do. Again thanks for trying.
    Randy
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-09-10 18:52
    Randy,

    I can't say I'm surprised by your results. Trust me on this: it's not the Propeller chip; it's your assumption that a mechanical process can possibly yield nanosecond repeatability. It just won't happen. Ever.

    Now, please fill us in on your project and what your ultimate goals are. I know there's a solution that the Propeller is more than capable of abetting.

    -Phil
  • yarisboyyarisboy Posts: 245
    edited 2010-09-10 19:56
    I had to build a tachometer simulator signal source for an other project I'm Spin teething on. My needs are, no doubt, less esoteric but I loaded Synth an FrequencySynth from the OBEX with very good results. The error of output is undetectable with my fleabay scope. With the tachometer program in the receiving Propeller my error is like 6000 rpm in, and 5997 RPM out on my PST window. I've applied a correction factor in the code I'm working on and have split the error in half over the 500-8000 RPM range of my custom automotive tachometer. The cumulative errors due to calculation time and integer truncation is non-linear.
  • radialrandyradialrandy Posts: 78
    edited 2010-09-10 21:06
    Randy,

    I can't say I'm surprised by your results. Trust me on this: it's not the Propeller chip; it's your assumption that a mechanical process can possibly yield nanosecond repeatability. It just won't happen. Ever.

    Now, please fill us in on your project and what your ultimate goals are. I know there's a solution that the Propeller is more than capable of abetting.

    -Phil
    Hey Phil,
    once again thanks for all your help. I NEVER assumed a mechanical process to be that accurate or repeatable. I just wanted 100% accurate measurements that could measure how inconsistant a mechanical process is. However I did find a way to make this work that has me satisfied. I now have it setup on a sampling method that yields the results I wanted.
    thanks for all your help:)
Sign In or Register to comment.