Accurately read 0 - 15000 + RPM with the SX using ignition 5v square wave
Here I am again
I have searched the entire Parallax forum for any good information on how to read high RPM fast and accurate, but I have not found anything yet [noparse]:([/noparse]
The code I am using now is :
The code works and is accurate, but not accurate enough. The problem with the code above is that around 1400 RPM, it acts up and outputs a strange number which throws off the rest of the calculations. I have tried using COUNT 300 but if the engine revs fast, the count cannot accurately read the RPM's because it takes too long. As you can see, the COUNT is nulled out since it is not accurate enough. I hope to use this commercially soon so I need it to be as accurate as possible.
Here is what I am working with....
I am using an SX28 running at 5mhz.
I only have enough room for 2 word variables.
There are 4 open TEMP variables if needed.
I am using the stock RPM signal from the ignition coils.
The signal wire is the stock signal wire which was originally connected to the circuit I am replacing. It is a 5v Square Wave.
Here is what I am wanting to accomplish :
Accurately read RPM's ranging from 0 to 15,000 + RPM's (Tolerance can be +/- 100 RPM)
RPM variable is a BYTE so 100 RPM would be 1 and 15,000 RPM would be 150
I do not want to use an interrupt since the RPM only needs to be read when called for.
Since this will eventually be used commercially and each vehicle may be modified different, the function has to be able to return the accurate RPM as fast as possible.
Here is Bean's RPM Code that works:
The way I have the SX connected to the signal wire is like this :
10K
RPM Signal
\/\/\/\/
RB.4
| 100K
\/\/\/\/
Ground
I do not have access to an O-Scope so I am not able to see the exact cycles and how clean they are. As said before, I hope to use this commercially so each car would also be different. One may have a clean signal, and another may not. I am not sure how to hook up the SX to the RPM signal wire and filter out any unclean signals.
So to basically sum up all my questions into one area, here they are...
Can someone help me convert my RPM function to read accurate results in a BYTE variable without using too much Variable space?
If there is a better way to connect the SX to the RPM signal and get clean results, could someone better explain it to me?
I greatly appreciate all the help I have received so far in the past threads and hope I can soon get this working!
I have searched the entire Parallax forum for any good information on how to read high RPM fast and accurate, but I have not found anything yet [noparse]:([/noparse]The code I am using now is :
FUNC getrpm
'tmpW1 = 300 + values1(15)
'COUNT RPM_signal, tmpW1, tmpW1
PULSIN RPM_signal, 0, tmpW1
PULSIN RPM_signal, 1, tmpW2
tmpW1 = tmpW1 + tmpW2
tmpW1 = 30000 / tmpW1
IF C = 0 THEN
RETURN rpm
ELSE
RETURN tmpW1_LSB
ENDIF
ENDFUNC
The code works and is accurate, but not accurate enough. The problem with the code above is that around 1400 RPM, it acts up and outputs a strange number which throws off the rest of the calculations. I have tried using COUNT 300 but if the engine revs fast, the count cannot accurately read the RPM's because it takes too long. As you can see, the COUNT is nulled out since it is not accurate enough. I hope to use this commercially soon so I need it to be as accurate as possible.
Here is what I am working with....
I am using an SX28 running at 5mhz.
I only have enough room for 2 word variables.
There are 4 open TEMP variables if needed.
I am using the stock RPM signal from the ignition coils.
The signal wire is the stock signal wire which was originally connected to the circuit I am replacing. It is a 5v Square Wave.
Here is what I am wanting to accomplish :
Accurately read RPM's ranging from 0 to 15,000 + RPM's (Tolerance can be +/- 100 RPM)
RPM variable is a BYTE so 100 RPM would be 1 and 15,000 RPM would be 150
I do not want to use an interrupt since the RPM only needs to be read when called for.
Since this will eventually be used commercially and each vehicle may be modified different, the function has to be able to return the accurate RPM as fast as possible.
Here is Bean's RPM Code that works:
rpm VAR WORD 'too much space used here
pWidth0 VAR WORD
pWidth1 VAR WORD
dividendMSW VAR WORD 'too much space used here
dividendLSW VAR WORD 'too much space used here
overflow VAR BIT
doneBit VAR BIT
PULSIN RPM_signal, 0, pWidth0
PULSIN RPM_signal, 1, pWidth1
watch pWidth0
pWidth0 = pWidth0 + pWidth1
pWidth0 = pWidth0 * 2
dividendMSW = $005B
dividendLSW = $8D80
rpm = 1
overflow = 0
DO
doneBit = rpm.15
rpm = rpm << 1
IF overflow = 1 THEN
rpm.0 = 1
dividendMSW = dividendMSW - pWidth0
ELSE
IF dividendMSW >= pWidth0 THEN
rpm.0 = 1
dividendMSW = dividendMSW - pWidth0
ENDIF
ENDIF
overflow = dividendMSW.15
dividendMSW = dividendMSW << 1
dividendMSW.0 = dividendLSW.15
dividendLSW = dividendLSW << 1
LOOP UNTIL doneBit = 1
rpm = rpm << 1
rpm.0 = overflow
IF dividendMSW >= pWidth0 THEN
rpm.0 = 1
ENDIF
The way I have the SX connected to the signal wire is like this :
10K
RPM Signal
\/\/\/\/
RB.4
| 100K
\/\/\/\/
Ground
I do not have access to an O-Scope so I am not able to see the exact cycles and how clean they are. As said before, I hope to use this commercially so each car would also be different. One may have a clean signal, and another may not. I am not sure how to hook up the SX to the RPM signal wire and filter out any unclean signals.
So to basically sum up all my questions into one area, here they are...
Can someone help me convert my RPM function to read accurate results in a BYTE variable without using too much Variable space?
If there is a better way to connect the SX to the RPM signal and get clean results, could someone better explain it to me?
I greatly appreciate all the help I have received so far in the past threads and hope I can soon get this working!

Comments
Doctor: Don't do that.
This (commercial?) project has been running in circles for what seems months and despite a lot of good feedback from very experienced SX users you continue to beat your head into a wall saying, "No, I refuse to do it that way!" Why?
You can't stuff 50 pounds of sausage into a 5 pound bag and, in a way, that's what you're trying to do. You demand accuracy and yet you run the SX at the creep-along speed of 5MHz. Again, why? One assumes that as an automotive device you're getting power from the car so the additional 75mA or so by cranking up to 50MHz is not going to be a problem. What this does is give functions like PULSIN much better accuracy.
And your absolute refusal to use an interrupt is also mind boggling -- especially since I and others have given you working code (I wrote a program for you that gave you RPMs in 100 RPM units -- exactly what you want). Using an interrupt would allow you to have an on-demand RPM reading (you can't get faster than that) without the use of a complicated 32-bit division routine; this would save program space and make the program far less complicated.
As for the interrupts....I have tinkered with them for quite a few months and can never seem to figure them out....especially the timing [noparse]:([/noparse] This is the biggest reason I wanted to try and stay away from them. It seems they must be the best way to go, but I don't want to ask a million more questions to try and figure them out. Math was never my best subject, but I try my best to learn now
Here is how I have it implemented into my program. Some parts have been removed to shorten code but they are not needed with this problem:
RPM_signal PIN RB.4 INPUT SCHMITT tmpW1 VAR Word 'tmpW2 VAR Word temp1 VAR Byte temp2 VAR Byte temp3 VAR Byte temp4 VAR Byte s3 VAR Byte s4 VAR Byte menuis VAR Byte idx VAR Byte values1 VAR Byte(16) values2 VAR Byte(16) rpm VAR Byte tachWork VAR Byte (5) tachCheck VAR tachWork(0) rpmTemp VAR tachWork(1) tachTmr_LSB VAR tachWork(2) tachTmr_MID VAR tachWork(3) tachTmr_MSB VAR tachWork(4) INTERRUPT NOCODE 300_000 ' (3) 3.333us Tach: ASM MOV FSR, #tachWork ' (2) MOVB C, RPM_signal ' (4) capture tach input RL tachCheck ' (1) move into tachCheck AND tachCheck, #%0000_0011 ' (2) mask off old bits CSNE tachCheck, #%0000_0001 ' (3/4) skip if not 0->1 INC rpmTemp ' (1) update on new edge Tach_Timer: INC tachTmr_LSB ' (1) increment timer ADDB tachTmr_MID, Z ' (2) ADDB tachTmr_MSB, Z ' (2) CJNE tachTmr_LSB, #$20, Tach_Exit ' (4/6) exit if not $02_BF20 CJNE tachTmr_MID, #$BF, Tach_Exit ' (4/6) CJNE tachTmr_MSB, #$02, Tach_Exit ' (4/6) Update_RPM: MOV rpm, rpmTemp ' (2) update user rpm CLR rpmTemp ' (1) reset tach input CLR tachTmr_LSB ' (1) CLR tachTmr_MID ' (1) CLR tachTmr_MSB ' (1) Tach_Exit: ENDASM RETURNINT wait SUB 1, 2 ' delay in milliseconds SEND_BYTE SUB 4 button_hold SUB 0 Set_level SUB 2 Limp_Mode SUB 0 RX_BYTE FUNC 1, 0 getrpm FUNC 1, 0 'No longer used. PROGRAM Start Start: PLP_A = %0011 ' pull up unused pins PLP_B = %00011111 TRIS_C = %11110000 s3 = 0 s4 = 0 ltimer = 0 menuis = 1 wait 100 'ensure both chips are running FOR idx = 0 to 20 wait 10 SEND_BYTE LoadData, idx, s3, s4 wait 10 Grab: temp2 = RX_BYTE IF temp2 = SYNC THEN Grab IF idx < 16 THEN values1(idx) = temp2 'Store eprom into array1 ELSE temp1 = idx - 16 values2(temp1) = temp2 'Store eprom into array2 if array1 filled ENDIF NEXT wait 50 IF values1(13) = $00 THEN values1(13) = 90 values1(14) = 255 ENDIF Main: IF MenuBt = 1 THEN GOTO ChangeMenu IF UpBt = 1 THEN GOTO Update IF DownBt = 1 THEN GOTO Update IF EnterBt = 1 THEN GOTO Save IF menuis = 1 THEN 'rpm = getrpm '........USE RPM DATA HERE....... ENDIF GOTO Main ChangeMenu: INC menuis IF menuis = 21 THEN menuis = 1 ENDIF READ Multiplier + menuis, s4 IF menuis < 16 THEN s3 = values1(menuis) ELSE temp1 = menuis - 16 s3 = values2(temp1) ENDIF SEND_BYTE ShowMenu, menuis, s3, s4 wait 50 button_hold GOTO MainI've attached an update of the program I wrote for you. Where you once used SEROUT, use TX_BYTE; replace SERIN with RX_BYTE, and PAUSE with DELAY_MS. This should help. When you need the last RPM update (which happens every 0.6 seconds) you do this:
someVariable = GET_RPM
The SX/B 2.00.16 allows us to update the EEPROM routines so they're as easy to use with a byte address as with a word (the program now works with both).
At some point you will look back and say, "Wow, that's pretty cool and wasn't too hard -- once everything made sense!"
TX_BYTE temp1
instead of :
SEROUT RX, Baud, temp1
Sorry for all the questions. I am new to the ASM stuff [noparse]:)[/noparse]
BANK __DEFAULT
"Symbol __DEFAULT is not defined."
I am using the 1.5 compiler since when using the 2.0 compiler nothing worked and the chip instantly locked up on power up with working code for the 1.5 version
To explain what this project is, it is basically a User programmable transmission control unit (TCU) for electronically controlled automatic transmissions. The throttle position sensor and RPM are used to determine shift points based on what the user saves for each gear. This part works pretty good so far except now since I added the interrupt. I have been driving with it for about 2 months now with no problems except the RPM readings not being accurate enough. Racing season is coming up soon and I need to try and get this working if possible.
File 1 : Serial-LCD.sxb - This file runs the EEPROM, the LCD display, the 7 segment LED display, stores all menu items, and converts all serial data from chip2 to display on the screen.
File 2 : Serial-LCD-chip2.sxb - This reads all the sensors used to control the transmission, does the major math to calculate shift points, processes button presses for the menu system and controls the solenoids to shift the vehicle.
Thanks for all the help in advance!
Post Edited (eagletalontim) : 3/22/2009 1:50:40 AM GMT
The LCD driver chip (Chip 1) pin connection is :
RA.0 -> SDA
RA.1 -> SCL
RA.2 -> RX (to other chip) Baud T9600
RA.3 -> LCD Serial Baud T19200
The Main chip (Chip 2) pin connection is :
RA.0 -> RX
RA.1 -> Drive lever switch. (Used to hold current gear if driver switches to neutral while driving.)
RA.2 -> open
RA.3 -> open
Couldn't you·almost double your RAM and double your EE by going to the SX48? Robert Doerr (RobotWorkshop) sells a very professional kit or assembled module that is a drop-in replacement for a BS2p40. I have a few running with 20mhz ceramic resonators.
--Bill
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
You are what you write.
www.efx-tek.com/php/smf/index.php?topic=994.0
This uses a lot of the code I've been trying to share with you.
Do you have a spare $15? buy this:
SX48 Proto Board
It has the SX48, a working power supply, the SX-Key interface (for programming and debugging), areas to plug in either resonators or TTL oscillators, and a prototyping area to add your own circuitry. Best of all is that it's already built, soldered together, and working when it arrives at your house.
Buy the proto board plus either a resonator or oscillator, add any custom circuitry you need to the prototyping area, and start working on your project. You can program and debug the proto board using the SX-Key, as well as plug in a resonator or oscillator and run the project in standalone mode without the SX-Key. You won't be moving the chip back and forth between projects, you'll just move the whole project.
Regarding a much earlier post you had, 7805 chips get hot when powering devices that draw any appreciable amount of current. That's why they need heatsinking. I have a lot of SX28 projects running at 50 MHz using a 7805 regulator and they work just fine. This proto board is the same, and it has the heatsinking built in already.
Once you have your circuit working, if you want to lay out a custom PCB or hand build a board, you'll have a working prototype to use as your guide.
Thanks,
PeterM