I am undertaking a project that may be of some interest with respect to engine position tracking and crank-angle-based event scheduling. This is pretty much a pipe dream so far with respect to implementing anything to actually control an engine.
Hardware and Capabilities
Recently I have been playing around a bit with the Propeller Chip, which in a nutshell is an 8-core (Cogs) 80mHz microprocessor with 32 all-purposed I/O pins, 2 multi-purpose timers per core, hardware input pattern matching, and no traditional interrupts.
Overview and Strategy
I have started into writing a firmware package for decoding engine position from toothed wheels, and driving 24 ignition/injection outputs (mixed any way desired) in a true sequential manner, using crank angle scheduling.
Core Functions and Interaction
I have separated tooth decoding and event scheduling into completely different layers. Although the PropChip lacks interrupts, each core has the ability to wait in a suspended state until a masked set of input pins match a predefined pattern, and resume full operation after only 6 clock cycles. This necessitates any tooth edge detection is contained in a Cog separate from code that needs to run continuously (non-blocked), as the edge-detection Cog must be suspended while it waits for the next tooth (when timestamps are critical).
Cog Architecture
(0) Main Loop - This starts at boot, and manages the state of following Cogs
(1) Event Scheduler - This maintains a simulated crank position and is periodically updated by the tooth decoders
(2) Crank Decoder (modular) - This waits for crank teeth and calculates crank position and speed, used to update the Event Scheduler
(3) Cam Decoder (optional) - This waits for cam teeth and calculates cam phase, engine phase, crank speed and position etc if supported
(4) to (7) Future: comms, math, datalogging, etc
Crank Decoder
I have actually written some code for a generic missing-tooth crank wheel decoder. There are a couple of strategies I am trying, one of which is maintaining an array that stores the clocks-per-revolution for each individual tooth. There is a method to this madness: by storing the time for one tooth to make a revolution, the crank speed can be calculated based on that tooth only, whenever that tooth is captured. This gives the effect of updating rpm at every tooth edge (fast response) and making the time period an entire revolution rather than the time since last tooth (less jitter).
Generic Crank Decoder Algorithm
I will provide some pseudocode that describes how my system decodes the wheel and passes the position to common memory. This assumes that we are triggering on the falling edge. By passing a timestamp between the Cogs it should be trivial to eliminate timing issues stemming from access delays.
In my code I have an array with three entries for each present tooth: tooth position, tooth capture time, and tooth period. The tooth counter is used to directly index the array location.
Code: Select all
Init:
Initialize Variables and Arrays to Zero
Set tooth counter to last tooth (total - missing)
Set Synch = False
Loop:
Wait for pin to go high
Wait for pin to go low
Store time
Calculate time since previous tooth
Are we on the last tooth?
Yes:
Has expected time elapsed for missing teeth? [ ((missing +0.5)*prev) to ((missing + 1.5)*prev) ]
Yes:
Set tooth counter to first tooth (0).
Update period and time for this tooth in local array and set Synch = True.
No:
Go to Init
No:
Has expected time elapsed since previous tooth? [ (0.5*prev) to (1.5*prev)]
Yes:
Increment tooth counter (+1)
Update period and time for this tooth in local array.
No:
Go to Init
Calculate speed and position for this tooth
Write Synch, Position, Speed, and Time to common memory
Go to Loop
Well, other than what I have babbled on about above, a key difference between my decoder code and say Megasquirt's decoder is that in my code the first tooth after the gap is Tooth 0, which I find to be less confusing. It also makes indexing the tooth data array much easier.
The above code actually handles wheels without missing teeth as well, although a second trigger is needed to set the zero tooth properly. The way it is is would set the second tooth it sees on resynch or cranking as tooth zero. An exception handler could block it from synching until a second trigger established the initial position.
What's next?
I have to write code for the Event Scheduling cog, which will be a free-running counter that indexes crank position in divisions of 1024/revolution, with a variable loop period corresponding with calculated engine speed. This gives me an angular resolution of about 1/3 of a degree. Even at 19,500 I will have around 240 clock cycles to work with for every loop iteration @ 1024 divisions/rev.
Because all Cogs can access all pins at any time, I am going to use a spare pin as a "synchronized" flag between cogs. When this flag is false, all charging events will be disabled but all discharging events will be enabled until all active outputs have been discharged and the wheel position simulator stops. It will double as a visual indicator of wheel synch by hooking an LED to that pin.
In each loop iteration, the crank angle will be incremented by one division, common memory polled for an update from the wheel decoder, adjustment of the loop period and crank position done if an update is present, corresponding output states changed, and next output changes decided. The only additional information that should need to be passed to this Cog is Ignition Advance (angular) and Injector Pulse Width (Angular), both of which can be calculated by the main loop or pushed via comms from another processor (my plan).