How can you sync two cogs/frequencies?
I have two cogs transmitting frequencies and would like to sync the frequencies. I'm using "frequency" generically as the rising to falling edge of a digital square wave
The cog A transmits 72 times before it repeats itself
The cog B transmits 3 times before it repeats itself
I would like for the cog B to transmit its 1st frequency when the cog A transmits its 19th frequency and based on timing the cog B should then transmit its 3rd frequency when the cog A transmits its 55th frequency. (rising edges aligned)
I spent the entire weekend rewriting these objects in PASM because of speed, I've tried having cog B read a frequency count value on Cog A, but I'm having timing errors because it will freeze.
Has anyone tried to sync two cogs this way, or is there a better way? I don't need a resolution of 10 clock cycles, but 1000 clock cycles or less would be nice. Unfortunately, I cannot attach an archive of the project until I get home (unless I could email it to someone), but here is the object for cog B. I've been at this for many hours and am not sure how to troubleshoot this. If someone is kind enough to look at this, and would like the whole project, please let me know your email address and I'll email it to you.
Thanks for reading.
The cog A transmits 72 times before it repeats itself
The cog B transmits 3 times before it repeats itself
I would like for the cog B to transmit its 1st frequency when the cog A transmits its 19th frequency and based on timing the cog B should then transmit its 3rd frequency when the cog A transmits its 55th frequency. (rising edges aligned)
I spent the entire weekend rewriting these objects in PASM because of speed, I've tried having cog B read a frequency count value on Cog A, but I'm having timing errors because it will freeze.
Has anyone tried to sync two cogs this way, or is there a better way? I don't need a resolution of 10 clock cycles, but 1000 clock cycles or less would be nice. Unfortunately, I cannot attach an archive of the project until I get home (unless I could email it to someone), but here is the object for cog B. I've been at this for many hours and am not sure how to troubleshoot this. If someone is kind enough to look at this, and would like the whole project, please let me know your email address and I'll email it to you.
Thanks for reading.
CON
_CLKMODE = XTAL1 + PLL16X
_CLKFREQ = 80_000_000
' current code only allows from 1Hz upwards
MIN_FREQUENCY = 1
' dunno which upper limit can be achieved by the SPIN-code
MAX_FREQUENCY = 9200
BAUDRATE = 115_200
OBJ
pst : "Parallax Serial Terminal"
VAR
'byte pin, presentTeeth, missingTeeth, cog
'long frequencyRate
long l_cog
long l_pst1
long l_pst2
long l_pst3
long l_pst4
long l_pst5
long l_pst6
long l_pst7
long l_pst8
long l_pst9
long l_pst10
long l_pst11
long l_pst12
long l_pst13
long l_pst14
long l_pst15
long l_pst16
long l_pin
long l_pinAddr
long l_rpm
long l_rpmAddr
long l_rotationalRatio
long l_rotationalRatioAddr
long l_teeth
long l_teethAddr
long l_missingTeeth
long l_missingTeethAddr
long l_gap1
long l_gap1Addr
long l_gap2
long l_gap2Addr
long l_gap3
long l_gap3Addr
long l_trigger
long l_triggerAddr
long l_correction
long l_correctionAddr
long l_crankToothNum
long l_crankToothNumAddr
long l_rpmTemp
long l_pinTemp
long l_tmp
long l_pinAddrTest
long l_rpmAddrTest
long l_rotationalRatioAddrTest
long l_teethAddrTest
long l_missingTeethAddrTest
long l_gap1AddrTest
long l_gap2AddrTest
long l_gap3AddrTest
long l_triggerAddrTest
long l_correctionAddrTest
long l_crankToothNumAddrTest
pub main
pst.Start( BAUDRATE )
l_tmp:=-1
repeat while l_tmp==-1
pst.str( string( " ",$0d,"== please press key to start ==",$0d ) )
l_tmp:=pst.rxcheck
if l_tmp==-1
waitcnt( clkfreq+cnt )
pst.Clear
pst.str( string( "Starting COG!",$0d ) )
pstInitScreen
l_pin := 9 ' quick start
l_pinAddrTest := @l_pin
'l_rpm := 11000
l_rpm := 700
l_rpmAddrTest := @l_rpm
l_rotationalRatio := 2
l_rotationalRatioAddrTest := @l_rotationalRatio
l_teeth := 3
l_teethAddrTest := @l_teeth
l_missingTeeth := 1
l_missingTeethAddrTest := @l_missingTeeth
l_gap1 := 72
l_gap1AddrTest := @l_gap1
l_gap2 := 72
l_gap2AddrTest := @l_gap2
l_gap3 := 159
l_gap3AddrTest := @l_gap3
l_trigger := 19
l_triggerAddrTest := @l_trigger
l_correction := 0
l_correctionAddrTest := @l_correction
l_crankToothNum := 19
l_crankToothNumAddrTest := @l_crankToothNum
' (p, r, rots, one, two, three, trig, degDiff, autoCorr, crTthAddr, caTthAddr, crToothRpmAddr)
'init(l_pinTemp, 1000, 2, 72, 72, 159, 19, 0, false, 21, 3, @l_rpmTemp)
init(l_pinAddrTest, l_rpmAddrTest, l_rotationalRatioAddrTest, l_gap1AddrTest, l_gap2AddrTest, l_gap3AddrTest, l_triggerAddrTest, l_teethAddrTest, l_missingTeethAddrTest, l_correctionAddrTest, l_crankToothNumAddrTest)
pst.char(13)
pst.char(13)
repeat
pstCom
'l_rpm += 1
'l_crankToothNum += 1
pub init(pinAddr, rpmAddr, rotsAddr, oneAddr, twoAddr, threeAddr, trigAddr, teethAddr, missingTeethAddr, correctionAddr, crankToothNumAddr)
waitcnt((clkfreq*1) + cnt) ' let screen initialize
l_pinAddr := pinAddr
l_rpmAddr := rpmAddr
l_rotationalRatioAddr := rotsAddr
l_gap1Addr := oneAddr
l_gap2Addr := twoAddr
l_gap3Addr := threeAddr
l_triggerAddr := trigAddr
l_TeethAddr := teethAddr
l_missingTeethAddr := missingTeethAddr
l_correctionAddr := correctionAddr
l_crankToothNumAddr := crankToothNumAddr
{
pst.char(13)
pst.char(13)
pst.dec(long[l_pinAddr])
pst.char(13)
pst.dec(long[l_rpmAddr])
pst.char(13)
pst.dec(long[l_rotationalRatioAddr])
pst.char(13)
pst.dec(long[l_gap1Addr])
pst.char(13)
pst.dec(long[l_gap2Addr])
pst.char(13)
pst.dec(long[l_gap3Addr])
pst.char(13)
pst.dec(long[l_triggerAddr])
pst.char(13)
pst.dec(long[l_TeethAddr])
pst.char(13)
pst.dec(long[l_missingTeethAddr])
pst.char(13)
pst.dec(long[l_correctionAddr])
pst.char(13)
pst.dec(long[l_crankToothNumAddr])
pst.char(13)
}
return l_cog := cognew(@camsim, @l_cog) + 1
PUB pstCom | lpcnt1,lpcnt2
lpcnt1:=0
lpcnt2:=0
'repeat
charAt( 12,2 , proc[ lpcnt1 ] )
lpcnt1 := (lpcnt1+1) & 3
charAt( 28,2 , proc[ lpcnt2 ] )
lpcnt2 := (lpcnt2+1) & 3
charAt( 12, 4, " " ) ' ("object1 cog:") )
pst.Dec(l_cog)
pst.str(@spaces)
charAt( 43, 4, " " ) ' ( "caGap RPM:" ) )
pst.Dec(-7)
pst.str(@spaces)
charAt( 72, 4, " " ) ' ( "+Degrees:" ) )
pst.Dec(-7)
pst.str(@spaces)
charAt( 102, 4, " " ) ' ( "Tar Ang:" ) )
pst.Dec(-7)
pst.str(@spaces)
charAt( 12, 5, " " ) ' ("crGap RPM:") )
pst.Dec(-7)
pst.str(@spaces)
charAt( 43, 5, " " ) ' ( "caGap RPM:" ) )
pst.Dec(-7)
pst.str(@spaces)
charAt( 72, 5, " " ) ' ( "+Degrees:" ) )
pst.Dec(-7)
pst.str(@spaces)
charAt( 102, 5, " " ) ' ( "Tar Ang:" ) )
pst.Dec(-7)
pst.str(@spaces)
charAt( 12, 6, " " ) ' ("crTth RPM:") )
pst.dec(-7)
pst.str(@spaces)
charAt( 43, 6, " " ) ' ( "caTth RPM:" ) )
pst.dec(-7)
pst.str(@spaces)
charAt( 72, 6, " " ) ' ( Not used ) )
pst.dec(-7)
pst.str(@spaces)
charAt( 102, 6, " " ) ' ( "angDiff:" ) )
pst.Dec(-7)
pst.str(@spaces)
charAt( 12, 7, " " ) ' ("crGapTime:" ) )
pst.dec(-7)
pst.str(@spaces)
charAt( 43, 7, " " ) ' ("caGapTime:") )
pst.dec(-7)
pst.str(@spaces)
charAt( 72, 7, " " ) ' ("cr-CaTime:") )
pst.dec(-7)
pst.str(@spaces)
charAt( 102, 7, " " ) ' ( "hz:" ) )
pst.Dec(-7)
pst.str(@spaces)
charAt( 12, 8, " " ) ' ("crAvgCyc:" ) )
pst.dec(-7)
pst.str(@spaces)
charAt( 43, 8, " " ) ' ("caAvgCyc:") )
pst.dec(-7)
pst.str(@spaces)
charAt( 72, 8, " " ) ' ("Cycl/Deg:") )
pst.dec(-7)
pst.str(@spaces)
charAt( 102, 8, " " ) ' ( "hzCycles:" ) )
pst.Dec(-7)
pst.str(@spaces)
charAt( 12, 9, " " ) ' ("toothNum:" ) )
pst.dec(-7)
pst.str(@spaces)
charAt( 12, 21, " " ) ' ("Pst1:") )
pst.bin(getPst1, 32)
pst.str(@spaces)
charAt( 12, 22, " " ) ' ("Pst2:") )
pst.dec(getPst2)
pst.str(@spaces)
charAt( 12, 23, " " ) ' ("Pst3:") )
pst.dec(getPst3)
pst.str(@spaces)
charAt( 12, 24, " " ) ' ("Pst4:") )
pst.dec(getPst4)
pst.str(@spaces)
charAt( 12, 25, " " ) ' ("Pst5:") )
pst.dec(getPst5)
pst.str(@spaces)
charAt( 12, 26, " " ) ' ("Pst6:") )
pst.dec(getPst6)
pst.str(@spaces)
charAt( 12, 27, " " ) ' ("Pst7:") )
pst.dec(getPst7)
pst.str(@spaces)
charAt( 12, 28, " " ) ' ("Pst8:") )
pst.dec(getPst8)
pst.str(@spaces)
charAt( 12, 29, " " ) ' ("Pst9:") )
pst.dec(getPst9)
pst.str(@spaces)
charAt( 12, 30, " " ) ' ("Pst10:") )
pst.dec(getPst10)
pst.str(@spaces)
charAt( 12, 31, " " ) ' ("Pst11:") )
pst.dec(getPst11)
pst.str(@spaces)
charAt( 12, 32, " " ) ' ("Pst12:") )
pst.dec(getPst12)
pst.str(@spaces)
charAt( 12, 33, " " ) ' ("Pst13:") )
pst.dec(getPst13)
pst.str(@spaces)
charAt( 12, 34, " " ) ' ("Pst14:") )
pst.dec(getPst14)
pst.str(@spaces)
charAt( 12, 35, " " ) ' ("Pst15:") )
pst.dec(getPst15)
pst.str(@spaces)
charAt( 12, 36, " " ) ' ("Pst16:") )
pst.dec(getPst16)'getPst16)
pst.str(@spaces)
PUB pstInitScreen
printAt( 1,2, string("Main loop [ ]") )
printAt( 20,2, string("Update [ ]") )
printAt( 3,4, string("cog:") )
printAt( 33, 4, string( "cog:" ) )
printAt( 63,4, string("cog:") )
printAt( 93,4, string("cog:") )
printAt( 3,5, string("crGap RPM:") )
printAt( 33, 5, string( "caGap RPM:" ) )
printAt( 63,5, string("+Degrees:") )
printAt( 93,5, string("Targ Ang:") )
printAt( 3,6, string("crTth RPM:") )
printAt( 33, 6, string( "caTth RPM:" ) )
printAt( 63, 6, string( ":" ) )
printAt( 93, 6, string( "angDiff:" ) )
printAt( 3,7, string("crGapTime:" ) )
printAt( 33,7, string("caGapTime:") )
printAt( 63,7, string("cr-CaTime:") )
printAt( 93,7, string("hz:") )
printAt( 3,8, string("crAvgCyc:") )
printAt( 33,8, string("caAvgCyc:") )
printAt( 63,8, string("Cycl/Deg:") )
printAt( 93,8, string("hzCycles:") )
printAt( 3,9, string("toothNum:") )
{printAt( 33,8, string("T Cnt Max") )
printAt( 3,9, string("Min") )
printAt( 33,9, string("Max") )
printAt( 3,10, string("Gap Cnt") )
printAt( 33,10, string("Gap Cnt/Sec") )
printAt( 33,11, string("Gap Rpm:") ) }
printAt( 3,21, string("Pst1:") )
printAt( 3,22, string("Pst2:") )
printAt( 3,23, string("Pst3:") )
printAt( 3,24, string("Pst4:") )
printAt( 3,25, string("Pst5:") )
printAt( 3,26, string("Pst6:") )
printAt( 3,27, string("Pst7:") )
printAt( 3,28, string("Pst8:") )
printAt( 3,29, string("Pst9:") )
printAt( 3,30, string("Pst10:") )
printAt( 3,31, string("Pst11:") )
printAt( 3,32, string("Pst12:") )
printAt( 3,33, string("Pst13:") )
printAt( 3,34, string("Pst14:") )
printAt( 3,35, string("Pst15:") )
printAt( 3,36, string("Pst16:") )
dat
spaces byte " ",0
proc byte "-/|\"
pub charAt( x,y,c )
' output a character if it's in screen-range and if it's a printable character
if x>0 and x<109 and y>0 and y<75 and c>31 and c<128
posbuf[1]:=x
posbuf[2]:=y
posbuf[3]:=c
pst.str(@posbuf)
pub printAt( x, y, text)
' set cursor position and print string
posbuf[1]:=x
posbuf[2]:=y
posbuf[3]:=0
pst.str(@posbuf)
pst.str( text )
dat
' a buffer to assemble the bytestream for positioning
posbuf byte 2, 0, 0, 0, 0
pub getCog
result := l_cog
pub setCorrection(val)
l_correction := val
pub getPst1
result := l_pst1
pub getPst2
result := l_pst2
pub getPst3
result := l_pst3
pub getPst4
result := l_pst4
pub getPst5
result := l_pst5
pub getPst6
result := l_pst6
pub getPst7
result := l_pst7
pub getPst8
result := l_pst8
pub getPst9
result := l_pst9
pub getPst10
result := l_pst10
pub getPst11
result := l_pst11
pub getPst12
result := l_pst12
pub getPst13
result := l_pst13
pub getPst14
result := l_pst14
pub getPst15
result := l_pst15
pub getPst16
result := l_pst16
DAT
org 0
camsim
add cogPtr, par
add pstPtr1, par
add pstPtr2, par
add pstPtr3, par
add pstPtr4, par
add pstPtr5, par
add pstPtr6, par
add pstPtr7, par
add pstPtr8, par
add pstPtr9, par
add pstPtr10, par
add pstPtr11, par
add pstPtr12, par
add pstPtr13, par
add pstPtr14, par
add pstPtr15, par
add pstPtr16, par
add pinPtr, par
add pinAddrPtr, par
add rpmPtr, par
add rpmAddrPtr, par
add rotationalRatioPtr, par
add rotationalRatioAddrPtr, par
add teethPtr, par
add teethAddrPtr, par
add missingTeethPtr, par
add missingTeethAddrPtr, par
add gap1Ptr, par
add gap1AddrPtr, par
add gap2Ptr, par
add gap2AddrPtr, par
add gap3Ptr, par
add gap3AddrPtr, par
add triggerPtr, par
add triggerAddrPtr, par
add correctionPtr, par
add correctionAddrPtr, par
add crankToothNumPtr, par
add crankToothNumAddrPtr, par
rdlong pinAddrPtr, pinAddrPtr
rdlong rpmAddrPtr, rpmAddrPtr
rdlong rotationalRatioAddrPtr, rotationalRatioAddrPtr
rdlong teethAddrPtr, teethAddrPtr
rdlong missingTeethAddrPtr, missingTeethAddrPtr
rdlong gap1AddrPtr, gap1AddrPtr
rdlong gap2AddrPtr, gap2AddrPtr
rdlong gap3AddrPtr, gap3AddrPtr
rdlong triggerAddrPtr, triggerAddrPtr
rdlong correctionAddrPtr, correctionAddrPtr
rdlong crankToothNumAddrPtr, crankToothNumAddrPtr
initialize
'mov pinMask, #1 ' create mask from pin #
'shl pinMask, pinAddrPtr
mov dira, pinMask
syncCr
rdlong crankToothNumSync, crankToothNumAddrPtr ' waits for the rising edge
cmp crankToothNumSync, #19 WC, WZ
'if_e wrlong phaseTwo, pstPtr12
'if_e jmp #syncCa
if_ne jmp #syncCr
mov timeStamp, cnt
restart
mov startTime, cnt
'rdlong rpm, rpmAddrPtr ' waits for the rising edge
rdlong pin, pinAddrPtr
rdlong rpm, rpmAddrPtr
rdlong rotationalRatio, rotationalRatioAddrPtr
rdlong teeth, teethAddrPtr
rdlong missingTeeth, missingTeethAddrPtr
rdlong gap1, gap1AddrPtr
rdlong gap2, gap2AddrPtr
rdlong gap3, gap3AddrPtr
rdlong trigger, triggerAddrPtr
rdlong correction, correctionAddrPtr
rdlong crankToothNum, crankToothNumAddrPtr
wrlong pinMask, pstPtr1
wrlong rpm, pstPtr2
wrlong rotationalRatio, pstPtr3
'wrlong teeth, pstPtr4
'wrlong missingTeeth, pstPtr5
'wrlong gap1, pstPtr6
'wrlong gap2, pstPtr7
'wrlong gap3, pstPtr8
'wrlong trigger, pstPtr9
'wrlong correction, pstPtr10
wrlong crankToothNum, pstPtr9
mov LNMultiplyMultiplicand, rpm '
mov LNMultiplyMultiplier, #10 ' * 10
call #LNMultiply
mov currentCrankRpm, multiplicationResult ' cycles per 10 degrees of rotation
mov LNDivideDividend, currentCrankRpm
mov LNDivideDivsor, #60
call #LNDivide
mov crankRpmPerSec, divisionResult ' crank rpm per second
mov LNDivideDividend, crankRpmPerSec
mov LNDivideDivsor, #2
call #LNDivide
mov camRpmPerSec, divisionResult ' cam rpm per second
mov LNDivideDividend, clk ' 80000000
mov LNDivideDivsor, camRpmPerSec ' /5000
call #LNDivide
mov cyclesDegOfRot, divisionResult
mov LNDivideDividend, cyclesDegOfRot ' 960000 cycles
mov LNDivideDivsor, #360 ' /360
call #LNDivide
mov cyclesDegOfRot, divisionResult ' cycles per degree of rotation
mov LNMultiplyMultiplicand, cyclesDegOfRot '
mov LNMultiplyMultiplier, #10 ' * 10
call #LNMultiply
mov cyclesDegOfRot, multiplicationResult ' cycles per degree of rotation
mov LNMultiplyMultiplicand, cyclesDegOfRot '
mov LNMultiplyMultiplier, #10 ' * 10
call #LNMultiply
mov cycles10DegOfRot, multiplicationResult ' cycles per 10 degrees of rotation
mov LNMultiplyMultiplicand, cyclesDegOfRot
mov LNMultiplyMultiplier, gap1
call #LNMultiply
mov empty1Wait, multiplicationResult ' gap wait 1
mov LNMultiplyMultiplicand, cyclesDegOfRot
mov LNMultiplyMultiplier, gap2
call #LNMultiply
mov empty2Wait, multiplicationResult ' gap wait 2
mov LNMultiplyMultiplicand, cyclesDegOfRot
mov LNMultiplyMultiplier, gap3
call #LNMultiply
mov empty3Wait, multiplicationResult ' gap wait 3
mov LNDivideDividend, empty3Wait ' 960000 cycles
mov LNDivideDivsor, #2 ' /360
call #LNDivide
mov empty3HalfWait, divisionResult ' cycles per degree of rotation
mov LNMultiplyMultiplicand, cyclesDegOfRot
mov LNMultiplyMultiplier, trigger
call #LNMultiply
mov trig1Wait, multiplicationResult ' trigger 1 wait
mov LNMultiplyMultiplicand, cyclesDegOfRot
mov LNMultiplyMultiplier, trigger
call #LNMultiply
mov trig2Wait, multiplicationResult ' trigger 2 wait
mov LNMultiplyMultiplicand, cyclesDegOfRot
mov LNMultiplyMultiplier, trigger
call #LNMultiply
mov trig3Wait, multiplicationResult ' trigger 3 wait
wrlong trig1Wait, pstPtr4
wrlong trig2Wait, pstPtr5
wrlong empty1Wait, pstPtr6
wrlong empty2Wait, pstPtr7
wrlong empty3Wait, pstPtr8
mov prevTimeStamp, timeStamp
add loopCount, #1
wrlong loopCount, pstPtr10
{mov endTime, cnt
sub endTime, startTime
wrlong endTime, pstPtr12}
wrlong timeStamp, pstPtr11
mov cnt, cnt
wrlong cnt, pstPtr12
:synctooth19
'rdlong crankToothNumAddrPtr, crankToothNumAddrPtr ' waits for the rising edge
' cmp crankToothNum, #19 WZ, WC
'if_ne jmp #:synctooth19
{calculate how long to wait after tooth 19 to tooth 24, which is a 50 degree spread}
add toothNumber, #1
or outa, pinMask ' set pin high
add timeStamp, trig1Wait ' add the amount of cycles that the pulseWidth should be HIGH to timeStamp, so cmp is not true until after that
waitcnt timeStamp, #0
andn outa, pinMask ' set pin low
add timeStamp, empty1Wait ' add the amount of cycles that the pulseWidth should be LOW to timeStamp, so cmp is not true until after that
waitcnt timeStamp, #0
add toothNumber, #1
or outa, pinMask ' set pin high
add timeStamp, trig2Wait ' add the amount of cycles that the pulseWidth should be HIGH to timeStamp, so cmp is not true until after that
waitcnt timeStamp, #0
andn outa, pinMask ' set pin low
add timeStamp, empty2Wait ' add the amount of cycles that the pulseWidth should be LOW to timeStamp, so cmp is not true until after that
waitcnt timeStamp, #0
add toothNumber, #1
or outa, pinMask ' set pin high
add timeStamp, trig3Wait ' add the amount of cycles that the pulseWidth should be HIGH to timeStamp, so cmp is not true until after that
waitcnt timeStamp, #0
andn outa, pinMask ' set pin low
add timeStamp, empty3Wait ' add the amount of cycles that the pulseWidth should be LOW to timeStamp, so cmp is not true until after that
mov toothNumber, #0
waitcnt timeStamp, #0
jmp #restart
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Unsigned Divide
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
LNDivide mov LNDivideQuotient, #0 ' Setup to divide.
mov LNDivideBuffer, #0 '
mov LNDivideCounter, #32 '
cmp LNDivideDivsor, #0 wz ' Clear if dividing by zero.
if_z mov LNDivideDividend, #0 '
if_z jmp #LNDivide_ret '
LNDivideLoopPre shr LNDivideDivsor, #1 wc, wz ' Align divisor MSB and count size.
rcr LNDivideBuffer, #1 '
if_nz djnz LNDivideCounter, #LNDivideLoopPre '
LNDivideLoopPost cmpsub LNDivideDividend, LNDivideBuffer wc ' Preform division.
rcl LNDivideQuotient, #1 '
shr LNDivideBuffer, #1 '
djnz LNDivideCounter, #LNDivideLoopPost '
'wrlong LNDivideQuotient, pstptr7
'wrlong LNDivideDividend, pstptr8
mov divisionResult, LNDivideQuotient
LNDivide_ret ret ' Return. Remainder in dividend on exit.
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Unsigned Multiply
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
LNMultiply mov LNMultiplyProduct, #0 ' Clear product.
LNMultiplyLoop shr LNMultiplyMultiplicand, #1 wc ' Preform multiplication.
if_c add LNMultiplyProduct, LNMultiplyMultiplier '
shl LNMultiplyMultiplier, #1 '
tjnz LNMultiplyMultiplicand, #LNMultiplyLoop '
mov multiplicationResult, LNMultiplyProduct
LNMultiply_ret ret ' Return.
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Data
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' --------------------------------------------------------------------------------------------------
cogPtr long 0
pstPtr1 long 4
pstPtr2 long 8
pstPtr3 long 12
pstPtr4 long 16
pstPtr5 long 20
pstPtr6 long 24
pstPtr7 long 28
pstPtr8 long 32
pstPtr9 long 36
pstPtr10 long 40
pstPtr11 long 44
pstPtr12 long 48
pstPtr13 long 52
pstPtr14 long 56
pstPtr15 long 60
pstPtr16 long 64
pinPtr long 68
pinAddrPtr long 72
rpmPtr long 76
rpmAddrPtr long 80
rotationalRatioPtr long 84
rotationalRatioAddrPtr long 88
teethPtr long 92
teethAddrPtr long 96
missingTeethPtr long 100
missingTeethAddrPtr long 104
gap1Ptr long 108
gap1AddrPtr long 112
gap2Ptr long 116
gap2AddrPtr long 120
gap3Ptr long 124
gap3AddrPtr long 128
triggerPtr long 132
triggerAddrPtr long 136
correctionPtr long 140
correctionAddrPtr long 144
crankToothNumPtr long 148
crankToothNumAddrPtr long 152
' Standard variables here
divisionResult long 0
multiplicationResult long 0
camSimPin long 0
crankRpmPerSec long 0
camRpmPerSec long 0
cyclesDegOfRot long 0
cycles10DegOfRot long 0
toothWaitDel long 0
trig1Wait long 0
trig2Wait long 0
trig3Wait long 0
empty1Wait long 0
empty2Wait long 0
empty3Wait long 0
empty3HalfWait long 0
skewableWait long 0
phaseState long 0
timeStamp long 0
currentCrankRpm long 0
syncPauseCycles long 0
camSimPinMask long 0
camPwmMonitorPin long 0
camPwmMonitorPinMask long 0
lowCycles long 0
highCycles long 0
wait long 0
tmptime1 long 0
tmptime2 long 0
ontix long 40000000
offtix long 40000000
timer long 0
loopCnt long 0
pinsState long 0
'pin 17/led pin 9
pinMask long %00000000000000000000001000000000'%00000000000000100000000000000000'%00000000000000000000001000000000
toothNumber long 0
pin long 0
rpm long 0
rotationalRatio long 0
teeth long 0
missingTeeth long 0
gap1 long 0
gap2 long 0
gap3 long 0
trigger long 0
correction long 0
crankToothNum long 0
crankToothNumSync long 0
' Variables with non 0 values here
clk long 80000000
phaseOne long 1
phaseTwo long 2
phaseThree long 3
phaseFour long 4
phaseFive long 5
phaseSix long 6
phaseSeven long 7
POS_DETECT long %01000 << 26
NEG_DETECT long %01100 << 26
oneHunFiftyThou long 150000
' Temporary troubleshooting values here
prevTimeStamp long 0
loopCount long 0
startTime long 0
endTime long 0
' Reserved variables here
tmp1 res 1
tmp2 res 1
mask res 1 ' mask for frequency input pin
' //////////////////////Math Run Time Variables/////////////////////////////////////////////////////////////////////////////////////
LNDivideBuffer res 1
LNDivideCounter res 1
LNDivideDividend res 1
LNDivideDivsor res 1
LNDivideQuotient res 1
LNMultiplyMultiplicand res 1
LNMultiplyMultiplier res 1
LNMultiplyProduct res 1
fit 492
{PUB simulateCamWheel(simCamWheelPinNum, engineRPM, crankRotsToCamRots, degreeOfTrigger1, degreeOfTrigger2, degreeOfTrigger3, camTriggerDegreeWidth, autoCorrectVvti, degreesDiff, crTooth) | localIndex, crankRpmPerSecond, camRpmPerSecond, localCnt, trigger1Wait, trigger2Wait, trigger3Wait, emtpySpace1Wait, emtpySpace2Wait, emtpySpace3Wait, cyclesPerDegOfRot, toothWaitDelay, startTooth, autoRangeChangesPerSecond, advancing, retarding, degreesShifted, updateCnt, tenDeg, fiftyDeg, emtpySpace3WaitSansDelay, variableDelay, loopCnt, prevLoopCnt, degAdv, crankTooth
dira[simCamWheelPinNum]~~
outa[simCamWheelPinNum]~
localIndex := 0
'if (engineRPM >= 720)
'crankRpmPerSecond := (engineRPM/60) ' 166.667 crank rots per second at 10k rpm
camRpmPerSecond := (((engineRPM*100)/60)/2) ' 83.333 cam rots per second at 10k rpm
cyclesPerDegOfRot := ((((clkfreq/camRpmPerSecond))*10/36))'0)*10) ' 960000/360 at 10k or 2660
'toothWaitDelay := (clkfreq/(crankRpmPerSecond*36))
'autoRangeChangesPerSecond := (clkfreq/degreeChangesPerSecond)
{else
'camRpmPerSecond := (((engineRPM*1000)/60)/2)
'cyclesPerDegOfRot := (((clkfreq/camRpmPerSecond)/360)*1000) ' 960000/360 at 10k or 2660 1600000000 at 6rpm
cyclesPerDegOfRot := 444444}
trigger1Wait := (camTriggerDegreeWidth*cyclesPerDegOfRot) ' 19 degrees
trigger2Wait := (camTriggerDegreeWidth*cyclesPerDegOfRot) ' 19 degrees
trigger3Wait := (camTriggerDegreeWidth*cyclesPerDegOfRot) ' 19 degrees
emtpySpace1Wait := (degreeOfTrigger1*cyclesPerDegOfRot) ' 72 degrees
emtpySpace2Wait := (degreeOfTrigger2*cyclesPerDegOfRot) ' 72 degrees
emtpySpace3Wait := (degreeOfTrigger3*cyclesPerDegOfRot) ' 159 degrees
tenDeg := (10*cyclesPerDegOfRot)
'emtpySpace3WaitSansDelay := (emtpySpace3Wait-tenDeg)
fiftyDeg := (50*cyclesPerDegOfRot)
emtpySpace3WaitSansDelay := (emtpySpace3Wait-fiftyDeg)
{
if rpm is < 1000, then leave cam at 0, if above 1000 check the pulse width of the factory ecu
0 is the rising edge of 0 crank tooth 26, the start needs to be aligned with this
factory ecu is 300hz
100% pulse width is 266666 cycles
50% is 133333
75% is 200000
25% is 66666
if pulse high of pin is measured at > 150000 cycles, you need to advance, meaning pulse 75% high / 25% low
if pulse high of a pin is measured between 100000 and 150000, you stay, meaning pulse 50% high / 50% low
if pulse high of a pin is measured < 100000, you retard, meaning pulse 25% high / 75% low
may be able to set up a cntr in this cog and then call it and test
}
'l_pst1 := trigger1Wait
'l_pst2 := trigger2Wait
'l_pst3 := trigger3Wait
'l_pst4 := emtpySpace1Wait
'l_pst5 := emtpySpace2Wait
'l_pst6 := emtpySpace3Wait
'l_pst9 := tenDeg
l_pst15 := camRpmPerSecond
l_pst16 := cyclesPerDegOfRot
'l_pst12 := fiftyDeg
repeat until (crankTooth == 26)
crankTooth := long[crTooth]
'startTooth := 26
localCnt := cnt
repeat
prevLoopCnt := loopCnt
loopCnt := cnt
l_pst7 := (loopCnt - prevLoopCnt)
degAdv := long[degreesDiff]
l_pst6 := degAdv
if (degAdv > 35)
l_variableDegreeDelay := 5
if (degAdv < 15)
l_variableDegreeDelay := -5
if (l_variableDegreeDelay <> 0)
variableDelay := (l_variableDegreeDelay * cyclesPerDegOfRot)
l_variableDegreeDelay := 0
waitcnt(localCnt += (variableDelay + fiftyDeg))
variableDelay := 0
outa[simCamWheelPinNum]~~ ' pin high
waitcnt(localCnt += trigger1Wait) ' wait cnt time stamp plus trigger degree width in cycles
outa[simCamWheelPinNum]~ ' pin low
'waitcnt(localCnt += (clkfreq/521))
waitcnt(localCnt += emtpySpace1Wait) ' wait cnt time stamp plus first empty space degree width in cycles
outa[simCamWheelPinNum]~~ ' pin high
waitcnt(localCnt += trigger2Wait) ' wait cnt time stamp plus trigger degree width in cycles
outa[simCamWheelPinNum]~ ' pin low
'waitcnt(localCnt += (clkfreq/521))
waitcnt(localCnt += emtpySpace2Wait) ' wait cnt time stamp plus second empty space degree width in cycles
outa[simCamWheelPinNum]~~ ' pin high
waitcnt(localCnt += trigger3Wait) ' wait cnt time stamp plus trigger degree width in cycles
outa[simCamWheelPinNum]~ ' pin low
'waitcnt(localCnt += (clkfreq/521))
'waitcnt(localCnt += emtpySpace3Wait) ' wait cnt time stamp plus third empty space degree width in cycles
waitcnt(localCnt += emtpySpace3WaitSansDelay)
}

