Shop OBEX P1 Docs P2 Docs Learn Events
Measuring Frequency with P2 — Parallax Forums

Measuring Frequency with P2

jcfjrjcfjr Posts: 74
edited 2021-03-19 21:21 in Propeller 2

I am using the following code to read a frequency on Fpin on a P1

Dira[Fpin] := 0
Ctra := 0
Ctra := (%01010 << 26)|(%001 << 23)|(0 << 9)|(Fpin)
Frqa := 1
Phsa := 0
waitcnt(40_000_000 + cnt) 'Count freq for 500ms seach for signal
Frequency1 := Phsa/1000
Frequency1 := Frequency1*2

Can anyone suggest some code for the P2 that will accomplish the same task?

In searching for a P2 solution, I ran across the Reciprocal Counter Demo that I believe was originally written by Cgracey, which outputs the frequency measured on a pin as one of its outputs. Problem is, when I try to run this program, I get nothing output on the PST. When I turn my P2 off, it spits out one line of output on the PST.

clocks: 21,200 states: 1 periods: 1 duty: 0/k frequency: 11,792.452830

I may be able to use this to accomplish what I want if I could get it to run.

Comments

  • JonnyMacJonnyMac Posts: 9,102
    edited 2021-03-19 22:34

    This duplicates what you're doing with your P1 code and seemed to work. I connected a PWM pin to an input pin and tested with a few frequencies. Obviously, this is not good for very low frequency signals.

    pub freq_in(pin) : result | tix
    
      tix := clkfreq >> 1                                           ' ticks in 1/2s (works w/FlexProp)
    
      org
                    fltl      pin                                   ' clear pin
                    wrpin     #P_COUNT_RISES, pin                   ' count rising edges
                    wxpin     tix, pin                              '  in 1/2 second
                    wypin     #0, pin
                    drvl      pin
    
                    waitx     tix                                   ' wait
                    nop
    
                    rdpin     result, pin                           ' get 1/2 freq
                    shl       result, #1                            ' x2
      end
    

    If you want to truncate to kHz like your code above...

    pub get_khz(pin) : result | tix
    
      tix := clkfreq >> 1                                           ' ticks in 1/2s (works w/FlexProp)
    
      org
                    fltl      pin                                   ' clear pin
                    wrpin     #P_COUNT_RISES, pin                   ' count rising edges
                    wxpin     tix, pin                              '  in 1/2 second
                    wypin     #0, pin
                    drvl      pin
    
                    waitx     tix                                   ' wait
                    nop
    
                    rdpin     result, pin                           ' get 1/2 freq
                    qdiv      result, #500                          ' truncate to kHz
                    getqx     result
      end
    
  • @jcfjr said:
    I am using the following code to read a frequency on Fpin on a P1

    Dira[Fpin] := 0
    Ctra := 0
    Ctra := (%01010 << 26)|(%001 << 23)|(0 << 9)|(Fpin)
    Frqa := 1
    Phsa := 0
    waitcnt(40_000_000 + cnt) 'Count freq for 500ms seach for signal
    Frequency1 := Phsa/1000
    Frequency1 := Frequency1*2

    Can anyone suggest some code for the P2 that will accomplish the same task?

    In searching for a P2 solution, I ran across the Reciprocal Counter Demo that I believe was originally written by Cgracey, which outputs the frequency measured on a pin as one of its outputs. Problem is, when I try to run this program, I get nothing output on the PST. When I turn my P2 off, it spits out one line of output on the PST.

    clocks: 21,200 states: 1 periods: 1 duty: 0/k frequency: 11,792.452830

    I may be able to use this to accomplish what I want if I could get it to run.

    In my "Using Smart Pins to Measure Frequency Output of TSL235R Light-to-Frequency Sensor" Quick Byte, I adapted Chip's "Reciprocal Counter Demo" to inline PASM2 routines. I created two routines "fb_measfreq2P.spin2" and "fb_measfreq3P.spin2" that you should be able to use. The 2P routine uses two smart-pins to measure Frequency (integer + fractional) and Periods. The 3P routine uses three smart-pins to measure Frequency (integer + fractional), Periods and Duty Cycle.

  • @JonnyMac said:
    This duplicates what you're doing with your P1 code and seemed to work. I connected a PWM pin to an input pin and tested with a few frequencies. Obviously, this is not good for very low frequency signals.

    Jon,
    Thanks for the code, I will play around with it this afternoon. Frequencies I am measuring are from 3.5Mhz to 28Mhz, so should be OK.

  • @"Francis Bauer" said:

    In my "Using Smart Pins to Measure Frequency Output of TSL235R Light-to-Frequency Sensor" Quick Byte, I adapted Chip's "Reciprocal Counter Demo" to inline PASM2 routines. I created two routines "fb_measfreq2P.spin2" and "fb_measfreq3P.spin2" that you should be able to use. The 2P routine uses two smart-pins to measure Frequency (integer + fractional) and Periods. The 3P routine uses three smart-pins to measure Frequency (integer + fractional), Periods and Duty Cycle.

    Thank you Francis for the link and code. I will be able to learn a lot from your examples. Will be studying to learn more about the elusive smart pin. I don't get much out of the Smart Pin docs I have seen, but examples should help.

  • JonnyMacJonnyMac Posts: 9,102
    edited 2021-03-20 18:34

    Frequencies I am measuring are from 3.5Mhz to 28Mhz, so should be OK.

    Yes, I think you will. One of the many great things about the Propeller is that we can use it to test itself -- much more easily, IMHO, than on other processors. My test code uses a smart pin (P16) to generate the test frequency, and a smart pin (P23 to measure). No assembly required. :)

    pub main() | hz
    
      setup()
    
      wait_for_terminal(true)
      waitms(100)
    
    
      hz := 28_123_456
    
      pinstart(16, P_NCO_FREQ | P_OE, 1, hz frac clkfreq)           ' create test frequency
    
      term.dec(get_khz(23))                                         ' measure & display
    
      repeat
        waitct(0)
    
    
    pub get_khz(pin) : result
    
      pinstart(pin, P_COUNT_RISES, clkfreq >> 1, 0)                 ' count rising edges for 1/2s
      waitms(500)                                                   ' let count finish
    
      return (rdpin(pin) + 250) / 500                               ' round up to nearest kHz
    
  • @JonnyMac said:
    This duplicates what you're doing with your P1 code and seemed to work. I connected a PWM pin to an input pin and tested with a few frequencies. Obviously, this is not good for very low frequency signals.

    Jon,
    I hooked up my RF signal Generator to pin 0, set it 4Mhz(sine wave), it is 1.72v pep, and I get the results in debug.log, which are incorrect. If I go beyond 5.8 Mhz, I get 0 as output, but my signal is still around 1.7v pep. The higher I go in freq, the lower the readout becomes, until above 5.8Mhz it is 0. Any hints as to what I am doing wrong.

  • @JonnyMac said:
    This duplicates what you're doing with your P1 code and seemed to work. I connected a PWM pin to an input pin and tested with a few frequencies. Obviously, this is not good for very low frequency signals.

    Jon,
    I hooked up my RF signal Generator to pin 0, set it 4Mhz(sine wave), it is 1.72v pep, and I get the results in debug.log, which are incorrect. If I go beyond 5.8 Mhz, I get 0 as output, but my signal is still around 1.7v pep. The higher I go in freq, the lower the readout becomes, until above 5.8Mhz it is 0. Any hints as to what I am doing wrong.

    something got messed up with the attached files in last comment

  • JonnyMacJonnyMac Posts: 9,102
    edited 2021-03-20 20:05

    Things to do:
    -- bump your signal level to 2+ volts (referenced to P2 ground). The P2 input threshold is 1.65v and you may have connector losses (you are dealing with RF frequencies) that are creating problems
    -- add SCHMITT behavior to the input pin to "square up" the signal

    I used the code above to test all the way up to 50MHz (generated by the P2); the input always matched what I was creating via the NCO.

    Here's how you add SCHMITT behavior to the input pin:

    pub get_khz(pin) : result
    
      pinstart(pin, P_COUNT_RISES | P_SCHMITT_A, clkfreq >> 1, 0)   ' count rising edges for 1/2s
      waitms(500)                                                   ' let count finish
    
      return (rdpin(pin) + 250) / 500                               ' round up to nearest kHz
    

  • JonnyMacJonnyMac Posts: 9,102
    edited 2021-03-20 20:06

    After thinking about it, given the frequency range you want to monitor, there is no need to sample so long -- it just slows the program. This works and gives the same results

    pub get_khz(pin) : result
    
      pinstart(pin, P_COUNT_RISES | P_SCHMITT_A, clkfreq / 100, 0)  ' count rising edges for 1/100th second
      waitms(10)                                                    ' let count finish
    
      return (rdpin(pin) + 5) / 10                                  ' round up to nearest kHz
    

    My logic is that the sample window should be 1000x your longest period; 10ms is well beyond that at 3.5MHz.

  • Jon,

    I am still trying to get your first sample to work. I get an error on "setup()" line, expected instruction or variable. If I comment it out, the program runs, but I am not getting anything out of pin 14(P_NCO_FREQ), and fin = 0. If I hook up my frequency generator up, still get fin=0. I can count freq with my P1with signals that are 1v pep, is the P2 threshold higher? Can you take a quick look at my program and tell me the error on my ways?

  • JonnyMacJonnyMac Posts: 9,102
    edited 2021-03-21 04:45

    I get an error on "setup()" line, expected instruction or variable.

    Yep. If you have symbol with a trailing "()" it is a method. If not highlighted by the editor, it's not built in (e.g. getms()). If you try to call a method that doesn't exist, the compiler will complain. I switched my suggestions to use debug() instead of PST.

    The code works -- here's proof (I moved my jumper to the pins you're using).

    is the P2 threshold higher?

    The P2 threshold is ~1.65 referenced to ground. Start simpler. Drop your frequency generator down to a much lower frequency, one that you can verify with an oscilloscope. I think the problem is like losses through connections and low output from the generator. Remember, the frequencies you want to measure are RF and you just can't treat them willy-nilly.

  • jmgjmg Posts: 15,173

    @jcfjr said:
    If I hook up my frequency generator up, still get fin=0. I can count freq with my P1with signals that are 1v pep, is the P2 threshold higher?

    P1 & P2 are much the same thresholds, but if you are using a RF generator, you may need to AC couple that to a pre-biased pin.
    Select SCHMITT as Jon did above, and you should be ok down to the ~hyst levels p-p.
    If you need more signal sensitivity, a buffer preamp may be needed, either a 1GU04, 74AUP1Z04, or a 74AHC1G42xx which includes a binary divider (choice of 2^8 thru 2^15 by xx part number)

    I'm not sure if the P2 DAC can work with the pin in other modes, but if it can, one way to bias would be to set the 1k DAC to 50% to pre-bias.
    A more precise way would be to sweep the DAC on init, to find the VTH. VTL of schmitt and set the bias mid-way.

  • @jmg said:

    P1 & P2 are much the same thresholds, but if you are using a RF generator, you may need to AC couple that to a pre-biased pin.

    Can you explain how to AC couple to the pin. I am currently taking the output of the generator directly into the pin.

    My signal strength varies form 1v pep at 10watts of power, up to almost 8v pep at 1000watts. So my P1 I use a voltage divider to limit the upper end to about 3.3v pep, but I can still read the frequency at 10w, which is probably less than 1v after the divider. I need to figure out a way to offset the pep envelope up by about 1v, then I think the pin would count the rises at the lower end.

  • @JonnyMac said:

    I think the problem is like losses through connections and low output from the generator

    Jon,

    I agree, my generator maxs out at 1.5v. I have ordered a new one that puts out more. I did get your software to work as advertised, something stupid I was doing. There was never any doubt about your software working. Thanks again for the code. Still have some work to do on hardware side, but I have to assume that if my P1 is working with the signals, that a P2 will also read them.

  • jmgjmg Posts: 15,173
    edited 2021-03-21 19:20

    @jcfjr said:
    Can you explain how to AC couple to the pin. I am currently taking the output of the generator directly into the pin.

    In the simplest form, a series capacitor eg 10nF from your signal generator, to the pin, and add two equal resistors (eg 10k), to GND and VCC to pre-bias the pin to 50% Vcc .

    or, if that is still not enough, you can add a preamp, this is an example of the gain from a unbuffered gate 74LVC1GX04.
    That also gives you a bit more protection to the P2 from high levels of RF :)

  • cgraceycgracey Posts: 14,151

    STARTPIN (pin, %0001_001011011_01_xxxxx_0, X, Y)

    xxxxx = smart pin mode

    I believe that will auto-center the pin and allow the coupling cap to swing around the threshold point.

  • evanhevanh Posts: 15,912
    edited 2021-03-21 20:46

    Chip's pin mode using built-in constants: pinstart( P_LOGIC_A_FB | P_INVERT_OUTPUT | P_OE | P_HIGH_150K | P_LOW_150K | xxxxx )

    Hehe, is that wise, Chip? You're setting up a negative feedback that, at the very least, will settle to a natural oscillator when there is no signal. I suppose, at 150kR resistors, the frequency can be quite low and therefore easy to ignore.

    EDIT: Same again but with Jon's smartpin mode added: pinstart( P_COUNT_RISES | P_LOGIC_A_FB | P_INVERT_OUTPUT | P_OE | P_HIGH_150K | P_LOW_150K )

    EDIT2: Corrected spelling of "pinstart"

  • jmgjmg Posts: 15,173

    @evanh said:
    Hehe, is that wise, Chip? You're setting up a negative feedback that, at the very least, will settle to a natural oscillator when there is no signal. I suppose, at 150kR resistors, the frequency can be quite low and therefore easy to ignore.

    Yes, good point, it will need 'user-care' as a floating input will oscillate, as also will a too-low-level driven input.
    With the schmitt mode, that oscillation frequency should be low enough to be obvious ?

    A local termination resistor, before the C, would lower that floating input oscillation.

    If you know a sufficient signal will always be present, Chip's self biasing feedback mode can save parts.

  • evanhevanh Posts: 15,912

    Here's a better get_khz()

    PUB get_khz(pin) : result
    
      repeat
        if pinread(pin)
          return (rdpin(pin) + 250) / 500                           ' round up to nearest kHz
    
  • evanhevanh Posts: 15,912
    edited 2021-03-21 22:21

    PS: Initial testing of Chip's idea is smooth externally on a scope but internally produces a high frequency count in the MHz. In fact it may well be above Nyquist point so is not even counting every pulse.

    PPS: P_SCHMITT_A stops the oscillation but throws in a big offset when no signal.

    PPPS: Both still work with a strong signal overriding.

  • cgraceycgracey Posts: 14,151

    Try 10uA mode for high/low drive.

  • evanhevanh Posts: 15,912
    edited 2021-03-21 23:39

    Opps, went back to try the 10uA and realised I'd used the wrong Schmitt mode. Should have replaced the P_LOGIC_A_FB with P_SCHMITT_A_FB. Doing this still with the 150 kR produces a 169 kHz arc'd triangle, 1.0 Vpk-pk, with just the scope probe hanging from the Eval Board accessory header. With a 100 pF cap to GND on there it goes down to 42 kHz and still 1.0 Vpk-pk.

    EDIT: Same again but using 10 uA instead of 150 kR produces 156 kHz straight triangle with just probe and 39 kHz with 100 pF cap. Both still 1.0 Vpk-pk.

    EDIT2: And finally, P_LOGIC_A_FB with 10 uA drive is same as 150 kR drive: Internal reading around 40 MHz but nothing visible on the scope.

  • jmgjmg Posts: 15,173
    edited 2021-03-21 23:43

    @evanh said:
    Opps, went back to try the 10uA and realised I'd used the wrong Schmitt mode. Should have replaced the P_LOGIC_A_FB with P_SCHMITT_A_FB. Doing this still with the 150 kR produces a 169 kHz arc'd triangle, 1.0 Vpk-pk, with just the scope probe hanging from the Eval Board accessory header. With a 100 pF cap to GND on there it goes down to 42 kHz and still 1.0 Vpk-pk.

    EDIT: Same again but using 10 uA instead of 150 kR produces 156 kHz with just probe and 39 kHz with 100 pF cap. Both still 1.0 Vpk-pk.

    So that indicates about 4.2kHz with 1nF and ~420Hz with 10nF coupling caps, (low Z driven), with a min AC sine level of some margin above 1.0V p-p (maybe 1.3V+ p-p ?)

    @evanh said:
    EDIT2: And finally, P_LOGIC_A_FB with 10 uA drive is same as 150 kR drive: Internal reading around 40 MHz but nothing visible on the scope.

    Yes, non Schmitt logic buffers biased to the linear region, often self oscillate internally, and 40MHz may be a sub-sample of the actual MHz.
    At AC drive the oscillation effect will improve, but there will still be some minimum V/us slew rate that is needed, to avoid multiple edges near the zero crossing point.

  • evanhevanh Posts: 15,912
    edited 2021-03-21 23:52

    Oh, and without the scope probe attached it goes from 156 kHz to 288 kHz, and 39 kHz to 44 kHz. And 168 kHz to 311 kHz, and 42 kHz to 47 kHz.

  • jcfjrjcfjr Posts: 74
    edited 2021-03-22 21:17

    @evanh said:

    EDIT: Same again but with Jon's smartpin mode added: pinstart( P_COUNT_RISES | P_LOGIC_A_FB | P_INVERT_OUTPUT | P_OE | P_HIGH_150K | P_LOW_150K )

    Not sure what the final verdict was on this statement. Tried the above and compiler is expecting a " ," at the last ")". I thought I saw a suggestion to use P_SCHMITT_A_FB, but this is not in your above pinstart. Could you share the final pinstart statement that was decided upon, so I can try it?

    Is there a document that gives a little more info than the P2 Spin manual on each of these options?

  • JonnyMacJonnyMac Posts: 9,102
    edited 2021-03-22 21:27

    That pinstart() suggestion is invalid, the syntax is:

      pinstart(pin, mode, x, y)
    

    If you want, you can verify this in the Spin2 documents
    -- https://docs.google.com/document/d/16qVkmA6Co5fUNKJHF6pBfGfDupuRwDtf-wyieh_fbqw/edit#heading=h.1h0sz9w9bl25

    As you can see, the example above has one parameter when four are required. I don't think Evan uses Spin2, so this would be easy to miss.

    Details on the smart pin modes and x and y values are in the P2 docs:
    -- https://docs.google.com/document/d/1gn6oaT5Ib7CytvlZHacmrSbVBJsD9t_-kmvjd7nUR6o/edit#heading=h.1h0sz9w9bl25

  • evanhevanh Posts: 15,912
    edited 2021-03-22 21:28

    Heh, yeah, not having really used Spin or Spin2, I was just following Chip's lead on the use of pinstart(). I've since used it and found pinstart() needs a pin number first and also X and Y parameters.

    Here's what I got done in my subsequent testing

  • jcfjrjcfjr Posts: 74
    edited 2021-03-24 16:22

    @evanh said:
    Heh, yeah, not having really used Spin or Spin2, I was just following Chip's lead on the use of pinstart(). I've since used it and found pinstart() needs a pin number first and also X and Y parameters.

    Here's what I got done in my subsequent testing

    The original code that Jon wrote (see attached) works fine. I was trying to measure a signal that was only 1v pep, and the pin did not see it. If I bias that signal by 1.5v, raise the mean voltage of the pep envelope up to straddle the threshold, both his routines work, the Spin2 and the PASM. I tried your suggestion with this same bias signal:

    pinstart( 0, P_COUNT_RISES | P_SCHMITT_A_FB | P_INVERT_OUTPUT | P_OE | P_HIGH_10UA | P_LOW_10UA, clkfreq >> 1, 0 )

    but this produces no output, fin = 0. Not sure why, but I don't fully understand all the parameters you have added either.

  • evanhevanh Posts: 15,912

    There's certainly no advantage in having the inline pasm. The smartpin is autonomous and does the whole measuring for you.

    Reinitialising the pin mode on every reading will mess with the balance when it disables the pin drive, don't do that. I've adapted Jon's code too, start there. Leaving the smartpin to cycle consecutively is more accurate too.

    JMG has given some hints on refining the inline capacitor to use. The smaller your signal voltage is, the larger the cap you're gonna want. If the target signal is only 1.0 Vpk-pk then the Schmitt mode won't work. Try the Logic mode instead. Then you just have to decide when there is a real signal vs the self-generated one.

    It'll probably be better to use external resistors instead. That way you can bias as desired and there's no feedback to interfere. pinstart( 0, P_COUNT_RISES, clkfreq >> 1, 0 ) But I still recommend my code improvement for the get_khz() function.

Sign In or Register to comment.