SpinDrive: A pet project of mine

Free Open Source Firmware project discussion forum. Post your Free Open Source firmware projects here!
thebigmacd
LQFP112 - Up with the play
Posts: 205
Joined: Thu Apr 10, 2008 5:51 pm

Re: SpinDrive: A pet project of mine

Post by thebigmacd »

EssEss wrote:I was thinking about this more last night .. it looks like a 'deocder' is a seperate concept from a 'position'. as in, a 'position' obj would be composed of one to multiple 'decoders' depending on if you needed to decode a cam input in addition to decoding crank.

is this prj entirely spin or are you going to mix in some pasm too ?
This project will definitely include both SPIN and PASM. I want this code to handle 10,000+ rpm without issues, so PASM is definitely going to be prevalent on the "Coordinator" chip. The math and ADC stuff on the other chip will be mostly SPIN because it uses less memory and fewer lines of code, but is 10x slower.
Keith MacDonald
Control Engineering (Systems) Technologist
User avatar
EssEss
LQFP112 - Up with the play
Posts: 244
Joined: Thu Sep 10, 2009 9:23 am
Location: Dayton, OH

Re: SpinDrive: A pet project of mine

Post by EssEss »

I get it. This is cool stuff - you should have another section of your spec that gets 'wordy' just like that.
thebigmacd
LQFP112 - Up with the play
Posts: 205
Joined: Thu Apr 10, 2008 5:51 pm

Re: SpinDrive: A pet project of mine

Post by thebigmacd »

EssEss wrote:I get it. This is cool stuff - you should have another section of your spec that gets 'wordy' just like that.
Give me time man, give me time :D

I'm almost ready writing a pseudo-code example of a decoder Cog, one that reads a simple single-window distributor hall sender and writes speed and position for the simulator to use. Should have it up here tonight!
Keith MacDonald
Control Engineering (Systems) Technologist
thebigmacd
LQFP112 - Up with the play
Posts: 205
Joined: Thu Apr 10, 2008 5:51 pm

Re: SpinDrive: A pet project of mine

Post by thebigmacd »

Well here it is in all its glory:
# This is pseudocode for a cog that decodes a single-window distributor-mounted hall sensor
# The input swings high at 60 deg BTDC#1
# The input swings low at 10 deg BTDC#1

# Startup Housekeeping
# Clear flags and initialize variables
INIT:

NEW_KNOWN_POSITION = NO
ENGINE_PHASE_0 = NO
ENGINE_PHASE_1 = NO

LAST_KNOWN_POSITION = 0
LAST_KNOWN_SPEED = 0


# Start here if sync lost
REINIT:
WHEEL_DATA_PRESENT = NO
WHEEL_DATA_OKAY = NO


# Pause until first positive-going transition
WAIT FOR (TRIG_0 == LOW)
WAIT FOR (TRIG_0 == HIGH)


# Grab the time of the first positive transition from CNT for the next go-around
LAST_POSITIVE_CNT = CNT

# Set flag indicating that wheel data is present (notimeout)
WHEEL_DATA_PRESENT = YES
# This flag will be cleared by the watchdog timer in the main Cog

# Pause until negative-going transition
WAIT FOR (TRIG_0 == LOW)

# Grab the time of the first positive transition from CNT for the next go-around
LAST_NEGATIVE_CNT = CNT

# Resync if timeout
If WHEEL_DATA_PRESENT == NO Then
Goto REINIT:
End If


# Set flag indicating that wheel data is good (sync)
WHEEL_DATA_GOOD = YES
# This flag will be cleared by the watchdog timer in the main Cog. There is no error detection in this example.

# Decode a positive transition, then a negative transition
LOOP:

# Pause until positive-going transition
WAIT FOR (TRIG_0 == HIGH)

# Grab the time of this positive transition from CNT
THIS_POSITIVE_CNT = CNT

# Resync if timeout
If WHEEL_DATA_PRESENT == NO Or WHEEL_DATA_GOOD == NO Then
Goto REINIT:
End If


