PULSOUT, PWM, or HIGH / LOW simulator for RPM
eagletalontim
Posts: 1,399
I have a project I am working with that will need a 5v square wave output to simulate RPM of a vehicle. The program I am using reads the RPM correctly when connected to my vehicle, but I cannot figure out how to simulate the RPM signal from an SX chip. I have tried the PWM function, PULSOUT function, and HIGH / LOW with no luck. I am running the simulator chip at 4mhz and the input chip at 5mhz. The input chip has a 7 segment display that will show a 1 if the RPM is within 300 to 12,000. If it is lower than 300, it will show an L. If it is Higher, it will show an H. Every test so far shows an L. Any help is appreciated!
Comments
If they do match something like:
DO
HIGH pin
PAUSE 30
LOW pin
PAUSE 30
LOOP
Will produce a pulse roughly every 60 milliseconds that lasts for 30 milliseconds. (I says "roughly" becasue this code does not take into account the time if take for the PIN command to execute nor the DO/LOOP, but they are insignificant if you're running the SX at a decent speed.)
[PHP]
DEVICE SX28, OSCHS2, TURBO, STACKX, OPTIONX
FREQ 50_000_000
'IRC_CAL IRC_FAST
ID "RPMTEST"
'
' IO Pins
'
idx VAR BYTE
LED PIN RB.0 OUTPUT ' LED pin
'
' Constants
'
Delay CON 1 ' time LED is on
' =========================================================================
PROGRAM Start
' =========================================================================
'
' Program Code
'
Start:
DO
high LED
PAUSE Delay
low LED
PAUSE Delay
LOOP
[/PHP]
Then, Here is the function I use for RPM. I don't want to post all of the code since it is a product I sell but am trying to upgrade. This chip runs at 5Mhz with a crystal.
[PHP]
getrpm:
PULSIN RPM_signal, 0, pWidth0
PULSIN RPM_signal, 1, pWidth1
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
RETURN rpm
ENDFUNC
[/PHP]
I cannot change the frequency of the RPM input chip so I need to have the simulator work with it. I have tried the default 4Mhz, then 5Mhz, then 40Mhz, and finally 50Mhz. Even with 1 millisecond between the high and low, the readout of the input chip display is "L" for low. I really need to get this working since this will be my testing chip to connect to my product.
Also, since your product works with a real engine running at 12 volts (?) and the simulator has to be running at a much lower voltage are you feeding the input into the device under test in a way that assures an accurate simulation?
[PHP]
DEVICE SX28, OSCXT2, TURBO, STACKX, OPTIONX
FREQ 5_000_000
' then here is the rpm verifier :
IF rpm > 300 THEN
IF rpm < 12000 THEN
isrpm = 1
ELSE
isrpm = 0
LIMP_MODE 0
ENDIF
ELSE
isrpm = 0
LIMP_MODE 1
ENDIF
' the function LIMP_MODE just changes the display to show an "H" for High RPM or an "L" for Low RPM. If it the RPM is in range, it just shows a "1"
[/PHP]
((60 / RPM) * 1000) / 4 = Delay Between HIGH / LOW
So if I want 4000 RPM, this is what would calculate in the code :
60 / 4000 = 0.015 (Would be __REMAINDER or __WREMAINDER)
0.015 * 1000 = 15 (Number of Milliseconds for 1 complete revolution) - Not used when calculated in chip since __REMAINDER = 15 anyways
15 / 4 = 3.75 (Rev Time / 4 to give 2 HIGH and 2 LOW periods during 1 revolution)
The problem I am running into is the remainder. Since 4000 RPM = 3.75 Milliseconds and 5000 RPM = 3 Milliseconds, I need to use the remainder somehow. The ISR is a little confusing since I don't usually use them due to me using PULSIN, SERIN, SEROUT in many of my projects.
What I think is also hindering me from getting it right is the fact that some remainders are infinite like .333333333333.... That is higher than the WORD max of 65k. This would majorly throw off calculations.
[PHP]
DEVICE SX28, OSCXT2, TURBO, STACKX, OPTIONX
FREQ 5_000_000
'
' IO Pins
'
LcdTx PIN RB.0 ' LCD serial connection
TachIn PIN RB.4 INPUT SCHMITT
'
' Constants
'
LcdBaud CON "T19200" ' or T2400, or T9600
LcdBkSpc CON $08 ' move cursor left
LcdRt CON $09 ' move cursor right
LcdLF CON $0A ' move cursor down 1 line
LcdCls CON $0C ' clear LCD (need 5 ms delay)
LcdBLon CON $11 ' backlight on
LcdBLoff CON $12 ' backlight off
LcdOn1 CON $16 ' LCD on; no crsr, no blink
LcdLine1 CON $80 ' move to line 1, column 0
LcdLine2 CON $94 ' move to line 2, column 0
Baud2400 CON 80
Baud4800 CON 40
Baud9600 CON 20
Baud19K2 CON 10
Baud38K4 CON 5
Baud1x0 CON Baud38K4 ' 1 bit period (ISR counts)
Baud1x5 CON Baud1x0 * 3 / 2 ' 1.5 bit periods
LF CON 10
CLS CON 12
CR CON 13
idx VAR Byte ' loop counter
line1 VAR Byte(16) ' line 1 buffer
line2 VAR Byte(16) ' line 2 buffer
cmd VAR Byte
temp1 VAR Byte ' subroutine work vars
temp2 VAR Byte
temp3 VAR Byte
temp4 VAR Byte
temp5 VAR Byte
tmpW1 VAR Word
tmpW2 VAR Word
nStr VAR Byte(5)
rpm VAR WORD
smallrpm VAR Byte
dividendMSW VAR WORD
dividendLSW VAR WORD
overflow VAR BIT
doneBit VAR BIT
' =========================================================================
PROGRAM Start
' =========================================================================
'
' Subroutine Declarations
'
wait SUB 1, 2 ' delay in milliseconds
LCD_OUT SUB 1, 2 ' send byte {+ count} to LCD
UPDATE_L1 SUB 0 ' update line 1 of LCD
UPDATE_L2 SUB 0 ' update line 2 of LCD
PUT_DIGIT FUNC 1,1
GET_RPM FUNC 2, 0
store SUB 4
'
' Program Code
'
Start:
PLP_A = %1011 ' pull up unused pins
PLP_B = %0001_0001
TRIS_C = %00000000
HIGH LcdTx
wait 50 ' let LCD initialize
LCD_OUT LcdOn1 ' no cursor or blink
LCD_OUT LcdCls ' clear the LCD
wait 5
PUT line1, "RPM Reader"
UPDATE_L1
Main:
smallrpm = GET_RPM
watch smallrpm
break
PUT line2, "RPM : "
UPDATE_L2
store smallrpm, 5, 2, 2
UPDATE_L2
wait 200
GOTO Main
wait:
temp1 = __PARAM1 ' get milliseconds
IF __PARAMCNT = 1 THEN ' if no multiplier
temp2 = 1 ' set to 1
ELSE ' else
temp2 = __PARAM2 ' get multiplier
ENDIF
IF temp1 > 0 THEN ' no delay if either 0
IF temp2 > 0 THEN
PAUSE temp1 * temp2 ' do the delay
ENDIF
ENDIF
RETURN
store:
temp1 = __PARAM1
temp2 = __PARAM2
temp3 = __PARAM3
cmd = __PARAM4
temp4 = 0
temp5 = 0
FOR idx = temp2 TO 15
IF temp3 = 2 THEN
PUT line2(idx), " "
ELSE
PUT line1(idx), " "
ENDIF
NEXT
STR nStr, temp1
DO WHILE temp4 < 3
IF nStr(temp4) <> 0 THEN
temp5 = 1
ENDIF
IF temp5 <> 0 THEN
IF temp3 = 2 THEN
PUT line2(temp2), nStr(temp4)
ELSE
PUT line1(temp2), nStr(temp4)
ENDIF
INC temp4
INC temp2
ENDIF
LOOP
IF cmd <> 0 THEN
DEC temp2
FOR idx = 1 to cmd
INC temp2
IF temp3 = 2 THEN
PUT line2(temp2), "0"
ELSE
PUT line1(temp2), "0"
ENDIF
NEXT
ENDIF
RETURN
UPDATE_L1:
LCD_OUT LcdLine1
wait 5
FOR idx = 0 TO 15
LCD_OUT line1(idx) ' transfer buffer
NEXT
RETURN
UPDATE_L2:
LCD_OUT LcdLine2
wait 5
FOR idx = 0 TO 15
LCD_OUT line2(idx) ' transfer buffer
NEXT
RETURN
LCD_OUT:
temp1 = __PARAM1 ' char to send
IF temp1 = $00 THEN
SEROUT LcdTx, LcdBaud, " "
ELSE
SEROUT LcdTx, LcdBaud, temp1 ' transmit to LCD
ENDIF
RETURN
FUNC PUT_DIGIT
temp1 = __PARAM1 ' copy value
LOOKUP temp1, $3F,$06,$5B,$4F,$66,$5E,$79, temp1
RETURN temp1
ENDFUNC
FUNC GET_RPM
PULSIN TachIn, 0, tmpW1
PULSIN TachIn, 1, tmpW2
watch tmpW1
watch tmpW2
tmpW1 = tmpW1 + tmpW2
tmpW1 = tmpW1 * 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 - tmpW1
ELSE
IF dividendMSW >= tmpW1 THEN
rpm.0 = 1
dividendMSW = dividendMSW - tmpW1
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 >= tmpW1 THEN
rpm.0 = 0
ENDIF
rpm = rpm / 100
RETURN rpm
ENDFUNC
[/PHP]
You can still do sigma-delta ADC (like ANALOGIN) but you would need to move the ADC code to the interrupt as well. As far as accuracy, you may want to check the sxlist ISR calculator -- it lets you look up various clock frequency / ISR rate combinations to see how dead nuts on it will be.
http://www.sxlist.com/techref/scenix/isrcalc.asp