Comments
In my first tries to write a video driver I used waitpeq to synchronize cogs. First cog displays a picture. Second cog waits for Vsync pin low, then do its work, counting Hsync pins low, also with waitpeq.
Thanks for the reply. I'm not sure how I would implement that, since I need to sync with a specific event and keep sync based on that specific event (with a small skew)? Maybe I'm not understanding and you can elaborate?
Do I need to pass a cnt value from the main cog to all of my other cogs that I want to sync?
You spurred me to a thought that I wouldn't have come up with on my own. I sync my timeStamp for the waitcnt of cog B, when cog A frequency count = 19. I sync them by obtaining the system count when frequency count = 19 ... similar to what you suggested.
This worked at 500hz and 6500hz, which is roughly the window I need : D.
In case someone comes across this in a search, here is my code adjustment. The way it works is that cog B reads a frequency count address from cog A. Cog B does the things it needs to do and then loops around to the synctooth19 synchronization loop and sits there until the frequency count address from cog A = 19. Then it does what it needs to do and repeats.
I'm going to create a skew value after that, so I can shift it back and forth a little right after count = 19.
:synctooth19 rdlong crankToothNum, crankToothNumAddrPtr ' waits for the rising edge wrlong crankToothNum, pstPtr16 cmp crankToothNum, #19 WZ, WC if_ne jmp #:synctooth19 if_e mov timeStamp, cntDo these Generators have the same frame-rates ?
From the above, it reads like B spins at 1/24 the NewFreq rate of A ? - but the 55-19 is 36, which does not quite match a fixed-ratio ?
If the master can calculate a known future time (which it should be able to do ?) it can pass that value to the slave, as #2 says, waitcnt reads a common counter across all cogs.
This would require matched frame rate, as one edge is considered a 'master edge', that other slave edges time relative to.
I over simplified the initial explanation as to not turn people away, but the more technical explanation is that cog A puts out 72 frequencies to cog B's 3 frequencies. Technically, at least in the calculations, cog A is oscillating at a hz that is twice that of cog B's hz. But realistically, the way that it works is that:
cog A puts out 34 square waves and is low for the equivalent of 2 square waves in 80,000,000 clk cycles
cog B puts out 3 square waves and is low for the equivalent of 1 square wave in 160,000,000 clk cycles
cog A has a square wave width of X
cog B has a square wave width of 2x
So cog A oscillates (36-2) square waves and cog B oscillates a frequency at tooth 19, at tooth 1 and at tooth 19 again before it repeats (the next one would be at 19 since it is silent for an equivalent tooth), because of the 2:1 ratio. So if X is the frequency, cog B is high at tooth#, at tooth#+0.5x and at tooth#+1x and silent at tooth#+1.5x.
Is that what you mean by frame rate? Am I explaining that well?
I think you mean pulses, rather than frequencies ?
Also, it is easier to move this into the time domain.
So A has a frame (repetition rate) of 80M clks divided into 36 slots, 34 of which have a pulse SlotA/2 wide ?
and B has a frame of 160M cycles, divided into 4 slots, 3 of which have a pulse SlotB/2 wide
Pulse A is then 1111111.1111 clks wide, pulse B is 20000000 Wide.
There you have one issue, Pulse A does not quite fit, so you may need to do something like this
Pw=round(80e6/36/2) = 1111111
80e6-(Pw*34*2) = 4444452 (80e6-(Pw*34*2))/Pw = 4.0000072...
- ie just over 4 exact half cycles of pause time.
Once you have same/integer ratio frames, you can lock then to any desired phase. Usually the slower one would be master, and the code for A repeats twice to match the frame rate of B.
One single COG could generate these numbers, but I guess you have 'other stuff' going on.. ?
You are right, pulse is a better word for it and time domain is probably easier to use. I'm glad you are following, this has taken me quite some time to get straightened out.
To make things more complicated, the rate of slots per 80m clks, will vary in the real world greatly. I used 36 slots per 80m clk cycles as an example for cog A, but realistically it is anywhere between 300 and 6900 slots per 80m clock, with each slot having 45% of the slot as a high and 55% of the slot as a low.
So if frame A is 300 slots per 80m clk, each of those slots is 45% high/55%low, except for the "-2" portion which is 2 complete slots that are 100% low.
The object I am working on tonight, will calculate the time difference between pulse 26 and pulse 3 and convert it into degrees.
This was my biggest challenge, to sync them even when the slots per frame rate was quite variable. I'm sure a very good programmer could manage to squeeze all of this into 1 cog, but that isn't me so I'm still using two : ) . I do have other calculations and reads going on as well.
In my first tries to write a video driver I used waitpeq to synchronize cogs. First cog displays a picture. Second cog waits for Vsync pin low, then do its work, counting Hsync pins low, also with waitpeq...written by pik33
Do you have a schematic or program example that shows how the sync cogs waitpeq works? Do you tie the Vsync line to both the VGA display and another propeller line? Is it safe to have the propeller pin send a signal to another propeller pin? I have been working on syncing many COGS and additional propeller chips and the waitpeq looks like a simple solution.