Shop OBEX P1 Docs P2 Docs Learn Events
Counting pulses without hanging code when no pulses are available? — Parallax Forums

Counting pulses without hanging code when no pulses are available?

eagletalontimeagletalontim Posts: 1,399
edited 2013-02-19 09:50 in Propeller 1
I am working on reading a vehicle speed sensor that outputs a 5v square wave but have not been able to figure out how to keep the COG from hanging while waiting for a high or low signal if the vehicle is stopped. The only ways that I know how to read frequency is using the waitpeq function, the COUNT function in the BS2_Function, or with the JM_FREQIN function that I think uses the same thing. The problem with the JM_FREQIN function is I cannot call it faster than the frequency... meaning, if I get 1 pulse per second, I cannot call it ever 300ms like I need. The pulses could be anywhere from 1 per 1 second to 400 per 1 second. The waitpeq would do just that... wait. And the COUNT function could miss a few pulses that could make the readings inaccurate. Does anyone have any suggestions on what I could use to accomplish what I am trying to get done?
«134

Comments

  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-02-10 18:24
    What kind of resolution to you need?

    There are a variety of objects for capture pulses the poll the pin to see how long it stays in one state of the other. I have four quadrature encoder object that does this (the motor object has a bug but I think the encoder object works fine). I used JonnyMac's Nuts & Volts article as a guide (see column #6). You could use a simplified quadrature encoder type object to do what you want.

    I'm sure there are other ways to do this I'm not thinking of, maybe someone else will chime in with some other options.
  • Duane C. JohnsonDuane C. Johnson Posts: 955
    edited 2013-02-10 18:36
    Hi eagletalontim;

    This is a possible solution.

    Have an oscillator generate a steady, say, 5Hz square wave and using an XOR gate.
    The tach signal on one input and the 5Hz on the other.
    Assuming the tach outputs 0Hz to 400Hz.
    The result will be 5Hz to 405Hz.
    Once the measurement is made simply subtract 5Hz.

    Basically with this scheme you never have no pulses to count.

    A possible advantage is the measurement could be done quicker than once per second, possibly 5 per second although the resolution would be lower.

    Duane J
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-02-10 19:10
    Why poll the pin in code? A counter can be set up to count rising or falling edges in the background. Of course, the edges do have to be clean, without bounce, for this to be effective.

    -Phil
  • eagletalontimeagletalontim Posts: 1,399
    edited 2013-02-10 19:12
    Wow, a quadrature encoder is way over my head :p I was hoping for something simple to get it working. I tried to count time between pulses, but can't figure out how to have a "timeout" if the pulses are too far apart or they have stopped pulsing all together.
  • eagletalontimeagletalontim Posts: 1,399
    edited 2013-02-10 19:21
    I am limited on what funds I can drop into this project so I need to keep it as simple as possible without purchasing anything else. I have basic resistors, transistors, diodes, and capacitors to work with. I am using a 2 resistor voltage divider to a pin to simply pick up the signal. that is about as simple as I can get for now. Hopefully this can be fixed with coding instead of changing the circuit.
  • eagletalontimeagletalontim Posts: 1,399
    edited 2013-02-10 20:07
    ok, maybe this would work...

    If there is a way to have a section of code that is only allowed to run for 1 second then "timeout", I could run a code that will count how long it takes to get lets say 3 pulses. If the 3 pulses take longer than 1 second, abort and set speed at 0.

    From what I have seen, there would be a total of 16364 pulses per mile. This would mean that if the vehicle were traveling at 1 MPH, there would be 4.54 pulses per second? (16364 pulses / 60 minutes per hour / 60 seconds per minute).

    So, is there a way to limit code to run in an allotted time and abort if time runs out?
  • jmgjmg Posts: 15,173
    edited 2013-02-10 20:13
    I am working on reading a vehicle speed sensor that outputs a 5v square wave

    How clean are those edges ? - Do you have a part number/link ?
    As Phil mentioned in #4, you can use a Counter to increment on a Rise or Fall, so run one as a time-counter, and one as a edge-counter, and then check the changes however often you need to.

    There is no stall : no sensor edges means the edge count holds, while the Time continues to increase.

    Grab AN001 and search for POSEDGE and NEGEDGE
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-02-10 20:15
    Once again, the Prop's internal counters can do what you want, without additional expenditure and without tying up a cog, so long as your encoder edges are clean.

    -Phil

    Edit: What jmg said. Get the AppNote.
  • jmgjmg Posts: 15,173
    edited 2013-02-10 20:19
    ok, maybe this would work...

    If there is a way to have a section of code that is only allowed to run for 1 second then "timeout", I could run a code that will count how long it takes to get lets say 3 pulses. If the 3 pulses take longer than 1 second, abort and set speed at 0.

    From what I have seen, there would be a total of 16364 pulses per mile. This would mean that if the vehicle were traveling at 1 MPH, there would be 4.54 pulses per second? (16364 pulses / 60 minutes per hour / 60 seconds per minute).

    So, is there a way to limit code to run in an allotted time and abort if time runs out?

    With slow pulses, you can run your own edge detection - you just compare two consecutive samples, and if they differ, then you check for Rising or Falling case, and on the one you want, you Increment a counter.
    - but the hardware can do that for you, or are you already using both timers ?
  • eagletalontimeagletalontim Posts: 1,399
    edited 2013-02-10 20:34
    I don't have an oscilloscope yet but I just ordered one from amazon that will probably work for what I need. it's a hand held one that I believe you can upload data to a computer for further viewing. I don't know how clean the edges are so i am kind of shooting in the dark at the moment. I just bought me a laptop so I can reprogram the prop while away from my house on test drives.

    Phil, are the counters you are talking about phsa and phsb? If so, yes they are being uses by the JM_FREQIN function for reading RPM. That function is working 100% perfect currently.

    If I used Edge detection, would that not include using waitpeq or waitpne? Those both must wait for a high or low. If there is no change for 10 seconds, they will continue to wait. The problem I see with that is if I am driving 45 mph and slam on my brakes locking them up, the prop would continue to think I am traveling 45mph since the pulses stopped and no variable can be updated.... right?
  • Duane C. JohnsonDuane C. Johnson Posts: 955
    edited 2013-02-10 20:39
    Once again, the Prop's internal counters can do what you want, without additional expenditure and without tying up a cog, so long as your encoder edges are clean.
    But he's not using a Prop, it sounded like a BS2.

    Duane J
  • eagletalontimeagletalontim Posts: 1,399
    edited 2013-02-10 20:42
    I am using a Prop. It is the prop proto board that I have hooked up in my car using an external voltage regulator plugged into the power jack on the proto board. Everything is soldered onto that board that I need so far.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-02-10 20:42
    No, he's using a Prop. (It's in the Prop forum, after all). He was just rying to do it with the BS2 work-alike object.

    -Phil
  • eagletalontimeagletalontim Posts: 1,399
    edited 2013-02-10 21:25
    I am still thinking about this, but if I am using the freqin code which uses the phsa and phsb in a cog and I start another cog, could I still use the phsa and b counter in the second cog without conflict?

    I also made a pulse counter for my power logger that i use to track my current power usage. I wonder if it would be similar to what I am needing. Here is the code to the pulse counter for that project :
    PUB PulseCounter | updated, cnt1, cnt2
      count1 := 0
      repeat
        updated := 0  
        dira[LED]~~
        repeat while ina[Sensor] == 1
          if updated == 0
            cnt2 := cnt
            pulseCount++
            updated := 1
          outa[LED] := 1
          waitcnt(clkfreq / 1000 * 10 + cnt)
          if count1 > 0
            pulsewidth := (cnt2 - cnt1) / (clkfreq / 10000) 
          count1 := 0
        if updated == 1
          cnt1 := cnt
        count1++
        outa[LED] := 0
    
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-02-10 22:57
    Each cog has its own two counters and associated counter registers. There will be no conflict.

    -Phil
  • eagletalontimeagletalontim Posts: 1,399
    edited 2013-02-11 20:59
    Well, I have found that using INA[pin] does not work :( I wonder if I have the wrong resistors for the voltage divider. If it is a 5 volt pulse, then I need to drop the low below the threshold of the pin and allow it to go above the threshold for that pin when there is a pulse, right?

    I don't remember what resistors I have in place, but if I remember correctly, I used 2 10k resistors for that circuit. I was attempting to read the pulse generators from the transmission instead of the speed sensor which is apparently a reed switch from my research.
  • pgbpsupgbpsu Posts: 460
    edited 2013-02-12 09:07
    Here's some example code using the counters to read input pulses. As Phil stated, you'll need your pulses to be clean for this to work and you'll need a free counter. I don't know the JM_FREQIN object, it might launch itself into its own cog or maybe you've got another free but this should give you and idea of what Phil's talking about.
    CON
    
      _CLKMODE = XTAL1 + PLL16X
      _XinFreq = 5_000_000
    
      INPIN    = 4  ' pin number to monitor
      REFRESH_RATE = 16_000_000' 80_000_000/5
    OBJ
      pst     : "Parallax Serial Terminal"
    
    VAR
      long waitUntil, inFreq
      
    PUB MAIN
      pst.start(38400)
      PAUSE_MS(2000)
      pst.str(String(13,13,13,"Welcome to the freq measurer."))
    
      ctra := %01010 << 26 + INPIN    ' counter A in POSedge mode
      frqa := 1                       ' increment 1 per pulse  waitcnt(clkfreq + cnt)
    
    ' measure the input duty cycle using counters.
      waitUntil := cnt + REFRESH_RATE
      repeat
        phsa := 0                     ' reset count registers
        waitcnt(waitUntil+=REFRESH_RATE)
        inFreq       := phsa * (constant(80_000_000/REFRESH_RATE))         ' read the number of edges; convert to 1 second
    
        pst.str(String(13,"Measure inFreq: "))
        pst.dec(inFreq)
        pst.str(String(" phsa: "))
        pst.dec(phsa)
    
    PUB PAUSE_MS(mS)
      waitcnt(clkfreq/1000 * mS + cnt)
    

    Peter
  • MicksterMickster Posts: 2,694
    edited 2013-02-12 12:12
    Not sure if I interpreted the requirements accurately and I don't write Spin but is this pseudo-code along the lines of what you are trying to achieve?

    High_One_Shot = 0
    Low_One_Shot = 0
    Micro_Second_Count = 0
    Time_Out = 1000000 'A million microseconds
    
    
    
    
    DO
    
    
        If Pin = High AND High_One_Shot = 0
            High_One_Shot = 1
            Low_One_Shot = 0
            Micro_Second_Count = 0
        ElseIf Pin = Low AND Low_One_Shot = 0
            Low_One_Shot = 1
            High_One_Shot = 0
            Micro_Second_Count = 0
        EndIF
    
    
        Pause_One_Micro_Second
    
    
        Micro_Second_Count = Micro_Second_Count + 1
    
    
        IF Micro_Second_Count = Time_Out
            'We stopped
        EndIF
    
    
        'Now here, based on the Max "Micro_Second_Count" between transitions, frequency can be determined
    
    
    
    
    Loop
    
    
    

    Mickster

    P.S. 1uS is not going to work with Spin but something like 100uS should (right?)
  • Jim EdwardsJim Edwards Posts: 54
    edited 2013-02-14 06:04
    In for updates.
  • borisgborisg Posts: 39
    edited 2013-02-14 14:45
    First of all, how long are your pulses? If they're > 1 msec in duration, then all you need to do is setup a 1 msec counter in a cog and then check for the existence of a pulse every time the cog cycles through the msec counter loop. This will also let you timestamp each pulse to an accuracy of 1 msec and one can use Spin code to do the slow calculations. I've just used the 1 msec cycle time as that's more than fast enough for physiologic applications. For your application, at 100 mph there would be 454 pulses/second and so you'd need to run you cog timing loop a lot faster.

    It's no problem to go faster with the cog timing loop and I'm guessing that a cycle time of 10-20 microseconds/loop would be the limit (that's with execution of the pulse finding code and writing to hub RAM). The only caveat is that the duration of the pulse needs to be longer than the clock time - preferably twice as long to guarantee catching the pulse. Obviously your "clock" will overflow a lot faster in the 20 microsecond/loop case. Have you been writing code in Spin or PASM?
  • eagletalontimeagletalontim Posts: 1,399
    edited 2013-02-14 16:35
    I only understand SPIN right now. I cannot find ANY understandable information on PASM programming except for other peoples codes in the forum. I don't expect any vehicle to exceed 200 MPH with this device so I calculated somewhere around 909 pulses per second. Problem is, I need to be able to track the pulses precisely and have a millisecond result output to the pulsewidth variable. If I use ANY function that has to "Wait" for a pulse, it could be disastrous since if the vehicles wheels were locked up at any speed, my program would still think it was traveling at the previous speed. My oscilloscope still has not come in yet so I can't get any reading of how long or clean each pulse is. Hopefully it will be here tomorrow!
  • JonnyMacJonnyMac Posts: 9,108
    edited 2013-02-14 19:11
    If the pulses are clean you could easily launch a Spin cog that updates a hub variable every second with the number of falling edges (I think you said it was a low-going pulse) -- something like this:
    pri pulse_counter(pin, p_hub) | t                               ' launch with cognew
    
      ctra := (%01110 << 26) | pin                                  ' set pin to neg edge detect
      frqa := 1
      phsa := 0
    
      t := cnt                                                      ' synchronize timing
      repeat
        waitcnt(t += clkfreq)                                       ' wait 1s
        long[p_hub] := phsa                                         ' report to hub
        phsa := 0                                                   ' clear for next period
    


    You need to create variables in your main program space.
    var
    
      long  accumulator
      long  accstack[16]
    


    Finally, you can launch the cog:
    cognew(pulse_counter(PULSE_PIN, @accumulator), @accstack)
    


    Easy-peazy.
  • pgbpsupgbpsu Posts: 460
    edited 2013-02-14 19:36
    Jon has laid this out very nicely for launching into a separate cog. Just keep in mind that what he's got has a 1 second update rate. If you want 1ms update you'll have to adjust the delay time inside the repeat loop.
  • eagletalontimeagletalontim Posts: 1,399
    edited 2013-02-14 21:09
    Ok, so I kind of see what is working there. It looks like the cog simply waits for 1 second to get the phsa count since last pin pulse? The variable updated is "accumulator" which is the time in milliseconds(?) between each pulse?

    To update every 200 milliseconds, I could simply change waitcnt(t += clkfreq) to waitcnt(t += clkfreq / 200)?
  • StefanL38StefanL38 Posts: 2,292
    edited 2013-02-14 21:59
    Hi,

    no. The system variable ClkFreq contains the number of clockticks that occure in one second. At 80 MHz these are 80 Million clockticks.

    so waitcnt(t += clkfreq) waits for 80 Mio clockticks (which is one second

    if you want to wait for 200 milliseconds. This is 1/5 second and the code is

    waitcnt(t += clkfreq / 5)
    As you are doing something for a vehicle. Is this project just some additional display or will the vehicle be controlled by this measurement?

    Only in case the vehicle is conrolled by this thing:
    If it is controlled I highly recommend to become an expert about programming in general and then become an expert about safety-requirements
    and fail-safe mechanisms to avoid unexpexted behaviour whenever possible.

    Again only in case the vehicle is CONTROLLED by this Huh !! Please can you tell me where and when this vehicle is driving that I can keep away from this area?

    best regards Stefan
  • eagletalontimeagletalontim Posts: 1,399
    edited 2013-02-14 22:16
    The speed sensor is use to prevent possible user error / issues with a current product I am upgrading to the PROP. I am currently using the outdated SX and have been for about 4 years now. The PROP is more powerful and can handle more processes for better error checking so I have decided to add a few extra features / measurements in there to enhance the product a bit more. I am getting more and more proficient with SPIN as I work with it and have been testing the new design for just over a year without any complications. Some things I don't fully understand, but I know enough to make it work the way I need it to work either by other's trials and errors, or my own. There are several fail-safe mechanisms in place that stop the user from causing problems by hitting the wrong button at the wrong time :) All currently work flawlessly. The speed sensor is just an added bonus!
  • borisgborisg Posts: 39
    edited 2013-02-15 01:57
    Here's a routine I wrote which should be exactly what you're looking for. Currently, it checks for a pulse every 2 microseconds and writes the pulses and pulse duration to hub RAM. High Speed Pulse Interval and Duration (HSPID) is written in PASM and was by far the easiest bit of the code to write. Was nice seeing my heart interbeat intervals timed at 2 microsecond resolution scrolling up the screen. WRT the Spin code, I'm really starting to dislike that language. Is there a good reference book on Spin anywhere? PASM is very well documented, totally obvious and don't see why most people aren't writing their code in PASM given the incredible speed of this machine and far greater ease of debugging.

    Top spin program should be HSPID_test and I've thrown in my modified version of Parallax Serial Terminal which had no feature to check for a character and give an indication that there wasn't one until I modified RxCheck to make it public.

    The other thing that shocked me immensely is that there's no Go To in Spin!!!!! How can one write code without a branch or jump instruction??

    My version of Firefox hangs when I attempt to upload an attachment so will try it from a far faster machine than my laptop.

    That's better - opened a folder in <1 second. So enjoy. This program will wait forever for a pulse and time them when they occur. Maybe you can help me figure out the Spin sections of the code.
  • kuronekokuroneko Posts: 3,623
    edited 2013-02-15 04:07
    borisg wrote: »
    High Speed Pulse Interval and Duration (HSPID) is written in PASM and was by far the easiest bit of the code to write.
    Is it intentional that a 2us pulse can - under certain circumstances - be logged as being 6us long (3 x 2us units)?

    Also, this fragment - while keeping pin 4 as an input (default) - also sets bits 5 and 2 as outputs (cog location for DataMask is $24). Even with the # removed the muxz would actually set bit 4 as an output. ???
    xor     TState, TState  wz            'pulse state var = 0
                      muxz    dira, #DataMask               ' set bit4 to input                  ' 
    
  • borisgborisg Posts: 39
    edited 2013-02-15 05:07
    Kuroneko, you're quite right, the:
    muxz dira, #DataMask should be muxz dira, #DataMask

    It was entirely fortuitous that it worked. As soon as I finally saw Polar HR monitor data timed at 2 microsecond precision rolling up the screen, I assumed the program was fine. That error was a lapse into PDP-11 assembler mode. Still getting the hand of Propeller programming even though I've now been at it for a month.

    As far as a 2 microsecond pulse coming in as 6 microseconds, I can't see any way of that happening, at least from the software perspective. The worst case execution of the loop, including hub writes, is <2 microseconds. The only way I can see that happening would be if there was enough capacitance on the input pin to stretch the pulse. How far apart were the 2 microsecond pulses spaced?

    I'll have another look at the code in the morning to see if there's any other stupid errors there. I just took a chunk out of my Micromedic timer code and ramped up the frequency as far as I could. Hopefully it will be fast enough to reverse engineer what I suspect is an I2C device being driven in a very bizarre manner through a serial port.

    And, sigh, you're right about the xor effects on the Z flag. 2 mistakes in 2 assembly instructions must be a new record for me. Updated code is:
    xor TState, TState wz 'pulse state var = 0
    muxnz dira, DataMask ' set bit4 to input '

    Of course, it seems to work the same as the incorrect code and I'm glad you pointed out the errors I made as this coding error would have likely resulted in a very mysterious failure of the code to function if I added more instructions after the existing ones. The detailed Propeller documentation is quite explicit about the setting of the Z flag whereas the short summary sheet is ambiguous. Time to do a large volume printout of the manual.

    Both the code with errors and the new code give 7820 as the duration of the Polar HR monitor receiver output pulse (or 15.64 msec). My previous code using a 1 msec timebase would return 14 or 15 msec for the duration of this pulse. Looking at the histogram of pulse widths for the Polar output pulse shows the width to vary by only +/- 2 microseconds.

    Thanks again for spotting the errors - a good lesson that if code just happens to pass a quick screening doesn't mean it won't fail in future situations.
  • kuronekokuroneko Posts: 3,623
    edited 2013-02-15 05:21
    borisg wrote: »
    As far as a 2 microsecond pulse coming in as 6 microseconds, I can't see any way of that happening, at least from the software perspective.
    mov     TmTime, cnt
                      add     TmTime, TimeTick
    Timer             cmp     TmTime, cnt  wz, wc           ' see if cnt >= to TmTime
              if_nc   jmp     #Timer                        ' no, jump back
    
    Imagine cnt is -TimeTick just before you prepare TmTime ... (and no, a signed compare doesn't help, it just shifts the problem) ...

    Apart from that, when the pulse lasts longer it levels out again. Which is good - in a way - but it's still something which shouldn't happen.
Sign In or Register to comment.