Shop OBEX P1 Docs P2 Docs Learn Events
Reciprocal Counter Demo - Page 4 — Parallax Forums

Reciprocal Counter Demo

124

Comments

  • jmgjmg Posts: 15,175
    Rayman wrote: »
    I had no idea what "reciprocal counter" meant, but I got curious just now and googled this up:
    https://community.keysight.com/community/keysight-blogs/general-electronics-measurement/blog/2018/02/26/how-does-a-frequency-counter-work

    It's a simpler thing than I imagined...

    Yes the block diagram and maths are simple.
    Chip added support in the smart pins to make the hardware details simple too..
    (it can get tricky to ensure the two captures of time and cycles are made on the same edge, and ideally, you want lossless/gapless capture too, so 10 or 100 captures of time and cycles can be summed to give higher precisions )
  • RaymanRayman Posts: 14,752
    I think it should actually be called "period counter".
    I guess they mean "reciprocal of frequency" counter.
    But "reciprocal of frequency" == period.
    I think that would make usage more clear.

    But, when you google "period counter" you get other stuff, which is maybe why the chose this name...
  • @evanh

    I used it to do the other waves that you helped me with in the past.
  • jmgjmg Posts: 15,175
    Rayman wrote: »
    I think it should actually be called "period counter".
    I guess they mean "reciprocal of frequency" counter.
    But "reciprocal of frequency" == period.
    I think that would make usage more clear.

    But, when you google "period counter" you get other stuff, which is maybe why the chose this name...

    I think it started as a simple one-word prefix so 'Frequency Counter' became 'Reciprocal Frequency Counter'.

    If you wanted to create a new name, 'period counter' infers it measures a single periods, which is inaccurate.
    'whole periods timer' is clearer, in that now includes key elements of measuring the time over N Whole Periods, and then calculates N/Time to give Cycles/Second units.
    - however, because the final units are Hz, including Frequency in the name seems important. - Instrument users care less about the how, than what they ultimately measure.

    On that note, the thread tile would be better as Reciprocal Frequency Counter Demo
  • @evanh
    @cgracey

    Hey guys been a while. I have been working at home teaching aircraft mechanics teaching our academic side without lab. Each 24 days is a new class. Took three classes to get all dialed in.
    I am now back learning this P2 asm and asm stuff.
    Much thanks to @cgracy for the reciprocal counter demo and much thanks to @evanh for his help.
    I stripped out the counter demo code to get to the asm code to print to the serial terminal.
    Think because of now starting from the basics I am getting a much better understanding. Thanks to evanh I think I am getting it. I looked up the pop command on youtube and get it.
    I have a couple of questions that I commented in the attached code. If either of you or anybody else have a minute would you please answer them.
    Much thanks.
    Martin
  • RamonRamon Posts: 484
    edited 2021-04-05 03:20

    Many Thanks for the test code.

    I have been testing it on recent FlexProp and PNUT and (Thanks to Ozpropdev!) have found a few thinks that I think it is worth mentioning.

    For flexprop will need to add a LineFeed (10) : (otherwise, output will rewrite the current line)

    Line 80 (old):      byte    13," clocks:",0
    Line 80 (new):      byte    13,10," clocks:",0
    

    For PNUT we can add this line (to avoid error message "DEBUG requires at least 10MHz of Crystal" when we use DEBUG, CTRL+F10):

    Line 8  (add):      _clkfreq    = 20_000_000        'system frequency
    

    I was able to check the frequency of my DP83848 Ethernet board 50MHz oscillator output pin (OSCIN)

     clocks:    2,500,003    states:    1,264,366    periods:      499,968    duty:  505/k    frequency:   49,996,740.003911
     clocks:    2,500,003    states:    1,264,705    periods:      499,968    duty:  505/k    frequency:   49,996,740.003911
     clocks:    2,500,003    states:    1,267,611    periods:      499,968    duty:  507/k    frequency:   49,996,740.003911
     clocks:    2,500,003    states:    1,266,037    periods:      499,968    duty:  506/k    frequency:   49,996,740.003911
     clocks:    2,500,003    states:    1,266,152    periods:      499,968    duty:  506/k    frequency:   49,996,740.003911
     clocks:    2,500,004    states:    1,266,724    periods:      499,968    duty:  506/k    frequency:   49,996,720.005247
     clocks:    2,500,003    states:    1,265,262    periods:      499,968    duty:  506/k    frequency:   49,996,740.003911
     clocks:    2,500,003    states:    1,266,263    periods:      499,968    duty:  506/k    frequency:   49,996,740.003911
    

    IMPORTANT: If there is no signal present, then there will be not output at all on the serial terminal or debug terminal !

  • evanhevanh Posts: 16,027
    edited 2021-04-05 06:44
    Not sure if Pilot is still around but I'll have a shot at answering his questions now anyway:

    tx_string	pop	x				'pop return address and make byte ptr
                  'DOES THIS TAKE WHAT EVER X IS AT THE TOP OF THE STACK (THE LAST VALUE OF X) 
    
    Not the last value of x, but takes the last value off the stack and puts it into the x variable (register). Overwriting the old value of x.


    'QUESTION: is x the last address of the caller????????????
    
    Almost. This is architecture dependant but the common (propeller2) behaviour is when a routine is CALLed the program counter of the calling routine is pushed to the stack for later retrieval by a RET instruction. At the CALL time, the program counter holds the address of the subsequent instruction after the CALL ... so that is the address that gets pushed on to the stack.

    EDIT: Formatting
  • evanhevanh Posts: 16,027
    edited 2021-04-05 06:55
            shl x,#2
    'I looked up altgb what is it doing to x and getbyte?
    
    .loop       altgb   x               'get character
            getbyte y
    

    The SHL is multiplying x by 4. This is typical when rescaling an index or pointer. In this case x contains a cogRAM address so is longword scaled. But to use GETBYTE requires byte scaling, hence the times 4.

    ALTGB is a prefixing instruction. A prefixing instruction means it modifies the behaviour of the subsequent instruction. The ALTxx instructions in general are a little hard to get and keep your head wrapped around. They provide what's called indirect addressing, or referencing in loose C speak. The trick is remembering that the variable holds an address, as opposed to holding the data.

    ALTGB is tailored for prefixing GETBYTE, it won't correctly prefix any other instruction. There is other prefixes that are generic and function with many or most other instructions. ALTGB has two forms, the above form is simply copy a byte from location x and place it in y. x can address anywhere in cogRAM.

  • How can I convert this PASM code to SPIN2 with the minimal lines of code?

    I tried in this way, but failed:

    '****************************************
    '*  Reciprocal Counter Demonstration    *
    '*  - inputs frequency on P0            *
    '*  - transmits serial text on P62      *
    '****************************************
    '
    con     sysfreq     = 250_000_000.0     'system frequency
            _clkfreq    = 20_000_000.0      'system frequency
            msr_us      = 10_000.0      'minimum measurement time in microseconds (float)
            msr_pin     = 0         'pin to measure frequency on, uses next two pins
            baud        = 921_600       'serial baud rate on P62 (float)
    
            msr_min     = sysfreq/1e6*msr_us    'minimum measurement time in system clocks
            msr_pins    = 2<<6 + msr_pin    'group of three pins starting at msr_pin
    
                    DOWNLOAD_BAUD   = 921_600
                    DEBUG_DELAY     = 100
                    DEBUG_BAUD      = 921_600
    
            rx_pin   = 63
            tx_pin   = 62
    
            tx_mode     = (round(sysfreq / baud * 65536.0) & $FFFFFC00) + 7 '8N1
            msr_time    = %0000_0000_000_0000_000000000_00_10101_0  'msr_pin+0 config
            msr_states  = %0111_0111_000_0000_000000000_00_10110_0  'msr_pin+1 config
            msr_periods = %0110_0110_000_0000_000000000_00_10111_0  'msr_pin+2 config
    
    VAR
    
      LONG clocks 
      LONG states
      LONG periods
    
    OBJ
      ser: "spin/SmartSerial"
      fmt: "spin/ers_fmt"
    
    PUB main
    
      ser.start(rx_pin, tx_pin, 0, baud)
      ser.printf("Frequency counter demo\n")
    
      wrpin(msr_time,msr_pin+0)
      wrpin(msr_states,msr_pin+1)
      wrpin(msr_periods,msr_pin+2)    
      wxpin(msr_min,msr_pins)
      wypin(%00,msr_pins)
    
      org
                dirh    #msr_pins           'concurrently enable smart pins
        .loop       akpin   #msr_pins           'clear any old measurement
                waitx   #3
        .wait       testp   #msr_pin    wc      'wait for new measurement
            if_nc   jmp #.wait
      end
      clocks  := rqpin(msr_pin+0)
      states  := rqpin(msr_pin+1)
      periods := rqpin(msr_pin+2)
      'duty = states * 1_000 / clocks
      'frequency = periods * sysfreq / clocks
      'frequency_sub = remainder / clocks * 1_000_000
    
      ser.printf("clocks: %x  states: %x periods: %x \n", clocks, states, periods )
    
      org
                jmp #.loop
    
      end
    
  • @Ramon said:
    How can I convert this PASM code to SPIN2 with the minimal lines of code?

    While not an answer to your Spin2 code conversion question, take a look at the TSL235R Quick Byte that I wrote. I converted Chip's standalone Reciprocal Counter PASM2 code to inline PASM2 and placed it into two driver routines:

    • fb_measfreq2P.spin2 <-- Read frequency, periods using 2 P2 smart pins.
    • fb_measfreq3P.spin2 <-- Read frequency, periods, duty cycle using 3 P2 smart pins.

    Inline PASM2 code lets you have the best of both worlds, easier programming/integration in Spin, while still having speedy PASM2 routines.

  • evanhevanh Posts: 16,027

    Ramon,
    You won't be able to JMP between assembly blocks like that. Each Pasm block is handled one at a time with just what is defined for it, not unlike Spin methods. Any branching will have to be within a block.

  • Francis, many thank for your help. I will take a look to that code, I am yet a little scared by PASM and try to use the most simple SPIN keywords. Nice that you can reduce the number of smart pins used. I was about to ask if it could be possible to just use one smart pin to detect frequency.

    Evanh, yes I thought that the loop would not execute but in fact was repeating the code. Those ASM instructions I am not sure how to correctly convert into SPIN.

  • AribaAriba Posts: 2,690

    @Ramon said:
    How can I convert this PASM code to SPIN2 with the minimal lines of code?

    Here is a simple version in Spin2. I use P40 as input and output a test-frequency at P39, which is connected to pin 40 with a wire. It only measures the frequency, not the duty.

    CON
      _clkfreq  =  200_000_000
    
      MSR_PIN   =  40    {connected to pin 39}
    
    PUB main() | ticks, periods, freq, mintime
      pinstart(39, P_NCO_FREQ+P_OE, 1, 1_000_150 frac clkfreq)   'test freq
    
      mintime := clkfreq / 1_000             '1ms min measure time
      pinstart(MSR_PIN+0, P_COUNTER_TICKS, mintime, %00)
      pinstart(MSR_PIN+1, P_MINUS1_A + P_MINUS1_B + P_COUNTER_PERIODS, mintime, %00)
    
      repeat
        akpin(MSR_PIN addpins 1)              'start next measurement
        repeat until pinr(MSR_PIN)            'wait until done
        ticks   := rqpin(MSR_PIN+0)           'read measured values
        periods := rqpin(MSR_PIN+1)
        freq := muldiv64(clkfreq, periods, ticks-1)     'calc frequency
    
        debug(udec(ticks), udec(periods), udec(freq))
        waitms(300)
    

    Andy

  • Thank you Ariba!

    I got it working, but I think you have some typo on lines 4 and 7.

    Line 4 :  should be -> MSR_PIN = 39
    Line 7 :  should be -> pinstart(MSR_PIN, P_NCO_FREQ+P_OE, 1, 1_000_150 frac clkfreq)   'test freq
    

    Also the last parameter (1_000_150 frac clkfreq) seems to result zero always in (%00).
    According to SPIN docs, the frac equivalent should be:

    1_000_150 frac clkfreq   =  (1_000_150<<32) / clkfreq      '  Result is Zero
    

    Is that some mistake, or copy&paste from another code?
    I am able to run the code with good results when I change the '1_000_150 frac clkfreq' with '%00'.

    I have made a Flexprop version (for Retroblade2) here (Flexprop doesn't allow addpins, frac, or debug instructions)

    CON
      _clkfreq  =  200_000_000
      rx_pin   = 63
      tx_pin   = 62
      baud     = 115_200
      DOWNLOAD_BAUD = 115_200
      DEBUG_DELAY = 100
      DEBUG_BAUD = 115_200
    
      MSR_PIN   =  0    {connected to pin 0}
    
    OBJ
      ser: "spin/SmartSerial"
    
    PUB main() | ticks, periods, freq, mintime
    
      ser.start(rx_pin, tx_pin, 0, baud)
      'pinstart(39, P_NCO_FREQ+P_OE, 1, 1_000_150 frac clkfreq)   'test freq
      pinstart(MSR_PIN, P_NCO_FREQ+P_OE, 1, (1_000_150<<32) / clkfreq)   'test freq
    
      mintime := clkfreq / 1_000             '1ms min measure time
      pinstart(MSR_PIN+0, P_COUNTER_TICKS, mintime, %00)
      pinstart(MSR_PIN+1, P_MINUS1_A + P_MINUS1_B + P_COUNTER_PERIODS, mintime, %00)
    
      repeat 
        'akpin(MSR_PIN addpins 1)              'start next measurement
        akpin( (MSR_PIN & $3F) | (1 & $1F) << 6)
        repeat until pinr(MSR_PIN)            'wait until done
        ticks   := rqpin(MSR_PIN+0)           'read measured values
        periods := rqpin(MSR_PIN+1)
        freq := muldiv64(clkfreq, periods, ticks-1)     'calc frequency
    
        'debug(udec(ticks), udec(periods), udec(freq))
        ser.printf("ticks: %u periods: %u  freq: %u \n", ticks, periods, freq)
    
        waitms(300)
    

    It is working. But still have no idea how it works. why do we need two pinstarts (first NCO mode, and later ticks counter) for MSR_PIN?

    pinstart(MSR_PIN, P_NCO_FREQ+P_OE, 1, (1_000_150<<32) / clkfreq) 'test freq
    pinstart(MSR_PIN+0, P_COUNTER_TICKS, mintime, %00)
    pinstart(MSR_PIN+1, P_MINUS1_A + P_MINUS1_B + P_COUNTER_PERIODS, mintime, %00)

    By the way, what is a TICK? rise or fall?
    And what are periods? Is a 'period' according to our system clock, or a full cycle (period) detected while reading the pin?

    These are the kind of examples that I will never understand until some nice diagrams or drawings are made.

  • AribaAriba Posts: 2,690

    I output a test frequency on Pin 39 which I can vary to test the Frequency counter. For this is the pinstart on the first line in main(), it just starts an NCO. If you don't need that you can delete this line.

    The counter input is on the MSR_PIN and the MSR_PIN + 1, which is pin 40 and pin 41 in my code (you seem to use pin 0 and 1).

    My code works also with Flexprop without any modification. Flexprop knows FRAC, ADDPINS and DEBUG (in this simple form). Maybe you have a very old version.

    By the way, what is a TICK? rise or fall?
    And what are periods? Is a 'period' according to our system clock, or a full cycle (period) detected while reading the pin?

    A tick is one single sysclock clock. Raise or fall defines if the positive or the negative edges of the input frequency are use to measure the periods. A period is the time from one positive edge to the next positive edge of the input signal.
    One smartpin (41) measures the number of periodes that fit into the 1ms window, and the other smartpin (40) measures the time that all these periods take. They don't stop instantly after the 1ms time window, like a simple frequency counter. No they both finish the last periode that goes over the 1ms window, that's the trick of this reciprocal counter.

    If you know the time that a certain number of periods take, you can calculate the frequency from that.

    Andy

  • @Ramon said:
    I have made a Flexprop version (for Retroblade2) here (Flexprop doesn't allow addpins, frac, or debug instructions)

    I think you need to update your flexprop. addpins, frac, and debug have been supported for some time now.

  • evanhevanh Posts: 16,027

    How is debug supported? I presume not via loadp2 -t.

  • @evanh said:
    How is debug supported? I presume not via loadp2 -t.

    It is exactly via loadp2 -t :). debug statements just get translated into the equivalent of BASIC "print" statements. The backtick kinds of debug (for graphics) just print the backtick strings, no graphics. debug() statements in PASM code are ignored.

  • evanhevanh Posts: 16,027
    edited 2021-04-08 01:56

    Ah, no graphics makes sense. But still not getting anything reported with the simple demo from Pnut's .zip

    CON _clkfreq = 10_000_000
    
    PUB go() | i
      repeat i from 0 to 9
        debug(udec(i))
    

    Propeller Spin/PASM Compiler 'FlexSpin' (c) 2011-2021 Total Spectrum Software Inc.
    Version 5.3.3-beta-v5.3.2-9-gf905169e Compiled on: Apr 8 2021

    PS: I'm not using Flexprop. Flexspin and Loadp2 are hand entered in the shell.

  • @evanh : DEBUG() statements are optional (as they are in PNut). In order to enable debug() flexspin needs the -g flag on the command line. We should probably continue this on the flexspin thread if you have more questions.

  • evanhevanh Posts: 16,027

    Thanks, that got it. The needed .c sources was a surprise.

  • @ersmith said:

    @Ramon said:
    I have made a Flexprop version (for Retroblade2) here (Flexprop doesn't allow addpins, frac, or debug instructions)

    I think you need to update your flexprop. addpins, frac, and debug have been supported for some time now.

    I was using 5.2.0. I just downloaded 5.3.2, and looks that it is the same error I had before.

    error: syntax error, unexpected identifier `frac', expecting ')' or ','
    
    error: syntax error, unexpected identifier `addpins', expecting ')' or ','
    
    error: unknown identifier debug used in function call
    error: unknown identifier udec used in function call
    error: unknown identifier udec used in function call
    error: unknown identifier udec used in function call
    

  • Ariba, Thank you for explaining all the details. Now I understand.

    Never occurred to me that something like that was possible !!

    Being able to configure the (smart)pin for output to create a test frequency and at the same time configure the same pin with counter function for input is like having two smartpins on each pin.

    That could be great to make a simple test program that can check all pin input/ouputs. (with a wire between each pin pair:

    • Frequency test output (pin 0, 2, 4, 6, 8, 10 ... 62) with NCO.
    • 'Loopback' cable between 0+1, 2+1, 4+1 ... 62+1.
    • And configure frequency counters on 0+1, 2+1 .. 62+1.

    Much cheaper than 64 resistors + LEDs.

  • I think you need to update your flexprop. addpins, frac, and debug have been supported for some time now.

    I was using 5.2.0. I just downloaded 5.3.2, and looks that it is the same error I had before.

    Ah, I see the problem, your file is named .spin (for Spin 1) instead of .spin2 (for Spin 2). The two languages are slightly different, unfortunately. To use Spin2 operators like addpin or frac you need to name the file with a .spin2 extension.

  • Haha, Sorry !! :D At least, that time was not wasted. I have learned what those instructions were doing. :s

  • evanhevanh Posts: 16,027
    edited 2021-04-08 23:48

    @Ramon said:
    Ariba, Thank you for explaining all the details. Now I understand.

    Never occurred to me that something like that was possible !!

    Being able to configure the (smart)pin for output to create a test frequency and at the same time configure the same pin with counter function for input is like having two smartpins on each pin.

    That could be great to make a simple test program that can check all pin input/ouputs. (with a wire between each pin pair:

    You can do all that and more without any wires at all. However, Ariba used smartpin's 39, 40 and 41. Three of them. Each pin has one smartpin. Each smartpin does only one job at a time.

    Each pin input can be rerouted, by the mode bits, to up to six other nearby pin INs. This saves using any linking wires. Which is what Chip has done for two of the three smartpins in the original Reciprocal Counter Demo.

  • evanhevanh Posts: 16,027

    Here's Ariba's setup without any wires needed:

      pinstart(MSR_PIN, P_NCO_FREQ+P_OE, 1, 1_000_150 frac clkfreq)   'test freq
      pinstart(MSR_PIN+1, P_MINUS1_A + P_MINUS1_B + P_COUNTER_TICKS, mintime, %00)
      pinstart(MSR_PIN+2, P_MINUS2_A + P_MINUS2_B + P_COUNTER_PERIODS, mintime, %00)
    
  • Is it possible to generate a test frequency and calculate its frequency, with just two pins?

    I don't want to save any wire. The wire is a requirement, to be able to test the pins output and input.

  • evanhevanh Posts: 16,027

    You only need the one active pin. The mode configurable re-routing works on physical pin inputs only. It doesn't shortcut anything. So, you can fully test each pin input and output without any loopback wires.

    In my config above the pin under test is MSR_PIN. The fact that there is two extra smartpins involved doesn't detract from it being a real physical action on that pin - With both the output driver slewing times and the input threshold propagation times.

  • evanhevanh Posts: 16,027
    edited 2021-04-09 01:17

    There is block diagram we put together that might help - https://forums.parallax.com/discussion/171420/smartpin-diagram/p1
    The "Logic Input" block is where to select the routed inputs. Each is from another nearby pin input, just the same way as the immediate input is routed. In fact, for the odd pin, -1 is even shown coming from the even pin. And, for the even pin, +1 is coming from the odd pin.

Sign In or Register to comment.