# Speed is in clock counts per 1/32768th of a crank revolution
# The angle between this transition and the last is 670 degrees
LAST_KNOWN_SPEED = (((THIS_POSITIVE_CNT - LAST_NEGATIVE_COUNT) / 670) * 720) / 65536

# Position is in 1/32768th of a crank revolution increments, rolling over at 32768 (360 deg)
# This transition occurs at 660 MOD 360 = 350 degrees
# POSITION = ((660 * 65536) / 720) - 32768
LAST_KNOWN_POSITION = 27306

# Clearing this flag indicates that the crank is NOT >= 0 and < 360
ENGINE_PHASE_0 = NO
# This flag will be set by the simulator Cog when engine position rolls over at 32768 (360 deg)

# Setting this flag indicates that the crank is >= 360 and < 720
ENGINE_PHASE_1 = YES
# This flag will be cleared by the simulator Cog when engine position rolls over at 32768 (360 deg).

# This flag is set every time a valid tooth is decoded
NEW_KNOWN_POSITION = YES
# This flag will be cleared by the simulator Cog when the new position is read.

# Set flag indicating that wheel data is good (sync)
WHEEL_DATA_GOOD = YES
# This flag will be cleared by the watchdog timer in the main Cog. There is no error detection in this example.

# Store the time of this positive transition from CNT for the next go-around
LAST_POSITIVE_CNT = THIS_POSITIVE_CNT

# Pause until negative-going transition
WAIT FOR (TRIG_0 == LOW)

# Grab the time of this negative transition
THIS_NEGATIVE_CNT = CNT

# Resync if timeout
If WHEEL_DATA_PRESENT == NO Or WHEEL_DATA_GOOD == NO Then
Goto REINIT:
End If


# Speed is in clock counts per 1/32768th of a crank revolution
# The angle between this transition and the last is 50 degrees
LAST_KNOWN_SPEED = (((THIS_NEGATIVE_CNT - LAST_POSITIVE_COUNT) / 50) * 720) / 65536

# Position is in 1/32768th of a crank revolution increments, rolling over at 32768 (360 deg)
# This transition occurs at 710 MOD 360 = 350 degrees
# POSITION = ((710 * 65536) / 720) - 32768
LAST_KNOWN_POSITION = 31857

# Clearing this flag indicates that the crank is NOT >= 0 and < 360
ENGINE_PHASE_0 = NO
# This flag will be set by the simulator Cog when engine position rolls over at 32768 (360 deg)

# Setting this flag indicates that the crank is >= 360 and < 720
ENGINE_PHASE_1 = YES
# This flag will be cleared by the simulator Cog when engine position rolls over at 32768 (360 deg)

# This flag is set every time a valid tooth is decoded
NEW_KNOWN_POSITION = YES
# This flag will be cleared by the simulator Cog when the new position is read

# Set flag indicating that wheel data is good (sync)
WHEEL_DATA_GOOD = YES
# This flag will be cleared by the watchdog timer in the main Cog. There is no error detection in this example.

# Store the time of this negative transition from CNT for the next go-around
LAST_NEGATIVE_CNT = THIS_NEGATIVE_CNT

# Go around and decode the next positive transition
GOTO LOOP
Keith MacDonald
Control Engineering (Systems) Technologist
thebigmacd
LQFP112 - Up with the play
Posts: 205
Joined: Thu Apr 10, 2008 5:51 pm

Re: SpinDrive: A pet project of mine

Post by thebigmacd »

I just realized that if I use a counter from 0-32767 (15-bit) to represent crank angle, at 10,000 rpm it only gives me < 15 clocks per increment @ 80MHz, which is only three instructions, and less than the Hub access interval. Oops

clocks/tick = ((80000000 clocks_per_second / 32768 ticks_per_rev) * 60 seconds_per_minute) / 10000 rev_per_minute = 14.xx clocks/tick

This is why I am writing a spec before I write too much code ;)

