Can someone help me figure out why my code is inconsistent?
Attached is my object with a pulse generator and a pulse reader method. On the bench the reader works perfect, but in real life I am getting inconsistent results as shown in the terminal screen capture posted below. I've went through my code for the past week about 50 times, tried everything I know to troubleshoot and I can't figure out what I'm doing wrong so it is time to ask for help. The code always reads a lower value, never reads a higher value, and because of that I do not think it is chatter on the pin?
If anyone has any ideas/suggestions/troubleshooting techniques, I'm looking to learn. Thank you.
Numbers.spin
Float32Full.spin
rpmTest7.spin
FullDuplexSerial64.spin
If anyone has any ideas/suggestions/troubleshooting techniques, I'm looking to learn. Thank you.
Numbers.spin
Float32Full.spin
rpmTest7.spin
FullDuplexSerial64.spin

Comments
repeat until ina[I_Rpm] ' Wait for high repeat while ina[I_Rpm] ' Wait for low rpmTimeStart := cnt repeat 5 repeat until ina[I_Rpm] ' Wait for high repeat while ina[I_Rpm] ' Wait for low rpmTime5 := cnt - rpmTimeStart ' Compute time for 5 intervalsSadly enough, I thought my debug setup was pretty slick, haha ... but I'm always wanting to learn from the wise, so I will try to implement what you've discussed. How would you implement the debug code saying "I'm here right now" at any specific location, with the method you've proposed? Thank you.
Does this in essence pause the cog? I wanted to have the routine free to run other things if necessary. The frequency is very low at 2hz per (rpm/60) so it would basically disable the cog if I read this correctly.
Indeed, using a function generator, your code reports a linear increase from 1Hz (30rpm) up to 28Hz (840rpm), then at 29Hz it glitches to ~435rpm.
Commenting out all of the debug statements, except the final one that shows the average, and it can show up to 9000rpm at 300Hz input.
Best advice there is to follow Jon's advice and use a dedicated debug cog.
Have you considered using the built in counters? Either an edge detector to count the number of transitions, or a level detector to count the number of clock ticks that the signal remains high or low.
Here're a few ideas:
PUB Main debug.start(31,30,0,115200) waitcnt(cnt + clkfreq) ' posedge_counter pos_counter ' pos_demo PUB posedge_counter | ticks { Use the posedge counter mode to count the number of positive transitions in one second. Fails when dealing fractional frequencies - 2.5Hz should be 75rpm Display alternates between 60 and 90 } ctra := %01010 << 26 | I_RPM ' Configure counter A to increment phsa on every positive transition frqa := 1 ' Increment phsa by 1 on each transition repeat phsa := 0 ' Clear phsa waitcnt(cnt + clkfreq) ' Wait one second ticks := phsa ' Copy phsa into ticks debug.dec(ticks * 60 / 2) ' Convert ticks into rpm debug.tx($0D) PUB pos_counter | ticks { Use the pos counter mode to count the number of ticks that the input remains high Divide by the clock frequency } ctra := %01000 << 26 | I_RPM ' Configure counter A to increment phsa on every clock that the input is high frqa := 1 ' Increment phsa by 1 on each clock that the input is high repeat phsa := 0 ' Clear phsa repeat until ina[I_RPM] ' Wait for the input to go high repeat until not ina[I_RPM] ' Wait for the input to go low ticks := phsa ' Copy phsa into ticks debug.dec(clkfreq / ticks * 15) ' Convert ticks into rpm debug.tx($0D) PUB pos_demo | i, av0, av1, av2, av3, av4, average ctra := %01000 << 26 | I_RPM ' Configure counter A to increment phsa on every clock that the input is high frqa := 1 ' Increment phsa by 1 on each clock that the input is high repeat repeat i from 0 to 4 av0[i] := (clkfreq / counter * 15) average := (av0 + av1 + av2 + av3 + av4) / 5 debug.dec(average) debug.tx($0D) PRI counter : ticks { Return the number of ticks that input was high } repeat until not ina[I_RPM] ' Wait for the input to go low phsa := 0 ' Clear phsa repeat until ina[I_RPM] ' Wait for the input to go high repeat until not ina[I_RPM] ' Wait for the input to go low ticks := phsa ' Copy phsa into ticksBecause printing to the console is sloowww, when I have precisely timed projects, I will do all of my logging in a single cog. When I need to log a value, I assign it to a global variable. My debug cog then prints the value in that variable at a pre-determined frequency.
As for the "I'm at line ___ now" type of statement... that's slightly harder. You might need two variables for that: previous and current. Initialize both to 0 (does Spin have "null"?). When you hit a point that you want to log, assign `current` to some unique value. Your debug cog then checks the value of `previous` and `current` and, when not equal, prints `current` (or some string related to the value in `current`).
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 I_RPM = 1 VAR long rpm_stack[10] long rpm OBJ debug : "FullDuplexSerial" PUB Main debug.start(31,30,0,115_200) waitcnt(cnt + clkfreq) cognew(sample_rpm,@rpm_stack) repeat waitcnt(cnt + clkfreq / 20) debug.dec(rpm) debug.tx($0D) PUB sample_rpm | total, high, low {{ Runs in a new cog, Stores a rolling average in rpm, rpm must be declared in a VAR block Glitches between 5KHz and 6KHz at 50% duty cycle Glitches around 175Hz with a 1% or 99% duty cycle }} ctra := %01000 << 26 | I_RPM ' Configure counter A to increment phsa on every clock that the input is high ctrb := %01100 << 26 | I_RPM ' Configure counter B to increment phsb on every clock that the input is low frqa := 1 ' Increment phsa by 1 on each clock that the input is high frqb := 1 ' Increment phsb by 1 on each clock that the input is low total := 0 ' Initialize total repeat repeat until not ina[I_RPM] ' Wait for the input to go low (high pulse finished) high := phsa ' Store high pulse width in high total := total - rpm ' Subtract the average from the total total := total + clkfreq / ((high + low) / 30) ' Add the new period to the total rpm := total / 5 ' Divide the total by the sample size phsa := 0 ' Reset high time accumulator repeat until ina[I_RPM] ' Wait for the input to go high (low pulse finished) low := phsb ' Repeating the calculations and using separate registers for high and low total := total - rpm ' allows for updating the rpm twice each period total := total + clkfreq / ((high + low) / 30) ' (faster response time to small changes in slow frequencies) rpm := total / 5 ' phsb := 0 'It would be more (I have no idea how much more) accurate to clear phs[ab] immediately after you read it using the post-clear operator x~, instead of after 3 slow divides.
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 I_RPM = 1 VAR long rpm_stack[10] long rpm OBJ debug : "FullDuplexSerial" PUB Main debug.start(31,30,0,115_200) waitcnt(cnt + clkfreq) cognew(sample_rpm,@rpm_stack) repeat waitcnt(cnt + clkfreq / 20) debug.dec(rpm) debug.tx($0D) PUB sample_rpm | total, high, low {{ Runs in a new cog, Stores a rolling average in rpm, rpm must be declared in a VAR block Glitches between 5KHz and 6KHz at 50% duty cycle Glitches around 175Hz with a 1% or 99% duty cycle }} ctra := %01000 << 26 | I_RPM ' Configure counter A to increment phsa on every clock that the input is high ctrb := %01100 << 26 | I_RPM ' Configure counter B to increment phsb on every clock that the input is low frqa := 1 ' Increment phsa by 1 on each clock that the input is high frqb := 1 ' Increment phsb by 1 on each clock that the input is low total := 0 ' Initialize total repeat repeat until not ina[I_RPM] ' Wait for the input to go low (high pulse finished) [B]high := phsa~[/B] ' Store high pulse width in high[B] - post-clear[/B] total := total - rpm ' Subtract the average from the total total := total + clkfreq / ((high + low) / 30) ' Add the new period to the total rpm := total / 5 ' Divide the total by the sample size repeat until ina[I_RPM] ' Wait for the input to go high (low pulse finished) [B]low := phsb~[/B] ' Repeating the calculations and using separate registers for high and low[B] - post-clear[/B] total := total - rpm ' allows for updating the rpm twice each period total := total + clkfreq / ((high + low) / 30) ' (faster response time to small changes in slow frequencies) rpm := total / 5 'Edited for a second thought.
The above is true if the pulse is symetrical (50% duty) where the calculations not being finished before the next pulse width would glitch regardless of when phsx was re-initialized, however it makes a significant difference at a 1% duty cycle. The signal only has to be high long enough for phsb to be copied and cleared, as it doesn't matter if the signal goes low during the calculations.
With the original code, the readings get glitchy around 175Hz, with Electrodude's modification the readings are stable up to 500Hz.
There's some flickering above 20Hz where the readings might be +/- 3, but aside from that it is stable.
At 29Hz, however, the readings should be 870rpm, are instead being read as 435rpm.
I took another look at it, and it seems you must have a counter rolling over somewhere. It actually does read correctly for about 12 seconds, then it reads low for about 42 seconds.
2^32 x 12.5ns = 53.687 seconds
good readings for ~12 seconds bad readings for ~42 seconds rpmTimeStamp5 !< rpmTimeStamp6 rpmTimeStamp5 !< rpmTimeStamp6 rpmTimeStamp5 !< rpmTimeStamp6 rpmTimeStamp5 !< rpmTimeStamp6 1056197782 - 1053445622 = 2752160 1058963478 - 1056197782 = 2765696 4374155 - 251899 = 4122256 7108587 - 4374155 = 2734432 rpmTimeStamp4 !< rpmTimeStamp5 rpmTimeStamp4 !< rpmTimeStamp5 rpmTimeStamp4 !< rpmTimeStamp5 rpmTimeStamp4 !< rpmTimeStamp5 1058963478 - 1056197782 = 2765696 1061722390 - 1058963478 = 2758912 7108587 - 4374155 = 2734432 9840267 - 7108587 = 2731680 rpmTimeStamp3 !< rpmTimeStamp4 rpmTimeStamp3 !< rpmTimeStamp4 rpmTimeStamp3 !< rpmTimeStamp4 rpmTimeStamp3 !< rpmTimeStamp4 1061722390 - 1058963478 = 2758912 1064484758 - 1061722390 = 2762368 9840267 - 7108587 = 2731680 12579147 - 9840267 = 2738880 rpmTimeStamp2 !< rpmTimeStamp3 rpmTimeStamp2 !< rpmTimeStamp3 rpmTimeStamp2 !< rpmTimeStamp3 rpmTimeStamp2 !< rpmTimeStamp3 1064484758 - 1061722390 = 2762368 1067264502 - 1064484758 = 2779744 12579147 - 9840267 = 2738880 15327435 - 12579147 = 2748288 rpmTimeStamp1 !< rpmTimeStamp2 rpmTimeStamp1 !< rpmTimeStamp2 rpmTimeStamp1 !< rpmTimeStamp2 rpmTimeStamp1 !< rpmTimeStamp2 1067264502 - 1064484758 = 2779744 1071421750 - 1067264502 = 4157248 15327435 - 12579147 = 2748288 18107627 - 15327435 = 2780192 Calculated rpm is: 867 Calculated rpm is: 786 Calculated rpm is: 795 Calculated rpm is: 873 ***** < 600 ***** ***** < 600 ***** Here's where it starts glitching Here's where it stops glitchingAt 30Hz and up, it is always misreading. Commenting out all of the debug statements up to the "Calculated RPM is:" line allows it to read correctly... within reason.100Hz produces readings from 2997 to 3003. 300Hz gives 8937 to 9057.
Even with the debugs commented out, this object starts running into problems at about 361Hz - should be 10830rpm, reading 9042 to 10767.
I replaced the function generator with the freq_synth object from the Propeller library demo so you can see what I see.
Your simulateRpm method does indeed appear to give good readings from 24Hz (720rpm) to 76Hz(2280rpm). I suspect that the reason we don't see it glitch is due to the way it is constantly varying, which somehow isn't giving the counters the opportunity of rolling over, or whatever's going wrong. Try a steady signal instead.
rpmTest7a.spin
Another flaw I found today and a hard lesson to learn, which was assuredly causing me issues is that I_Rpm is based on voltage into a voltage divider of 10k and 2.7k, so voltage should be anywhere from 2.34vDc to 2.98vDc with a 11 to 14v input. This was the case when the car was not running, but when it runs the alternator does some craziness to the electrical system. Unknowingly until today, I was plagued by I_Rpm == 0 every once in a while, which puts the code in a lower power state of 1hz per loop and outputs are turned off. This was hosing my timing calculations for a frequency generator (among other things) and I've been chasing this for a few weeks now. Finally driving around with the laptop, the com window open, some additional commenting and my scope I was able to identify it. I mention all of this, because the calculateRpm was also dependent on I_Rpm == 1.
I thought about trying to solve this by using a 3k/10k resistor ladder, but decided to put a 100uF cap inline with the resistor ladder. Do you think this was the best solution? How do you normally tackle these type of automotive issues?
As for connecting to a 14 volt signal, there are level shifters, buffers, optocouplers, current limiters, voltage dividers... whatever works.
See this thread for interfacing to 5V.