Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1369 lines (1106 sloc) 48.5 KB
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// this code is to be considered experimental and is provided "as is", without warranty of any kind,
// express or implied, including but not limited to fitness for a particular purpose.
// if your LT1 comes apart using it i will not be held responsible
// you were warned
//
// this software is licensed under GNU General Public Licencse v3 - please see file: LICENSE in repository
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define VERSION "0.9.17"
// only select one of the below options (or none)
//#define SERIAL_DEBUG 1 // dumps misc debugging info to uart - disable if unused
//#define LOGGING 1 // streams operational stats to the uart - only use for testing
//#define INPUT_TEST 1 // mirrors state of inputs to leds - opti hi-res cyl #6, opti lo-res cyl #8
//#define DUMP_TABLES 1 // dump all the tables to uart at startup to verify values
//#define STANDALONE 1 // UNFINISHED > fires coils from internal degree count instead of utilizing est line for timing signal
//#define DEBUG_STREAM 1 // UNFINISHED > stream coil outputs and hrdegrees @ 1khz to uart
//#ifdef LOGGING
//#define RUN_RPM 4000 // threshold to switch from 1 uart message per tdc event to 1 per every ERROR_REVS events in timer2 "ticks"
//#endif
// 22058 = 170rpm
// 18750 = 200rpm
// 14062 ~ 300rpm
// 9375 = 400rpm
// 6250 = 600rpm
// 4687 = 800rpm
// 3750 = 1000rpm
// 3125 = 1200rpm
// if you require a tach driver on A0 uncomment - note the A0 output is still used / configured for INPUT_TEST mode
//#define TACH_DRIVER 1
// ion sense trigger on A1
//#define ION_TRIGGER 1
//#define ION_TRG_DEGREE 74
// coil constants - uncomment the ones you have or create your own dwell tables from test data
//#define DWELL_TGT 6.0 // test bench
// for GM 12658183 coils / D585
#define DWELL_TGT 4.5 // dwell target in milliseconds - edit to your liking (using 4.5ms @ 12v for standard ls2 coils)
#define CRANK_DWELL_TGT 3.2 // dwell target for cranking / startup
#define ACCEL_COMP 0.5 // for accell comp table
// for ls1 / D580 coils
//#define DWELL_TGT 6.1 // dwell target in milliseconds - edit to your liking
//#define CRANK_DWELL_TGT 3.6 // dwell target for cranking / startup
//#define ACCEL_COMP 0.6 // for accell comp table
#define VOLT_COMP_8 2.4 // adds / subtracts ms from dwell target at given input voltage
#define VOLT_COMP_9 1.5
#define VOLT_COMP_10 0.9
#define VOLT_COMP_11 0.4
#define VOLT_COMP_12 0.0
#define VOLT_COMP_13 -0.2
#define VOLT_COMP_14 -0.5
#define VOLT_COMP_15 -0.7
#define VOLT_COMP_16 -0.9
#define VOLT_COMP_17 -1.0
// plug-in your values for R1 and R2 here (voltage divider network)
// be sure to include mantissa of zero or the precompiler will convert to integers and data will be lost
#define R1_VAL 9850.0 // design 10000.0
#define R2_VAL 3844.0 // design 3900.0
// need to smarten this up and add a decay timer for accel comp
#define MAP_MIN_THOLD 490 // minimum map value for accel compensation - this should be above your normal idle map by about 50 points
#define MAP_DELTA 32 // minimum delta to consider accel event - for hysteresis
#define MAP_DECAY_TIME 300 // time in ms to apply accel comp after MAP drops suddenly
#define BUILD_ENV_DPTI 1 // comment out if you get compiler errors about undeclared digitalPinToInterrupt() function
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// end of typical user controls - do not edit below this line unless you know what you're doing
//
//////////////////////////////////////////////////////////////////////////////////////////////////
// some pin definitions - mainly informational / used in setup() but most port control is handled w/ direct reads / writes (faster)
#define PCM_EST 2
#define OPTI_HI_RES 3
#define OPTI_LO_RES 8 // must use pin 8 for capture interrupt
#define TACH_SIGNAL A0 // for pin setup only
#define ION_SIGNAL A1 // for pin setup only
#define IGN_VSENSE 5 // voltage sensing on adc pin 5
#define MAP_VSENSE 4 // map voltage sensing on adc pin 4
// basic v8 constants
#define NUM_CYLINDERS 8
#define MAX_SPK_ADV 46 // for calculating maximum dwell begin angle (dwellDegrees + accelComp + MAX_SPK_ADV) and to allow for ATDC spark timing
#define ENGINE_GEOMETRY 90 // how many degrees between TDC signals / combustion events
#ifdef STANDALONE
#define CRANKING_SPK_ADV 36 // standalone (test bench mode) fixed spark advance
#else
#define CRANKING_SPK_ADV 10 // arbitrary value to use before we pickup commanded sa from the pcm
#endif
#define DWELL_BEGIN_WINDOW 3 // degrees of window (minus 1) where dwell is allowed to be initiated
#define LR_OVERFLOW 2 // low res timer overflow limit - engine has stalled if timer1 overflows this many times 2 x ((2x65535)x4us) = 524.28 ms between tdc signals - about 26rpm)
// table definition constants
#define VOLT_DIVS 10 // the # of voltage rows in the dwell arrays see VOLT_COMP_* declarations
#define VOLT_OFFSET 8 // the initial voltage offset (8v = dwell[0][rpm]) so lookup is dwell[volts - volt_offset][rpm]
#define RPM_DIVS 31 // the # of rpm cols in the dwell arrays
#define CRANK_RPM_DIVS 6 // these low rpm cells (25-200rpm) get lesser CRANK_DWELL_TGT
#define ACCEL_RPM_DIV 13 // lowest rpm to start applying accel compensation
#define RPM_DIV_1 80
#define RPM_DIV_2 110
#define RPM_DIV_3 140
#define RPM_DIV_4 170
#define RPM_DIV_5 200
#define RPM_DIV_6 400
#define RPM_DIV_7 600
#define RPM_DIV_8 800
#define RPM_DIV_9 1000
#define RPM_DIV_10 1200
#define RPM_DIV_11 1400
#define RPM_DIV_12 1600
#define RPM_DIV_13 1800
#define RPM_DIV_14 2000
#define RPM_DIV_15 2200
#define RPM_DIV_16 2400
#define RPM_DIV_17 2600
#define RPM_DIV_18 2800
#define RPM_DIV_19 3000
#define RPM_DIV_20 3200
#define RPM_DIV_21 3400
#define RPM_DIV_22 3600
#define RPM_DIV_23 3800
#define RPM_DIV_24 4000
#define RPM_DIV_25 4400
#define RPM_DIV_26 4800
#define RPM_DIV_27 5200
#define RPM_DIV_28 5600
#define RPM_DIV_29 6000
#define RPM_DIV_30 6400
#define RPM_DIV_31 6800
// formulas for precompiler macros
// voltage conversion formula vref / (r2 / (r1 + r2))) / 1024
#define VOLT_CONVERSION (float) (((5.0 / (float) (R2_VAL / (R1_VAL + R2_VAL))) / 1024.0))
#define RPM_TO_US(x) (( 250000UL * 60UL / ( x ) )) // Convert RPM to microseconds per 90* (us between low res signals)
//#define RPM_TO_TMR1_TICKS(x) (( 15625UL * 60UL / ( x) )) // convert RPM to timer1 ticks per 90* - 16us per tick using 256 prescaler
#define RPM_TO_TMR1_TICKS(x) (( 62500UL * 60UL / ( x ) )) // convert RPM to timer1 ticks per 90* - 4us per tick using 64 prescaler
// (((CRANK_DWELL_TGT + VOLT_COMP_8) * 1000.0) / (float (rpmTable[i] / 90)) + 0.5)
#define CALC_DWELL(rpm, volts) (uint16_t) (((DWELL_TGT + VOLT_COMP_ ## volts ) * 90000UL / (RPM_TO_US(rpm))))
#define CALC_CRANK_DWELL(rpm, volts) (uint16_t) ((_CRANK_DWELL(rpm, volts) == 0) ? 1:_CRANK_DWELL(rpm, volts))
#define _CRANK_DWELL(rpm, volts) (((CRANK_DWELL_TGT + VOLT_COMP_ ## volts ) * 90000UL / (RPM_TO_US(rpm))))
#define CALC_ACCEL_COMP(rpm) (uint8_t) (((ACCEL_COMP) * 90000UL / (RPM_TO_US(rpm))))
#define CALC_ADC_THOLD(volts) (uint16_t) ((volts / VOLT_CONVERSION))
#define FILL_DWELL_COLUMN(rpm) { (CALC_DWELL( rpm, 8)), \
(CALC_DWELL(rpm, 9)), \
(CALC_DWELL(rpm, 10)), \
(CALC_DWELL(rpm, 11)), \
(CALC_DWELL(rpm, 12)), \
(CALC_DWELL(rpm, 13)), \
(CALC_DWELL(rpm, 14)), \
(CALC_DWELL(rpm, 15)), \
(CALC_DWELL(rpm, 16)), \
(CALC_DWELL(rpm, 17))}
#define FILL_CRANK_DWELL_COLUMN(rpm) { (CALC_CRANK_DWELL( rpm, 8)), \
(CALC_CRANK_DWELL(rpm, 9)), \
(CALC_CRANK_DWELL(rpm, 10)), \
(CALC_CRANK_DWELL(rpm, 11)), \
(CALC_CRANK_DWELL(rpm, 12)), \
(CALC_CRANK_DWELL(rpm, 13)), \
(CALC_CRANK_DWELL(rpm, 14)), \
(CALC_CRANK_DWELL(rpm, 15)), \
(CALC_CRANK_DWELL(rpm, 16)), \
(CALC_CRANK_DWELL(rpm, 17))}
// the dwell table - contains dwell in degrees required to meet DWELL_TGT at different rpms
const uint16_t PROGMEM dwellTable[RPM_DIVS][VOLT_DIVS] = {FILL_CRANK_DWELL_COLUMN(RPM_DIV_1),
FILL_CRANK_DWELL_COLUMN(RPM_DIV_2),
FILL_CRANK_DWELL_COLUMN(RPM_DIV_3),
FILL_CRANK_DWELL_COLUMN(RPM_DIV_4),
FILL_CRANK_DWELL_COLUMN(RPM_DIV_5),
FILL_DWELL_COLUMN(RPM_DIV_6),
FILL_DWELL_COLUMN(RPM_DIV_7),
FILL_DWELL_COLUMN(RPM_DIV_8),
FILL_DWELL_COLUMN(RPM_DIV_9),
FILL_DWELL_COLUMN(RPM_DIV_10),
FILL_DWELL_COLUMN(RPM_DIV_11),
FILL_DWELL_COLUMN(RPM_DIV_12),
FILL_DWELL_COLUMN(RPM_DIV_13),
FILL_DWELL_COLUMN(RPM_DIV_14),
FILL_DWELL_COLUMN(RPM_DIV_15),
FILL_DWELL_COLUMN(RPM_DIV_16),
FILL_DWELL_COLUMN(RPM_DIV_17),
FILL_DWELL_COLUMN(RPM_DIV_18),
FILL_DWELL_COLUMN(RPM_DIV_19),
FILL_DWELL_COLUMN(RPM_DIV_20),
FILL_DWELL_COLUMN(RPM_DIV_21),
FILL_DWELL_COLUMN(RPM_DIV_22),
FILL_DWELL_COLUMN(RPM_DIV_23),
FILL_DWELL_COLUMN(RPM_DIV_24),
FILL_DWELL_COLUMN(RPM_DIV_25),
FILL_DWELL_COLUMN(RPM_DIV_26),
FILL_DWELL_COLUMN(RPM_DIV_27),
FILL_DWELL_COLUMN(RPM_DIV_28),
FILL_DWELL_COLUMN(RPM_DIV_29),
FILL_DWELL_COLUMN(RPM_DIV_30),
FILL_DWELL_COLUMN(RPM_DIV_31)
};
// the rpm lookup for the dwell table - contains rpm reference in timer 1 ticks (4us each) per 90* (between low res signals)
const PROGMEM uint16_t rpmTable[RPM_DIVS] = {RPM_TO_TMR1_TICKS(RPM_DIV_1),
RPM_TO_TMR1_TICKS(RPM_DIV_2),
RPM_TO_TMR1_TICKS(RPM_DIV_3),
RPM_TO_TMR1_TICKS(RPM_DIV_4),
RPM_TO_TMR1_TICKS(RPM_DIV_5),
RPM_TO_TMR1_TICKS(RPM_DIV_6),
RPM_TO_TMR1_TICKS(RPM_DIV_7),
RPM_TO_TMR1_TICKS(RPM_DIV_8),
RPM_TO_TMR1_TICKS(RPM_DIV_9),
RPM_TO_TMR1_TICKS(RPM_DIV_10),
RPM_TO_TMR1_TICKS(RPM_DIV_11),
RPM_TO_TMR1_TICKS(RPM_DIV_12),
RPM_TO_TMR1_TICKS(RPM_DIV_13),
RPM_TO_TMR1_TICKS(RPM_DIV_14),
RPM_TO_TMR1_TICKS(RPM_DIV_15),
RPM_TO_TMR1_TICKS(RPM_DIV_16),
RPM_TO_TMR1_TICKS(RPM_DIV_17),
RPM_TO_TMR1_TICKS(RPM_DIV_18),
RPM_TO_TMR1_TICKS(RPM_DIV_19),
RPM_TO_TMR1_TICKS(RPM_DIV_20),
RPM_TO_TMR1_TICKS(RPM_DIV_21),
RPM_TO_TMR1_TICKS(RPM_DIV_22),
RPM_TO_TMR1_TICKS(RPM_DIV_23),
RPM_TO_TMR1_TICKS(RPM_DIV_24),
RPM_TO_TMR1_TICKS(RPM_DIV_25),
RPM_TO_TMR1_TICKS(RPM_DIV_26),
RPM_TO_TMR1_TICKS(RPM_DIV_27),
RPM_TO_TMR1_TICKS(RPM_DIV_28),
RPM_TO_TMR1_TICKS(RPM_DIV_29),
RPM_TO_TMR1_TICKS(RPM_DIV_30),
RPM_TO_TMR1_TICKS(RPM_DIV_31)
};
const uint8_t PROGMEM accelComp[RPM_DIVS] = {CALC_ACCEL_COMP(RPM_DIV_1),
CALC_ACCEL_COMP(RPM_DIV_2),
CALC_ACCEL_COMP(RPM_DIV_3),
CALC_ACCEL_COMP(RPM_DIV_4),
CALC_ACCEL_COMP(RPM_DIV_5),
CALC_ACCEL_COMP(RPM_DIV_6),
CALC_ACCEL_COMP(RPM_DIV_7),
CALC_ACCEL_COMP(RPM_DIV_8),
CALC_ACCEL_COMP(RPM_DIV_9),
CALC_ACCEL_COMP(RPM_DIV_10),
CALC_ACCEL_COMP(RPM_DIV_11),
CALC_ACCEL_COMP(RPM_DIV_12),
CALC_ACCEL_COMP(RPM_DIV_13),
CALC_ACCEL_COMP(RPM_DIV_14),
CALC_ACCEL_COMP(RPM_DIV_15),
CALC_ACCEL_COMP(RPM_DIV_16),
CALC_ACCEL_COMP(RPM_DIV_17),
CALC_ACCEL_COMP(RPM_DIV_18),
CALC_ACCEL_COMP(RPM_DIV_19),
CALC_ACCEL_COMP(RPM_DIV_20),
CALC_ACCEL_COMP(RPM_DIV_21),
CALC_ACCEL_COMP(RPM_DIV_22),
CALC_ACCEL_COMP(RPM_DIV_23),
CALC_ACCEL_COMP(RPM_DIV_24),
CALC_ACCEL_COMP(RPM_DIV_25),
CALC_ACCEL_COMP(RPM_DIV_26),
CALC_ACCEL_COMP(RPM_DIV_27),
CALC_ACCEL_COMP(RPM_DIV_28),
CALC_ACCEL_COMP(RPM_DIV_29),
CALC_ACCEL_COMP(RPM_DIV_30),
CALC_ACCEL_COMP(RPM_DIV_31)
};
const uint16_t PROGMEM voltsTable[VOLT_DIVS] = { CALC_ADC_THOLD(7.5),
CALC_ADC_THOLD(8.5),
CALC_ADC_THOLD(9.5),
CALC_ADC_THOLD(10.5),
CALC_ADC_THOLD(11.5),
CALC_ADC_THOLD(12.5),
CALC_ADC_THOLD(13.5),
CALC_ADC_THOLD(14.5),
CALC_ADC_THOLD(15.5),
CALC_ADC_THOLD(16.5)
};
volatile int8_t sequenceIndex = -1; // if negative we've lost sequence, else pointer to the current cylinder in the sequencer[] struct
volatile boolean invertSeq; // invert sequence detection - when true the engine came to rest with the low res trigger in a gap (beam not broken)
volatile uint8_t hrDegrees = ENGINE_GEOMETRY; // the high resolution counter - counts down from 90 after each falling edge low res pulse
volatile int8_t saDegrees; // stored by the EST line interrupt this is a snapshot of most recent spark advance in degrees btdc
volatile uint8_t estEnabler; // count degrees while est is high inside high res isr - used to debounce est input
//volatile uint8_t dwellFlag;
//volatile uint8_t errCount; // for below error and lag statistics
//uint8_t errorDegs; // hrDegrees should be 0 when the low res TDC signal is caught - this tracks cumulative # of errors per last 10 logged messages
//uint8_t dwellLag; // difference between targeted dwell and when dwell actually started - cumulative over last 10 logged messages
#define ERROR_REVS 40 // match constant for resetting error tracking statistics - 4 counts = 1 crank revolution
//#define ERROR_LOGS 10 // match constant for resetting errCount and above statistics
volatile uint8_t lrSemaphoreRising; // triggers sequence detection in main loop and TDC semaphore
volatile uint8_t lrSemaphoreFalling; // for sequence detection when invertSeq = true
volatile uint16_t lrTimeHi; // low res falling edge timer - ticks between tdc signals
volatile uint8_t lrHi_overflow; // the timer1 overflow counter used for stall detection
volatile uint8_t tmr0Counter; // for internal timer use
volatile uint8_t tmr0Ticks; // for counting quarter seconds
volatile uint16_t tmr0Seconds; // for tracking seconds (engine runtime)
volatile uint8_t TIMSK0_restore; // for toggling the timer0 compare interrupt
// this array tracks the cylinder currently on its compression stroke
typedef struct {
int cylNo; // user readable cylinder no
volatile uint8_t *portAddr; // used as a pointer to the output registers so coil drivers can be controlled directly (faster than using digital[write/read]() arduino functions)
byte bitMask; // the bit mask of each output pin - see the atmega datasheet for more info on this and timers
} cylinders;
cylinders sequencer[NUM_CYLINDERS] = {
{1, &PORTD, B00010000}, // arduino d4
{8, &PORTB, B00000010}, // arduino d9
{4, &PORTB, B00001000}, // arduino d11
{3, &PORTD, B00100000}, // arduino d5
{6, &PORTB, B00000100}, // arduino d10
{5, &PORTD, B01000000}, // arduino d6
{7, &PORTD, B10000000}, // arduino d7
{2, &PORTB, B00010000}, // arduino d12
};
void setup() {
// configure misc output pins
pinMode(TACH_SIGNAL, OUTPUT);
#ifdef TACH_DRIVER
digitalWrite(TACH_SIGNAL, HIGH);
#endif
pinMode(ION_SIGNAL, OUTPUT);
// disable digital buffers on unused adc pins
bitSet (DIDR0, ADC2D); // disable digital buffer on A2
bitSet (DIDR0, ADC3D); // disable digital buffer on A3
bitSet (DIDR0, ADC4D); // disable digital buffer on A4
bitSet (DIDR0, ADC5D); // disable digital buffer on A5
// configure input pins and enable pullups
pinMode(OPTI_HI_RES, INPUT_PULLUP);
pinMode(OPTI_LO_RES, INPUT_PULLUP);
pinMode(PCM_EST, INPUT);
// setup adc input for voltage comparator
ADCSRA &= ~(bit (ADPS0) | bit (ADPS1) | bit (ADPS2)); // clear prescaler bits
ADCSRA |= bit (ADPS0) | bit (ADPS1) | bit (ADPS2); // prescaler 128 - "slowest" prescaler gives most accuracy
ADMUX = bit (REFS0) | (IGN_VSENSE & 0x07); // select AVcc for aref and select input port
// configure output pins
DDRD = DDRD | B11110000; // configure PD4 - PD7 (arduino d4 - d7) as outputs
DDRB = DDRB | B00111110; // configure PB1 - PB5 (arduino d9 - d12) as outputs
Serial.begin(115200);
Serial.print(F("diy-ltcc-"));
Serial.println(VERSION);
#ifdef DUMP_TABLES
dumpRPMTable();
dumpVoltsTable();
dumpDwellTable();
dumpAccelComp();
#endif
cli(); // disable interrupts
lrSemaphoreRising = ENGINE_GEOMETRY;
lrSemaphoreFalling = 0;
saDegrees = CRANKING_SPK_ADV;
// attach the high res interrupt to count crankshaft degrees
#ifdef BUILD_ENV_DPTI
attachInterrupt(digitalPinToInterrupt(OPTI_HI_RES), isr_hi_res, CHANGE);
#else
attachInterrupt(1, isr_hi_res, CHANGE);
#endif
// attach the EST line interrupt to service routine to trigger ignition
#ifdef BUILD_ENV_DPTI
attachInterrupt(digitalPinToInterrupt(PCM_EST), isr_est, FALLING);
#else
attachInterrupt(0, isr_est, FALLING);
#endif
/* // store the timer0 config register
TIMSK0_restore = TIMSK0;
// set timer0 compare match register A
OCR0A = 249;
// turn on CTC mode
TCCR0A |= (1 << WGM01);
// enable compare match interrupt
TIMSK0 |= (1 << OCIE0A); */
// configure timer1 for capture interrupt
TCCR1A = 0; // clear timer1 config registers
TCCR1B = 0;
//if ( digitalRead(OPTI_LO_RES) ) {
if (PINB & B00000001) {
TCCR1B &= ~(1 << ICES1); // set first capture on falling edge (see declaration of invertSeq variable for more info)
invertSeq = true;
} else {
TCCR1B |= (1 << ICES1); // set first capture on rising edge
invertSeq = false;
}
//TCCR1B |= (1<<CS12); // prescaler 256 - gives timer1 16 us resolution per tick - RPM formula 937500 / lrTimeHi
TCCR1B |= (1 << CS11) | (1 << CS10); // prescaler 64 - gives timer1 4us resolution per tick - RPM formula 3750000 / lrTimeHi
TCCR1B |= (1 << ICNC1); // enable noise canceller
lrHi_overflow = LR_OVERFLOW + 1; // set the timeout condition
TIMSK1 |= (1 << ICIE1) | (1 << TOIE1); // enable input capture and overflow interrupts
sei(); // enable global interrupts
bitSet (ADCSRA, ADSC); // request first adc conversion
}
void isr_est() {
// this isr is time critical, keep to minimum number of instructions here before sei() (enables interrupts)
// check the est debouncer
if (estEnabler > 2) {
// store the current spark advance
if (hrDegrees < MAX_SPK_ADV) {
*sequencer[sequenceIndex].portAddr &= ~sequencer[sequenceIndex].bitMask;
saDegrees = hrDegrees;
} else {
if (hrDegrees > ENGINE_GEOMETRY ) {
// ATDC spark timing with wraparound - hi res counted < 0 before low res signal (and / or EST) was caught
*sequencer[sequenceIndex].portAddr &= ~sequencer[sequenceIndex].bitMask;
saDegrees = -1; // may need to account for larger amount of wraparound ???
//Serial.print("*");
} else { // (hrDegrees < 90 (for v8) and > 46 (lt-1) )
// ATDC spark timing (hrDegrees < ENGINE_GEOMETRY and > MAX_SPK_ADV) fire coil on previous cylinder
if (sequenceIndex == 0) {
*sequencer[NUM_CYLINDERS - 1].portAddr &= ~sequencer[NUM_CYLINDERS - 1].bitMask;
} else {
*sequencer[sequenceIndex - 1].portAddr &= ~sequencer[sequenceIndex - 1].bitMask;
}
saDegrees = hrDegrees - ENGINE_GEOMETRY;
}
}
estEnabler = 0; // clear the est debouncer
/*#ifdef SERIAL_DEBUG
// puke out firing event info
sei();
Serial.print("[");
Serial.print(sequencer[sequenceIndex].cylNo);
Serial.print("@");
Serial.print(saDegrees);
Serial.println("]");
#endif*/
#ifdef TACH_DRIVER
sei();
// drive tach output line high
//*tachOutput[0].portAddr |= tachOutput[0].bitMask;
PORTC |= B00000001; // drive A0 high
#endif
}
}
void isr_hi_res() { // hi res change interrupt - counts crankshaft degrees
// this isr is time critical, keep to minimum number of instructions here
hrDegrees--;
// if PCM_EST line is high count degrees into estEnabler to debounce
uint8_t estState = PIND & B00000100; // read D2 state
if (estState) estEnabler++;
#ifdef STANDALONE
if ((hrDegrees < saDegrees) || (hrDegrees == saDegrees)) {
/*#ifdef SERIAL_DEBUG
if (*sequencer[sequenceIndex].portAddr & sequencer[sequenceIndex].bitMask) {
Serial.print("[");
Serial.print(sequencer[sequenceIndex].cylNo);
Serial.print("@");
Serial.print(hrDegrees);
Serial.println("]");
}
#endif*/
*sequencer[sequenceIndex].portAddr &= ~sequencer[sequenceIndex].bitMask;
}
#endif
#ifdef ION_TRIGGER
sei();
if (hrDegrees == ION_TRG_DEGREE)
PORTC |= B00000010; // drive A1 high
//PORTC &= ~B00000010; // drive A1 low
#endif
#ifdef INPUT_TEST
if (digitalRead(OPTI_HI_RES)) {
*sequencer[4].portAddr |= sequencer[4].bitMask; // drive #6 coil pin high
} else {
*sequencer[4].portAddr &= ~sequencer[4].bitMask; // drive #6 coil pin low
}
#endif
}
/*ISR(TIMER0_COMPA_vect) { // timer0 compare A interrupt
// 1ms interrupt
tmr0Counter++;
if (tmr0Counter == 249) {
tmr0Counter = 0;
tmr0Ticks ++;
}
if (tmr0Ticks == 4) {
tmr0Seconds++;
tmr0Ticks = 0;
}
#ifdef DEBUG_STREAM
static uint8_t toggle = 0;
if (toggle == 0) {
toggle = 1;
} else {
toggle = 0;
if (sequenceIndex != -1) {
for(int i=0; i<NUM_CYLINDERS; i++) {
// dump the state of the outputs - TODO this needs to be done raw - ascii is too much overhead
//if (*sequencer[i].portAddr & sequencer[i].bitMask) {
// Serial.print("*");
//} else {
// Serial.print(" ");
//}
}
//Serial.println(hrDegrees);
}
}
#endif
}*/
ISR(TIMER1_OVF_vect) { // timer1 overflow interrupt vector
lrHi_overflow++;
if ( lrHi_overflow >= LR_OVERFLOW ) {
// timeout on low res pulse - nothing here terribly time critical b/c engine has stalled
lrHi_overflow = LR_OVERFLOW + 1;
// for new version TIMER1_CAPT_vect where trigger edge stays at rising after sequence detection
if (PINB & B00000001) {
TCCR1B &= ~(1<<ICES1); // set first capture on falling edge (see declaration of invertSeq variable for more info)
invertSeq = true;
} else {
TCCR1B |= (1<<ICES1); // set first capture on rising edge
invertSeq = false;
}
/*// disable timer0 compare interrupt
TIMSK0 = TIMSK0_restore;
tmr0Seconds = 0;
tmr0Ticks = 0;*/
}
}
ISR(TIMER1_CAPT_vect) { // timer1 capture interrupt vector - measures timer1 ticks between low res rising edge (TDC) signals
// this isr is time critical, keep to minimum number of instructions here before sei() (enables interrupts)
if ( bit_is_set(TCCR1B, ICES1) ) { // triggered on rising edge of low res signal
#ifndef INPUT_TEST
lrSemaphoreRising = hrDegrees; // degree count @ tdc signal - for sequence detection and tdc semaphore
hrDegrees = ENGINE_GEOMETRY; // reset the degree counter
lrTimeHi = ICR1; // push the timer value into the semaphore variable
TCNT1 = 0; // reset the count register
if ( sequenceIndex > -1 ) {
sequenceIndex++; // increment the sequence index
if (sequenceIndex == NUM_CYLINDERS) {
sequenceIndex = 0;
}
}
#ifdef TACH_DRIVER
PORTC &= ~B00000001; // drive A0 low
#endif
# else
// input test functionality
*sequencer[1].portAddr |= sequencer[1].bitMask; // drive #8 coil pin high
Serial.print("hrDegRise=");
Serial.println(hrDegrees);
hrDegrees = ENGINE_GEOMETRY;
#endif
if (sequenceIndex < 0)
TCCR1B ^= _BV(ICES1); // toggle ICR edge select bit value so interrupt triggers on the other edge
} else { // triggered on falling edge
#ifndef INPUT_TEST
lrSemaphoreFalling = hrDegrees; // for sequence detection
#else
// input test functionality
*sequencer[1].portAddr &= ~sequencer[1].bitMask; // drive #8 coil pin low
Serial.print("hrDegFall=");
Serial.println(hrDegrees);
#endif
TCCR1B ^= _BV(ICES1); // toggle ICR edge select bit value so interrupt triggers on the other edge
}
}
void loop() {
#ifdef SERIAL_DEBUG
static bool initialized;
if ( ! initialized ) {
Serial.print("ready in: ");
Serial.print(millis());
Serial.println("ms");
initialized = true;
}
#endif
static uint8_t dwellDegrees; // current dwell requirement in degrees
static uint8_t tdcEvents; // number of firing events the dwell requirement is ahead of current compression cylinder
static uint8_t tdcEventsLast; // state machine
//uint8_t lrRising_l = 0; // local variable for low res degree count
uint8_t t1TIMSK_l = 0; // timer1 interrupt mask temporary storage
static uint16_t rpmTimeLast = 0; // for reference in getDwell lookup function
static uint8_t rpmIndex = 0; // for human readable rpm and dwell lookup hint // < NOT NEEDED !!!
static uint8_t voltsIndex = 0; // for voltage lookups and maxLookAhead[] in dwellCoils
static uint16_t rpmTimeHi = 0; // holds the most recent low-res interval
static uint16_t adcVoltsRaw; // ignition voltage in adc raw format
static uint16_t adcVoltsRawLast; // for voltage lookup hint
static uint16_t adcMapRaw; // map sensor input in adc raw format
static uint16_t adcMapRawLast; // map sensor hysteresis
static uint8_t tdcCount; // for tracking engine revolutions (4 tdc ticks = 1 rev)
static boolean accelComp = false; // flag for accel compensation
static uint16_t accelCompEnd = 0; // mills() value (was timer2 tick) to set accelComp = false
//static uint16_t adcFrequency = 0; // for profiling adc conversion frequency
static uint8_t dwellReq = 0; // set to request dwell lookup on next cycle of loop
static uint8_t revCount_maj = 0; // for keeping track of crank revolutions 0x01 = 0xff revolutions
static uint8_t revCount_min = 0; // minor variable (0-255 revs) for above
// begin main branch
if ( sequenceIndex < 0 ) {
// we haven't detected sequence yet so do that first
int8_t nextCyl = -1;
if (invertSeq) { // engine stopped in a low res trigger gap (beam not broken)
if ((lrSemaphoreRising != ENGINE_GEOMETRY) && (invertSeq)) { // we have a rising edge degree count
#ifdef SERIAL_DEBUG
Serial.print("detect inv, degRising=");
Serial.print(lrSemaphoreRising);
Serial.print(", degFalling=");
Serial.println(lrSemaphoreFalling);
#endif
nextCyl = detectSeqInverse(lrSemaphoreRising, lrSemaphoreFalling);
if (nextCyl < 0) {
// clear the falling semaphore and invert bit to detect normally on next falling edge
invertSeq = false;
lrSemaphoreFalling = 0;
}
lrHi_overflow = 0; // clear the stall timer
}
} else {
// engine stopped on a low res trigger (beam broken)
if ((lrSemaphoreFalling != 0) && (! invertSeq)) { // we have a falling edge degree count
#ifdef SERIAL_DEBUG
Serial.print("detect norm, degFalling=");
Serial.println(lrSemaphoreFalling);
#endif
nextCyl = detectSequence(lrSemaphoreFalling);
if (nextCyl < 0) lrSemaphoreFalling = 0; // clear the falling semaphore and wait for next falling edge
lrHi_overflow = 0; // clear the stall timer
}
}
// end sequence detection
if (nextCyl > -1) {
// sequence caught
sequenceIndex = nextCyl;
// turn off pin 13 status led
PORTB &= ~B00100000;
// reset running time
//tmr0Seconds = 0;
//tmr0Ticks = 0;
//tmr0Counter = 0;
// enable timer0 compare interrupt
//TIMSK0 |= (1 << OCIE0A);
#ifdef SERIAL_DEBUG
Serial.println("sequenced");
#endif
}
// branch above only runs when not sequenced
} else {
// branch below runs continuously once sequence is caught and engine remains running
// look at this as often as possible - starvation would cause missed firing events
// TODO - may need to profile main loop and determine minimum window below based on maximum
// theoretical (or practical) rpm
//boolean dwellInit = false;
// hrDegrees is reset to ENGINE_GEOMETRY (90) at each tdc interrupt and counts down until next tdc interrupt
if (hrDegrees > dwellDegrees - DWELL_BEGIN_WINDOW && hrDegrees < dwellDegrees + 1) {
//dwellInit = dwellCoils(dwellDegrees, tdcEvents, sequenceIndex);
dwellCoils(dwellDegrees, tdcEvents, sequenceIndex);
} else if (dwellDegrees < DWELL_BEGIN_WINDOW && hrDegrees < dwellDegrees + DWELL_BEGIN_WINDOW) {
//dwellInit = dwellCoils(dwellDegrees, tdcEvents, sequenceIndex);
dwellCoils(dwellDegrees, tdcEvents, sequenceIndex);
}
/*if (tdcEvents) {
dwellFlag = true;
} else {
dwellFlag = false;
}*/
}
// main loop scheduling tree - highest priority at top lowest at bottom
// open to "cleaner code" suggestions on this if anyone has them
if (lrSemaphoreRising != ENGINE_GEOMETRY) {
// low res tdc semaphore caught
t1TIMSK_l = TIMSK1; // store timer1 interrupt config register
TIMSK1 = 0; // disable timer1 interrupts to ensure atomicity - begin critical section
//lrRising_l = lrSemaphoreRising; // copy to local variable
lrSemaphoreRising = ENGINE_GEOMETRY; // reset the semaphore
rpmTimeHi = lrTimeHi; // store the timer value
TIMSK1 = t1TIMSK_l; // restore timer1 interrupts - end critical section
tdcCount++;
dwellReq = 1;
/*// accumulate error degrees
if (lrRising_l > ENGINE_GEOMETRY) {
// wraparound
errorDegs += 255 - lrRising_l;
} else {
errorDegs += lrRising_l;
}*/
#ifdef ION_TRIGGER
//PORTC |= B00000010; // drive A1 high
PORTC &= ~B00000010; // drive A1 low
#endif
#ifdef LOGGING
if (rpmTimeHi > RUN_RPM) {
uartDatastreamMsg(rpmTimeHi, tdcEvents, dwellDegrees, adcMapRaw, adcVoltsRaw, tdcCount, errorDegs, dwellLag);
//adcFrequency = 0;
}
#endif
} else {
// no tdc semaphore so do lower priority stuff
if ( dwellReq ) { // try to determine dwell once per combustion event
dwellReq = 0;
dwellDegrees = getDwell(saDegrees, rpmTimeHi, rpmTimeLast, rpmIndex, voltsIndex, accelComp, tdcEvents);
if (tdcEvents > tdcEventsLast) {
// increasing dwell requirement, dwell current coil now
//boolean dwellInit = false;
//dwellInit = dwellCoils(dwellDegrees, tdcEventsLast, sequenceIndex);
dwellCoils(dwellDegrees, tdcEventsLast, sequenceIndex);
}
tdcEventsLast = tdcEvents;
/*#ifdef SERIAL_DEBUG
// puke out dwell lookup results
Serial.print(tdcEvents);
Serial.print(".");
Serial.print(dwellDegrees);
Serial.print(" ");
Serial.println(hrDegrees);
#endif*/
} else {
// do other low priority stuff
// check if the ADC conversion in process bit has been cleared
if (bit_is_clear(ADCSRA, ADSC)) {
uint8_t adMux = ADMUX & B00000111;
//adcFrequency++;
// TODO possibly convert this to a switch tree so we can also read mcu internal temperature ???
if ( adMux == IGN_VSENSE ) {
adcVoltsRaw = ADC;
ADMUX = bit (REFS0) | (MAP_VSENSE & B00000111); // toggle to reading the map input
voltsIndex = getVoltageIndex(adcVoltsRaw, adcVoltsRawLast, voltsIndex);
adcVoltsRawLast = adcVoltsRaw;
} else {
// TODO the accelComp logic could probably use improvement
if (accelComp) {
if (accelCompEnd < millis()) {
accelComp = false;
/*#ifdef SERIAL_DEBUG
//Serial.print("-AC");
//Serial.println(millis());
Serial.print("ending accelComp map=");
Serial.println(adcMapRawLast);
#endif*/
}
}
adcMapRaw = ADC;
if (adcMapRaw > MAP_MIN_THOLD && ! accelComp) {
if (adcMapRaw > (adcMapRawLast + MAP_DELTA) && revCount_maj > 2) {
accelCompEnd = millis();
/*#ifdef SERIAL_DEBUG
Serial.print("+AC");
Serial.println(accelCompEnd);
#endif*/
accelCompEnd += MAP_DECAY_TIME;
accelComp = true;
/*#ifdef SERIAL_DEBUG
// puke out map data
Serial.print("AC map=");
Serial.print(adcMapRaw);
Serial.print(" maplast=");
Serial.println(adcMapRawLast);
#endif*/
}
}
adcMapRawLast = adcMapRaw; // copy current map to hysteresis variable
ADMUX = bit (REFS0) | (IGN_VSENSE & B00000111); // toggle to reading the voltage sense input
}
bitSet (ADCSRA, ADSC); // request another conversion
} else {
// update / clear error statistics every N revolutions
if (tdcCount >= ERROR_REVS) {
#ifdef LOGGING
if (rpmTimeHi < RUN_RPM) {
uartDatastreamMsg(rpmTimeHi, tdcEvents, dwellDegrees, adcMapRaw, adcVoltsRaw, tdcCount, errorDegs, dwellLag);
//adcFrequency = 0;
}
#endif
tdcCount = 0;
//errorDegs = 0;
//dwellLag = 0;
revCount_min++;
if (revCount_min == 255) revCount_maj++;
} else {
// check for stall condition
uint8_t pin13State = PORTB & B00100000; // read pin 13 state
if (lrHi_overflow >= LR_OVERFLOW && ! pin13State) {
#ifdef SERIAL_DEBUG
Serial.println("stalled");
#endif
// stalled, reset everything
hrDegrees = ENGINE_GEOMETRY;
sequenceIndex = -1;
lrSemaphoreRising = ENGINE_GEOMETRY;
lrSemaphoreFalling = 0;
saDegrees = CRANKING_SPK_ADV;
// set pin 13 high - yellow status led on solid
PORTB |= B00100000;
tdcCount = 0;
dwellDegrees = getDwell(saDegrees, pgm_read_word(&rpmTable[0]) + 1, rpmTimeLast, rpmIndex, voltsIndex, false, tdcEvents);
//Serial.println(dwellDegrees);
}
}
}
}
}
// end of main loop
}
int8_t detectSeqInverse(uint8_t risingDegrees, uint8_t fallingDegrees) {
// note: returns the index of sequencer array, not cylNo
uint8_t seqDegrees = fallingDegrees - risingDegrees;
if ((seqDegrees > 82) && (seqDegrees < 90)) {
return -1;
} else {
// 46 deg of broken beam #8
if ((seqDegrees > 42) && (seqDegrees < 50)) {
// next cyl #8
return 1;
// 56 deg of broken beam #2
} else if ((seqDegrees > 52) && (seqDegrees < 60)) {
// next cyl #2
return 7;
// 66 deg of broken beam #5
} else if ((seqDegrees > 62) && (seqDegrees < 70)) {
// next cyl #5
return 5;
// 76 deg of broken beam #3
} else if ((seqDegrees > 72) && (seqDegrees < 80)) {
// next cyl #3
return 3;
}
}
return 0;
}
int8_t detectSequence(uint8_t seqDegrees) {
// note: returns the index of sequencer array, not cylNo
if ((seqDegrees > 82) && (seqDegrees < 90)) {
return -1;
} else {
// 90 - 14 = 76 #4
if ((seqDegrees > 72) && (seqDegrees < 80)) {
// next cyl #4
return 2;
// 90 - 24 = 66 #6
} else if ((seqDegrees > 62) && (seqDegrees < 70)) {
// next cyl #6
return 4;
// 90 - 34 = 56 #7
} else if ((seqDegrees > 52) && (seqDegrees < 60)) {
// next cyl #7
return 6;
// 90 - 44 = 46 #1
} else if ((seqDegrees > 42) && (seqDegrees < 50)) {
// next cyl #1
return 0;
}
}
return 0;
}
//boolean dwellCoils(uint8_t dwellDeg_l, uint8_t tdcEvents_l, int8_t seqIndex_l) {
void dwellCoils(uint8_t dwellDeg_l, uint8_t tdcEvents_l, int8_t seqIndex_l) {
boolean dwelling = false;
uint8_t cylIdx = 0;
if (tdcEvents_l) {
cylIdx = seqIndex_l + tdcEvents_l;
//if (int8_t (seqIndex_l + tdcEvents_l >= NUM_CYLINDERS))
if (cylIdx >= NUM_CYLINDERS)
cylIdx -= NUM_CYLINDERS;
} else {
cylIdx = seqIndex_l;
}
dwelling = *sequencer[cylIdx].portAddr & sequencer[cylIdx].bitMask;
if (! dwelling) {
// begin dwelling coil
*sequencer[cylIdx].portAddr |= sequencer[cylIdx].bitMask;
/*int8_t dwellLag_t = dwellDeg_l - hrDegrees;
//if (dwellLag_t < 0) dwellLag_t += ENGINE_GEOMETRY; // this one is all wrong...
if (dwellLag_t > 0) dwellLag += dwellLag_t;*/
/*#ifdef SERIAL_DEBUG
// puke out the dwell events
Serial.print("d");
Serial.print(sequencer[cylIdx].cylNo);
Serial.print("@");
Serial.print(sequencer[seqIndex_l].cylNo);
Serial.print(".");
Serial.println(hrDegrees);
#endif*/
//return true;
}
//return false;
}
uint8_t getDwell(int8_t saDegrees_l, uint16_t rpmTicks, uint16_t &rpmTicksLast, uint8_t &dwellIndex_l,
uint8_t voltIndex_l, boolean accelAdder, uint8_t &tdcEvents_l) {
// lookup the degrees of dwell required for the current rpm from the dwell table
boolean dwellMatch = false;
uint8_t accelCompAdder = 0;
if ( rpmTicksLast == 0 ) {
// no hint to work from, iterate the whole array
for (uint8_t indexTmp = 0; indexTmp < RPM_DIVS; indexTmp++ ) {
if (pgm_read_word(&rpmTable[indexTmp]) < rpmTicks) {
if (indexTmp > 0) {
dwellIndex_l = indexTmp - 1;
}
dwellMatch = true;
break;
}
}
if ( ! dwellMatch )
dwellIndex_l = RPM_DIVS - 1;
} else {
// use the rpmTicksLast hint to look near last known rpm and save some iterations / instructions
if ( rpmTicksLast < rpmTicks ) {
// engine is decelerating
if (dwellIndex_l > 0) {
if (rpmTicks > pgm_read_word(&rpmTable[dwellIndex_l - 1])) {
while ( ! dwellMatch ) {
dwellIndex_l--;
if (dwellIndex_l == 0) {
dwellMatch = true;
break;
}
if (rpmTicks < pgm_read_word(&rpmTable[dwellIndex_l])) {
dwellMatch = true;
break;
}
}
}
}
} else if (rpmTicksLast > rpmTicks) {
// engine is accelerating
if (dwellIndex_l < (RPM_DIVS - 1)) {
if (rpmTicks < pgm_read_word(&rpmTable[dwellIndex_l + 1])) {
while ( ! dwellMatch ) {
dwellIndex_l++;
if (dwellIndex_l == RPM_DIVS) {
dwellIndex_l = RPM_DIVS - 1;
dwellMatch = true;
break;
}
if (pgm_read_word(&rpmTable[dwellIndex_l]) < rpmTicks) {
dwellMatch = true;
dwellIndex_l--;
break;
}
}
}
}
// add some accel compensation
// for acceleration conditions (hint taken from megasquirt sequencer coil selection http://www.megamanual.com/seq/coils.htm)
if (accelAdder)
accelCompAdder = pgm_read_byte(&accelComp[dwellIndex_l]);
}
}
uint16_t dwellBeginTotal = saDegrees_l + pgm_read_word(&dwellTable[dwellIndex_l][voltIndex_l]) + accelCompAdder;
tdcEvents_l = dwellBeginTotal / ENGINE_GEOMETRY;
rpmTicksLast = rpmTicks;
return dwellBeginTotal - (tdcEvents_l * ENGINE_GEOMETRY);
}
uint8_t getVoltageIndex(uint16_t adcValue, uint16_t adcValueLast, uint8_t voltsIndex) {
// lookup the raw adc value in the voltsTable and return index
boolean voltsMatch = false;
if ( adcValueLast == 0 ) {
// no hint to work from, iterate the whole array
for (uint8_t indexTmp = 0; indexTmp < VOLT_DIVS; indexTmp++ ) {
if (pgm_read_word(&voltsTable[indexTmp]) > adcValue) {
if (indexTmp > 0) {
voltsIndex = indexTmp - 1;
}
voltsMatch = true;
break;
}
}
if ( ! voltsMatch )
voltsIndex = VOLT_DIVS - 1;
} else {
// use the adcValueLast hint to look near last known adc value and save some iterations / instructions
if ( adcValueLast > adcValue ) {
// voltage decreased
if (voltsIndex > 0) {
if (adcValue < pgm_read_word(&voltsTable[voltsIndex - 1])) {
while ( ! voltsMatch ) {
voltsIndex--;
if (voltsIndex == 0) {
voltsMatch = true;
break;
}
if (adcValue > pgm_read_word(&voltsTable[voltsIndex])) {
voltsMatch = true;
voltsIndex++;
break;
}
}
}
}
} else if (adcValueLast < adcValue) {
// voltage increased
if (voltsIndex < (VOLT_DIVS - 1)) {
if (adcValue > pgm_read_word(&voltsTable[voltsIndex + 1])) {
while ( ! voltsMatch ) {
voltsIndex++;
if (voltsIndex == VOLT_DIVS) {
voltsIndex = VOLT_DIVS - 1;
voltsMatch = true;
break;
}
if (pgm_read_word(&voltsTable[voltsIndex]) > adcValue) {
voltsMatch = true;
break;
}
}
}
}
}
}
return voltsIndex;
}
#ifdef LOGGING
void uartDatastreamMsg(uint16_t rpmTimeHi_l, uint8_t tdcEvents_l, uint8_t dwellDegrees_l,
uint16_t adcMapRaw_l, uint16_t adcVoltsRaw_l, uint8_t tdcCount_l, uint8_t errorDegs_l, uint8_t dwellLag_l) {
// TODO should convert this to output raw data - ascii may be too much overhead
uint8_t cylNo;
if ( sequenceIndex < 0 ) {
cylNo = 0;
} else {
cylNo = sequencer[sequenceIndex].cylNo;
}
Serial.print("R");
Serial.print(rpmTimeHi_l);
Serial.print(":A");
Serial.print(saDegrees);
Serial.print(":D");
Serial.print(tdcEvents_l);
Serial.print(".");
Serial.print(dwellDegrees_l);
Serial.print(":C");
Serial.print(cylNo);
Serial.print(":M");
Serial.print(adcMapRaw_l);
Serial.print(":V");
Serial.print(adcVoltsRaw_l);
Serial.print(":T");
Serial.print(tdcCount_l);
Serial.print(":E");
Serial.print(errorDegs_l);
Serial.print(":L");
Serial.print(dwellLag_l);
Serial.print(":S");
Serial.println(tmr0Seconds);
/*Serial.print(":F");
Serial.println(adcFreq_l);*/
}
#endif
#ifdef DUMP_TABLES
void dumpRPMTable() {
// dump rpmTable to uart
Serial.println("dumping uint16_t rpmTable[RPM_DIVS] PROGMEM = {");
Serial.print(" ");
char str[30];
for (int i = 0; i < RPM_DIVS; i++) {
//Serial.print(uintToStr(pgm_read_word(&rpmTable[i]), str));
Serial.print(pgm_read_word(&rpmTable[i]));
if (i < RPM_DIVS - 1) Serial.print(", ");
}
Serial.println(" };");
Serial.println("");
}
void dumpVoltsTable() {
// dump voltsTable[]
Serial.print("dumping uint16_t voltsTable[VOLT_DIVS] PROGMEM = { ");
for (int i = 0; i < VOLT_DIVS; i++) {
Serial.print(pgm_read_word(&voltsTable[i]));
if (i < VOLT_DIVS - 1) Serial.print(", ");
}
Serial.println(" };");
}
void dumpDwellTable() {
// print out the dwell table to uart
Serial.println("dumping uint16_t dwellTable[RPM_DIVS][VOLT_DIVS] PROGMEM = {");
Serial.print(" { ");
for (int v = 0; v < VOLT_DIVS; v++) {
for (int i = 0; i < RPM_DIVS; i++) {
Serial.print(pgm_read_word(&dwellTable[i][v]));
if (i < RPM_DIVS - 1) Serial.print(", ");
}
if (v == VOLT_DIVS - 1) {
Serial.println(" }");
Serial.println("};");
Serial.println("");
} else {
Serial.println(" },");
Serial.print(" { ");
}
}
}
void dumpAccelComp() {
// compensate dwell adder for voltage and convert to degrees
Serial.print("dumping uint8_t accelComp[RPM_DIVS] PROGMEM = { ");
for (int i = 0; i < RPM_DIVS; i++) {
//accelComp[i] = uint8_t ((ACCEL_COMP * 1000.0) / float (pgm_read_word(&rpmTable[i]) / 90) + 0.5);
Serial.print(pgm_read_byte(&accelComp[i]));
if (i < RPM_DIVS - 1) Serial.print(", ");
}
Serial.println(" };");
}
#endif
#ifdef DUMP_TABLES //|| SERIAL_DEBUG || SIMULATE
char * uintToStr( const uint32_t num, char *str )
{
uint8_t i = 0;
uint32_t n = num;
do
i++;
while ( n /= 10 );
str[i] = '\0';
n = num;
do
str[--i] = ( n % 10 ) + '0';
while ( n /= 10 );
return str;
}
#endif
#ifdef SERIAL_DEBUG
void cylInfoUart(uint16_t dwellDeg, uint16_t rpmTicks) {
Serial.print("cyl=");
if (sequenceIndex < 0) {
Serial.println(sequenceIndex);
} else {
Serial.println(sequencer[sequenceIndex].cylNo);
}
Serial.print("sa=");
Serial.println(saDegrees);
Serial.print("dwl=");
Serial.println(dwellDeg);
Serial.print("lri=");
//Serial.println(uintToStr(rpmTimeHi, str));
Serial.println(rpmTicks);
Serial.print("deg=");
Serial.println(lrSemaphoreRising); // TODO need to pass this variable in now
}
#endif
You can’t perform that action at this time.