If I divide the counter by 8 (resulting in a 12-bit counter) I still get better than 0.1 deg resolution, and now have 112 clocks (28 instructions) per increment @ 10,000 rpm, which isn't bad. If I divide the counter by 16 (11-bit) I get just less than 0.2 deg resolution, and have something like 224 clocks (56 instructions) per increment.

The question I guess is for an angle-based system, what resolution is acceptable. I would have tons of free time in the simulator cog if I could use ~1 deg resolution (0-511 = 9-bit counter would do it). I want to use a power of two for the counter so the math involves simple bit shifts as much as possible (no hardware divide on these chips), and rollovers involve only an AND bitmask.

Suggestions?

If I wanted to get tricky, I could go with whatever resolution I wanted and put logic in the simulator that determines how many increments to jump if the interval gets shorter than the loop runtime. That way my angular precision would get worse as RPM went up, but still extremely precise at low RPMs (we're talking 0.011 deg here!). The question I guess ends up being how accurate will my angular simulation be? No point in being precisely inaccurate!

I guess it depends too on how frequently the decoder can grab position data from the wheel. With Optispark it could update every 1/360th of a revolution if we look at both the rising and falling edge of the high-resolution pattern. We could then afford to be very precise but would lose high speed support. The question then is can the Optispark even spin fast enough to start severely limiting our loop time without exploding? Lots of things to consider!

The one thing that the Propeller makes very easy for us is deterministic loop timing; no interrupt routines will be be pausing the simulator Cog to do some random procedure, and the decoder Cog can handle as many tooth transitions as we can throw at it without choking the other Cogs.
Keith MacDonald
Control Engineering (Systems) Technologist
thebigmacd
LQFP112 - Up with the play
Posts: 205
Joined: Thu Apr 10, 2008 5:51 pm

Re: SpinDrive: A pet project of mine

Post by thebigmacd »

p.s. Fred can you allow .spin file extensions?

Here is the SPIN version of the single_window_distributor object. It compiles but I haven't actually tested it. :oops: The compiled object is only 331 bytes. :shock: Might need to make the stack buffer bigger than 10 longs, not sure. There's a good chance this object would actually be good to over 10,000 rpm simply because there are so few tooth transitions per revolution. Larger tooth counts will require PASM.

The file should be saved as single_window_distributor.spin. To use this object, the parent object has to have decoder : "single_window_distributor" in the CON section. To start it, call the function decoder.start(@LAST_KNOWN_POSITION,@LAST_KNOWN_SPEED,@FLAGS) and it will write the position, speed, and flags to the specified Hub addresses accordingly. I have specified the flag bits in the CON section, eventually they will be defined in a special "definition object".

Code: Select all

CON

  _CLKMODE = XTAL1 + PLL16X
  _XinFREQ = 5_000_000

' Flag bit positions
  NEW_KNOWN_POSITION = 0
  ENGINE_PHASE_0 = 1
  ENGINE_PHASE_1 = 2
  WHEEL_DATA_PRESENT = 3
  WHEEL_DATA_GOOD = 4

' Using PA0 (Pin1) for trigger input
  TRIG_0_PIN = 0

' Using an 11-bit counter for engine position
  POSITION_COUNTER_BITS = 11

' Predefined values for degrees based on counter size
  _60DEG_BTDC1 = ((660 << POSITION_COUNTER_BITS) / 720) - (1 << (POSITION_COUNTER_BITS - 1))
  _10DEG_BTDC1 = ((710 << POSITION_COUNTER_BITS) / 720) - (1 << (POSITION_COUNTER_BITS - 1))


VAR

  long stack[10]
  long THIS_POSITIVE_CNT, LAST_POSITIVE_CNT, THIS_NEGATIVE_CNT, LAST_NEGATIVE_CNT


PUB start(addrCRANKposition,addrCRANKspeed,addrFLAGS)

  cognew(single_window_distributor(addrCRANKposition,addrCRANKspeed,addrFLAGS),stack)


PRI single_window_distributor(addrCRANKposition,addrCRANKspeed,addrFLAGS) | flagBUFFER

'Clear ENGINE_PHASE_0, ENGINE_PHASE_1, NEW_KNOWN_POSITION, WHEEL_DATA_PRESENT, WHEEL_DATA_GOOD in HUB
  byte[addrFLAGS] := byte[addrFLAGS] & !((|< ENGINE_PHASE_0) | (|< ENGINE_PHASE_1) | (|< NEW_KNOWN_POSITION) | (|< WHEEL_DATA_PRESENT) | (|< WHEEL_DATA_GOOD))

'RESYNC LOOP
  repeat
'Clear WHEEL_DATA_PRESENT, WHEEL_DATA_GOOD in HUB
    byte[addrFLAGS] := byte[addrFLAGS] & (-1 ^ ((|< WHEEL_DATA_PRESENT) | (|< WHEEL_DATA_GOOD)))

'Wait for first positive transition
'60 deg BTDC1
    waitPNE(|< TRIG_0_PIN,|< TRIG_0_PIN,0)
    waitPEQ(|< TRIG_0_PIN,|< TRIG_0_PIN,0)

'Store time for later
    LAST_POSITIVE_CNT := cnt

'Set WHEEL_DATA_PRESENT in HUB
'We will have sync on next tooth
    byte[addrFLAGS] |= byte[addrFLAGS] |< WHEEL_DATA_PRESENT

'Wait for first negative transition
'10 deg BTDC1
    waitPNE(|< TRIG_0_PIN,|< TRIG_0_PIN,0)

'Store time for later
    LAST_NEGATIVE_CNT := cnt

'Read flags from HUB
    flagBUFFER := byte[addrFLAGS]

'Restart this loop if main loop watchdog timer times out
    if (flagBUFFER & |< WHEEL_DATA_PRESENT) == 0
      next

'Set WHEEL_DATA_GOOD in HUB
'We have sync now
    byte[addrFLAGS] := flagBUFFER | |< WHEEL_DATA_GOOD

'DECODER LOOP
    repeat

'Wait for next positive transition
'60 deg BTDC1
      waitPEQ(|< TRIG_0_PIN,|< TRIG_0_PIN,0)

'Get current time
      THIS_POSITIVE_CNT := cnt

'Read flags from HUB
      flagBUFFER := byte[addrFLAGS]

'Quit this loop if main loop watchdog timer times out
      if (flagBUFFER & (|< WHEEL_DATA_PRESENT) & (|< WHEEL_DATA_GOOD)) == 0
        quit

'Write LAST_KNOWN_POSITION, LAST_KNOWN_SPEED to HUB
      long[addrCRANKspeed] := (((THIS_POSITIVE_CNT - LAST_NEGATIVE_CNT) / 670) * 720) >> POSITION_COUNTER_BITS
      long[addrCRANKposition] := _60DEG_BTDC1

'Clear ENGINE_PHASE_0
'Set ENGINE_PHASE_1, NEW_KNOWN_POSITION, WHEEL_DATA_PRESENT, WHEEL_DATA_GOOD
      byte[addrFLAGS] := (flagBUFFER & !(|< ENGINE_PHASE_0)) | (|< ENGINE_PHASE_1) | (|< NEW_KNOWN_POSITION) | (|< WHEEL_DATA_PRESENT) | (|< WHEEL_DATA_GOOD)

'Store time for later
      LAST_POSITIVE_CNT := THIS_POSITIVE_CNT

'Wait for first negative transition
'10 deg BTDC1
      waitPNE(|< TRIG_0_PIN,|< TRIG_0_PIN,0)

'Get current time
      THIS_NEGATIVE_CNT := cnt

'Read flags from HUB
      flagBUFFER := byte[addrFLAGS]

'Quit this loop if main loop watchdog timer times out
      if (flagBUFFER & (|< WHEEL_DATA_PRESENT) & (|< WHEEL_DATA_GOOD)) == 0
        quit

'Write LAST_KNOWN_POSITION, LAST_KNOWN_SPEED
      long[addrCRANKspeed] := (((THIS_NEGATIVE_CNT - LAST_POSITIVE_CNT) / 670) * 720) >> POSITION_COUNTER_BITS
      long[addrCRANKposition] := _10DEG_BTDC1

'Clear ENGINE_PHASE_0
'Set ENGINE_PHASE_1, NEW_KNOWN_POSITION, WHEEL_DATA_PRESENT, WHEEL_DATA_GOOD
      byte[addrFLAGS] := (flagBUFFER & !(|< ENGINE_PHASE_0)) | (|< ENGINE_PHASE_1) | (|< NEW_KNOWN_POSITION) | (|< WHEEL_DATA_PRESENT) | (|< WHEEL_DATA_GOOD)

'Store time for later
      LAST_NEGATIVE_CNT := THIS_NEGATIVE_CNT

'-----| Main Loop repeats here
'---| Resync Loop repeats here
Keith MacDonald
Control Engineering (Systems) Technologist
User avatar
Fred
Moderator
Posts: 15431
Joined: Tue Jan 15, 2008 2:31 pm
Location: Home sweet home!
Contact:

Re: SpinDrive: A pet project of mine

Post by Fred »

I could, but if you zipped it that would be better. I have to migrate this site off this awful server at some point soon and it'll be painful anyway without excess data. Others have asked for similar things before. I think I decided not to when I myself tried to upload some .c files and was denied :-)
DIYEFI.org - where Open Source means Open Source, and Free means Freedom
FreeEMS.org - the open source engine management system
FreeEMS dev diary and its comments thread and my turbo truck!
n00bs, do NOT PM or email tech questions! Use the forum!
The ever growing list of FreeEMS success stories!
thebigmacd
LQFP112 - Up with the play
Posts: 205
Joined: Thu Apr 10, 2008 5:51 pm

Re: SpinDrive: A pet project of mine

Post by thebigmacd »

Today's progress: mainly website organization and some more diagrams

Website: mostly switched over to stylesheets, now using php includes for the Menu Bar and some diagrams. I make my flowcharts in html by hand in a text editor, so my plan is to put each one in its own .inc file in case I want to use them more than once.

Diagrams:
Added a "General Components" diagram to the Index page.
Added a "Hardware Block Diagram" to the Architecture page.
Added a "Supervisor Layout" and "Coordinator Layout" to the Connections page.
Last edited by thebigmacd on Sun May 02, 2010 2:37 am, edited 1 time in total.
Keith MacDonald
Control Engineering (Systems) Technologist
thebigmacd
LQFP112 - Up with the play
Posts: 205
Joined: Thu Apr 10, 2008 5:51 pm

Re: SpinDrive: A pet project of mine

Post by thebigmacd »

Small pin-usage change:

In Coordinator:
RUN (Pin 3, PA2) is now TRIG_2
START(Pin 4, PA3) is now RUN

In Supervisor (to maintain chip-chip consistency):
RUN (Pin 3, PA2) is now START
START (Pin 4, PA3) is now RUN

I did this because my brother-in-law requested 3 trigger wheel inputs on the Coordinator. He is an apprentice mechanic and is seeing a lot of modern engines with 3 sensors. RUN is important to have on both chips so that limp mode responds to the ignition switch as quickly as physically possible. I figure START can be relegated to the Supervisor only as it will only be used for afterstart logic and datalogging.
Keith MacDonald
Control Engineering (Systems) Technologist
thebigmacd
LQFP112 - Up with the play
Posts: 205
Joined: Thu Apr 10, 2008 5:51 pm

Re: SpinDrive: A pet project of mine

Post by thebigmacd »

Due to lack of time, this project has been completely stalled for quite some time. It turns out there is a fairly mature propeller-based ECU project out there already anyway.

I am going to turn my attention to something a little simpler...an electronic boost controller. Something that I can sell to people around here.
Keith MacDonald
Control Engineering (Systems) Technologist
Post Reply