To operate an ECM while not installed, besides all simulated sensors and actuators (replaced by resistors and LED) the camshaft position sensor's (CPS) signal is required too. The CPS signal synchronizes everything inside the ECM. Originally this signal is generated by detecting slots in a rotating cup utilizing a Hall sensor. At the start of each slot the sensor voltage will drop from 5 to 0 volts, at the end it will jump up from 0 to 5 volts again.
Depending on the Buell model, two versions of rotor cups are available: for EFI equipped bikes a 6-slot cup is used, for carburettor Buells it's a cup with only 2 slots. Later models (DDF-3 and up) do not have a camshaft sensor at all, but a crankshaft sensor instead. This sensor signal can not be simulated that easily, therefore it is not taken into account here.
To set up a signal generator, a small micro controller can be used, like a AVR or PIC (to name just two of the many available). A very common platform is the Arduino UNO, exemplary shown below. This board will be connected to a PC with a USB cable, which will provide power and also will be used to transfer new programs ("sketches" in Arduino speak) to the board.
The sketch made available below will provide 5 signals, as listed:
All signals combined will look similar to the image below (the crankshaft signal has been suppress for better visibility):
Click image for unscaled view
The source code for this sketch can also be downloaded here.
// Arduino XB/S1 CPS signal generator // // To set engine speed: open serial monitor, send "+" to increase and "-" to decrease engine speed // // Pins used: // 13 = onboard LED (toggles once per ignition cycle) // 12 = CPS signal XB (6 slots in cup) // 11 = crank signal (toggles once per crankd degree) // 10 = CPS signal S1 (2 slots in cup) // 8 = O2 signal (toggles once per 8 cycles) - voltage divider required for ECM input! // // avr-libc library includes #include#include #define O2 PINB0 #define CPSS1 PINB2 #define CRANK PINB3 #define CPSXB PINB4 #define LED PINB5 static volatile unsigned int degCount; // count timeticks static volatile byte revCount; // count revolutions static volatile byte count1, count2, count3, count4, count5; // GP counters static volatile byte blipXBIndex, blipS1Index; // rotor cup description // 6 slot rotor cup for fuel injected engines, degrees crankshaft where CPSXB signal toggles static unsigned int blipsXB[12] = {45, 90, 135, 180, 225, 405, 450, 495, 540, 585, 630, 720}; // 2 slot rotor cup for carbed engines, degrees crankshaft where CPSS1 signal toggles static unsigned int blipsS1[4] = {270, 315, 675, 720}; // overflow compare register values @ 8 MHz for specific engine speeds //OCR1A = 168; // 8k RPM //OCR1A = 178; // 7k5 RPM //OCR1A = 192; // 7k RPM //OCR1A = 199; // 6750 RPM //OCR1A = 207; // 6k5 RPM //OCR1A = 225; // 6k RPM //OCR1A = 244; // 5k5 RPM //OCR1A = 269; // 5k RPM //OCR1A = 299; // 4k5 RPM //OCR1A = 338; // 4k RPM //OCR1A = 384; // 3k5 RPM //OCR1A = 448; // 3k RPM //OCR1A = 537; // 2k5 RPM //OCR1A = 673; // 2k RPM //OCR1A = 897; // 1k5 RPM //OCR1A = 1224; // 1k1 RPM //OCR1A = 1346; // 1k RPM //OCR1A = 1682; // 800 RPM //static unsigned int compares[18] = {168, 178, 192, 199, 207, 225, 244, 269, 299, 338, 384, 448, 537, 673, 897, 1224, 1346, 1682}; // @ 8 MHz system clock static unsigned int compares[18] = {332, 355, 380, 394, 409, 443, 484, 532, 592, 665, 761, 888, 1066, 1332, 1778, 2425, 2670, 3340}; // @ 16 MHz system clock static unsigned int rpms[18] = {8000, 7500, 7000, 6750, 6500, 6000, 5500, 5000, 4500, 4000, 3500, 3000, 2500, 2000, 1500, 1100, 1000, 800}; // index to compares/rpm arrays static volatile char speedIndex = 15; // start @ 1100 rpm // main loop void loop() { int input = 0; if(Serial.available()) { input = Serial.read(); //Serial.println(input, HEX); // mirror input, for testing only // parse input if (input == (int) '+') speedIndex --; else if (input == (int) '-') speedIndex ++; // index rotation if (speedIndex < 0) speedIndex = 17; else if (speedIndex > 17) speedIndex = 0; // update compare match register OCR1A = compares[speedIndex]; Serial.println(rpms[speedIndex]); Serial.print ("? "); } } // timer1/counter1 compare_A interrupt signal handler ISR(TIMER1_COMPA_vect) { // called, when a timer1/counter1 matches compare value // toggle crankPin @ every degree of crankshaft PORTB ^= (1 << CRANK); // increase degress crankshaft degCount ++; if (degCount >= blipsXB[blipXBIndex]) { // toggle CPSXB signal @ every blip of CPSXB PORTB ^= (1 << CPSXB); // increase blip index blipXBIndex ++; // reset index if (blipXBIndex >= 12) blipXBIndex = 0; } if (degCount >= blipsS1[blipS1Index]) { // toggle CPSS1 signal @ every blip of CPSS1 PORTB ^= (1 << CPSS1); // increase blip index blipS1Index ++; // reset index if (blipS1Index >= 4) blipS1Index = 0; } // reset crankshaft degrees counter after 2 revolutions if (degCount == 720) { // toggle LED @ every ignition cycle PORTB ^= (1 << LED); degCount = 0; revCount ++; } if (revCount == 8) { // toggle O2 signal @ 8 ignition cycles PORTB ^= (1 << O2); revCount = 0; } } // setup and initialization void setup() { // set pins of port B to output mode DDRB = 0xFF; // set CPS signal to high on start, // otherwise output will be inverted PORTB ^= (1 << CPSXB); PORTB ^= (1 << CPSS1); // initialize counter and indexes degCount = 0; revCount = 0; blipXBIndex = 0; blipS1Index = 0; //speedIndex = 15; // start @ 1100 rpm //speedIndex = 17; // initialize serial connection Serial.begin(9600); Serial.print("? "); Serial.println(rpms[speedIndex]); Serial.print ("? "); // initialize Timer1 cli(); // disable global interrupts TCCR1A = 0; // set entire TCCR1A register to 0 TCCR1B = 0; // same for TCCR1B // turn on CTC mode TCCR1B |= (1 << WGM12); // Set CS10 and CS12 bits for 1024 prescaler (test only) //TCCR1B |= (1 << CS10); //TCCR1B |= (1 << CS12); // Set CS10 for no prescaler TCCR1B |= (1 << CS10); // set compare match register to desired timer count OCR1A = compares[speedIndex]; // enable timer compare interrupt: TIMSK1 |= (1 << OCIE1A); // enable global interrupts: sei(); }