/************************************************************************* ************************************************************************** ** M E G A S Q U I R T II - 2 0 0 4 - V1.000 ** ** (C) 2003 - B. A. Bowling And A. C. Grippo ** ** This header must appear on all derivatives of this code. ** *************************************************************************** **************************************************************************/ /************************************************************************* ************************************************************************** ** GCC Port ** ** (C) 2004,2005 - Philip L Johnson ** ** This header must appear on all derivatives of this code. ** *************************************************************************** **************************************************************************/ /*------------------------ Version 1.000 This version has all the basic capabilities of MS, but much higher precision - in the usec range. It adds larger tuning tables (12x12), WBO2, IAC, dual tables, and ignition control which assumes 1 coil and a mechanical distributor to distribute spark or EDIS for multicoil wasted spark. Version 1.100 Made serial comms more robust: added one word read, write + verify commands, write to ram inhibit when overrun detection, receiver timeout check. Version 1.200 Fixed various problems with trigger offsets ATDC (charge time can occur after input capture at very low rpms and other related ignition timing problems.) Version 1.300 Changed tps, tpsdot units to % x 10; added accel tailoff algorithm. Version 2.000 This version works with the CAN v3 pcb and has a template for CAN comms. Additional changes: increased input area to 4 x 512 byte blocks; inhibit coil charge 2 sec after turning ign key on; changed ports for FP, Idle (but connect to same pins on motherboard); knock ADC input in Vx100; all unused ports initialized as inputs. Made IACstart, PrimeP, CWU,CWH, AWEV,AWC,asecount ints instead of bytes. PrimeP,AWEV,AWC made function of initial coolant temperature. Expanded IAC control options for cranking plus switch to time based after start; added pulse tolerance inputs for cranking and after start; IAC option(5) to always enable stepper motor chip for 15 minutes after start, then revert to option 2. Version 2.100 Added crank trigger mode.Ignition fires with only mech adv during cranking; added kpa to display outputs. Version 2.200 Modified/ added ADC averaging: provided lag filter averaging for all ADC inputs (also for rpm). Relocated VE tables in anticipation of Block Learn Mode. Version 2.300 Improved ego closed loop correction algorithms; flex fuel correction for alternative fuels; generic spare output ports;fixed logic error for IdleCtl=5; added fix to maintain coherence of inputs when changed thru serial port. Version 2.310, 2.320, 2.330, 2340, 2.350, 2.360 Fixed bug in ASE logic;set PW=0 in RevLimit mode;gcc compatibility changes; fixed bug in spare port hysteresis; changed some defaults; fixed bug in masking of TimerIn interrupts for dt/2; limited pw_open in case of battery spikes. Fixed stepper motor bug. Version 2.400 Added automatic mixture control;mod to AE;expanded warmup inputs; moved DoOnStack to permanent ram;added option for 2 NBs;added knock retard based on ADC7 sensor. Version 2.500 Added wheel decoding; added simple AE taper based on rpm;added time,% parameters to control tach input interrupt masking; 5 sec min for persistence of AS pulse tolerance; extended pulse tol range from 100 to 255. Version 2.510, 2.520, 2.530, 2.540 Added fix to extend rpm range for wheel decoding; fixed AMC logic. Version 2.600, 2.610, 2.620, 2.630, 2.640, 2.660, 2.670, 2.671, 2.672, 2.681-2.687 Added small model multi-page memory; added X-tau transient compensation; spark retard as function of manifold air temperature; added table fuel corrections for air density and barometric pressure; added spk retard correction for flex fuel; added injector test mode. Fixed bug in table burner; added ego volt outputs; shortened serial ISR time to expand wheel decoder range. Provided input for pwm stepper freq. Added check for serial overrun flag. Limit baro to 80-120 kPa for option 1. Put COP timeout in main loop. Made NB AFRtarget Vx100 in code. Reduced default tau table. Fixed burn in Inj Test mode. Added wheel decode improvements to check for lost synch, individual tooth timing correction.Omit check for xtra/ missing tach pulse if accelerating/ decelerating. Omit immed. f/p off. RPM limit for AMC burns. Made fixes to improve IAC performance. Version 2.700 Added dual spark outputs; allow alpha-N at lo rpm or at hi rpm. Version 2.800 Added MAF capability, including MAP/ MAF blend, 3-port selection for maf input; added option to multiply pw eq x AFRStoich / AFRtgt[map,rpm]; added option to use map/baro as index in ve,spk,afr tables. Set pw to 0 when ve = 0 to avoid any fuel dribble. Added option for wasted spark/ COP (requires external hw). For PredOpt=2, use 1st deriv ign prediction if slow rate of change. Allow tau=0. Fixed code to make all longs atomic. Put in AMCdve logic. Version 2.810, 2.820-2.823, 2.830-2.832, 2.840, 2.850, 2.860-2.864 Removed option for wasted spark/ COP with router chip (will be moved to router board). Added 2 cyl cop/ 4 cyl wasted spark using cam sync and/or toothed wheel. Changes to make starting faster.Shortened cpu time in TimerClock. At startup set IAC step to crankpos instead of fully open. Replaced derivative next tach predi- ction algorithms with alpha, beta, gamma prediction filter. Added serial/ CAN code to interface between MT and auxilliary boards. Improved serial reliability. Added option for dual spark cam inputs for 2 cyl. Scaled MAT-based spark retard by rpm. Put kpaix into outpc. Provided spark offset to allow odd-fire for all dual output spark options. Provided user input for minimum step size for IAC motor moves. Provided option to do error check of tach synch even with accel/decel. Fixed bug in kpaix calculation. Added fuel,spark,Idle adjustments for use with GPIO board. Fixed problem with trig offset in dual spk mode. Added serial reconnect for all cases + reduced timeout from 10 to 5 sec. Fixed oddang/dual spk error. Added more CAN code for compatibility with GPIO board. Added spare variable counter for detecting extra(++)/ missing(--) tach or tooth pulses. Improved reliability of overdwell detection logic.Fixed/upgraded CAN comms for gpio board.Added dual spark options 7,8 = options 2,3 with crank synch on 2nd input. Version 2.870-2.871 Increased threshhold for dwell accel correction to keep dwell more stable. Put delay in for VR sensor and spark driver delays. Put loop for serial table ram vs flash verification in main loop. Removed board_id_type array and made mycan_id an input variable. Merged canvar_blkptr into regular tables structure. Fixed timing of injector led during priming pulse. Added capability of flash table updates to GPIO boards via serial and CAN - compatible with gpiov1.041. Version 2.872-2.873 Moved map sampling, pw calc and actual injection(s) to each tach pulse + InjStart deg. Added asynchronous tpsdot sample rate input. Simplified, improved x-tau algorithm. Added impulse pw which is added to normal pw if pin E0 LOW (N2O trigger) or every impulseSec, if impulseSec > 0. The latter can be turned on/off from PC and can, in conjunction with O2 sensor,be used to tune X-Tau parameters. Removed decel enleanment from gammae. rpm > 0 required for cranking. Refined X-Tau to handle dry port at startup, re- start, and re-synch. On re-synch, inject on 1st tach pulse rather than waiting Divider pulses. Cranking PW now goes thru topen, batcor, flex fuel and x-tau calculations. Fixed asecount logic so warmup light doesn't come on incorrectly. Added map trending to predict significant change in map faster. Added independent x-tau loops for each injection bank. Made Tau long variable to handle warmup and cold AE. Replaced map with kpa for knock detect, ego corr; this allows these algorithms to also work with maf and/or alphaN. Fixed bug in dual spark ign ISR. Version 2.88, 2.881-2.889 Fixed problem with prime pulse and extended max length to seconds. Multiplied alpha_map by baro/1000 to emulate change of alpha_map with altitude. Added ECU type input to prevent damaging components by a default configuration incompatible with the hardware. Also cut dwell timeout to 0.3 sec to reduce chance of overdwell. Improved dual spark logic to prevent missed ign outputs. Added barocor and aircor correction to crank pulsewidth. Default warmup x-tau % values reduced to avoid low rpm oscillation. Fixed ASE if start at rpm > crank_rpm. Fixed N2O enrichment calculation to be % of time between tach pulses. Fixed bugs causing spark time tooth offset with 1,2 cylinder wheel. Changed wheel decode and wasted/COP Dual Spark ignition into a tooth(angle) based system to improve accuracy of spark timimng. The fractional excess of tooth is the only part that remains time based. Moved Ign_TimerIn ISR to page 3D. Update altcount during cranking so will keep injector banks in synch when come out of cranking (only significant with cam sync). Fixed tach output logic for microsquirt. Made rev lim spk retard uchar, so max of 25.5 deg. Fixed injector test mode. Fixed rpm calc for 2 cyl dual spark, M-0 wheel with crank synch. Added option for spark retard while N2O turned on. Fixed bug with tpsWOT and altcount. Prevented IAC from going to cranking pos on missed VR resynchs. Fixed x-tau sum reset bug. Locked out wheel decode when EDIS selected. Added spark adjustment vs baro for alphaN. Decreased dwell timeout at lost synch to 20 ms. Set up ignition as outputs = 0 rather than inputs with pullups, since this can cause Ign On when power up after a b/load, before setting ECU type. Version 2.890 Fixed problem with rejecting noise (extra tooth) in missing tooth gap. Set PulseTol to cover manual as well as auto resynch case. -----------------------*/ #include "hcs12def.h" /* common defines and macros */ #include "flash.h" /* flashburner defines, structures */ #include //#define CAN_TEST #ifdef GCC_BUILD #define FAR_TEXT1_ATTR __attribute__ ((far)) __attribute__ ((section (".text1"))) #define TEXT1_ATTR __attribute__ ((section (".text1"))) #define EEPROM_ATTR __attribute__ ((section (".eeprom"))) #define INTERRUPT #define POST_INTERRUPT __attribute__((interrupt)) #define ENABLE_INTERRUPTS __asm__ __volatile__ ("cli"); #define DISABLE_INTERRUPTS __asm__ __volatile__ ("sei"); #define NEAR #define VECT_ATTR __attribute__ ((section (".vectors"))) extern void _start(void); /* Startup routine */ #else #define FAR_TEXT1_ATTR #define TEXT1_ATTR #define EEPROM_ATTR #define INTERRUPT interrupt #define POST_INTERRUPT #define ENABLE_INTERRUPTS asm cli; #define DISABLE_INTERRUPTS asm sei; #define NEAR near #define VECT_ATTR @0xFF80 #pragma CODE_SEG NON_BANKED /* Interrupt section for this module. Placement will be in NON_BANKED area. */ extern void near _Startup(void); /* Startup routine */ #endif INTERRUPT void UnimplementedISR(void) POST_INTERRUPT; INTERRUPT void ISR_Ign_TimerIn(void) POST_INTERRUPT; INTERRUPT void ISR_Ign_TimerOut(void) POST_INTERRUPT; INTERRUPT void ISR_Inj1_TimerOut(void) POST_INTERRUPT; INTERRUPT void ISR_Inj2_TimerOut(void) POST_INTERRUPT; INTERRUPT void ISR_TimerOverflow(void) POST_INTERRUPT; INTERRUPT void ISR_Timer_Clock(void) POST_INTERRUPT; INTERRUPT void ISR_SCI_Comm(void) POST_INTERRUPT; INTERRUPT void CanTxIsr(void) POST_INTERRUPT; INTERRUPT void CanRxIsr(void) POST_INTERRUPT; void ign_reset(void); typedef void (* NEAR tIsrFunc)(void); const tIsrFunc _vect[] VECT_ATTR = { /* Interrupt table */ UnimplementedISR, /* vector 63 */ UnimplementedISR, /* vector 62 */ UnimplementedISR, /* vector 61 */ UnimplementedISR, /* vector 60 */ UnimplementedISR, /* vector 59 */ UnimplementedISR, /* vector 58 */ UnimplementedISR, /* vector 57 */ UnimplementedISR, /* vector 56 */ UnimplementedISR, /* vector 55 */ UnimplementedISR, /* vector 54 */ UnimplementedISR, /* vector 53 */ UnimplementedISR, /* vector 52 */ UnimplementedISR, /* vector 51 */ UnimplementedISR, /* vector 50 */ UnimplementedISR, /* vector 49 */ UnimplementedISR, /* vector 48 */ UnimplementedISR, /* vector 47 */ UnimplementedISR, /* vector 46 */ UnimplementedISR, /* vector 45 */ UnimplementedISR, /* vector 44 */ UnimplementedISR, /* vector 43 */ UnimplementedISR, /* vector 42 */ UnimplementedISR, /* vector 41 */ UnimplementedISR, /* vector 40 */ CanTxIsr, /* vector 39 */ CanRxIsr, /* vector 38 */ CanRxIsr, /* vector 37 */ UnimplementedISR, /* vector 36 */ UnimplementedISR, /* vector 35 */ UnimplementedISR, /* vector 34 */ UnimplementedISR, /* vector 33 */ UnimplementedISR, /* vector 32 */ UnimplementedISR, /* vector 31 */ UnimplementedISR, /* vector 30 */ UnimplementedISR, /* vector 29 */ UnimplementedISR, /* vector 28 */ UnimplementedISR, /* vector 27 */ UnimplementedISR, /* vector 26 */ UnimplementedISR, /* vector 25 */ UnimplementedISR, /* vector 24 */ UnimplementedISR, /* vector 23 */ UnimplementedISR, /* vector 22 */ UnimplementedISR, /* vector 21 */ ISR_SCI_Comm, /* vector 20 */ UnimplementedISR, /* vector 19 */ UnimplementedISR, /* vector 18 */ UnimplementedISR, /* vector 17 */ ISR_TimerOverflow, /* vector 16 */ UnimplementedISR, /* vector 15 */ UnimplementedISR, /* vector 14 */ ISR_Ign_TimerOut, /* vector 13 */ ISR_Ign_TimerOut, /* vector 12 */ ISR_Inj2_TimerOut, /* vector 11 */ ISR_Ign_TimerIn, /* vector 10 */ ISR_Inj1_TimerOut, /* vector 09 */ ISR_Ign_TimerIn, /* vector 08 */ ISR_Timer_Clock, /* vector 07 */ UnimplementedISR, /* vector 06 */ UnimplementedISR, /* vector 05 */ UnimplementedISR, /* vector 04 */ UnimplementedISR, /* vector 03 */ UnimplementedISR, /* vector 02 */ UnimplementedISR, /* vector 01 */ #ifdef GCC_BUILD _start /* Reset vector */ }; #else _Startup /* Reset vector */ }; #pragma CODE_SEG ROM_7400 void switch_page(unsigned char sub_no,char carg1,char carg2); int intrp_1dctable(char sgnx, int x, unsigned char n, int * x_table, char sgnz, unsigned char * z_table) TEXT1_ATTR; int intrp_2dctable(unsigned int x, int y, unsigned char nx, unsigned char ny, unsigned int * x_table, int * y_table, unsigned char * z_table) TEXT1_ATTR; unsigned int intrp_1ditable(unsigned int x, unsigned char nx, unsigned int * x_table, unsigned int * z_table) TEXT1_ATTR; int CW_table(int clt, int *table) TEXT1_ATTR; #pragma CODE_SEG DEFAULT #endif #include "cltfactor.inc" #include "matfactor.inc" #include "egofactor.inc" #include "maffactor.inc" #define NO_TBLES 16 #define NO_FMAPS 12 #define NO_SMAPS 12 #define NO_FRPMS 12 #define NO_SRPMS 12 #define NO_ARPMS 6 #define NO_ATPSS 6 #define NO_INJ 2 #define NO_TEMPS 10 #define NO_MAT_TEMPS 6 #define NO_BARS 6 #define NO_MATS 6 #define NO_TPS_DOTS 4 #define NO_MAP_DOTS 4 #define NO_MAFS 12 #define NO_KNKRPMS 6 #define NO_XTRPMS 5 #define NPORT 7 #define NO_COILCHG_PTS 5 #define EGODT 78 /* 78 .128 ms tics = 10 ms */ #define END_SYNCH 3 #define AMCBURN_RPM_THRESH 1200 #define SER_TOUT 5 #define NO_ECUS 3 /* includes unknown type */ #define MSG_CMD 0 #define MSG_REQ 1 #define MSG_RSP 2 #define MSG_XSUB 3 #define MSG_BURN 4 #define NO_CANMSG 10 #define MAX_CANBOARDS 16 // Error status words: // -bits 0-7 are current errors // -bits 8-15 are corresponding latched errors #define XMT_ERR 0x0101 #define CLR_XMT_ERR 0xFFFE #define XMT_TOUT 0x0202 #define CLR_XMT_TOUT 0xFFFD #define RCV_ERR 0x0404 #define CLR_RCV_ERR 0xFFFB #define SYS_ERR 0x0808 // User inputs - 1 set in flash, 1 in ram typedef struct { unsigned char no_cyl,no_skip_pulses, // skip >= n tach pulses at startup ICIgnOption, // Bit 0: Input capture: 0 = falling edge, 1 rising // Bit 1: 1= trigger return mode in cranking; for this // mode, set bit 0 to normal edge, then opp.edge used in cranking. // trigger return not applicable to EDIS // Bit 2: 1 = fire on tach trigger with no advance // while cranking; not applicable for EDIS. Bits 1,2 cannot both=1 // Bit 3: spare // Bits 4-7: 0 = standard - charge coil, spark // 1 = Ford EDIS option // 2 = EDIS with multispark spkout_hi_lo, // Ign Output compare: 0 = spark low (gnd) max_coil_dur,max_spk_dur,DurAcc; // ms x 10 char deltV_table[NO_COILCHG_PTS],deltDur_table[NO_COILCHG_PTS], // Vx10,msx10 RevLimOption; // 0=none, 1=spark retard, 2=fuel cut unsigned char RevLimMaxRtd; // max amount of spark retard (degx10) (at Rpm2 below) char PredOpt; // Option for prediction algorithm for next tach plse // 0=use last time interval // 1=use alpha-beta-gamma filter int crank_rpm; // rpm at which cranking is through (~300 - 400 rpm) int cold_adv_table[NO_TEMPS], adv_offset, // all in deg x 10 // adv_offset is no deg(x10) between trigger (Input Capture) and TDC. // It may be +btdc (IC1 to TDC) or -atdc (TDC to IC2). // // | | | // | | | // -|---------------|-----|- // IC1 TDC IC2 RevLimRpm1,RevLimRpm2; // Optn 1:Retard spk by 0 deg at Rpm1 and increase // to RevLimMaxRtd at Rpm2 // Optn 2:Cut fuel above Rpm2, restore below Rpm1 unsigned char afr_table[NO_INJ][NO_FMAPS][NO_FRPMS], // afr x 10 warmen_table[NO_TEMPS], // % enrichment vs temp tpsen_table[NO_TPS_DOTS], // accel enrichment pw in .1 ms units vs tpsdot mapen_table[NO_MAP_DOTS]; // accel enrichment pw in .1 ms units vs mapdot int iacstep_table[NO_TEMPS]; // iac steps vs temp unsigned int frpm_table[NO_FRPMS],srpm_table[NO_SRPMS]; // fuel, spk rpm tables int fmap_table[NO_FMAPS],smap_table[NO_SMAPS], // kpa x 10, temp_table[NO_TEMPS], // deg x 10 (C or F) tpsdot_table[NO_TPS_DOTS], // change in % x 10 per .1 sec mapdot_table[NO_MAP_DOTS]; // change in kPa x 10 per .1 sec int map0,mapmax,clt0,cltmult,mat0,matmult,tps0,tpsmax,batt0,battmax, // kPa x 10, deg F or C x 10, adc counts, volts x 10 ego0,egomult,baro0,baromax,bcor0,bcormult,knock0,knockmax; // afr x 10, kpa x 10, %, volts x 100 char cpad3,cpad4; // pad bytes unsigned char CrnkTol,ASTol,PulseTol, // % tolerance for next input pulse // timing during cranking, after start/warmup and normal running IdleCtl, // idle: 0 = none, 1= solenoid, 2= iac stepper motor // - enabled only when moving, 3 = iac motor - always enabled. // 4 = Ford pwm, 5 = 3 for 15 min, then = 2. IACtstep, // iac stepper motor nominal time between steps (.1 ms units) IACaccstep, // iac stepper motor accel/decel step time (.1 ms)-future use IACminstep, // iac stepper motor min step size for a move.Must be >= 1. IACpwm_step; // controls freq of pwm FIdle solenoid = 80 Hz * IACpwm_step. int IACStart, // no. of steps to send at startup to put stepper // motor at reference (wide open) position IdleHyst, // amount clt temp must move before Idle position is changed IACcrankpos, // IAC pos must be open(<) at least this much(steps) in cranking IACcrankxt, // no. seconds from end of cranking for IAC pos to blend into // coolant dependent pos. IACcoldtmp,IACcoldpos,IACcoldxt, // when startup coolant temp < IACcoldtmp, // then when reach (>=) IACcoldpos switch to time based ctl: interpolate // bet IACcoldpos and final table IAC pos for period IACcoldxt secs. PrimePU,PrimePH, // priming pulsewidths at min, max temps in temp_table (ms x 10) CWU,CWH, // crank pulsewidths at min, max temps in temp_table (ms x 10) AWEVU,AWEVH, // after start warmup % enrich add-on value at min,max temps in // temp_table AWCU,AWCH; // after start enrichment no. cycles at min,max temps in temp_table unsigned char Tpsacold, // cold (-40F) accel amount in .1 ms units AccMult, // cold (-40F) accel multiply factor (%) TpsThresh, // tpsdot threshhold for acc/decel enrichment(change in %x10 per .1 sec) MapThresh, // mapdot threshhold for acc/decel enrichment(change in kPax10 per .1 s) TpsAsync, // clock duration (in .1 sec tics) for accel enrichment TPSDQ; // deceleration fuel cut option (%) int TPSWOT, // TPS value at WOT (for flood clear) in %x10 TPSOXLimit; // Max tps value (%x10) where O2 closed loop active unsigned char Tps_acc_wght, // weight (0-100) to be given to tpsdot for accel enrichment. // 100 - Tps_acc_wght will then be given to mapdot. BaroOption, // 0=no baro, 1=baro is 1st reading of map (before cranking), // 2=independent barometer (=> EgoOption not 2 or 4) EgoOption, // 0 = no ego;1= nb o2;2=2 nb o2;3=single wbo2;4=dual wbo2. NOTE: // BaroOption MUST be < 2 if EgoOption = 2 or 4. EgoCountCmp, // Ign Pulse counts between when EGO corrections are made EgoStep, // % step change for EGO corrections EgoLimit, // Upper/Lower rail limit (egocorr inside 100 +/- limit) AFRTarget, // NBO2 AFR(Vx100) determining rich/ lean Temp_Units, // 0= coolant & mat in deg F; 1= deg C MAFOption, // MAF options. Bits 0-3: =0 is no MAF; =1 is MAF-only; =2 is MAF - // MAP blend. Bits 4-7: =0, MAF signal on ADC0=MAP pin. Must put MAF // here if MAFOption = 1. =1, MAF signal on ADC6=Baro/Ego2 pin; =2, // MAF signal on ADC7 (knock pin). DualSpkOptn; // Option = 1 for 2 ignition inputs and 2 outputs. Specifying this option // uses the 2 pwm1,2 pins (TC2,TC4) for the extra ignition in, out; // therefore ** NO injector PWM ** with this option.Also, NO trigger // return, NO wheel decode, NO EDIS. Also set ISR_tmask=0,_pmask < 20. // The inputs are from 2 or 4 cyl crank or 4 cyl cam. They are in- // dependent, allowing 2 cyl, possibly 4 cyl odd fire. // Option = 2,3 for 2 cyl COP/ 4 cyl wasted spark with normal tach pulse // or m-n or m-0 wheel on input 1, cam synch on input 2;option 2= falling, // 3= rising edge for cam synch. When cam synch occurs, fire Output 1, // then fire O/P 2 on next tach, etc. No inj pwm, NO trig_ret, No EDIS. // Option = 4 is same as 2,3 but 1 toothed wheel on input 1 (input 2 // is unused). On tooth after missing tooth(teeth) fire O/P 1, then // fire O/P 2 after skip teeth (= next tach), etc. // Option = 5 same as 4 except wheel on cam, so use 1/2 crank skip teeth // Option = 6 same as 1 except timing signal from 1 cam tooth, 2 cyl only. // Options 7,8 same as 2,3 but with crank synch on 2nd input. // These options can support odd-fire through the OddAng input. int FastIdle, // fast idle Off temperature (idle_ctl = 1 only) EgoTemp, // min clt temp where ego active RPMOXLimit; // Min rpm where O2 closed loop is active unsigned int ReqFuel; // fuel pulsewidth (usec) at wide open throttle unsigned char Divider, // divide factor for input tach pulses Alternate, // option to alternate injector banks InjOpen, // Injector open time (.1 ms units) InjPWMTim, // Time (.1 ms units) after Inj opening to start pwm InjPWMPd, // Inj PWM period (us) - keep between 10-25 KHz (100-40 us) InjPWMDty, // Inj PWM duty cycle (%) BatFac, // Battery fuel pw correction factor (msx10) EngStroke, // 0 = 4 stroke // 1 = 2 stroke InjType, // 0 = port injection // 1 = throttle body NoInj; // no. of injectors (1-12) int OddAng; // Spark advance offset(degx10) for dual spark outputs. This offset // added to the 2nd output to adv(+) or retard(-) relative to o/p 1. // Must use 'calculated' for cranking trigger if OddAng != 0.. unsigned char rpmLF,mapLF,tpsLF, // Lag filter coefficient for Rpm,Map/ Maf,Tps. Acts like egoLF, // averager: xnew = xold + xLF * (xmeas - xold), so xLF=0 adcLF,knkLF, // means value will never change, xLF=100 means no filtering. // egoLF is coefficient for ego filter, adcLF is for clt,mat,batt, // knkLF is for knock. AMCOption, // Automatic Mixture Control Option: 0 = none; 1= automatic updates // to ram ve table(s); 2= also automatically updates flash ve tables. dual_tble_optn, // 1 = use dual table option - ve, afr tables for each inj FuelAlpha, // option for alpha-N mode: bits 0-3: 0=none;1=use map,tps blend // algorithm; 2=same as 1, but don't use kpa multiplier in pwidth eq; // bit 4 used to specify whether alphaN used below alpha_lorpm // or above alpha_hirpm. alpha_baro_spkrtd, // alphaN spark retard vs baro factor (deg/ baro kPa above 100 kPa)x100 // Next option can be implemented if needed. Presently afr table will be handled as // AlphaN if FuelAlpha selected. AfrAlpha, // option to use map,tps(alphaN) blend algorithm for WBO2 AFR AFRStoich; // stoich afr ratio (afrx10 or Vx100) for fuel used. If AFRMult=1, this // input used to normalize afrtgt or afr_table multiplier in fuel pw eq. int alpha_lorpm, // option to use map,tps(alphaN) blend algorithm for fuel alpha_hirpm, // use tps if < lorpm; use map if > hirpm; else blend, in which // case lo, hirpm should be consecutive entries in frpm_table. // set lorpm=hirpm =0 to use map enrichment only, // set lorpm =25K to use alpha-N enrichment only, // If bit 4 set in FuelAlpha, use tps if > hirpm,map if < lorpm. alpha_map_table[NO_ATPSS][NO_ARPMS], // Table of ave map values (kpa x 10) for // [tps][rpm] pairs. Used only for alpha-N algorithm. // Allows ve table to retain same ve values // whether in alpha-N mode or not, amap_tps[NO_ATPSS]; // Tps values (% x 10) for alpha_map table unsigned int amap_rpm[NO_ARPMS]; // Rpm values for alpha_map table unsigned long baud; // baud rate int MAPOXLimit; // Max map value (kPax10) where O2 closed loop active unsigned char board_type, // board type (1-255) of this board(not used now); // type=0, reserved. // type=1, ECU (MS II) // type=2. Router board // type=3, Generic I/O board // type=4, Transmission Controller, ...... mycan_id, // can_id (address) of this board (< MAX_CANBOARDS). Always 0 for ECU. fet_delay; // delay in us from time ignition FET line goes active to spark. char vr_delay; // delay of VR sensor in us / 1000 rpm from tooth present to 0 crossing. unsigned char ECUType, // Type of ECU: 255 = Unknown; 1 = MS II; 2 = microsquirt. MapThreshXTD2, // see ...XTD below pwImpulse, // Addition (msx10) to be added to normal PW every impulseSec while // impulseSec > 0. If triggered while pin E0 low, it represents a % impulseSec, // of time between squirts to be added to the normal fuel pulsewidth. // (latter not allowed with FlexFuel option). Set both 0 to inhibit. nImpulse, // Number of injections to be made every impulseSec. This and // impulseSec apply only to first option. mapdotSample, // Minimum time between map samples (ms) to compute mapdot. // If time shorter, wait for next tach pulse. tpsdotSample, // Time between map samples (ms) to compute mapdot. ASEHot, // option, =1 to implement ASE on warm restarts. InjStart, // Specifies when after tach pulse to start fuel injection. // Value in % of crank angle between tach pulses. For example, // for 8 cyl this is 90 deg, so to fire at 30 deg later, set // InjStart to 30/90 = 33%. MapThreshXTD, // mapdot threshhold for the start of the switch from X-Tau Acc // to Decel parameters(change in kPax10 per .1 s). The switch // will be complete when mapdot < -MapThreshXTD2. MapThreshXTA, // mapdot threshhold for the start of Map Trending for faster // AE/ XTau enrichment (in kPax10 per .1 s). trendmapLimit; // max amount of change in map at any given sample (kPa). // Generic spare port parameters: spr_port = 0(don't use)/1(use),where // spr_port[0-6] = PM2 - PM5; PTT6,7; PORTA0; they are set as outs to main // pcb; out_offset,out_byte= byte offset from start of outpc structure and // size in bytes of 1st, 2nd variables to be tested for setting port; // condition='<', '>', '=';cond12 = '&','|',' ' connects the conditions for // the two variables with ' ' meaning only the first variable condition is // desired; thresh = value for the condition(e.g., var1 > thresh1); init_val, // port_val=value (0/1) to which the pin will be set at startup and when the // condition(s) is met; hyst is a hysteresis delta and works as ff: if a // setpoint condition is > and it is met, set port to val and leave until // variable is < thresh - hyst, then set pin back to 1 - val. Similarly if // condition is <, wait til var > thresh + hyst. For dual conditions, the // hysteresis conditions are evaluated the same way, but use the opposite // of cond12 to connect them (if cnd12 is &, use | and vice versa). char spr_port [NPORT], // NPORT == 7 condition1 [NPORT], condition2 [NPORT], cond12 [NPORT], init_val [NPORT], port_val [NPORT], out_byte1 [NPORT], out_byte2 [NPORT]; int out_offset1[NPORT], out_offset2[NPORT], thresh1 [NPORT], thresh2 [NPORT], hyst1 [NPORT], hyst2 [NPORT]; unsigned char TpsAsync2, // accel tailoff duration (sec x 10) AFRMult; // = 1 means multiply fuel pw eq by AFRStoich/AFRTgt(NB) // = 2 means multiply fuel pw eq by AFRStoich/afr_table(WB). int TpsAccel2; // end pulsewidth of accel enrichment (ms x 10) unsigned char EgoAlg, // 0=simple prop error algorithm; // 1=same algorithm with variable transport delay; // 2=full PID with Smith Pred correction. egoKP, egoKI, egoKD; // PID coefficients in %; egoKP also gain for // EgoAlg=0, with 100 = no gain; egoKD includes // 1/dt factor since fixed time step. unsigned int egoKdly1, // coefficients used to calculate ego transport egoKdly2; // delay (ms) = Kdly1 + Kdly2*120000 / (map(kPax10)*rpm) unsigned char FlexFuel, // Flex fuel option - modifies pw and spk adv based on % alcohol. prime_delay, // wait for fuel pump to prime, sec fuelFreq[2], // Table of fuel sensor freq(Hz) vs %fuel corr; fuelCorr[2], AMCStep, // % of AMC correction to be applied when ramve updated. AMCdve; // AMC correction must be > AMCdve % to be applied in burn unsigned int AMCve_drpm,AMCve_dmap, // current rpm, map(kPax10) must be within this AMCramve_dt, // tolerance of a ve table vertex for AMCramve_dt secs // before AMC ve correction is applied AMCT_thresh, // Min time (secs) between flash burns of ram ve table AMCupdate_thresh;// Min number of AMC ram ve updates before burn table. unsigned char CWOption, // Cold warmup option - 0 = linear, 1 = custom table. knk_option, // Bits 0-3: 0=no knock detection;1=operate at table value or 1 // step below knock; 2=operate at table value or edge of knock. // Bits 4-7: 0/1 = knock signal < or > knk_thresh indicates knock occurred. knk_maxrtd, // max total retard when knock occurs, (degx10), knk_step1,knk_step2, // ign retard/ adv steps when 1st knock or after stopped, // (degx10); step1 large to quickly retard/ stop knock knk_trtd,knk_tadv, // time between knock retard, adv corrections, (secx10); // allows short time step to quickly retard, longer to try advancing. knk_dtble_adv, // change in table advance required to restart adv til knock or // reach table value (0 knock retard) process, deg x10. // This only applies with knk_option = 1. knk_ndet,cpad1; // number knock detects required for valid detection; pad byte. unsigned int knk_maxmap, // no knock retard above this map (kPax10). knk_lorpm,knk_hirpm, // no knock retard below, above these rpms. knk_rpm[NO_KNKRPMS], // tables of rpm vs knock threshhold(Vx100). knk_thresh[NO_KNKRPMS], No_Teeth; // Nominal (include missing) teeth for wheel decoding. (0= // no wheel decoding). unsigned char No_Miss_Teeth, // Number of consecutive missing teeth. No_Skip_Teeth, // Number of teeth (between tach pulses) to be skipped. Delay_Teeth, // Number of teeth to delay after 1st tooth after the missing // teeth before 1st tach synch declared(< No_Teeth- Miss_Teeth) // In this mode, for 16 - 2 wheel, 8 cyl,4 cycle, No_Skip_Teeth= 4,Delay_Teeth=0, // // \/ \/ \/ \/ // /\/\__ __/\/\/\/\/\/\/\/\/\/\/\/\ // // \/ => when the first tooth after all the missing teeth is input captured, // plus Delay_Teeth after, then we are at adv offset deg btdc, that is, this tooth // and every 'No_Skip_Teeth' thereafter mark the tach pulses. // Note: No_Miss_Teeth must be < No_Skip_Teeth. Also, Delay_Teeth must be such that // every tach pulse tooth is a real tooth - not a missing one. ICISR_tmask, // Time (msx10) after tach input capture during which further // interrupts are inhibited to mask coil ring or VR noise. ICISR_pmask, // % of dtpred after tach input capture during which further // interrupts are inhibited to mask coil ring or VR noise. // This and prior mask not applicable with wheel decoding. InjTestOption; // Puts program in injector test mode: program fires both // injectors according to parameters further below. when done // program halts until hard or soft reset issued. unsigned int ae_lorpm,ae_hirpm; // lorpm is rpm at which normal accel enrichment just starts // to scale down, and is reduced to 0 at ae_hirpm. To omit scaling, set _lorpm // = _hirpm= very large number. int ffSpkDel[2]; // Table of fuel sensor freq(Hz) vs spk corr (degx10); unsigned int InjTestNoSqrts, // No of squirts in test/clean mode. InjTestPW, // Pulse width(usec) for testing/ cleaning injectors. InjTestOff; // Time(msx10) between inj test squirts. unsigned int CID; // Engine cubic in displacement unsigned char alpha,beta, // coefficients for ignition time prediction(%) gamma, acc_synchk; // if =0, don't check for tach timing synch if accel/ decel (like // 2.687 to 2.860 code) // if =1, always check for extra pulse; check for missing pulse only // if steady or accel rpm // if =2,check for extra pulse only if steady or decel rpm; // always check for missing pulse; // if =3, always check for tach timing synch (like 2.36 code) } inputs1; typedef struct { unsigned char ve_table[NO_INJ][NO_FMAPS][NO_FRPMS]; int adv_table[NO_SMAPS][NO_SRPMS]; unsigned int AMCNBurns; int CWPrime[NO_TEMPS],CWCPW[NO_TEMPS],CWAWEV[NO_TEMPS],CWAWC[NO_TEMPS]; int MatTemps[NO_MAT_TEMPS]; // MAT temperatures for spark retard, degx10 F or C unsigned char MatSpkRtd[NO_MAT_TEMPS]; // degx10 of spark retard vs mfld air temp unsigned char XTauOption, // Bits 0-3: // 0 = no XTau algorithm; 1 = XTau for accel/ decel only; // 2 = XTau for AE/DE during warmup also, based on coolant temp. // Note X-Tau applies to both injectors. If needed (for // example with dual table option), Bit 4 (=1) can be used to // apply correction only to Inj1. spkN2O; // spark retard subtracted from normal advance while N2O is on // (degx10) Only used with pwImpulse option.. unsigned int XAccTable[NO_XTRPMS], // X factor (%x10) for amount of injected fuel that // clings to port wall as function of rpm (during accel). TauAccTable[NO_XTRPMS], // time constant (ms) for fuel puddle dissipation vs rpm. XDecTable[NO_XTRPMS], // Same tables but to be used during decel. TauDecTable[NO_XTRPMS]; unsigned int XTrpms[NO_XTRPMS]; // map, rpm values for above // tables (map in kPax10) unsigned int XClt[NO_TEMPS],TauClt[NO_TEMPS]; // same tau, but % scale as function of coolant temp. int ipada[48]; // pad integers int BaroVals[NO_BARS], // Barometric pressures (kPa x 10) for baro correction table MatVals[NO_MATS]; // air temperatures (degx10) for air density correction table char BaroCorDel[NO_BARS], // Barometric corrections (+/-%) - added to eq. value // to correct for varying exhaust size AirCorDel[NO_MATS]; // Air density orrections (+/-%) - added to eq. value // to correct for varying mat sensor location unsigned int MAFFlow[NO_MAFS]; // MAF flows (mg/ secx10) for below corrections. unsigned char MAFCor[NO_MAFS]; // Corrections to maf factor table (%) for real time tuning/ // intake mods.as function of MAFFlow. 100% = no correction. unsigned int MAFRPM1,MAFRPM2; // For MAFOption=2, if rpm < rpm1 use MAP only; > rpm2 use MAF // only. In between, use MAP/ MAF blend. unsigned char MAFDir, // If = 0, use above for MAP/ MAF blend; =1, reverse - use // MAF only below rpm1 and MAP only above rpm2. VEIXOptn; // =1 means use (kpa*1000)/baro instead of kpa as the index // into ve, afr, spk tables. (per Ben Strader (EFI Univ), verified // from Heywood, Aquino,Taylor, and Moskwa. When this done pw = // .....*kpa/1000... instead of *kpa/baro. unsigned int MatRtdRPMHi,MatRtdRPMLo; // Full MAT spark retard applied above Hi rpm, none // applied below Lo rpm, interpolate in between. int FuelAdj,SpkAdj, // Adjustments for fuel(% +/- to ve),spark(degx10),idle step,spare IdleAdj,SprAdj; // for GPIO boards. } inputs2; #ifndef GCC_BUILD #pragma ROM_VAR INP_ROM #endif // flash copy of inputs - initialized #define SECTOR_BYTES 1024 // bytes #define SECTOR_WORDS ((int)(SECTOR_BYTES/2)) #define N_SECTORS(theStruct) ((int)((sizeof(theStruct)+SECTOR_BYTES-1)/SECTOR_BYTES)) #define N_PADDING(theStruct) ((int)(N_SECTORS(theStruct)*SECTOR_BYTES - sizeof(theStruct))) const inputs1 in1flash EEPROM_ATTR = { 8, // no_cyl (1-12) 3, // no_skip_pulses (tach pulses) 0x05, 1, // ICIgnOption,spkout_hi_lo, 31, 20, 6, // max_coil_dur,max_spk_dur,DurAcc msx10 {-40,-20,0,20,40}, // deltaV_table[], Vx10 = batt_voltx10 - 120 {24, 9, 0,-5,-9}, // deltaDur_table[], msx10 = correction for batt_volt 1, // RevLimOption:0,none; 1,retard spk; 2,fuel cut 120, // RevLimMaxRtd, deg x 10 1, // PredOpt (For EDIS, toothed wheel > 12 PredOpt=0 is sufficient) // For EDIS keep total adv (incl. offset & cold adv) < 60 deg 300, // rpm at which cranking through {60, // cold_adv_table[TEMP no = 0], deg x 10 50,40,30,20,10,0,0,0,0}, 0, // adv_offset, deg x 10 5500, 6000, // RevLimRpm1,2 {{{130, // afr_table[inj1][MAP/tps no =0][RPM no = 0], afrx10 135,160,160,160,149,143,132,131,132,131,130}, {132, // afr_table[inj1][MAP/tps no =1][RPM no = 0], afrx10 137,157,157,155,149,142,132,130,129,128,127}, {134, // afr_table[inj1][MAP/tps no =2][RPM no = 0], afrx10 139,155,155,154,149,141,130,129,128,127,127}, {135, // afr_table[inj1][MAP/tps no =3][RPM no = 0], afrx10 140,152,152,150,147,140,130,129,128,127,126}, {136, // afr_table[inj1][MAP/tps no =4][RPM no = 0], afrx10 141,150,149,147,147,140,129,128,127,126,126}, {135, // afr_table[inj1][MAP/tps no =5][RPM no = 0], afrx10 138,145,143,141,141,135,128,127,126,126,126}, {134, // afr_table[inj1][MAP/tps no =6][RPM no = 0], afrx10 134,139,137,136,136,131,127,126,126,126,126}, {132, // afr_table[inj1][MAP/tps no =7][RPM no = 0], afrx10 132,135,133,133,132,130,126,125,125,125,125}, {130, // afr_table[inj1][MAP/tps no =8][RPM no = 0], afrx10 130,131,130,130,129,130,125,125,125,125,125}, {130, // afr_table[inj1][MAP/tps no =9][RPM no = 0], afrx10 129,129,128,128,127,126,125,125,125,125,124}, {130, // afr_table[inj1][MAP/tps no =10][RPM no = 0], afrx10 129,129,128,128,127,126,125,125,125,123,123}, {130, // afr_table[inj1][MAP/tps no =11][RPM no = 0], afrx10 129,129,128,128,127,126,125,125,122,122,122}}, {{130, // afr_table[inj2][MAP/tps no =0][RPM no = 0], afrx10 135,160,160,160,149,143,132,131,132,131,130}, {132, // afr_table[inj2][MAP/tps no =1][RPM no = 0], afrx10 137,157,157,155,149,142,132,130,129,128,127}, {134, // afr_table[inj2][MAP/tps no =2][RPM no = 0], afrx10 139,155,155,154,149,141,130,129,128,127,127}, {135, // afr_table[inj2][MAP/tps no =3][RPM no = 0], afrx10 140,152,152,150,147,140,130,129,128,127,126}, {136, // afr_table[inj2][MAP/tps no =4][RPM no = 0], afrx10 141,150,149,147,147,140,129,128,127,126,126}, {135, // afr_table[inj2][MAP/tps no =5][RPM no = 0], afrx10 138,145,143,141,141,135,128,127,126,126,126}, {134, // afr_table[inj2][MAP/tps no =6][RPM no = 0], afrx10 134,139,137,136,136,131,127,126,126,126,126}, {132, // afr_table[inj2][MAP/tps no =7][RPM no = 0], afrx10 132,135,133,133,132,130,126,125,125,125,125}, {130, // afr_table[inj2][MAP/tps no =8][RPM no = 0], afrx10 130,131,130,130,129,130,125,125,125,125,125}, {130, // afr_table[inj2][MAP/tps no =9][RPM no = 0], afrx10 129,129,128,128,127,126,125,125,125,125,124}, {130, // afr_table[inj2][MAP/tps no =10][RPM no = 0], afrx10 129,129,128,128,127,126,125,125,125,123,123}, {130, // afr_table[inj2][MAP/tps no =11][RPM no = 0], afrx10 129,129,128,128,127,126,125,125,122,122,122}}}, {180, // warmen_table[TEMP no = 0], % enrichment vs temp 180,160,150,135,125,113,108,102,100}, {20, // tpsen_table[TPS_DOT no = 0], enrichment in .1ms vs tpsdot 50,105,150}, {0, // mapen_table[TPS_DOT no = 0], enrichment in .1ms vs mapdot 0,0,0}, {40, // iacstep_table[TEMP no = 0], 60,75,90,105,120,130,140,150,160}, {500, // frpm_table[RPM no = 0] , use in VE, AFR tables 800,1100,1400,2000,2600,3100,3700,4300,4900,5400,6000}, {700, // srpm_table[RPM no = 0] , use in spark advance table 900,1200,1500,2000,2600,3100,3700,4300,4900,5400,6000}, {300, // fmap_table[MAP/tps no = 0], kPa x 10 , use for VE, AFR 350,450,500,550,600,700,750,800,850,950,1000}, {200, // smap_table[MAP/tps no = 0], kPa x 10 , use for spk adv 250,300,350,400,450,500,600,700,800,900,1000}, {-400, // temp_table[TEMP no = 0], deg x 10 -200,0,200,400,600,800,1000,1300,1600}, {100, // tpsdot_table[TPS_DOT no =0], 400,800,1540}, // change in % x 10 per .1 sec {0, // mapdot_table[TPS_DOT no =0], 0,0,0}, // change in kPa x 10 per .1 sec 93, // map0, kPa x 10, value @ 0 ADC counts 2609, // mapmax, kPa x 10, value @ max(1023) ADC counts 0, // clt0, deg (C or F) x 10 100, // cltmult, % 0, // mat0, deg (C or F) x 10 100, // matmult, % 0, // tps0, adc counts 1023, // tpsmax, adc counts 1, // batt0, v x 10 297, // battmax, v x 10 0, // ego0, afr x 10 100, // egomult, % 93, // baro0, kPa x 10 2609, // baromax, kPa x 10 147, -47, // bcor0,bcormult %, slope 0, // knock0, v x 100 500, // knockmax, v x 100 0,0, // pad bytes 50,70,25, // PulseTol, % tolerance for next input pulse timing during // cranking, after start/ warmup, normal running 5, // IdleCtl, idle: 0 = none, 1= solenoid, 2= iac stepper motor // - enabled only when moving, 3 = iac motor - always enabled. // 4 = Ford PWM iac, 5 = 3 for 15 min, then = 2 25, // IACtstep, .1 ms units (25 gives pulse freq of 400 Hz) 0, // IACaccstep 1, // IACminstep 1, // IACpwm_step = freq of pwm FIdle solenoid= 80 Hz* IACpwm_step. 160, // IACStart, no. of steps to send at startup to put stepper // motor at reference (wide open) position 50, // IdleHyst amount (degx10) 100,5, // > IAC opening (< steps) during cranking and few secs after 0,120,40, // when startup coolant temp < IACcoldtmp (degx10), // then when reach (>=) IACcoldpos switch to time based ctl: interpolate // bet IACcoldpos and final table IAC pos for period IACcoldxt secs. 60, 20, // PrimePU,H priming pulsewidths at min, max temps in temp_table (ms x10) 110, 30, // CWU,CWH, crank pw at min, max temps in temp_table (ms x 10) 45,25, // AWEV, after start warmup % enrich add-on value at min, max clt temps, 350,150, // AWC, after start enrichment no. of cycles at min, max clt temps 30, // Tpsacold, cold (-40F) accel amount in .1 ms units 130, // AccMult, cold (-40F) accel multiply factor (%) 200, // TpsThresh, tpsdot threshhold for accel enrichment(change in %x10 per .1 s) 100, // MapThresh, mapdot threshhold for accel enrichment(change in kPax10 per .1 s) 2, // TpsAsync, clock duration (in .1 sec tics) for accel enrichment 90, // TPSDQ, deceleration fuel cut option (%) 700, // TPSWOT, TPS value at WOT (for flood clear), %x10 700, // TPSOXLimit, Max tps value (%x10) where O2 closed loop active 100, // Tps_acc_wght, weight to be given to tpsdot for accel enrichment. // 100 - Tps_acc_wght will then be given to mapdot. 1, // BaroOption, 0=no baro, 1=baro is 1st reading of map (before cranking), // 2=independent barometer 1, // 0 = no ego;1= nb o2;2=2 nb o2;3=single wbo2;4=dual wbo2. 16, // EgoCountCmp, Ign Pulse counts between when EGO corrections are made 1, // EgoStep, % step change for EGO corrections 15, // EgoLimit, Upper/Lower rail limit (egocorr inside 100 +/- limit) 45, // AFRTarget, NBO2 afr (Vx100) determining rich/ lean 0, // Temp_Units, 0= coolant & mat in deg F; 1= deg C 0, // MAFOption. Bits 0-3: =0 is no MAF; =1 is MAF-only; =2 is MAF - // MAP blend. Bits 4-7: =0, MAF signal on ADC0=MAP pin. Must put MAF // here if MAFOption = 1. =1, MAF signal on ADC6=Baro/Ego2 pin; =2, // MAF signal on ADC7 (knock pin). 0, // DualSpkOptn; Option = 1 for 2 ignition inputs and 2 outputs. This option // uses the 2 pwm1,2 pins (TC2,TC4) for the extra ignition in, out; // therefore ** NO injector PWM ** with this option.Also, NO trigger // return, NO wheel decode, NO EDIS. Also set ISR_tmask=0,_pmask < 20. // The inputs are from 2 or 4 cyl crank or 4 cyl cam. They are in- // dependent, allowing 2 cyl, possibly 4 cyl odd fire. // Option = 2,3 for 2 cyl COP/ 4 cyl wasted spark with normal tach pulse // or m-n or m-0 wheel on input 1, cam synch on input 2;option 2= falling, // 3= rising edge for cam synch. When cam synch occurs, fire Output 1, // then fire O/P 2 on next tach, etc. No inj pwm, NO trig_ret, No EDIS. // Option = 4 is same as 2,3 but 1 toothed wheel on input 1 (input 2 // is unused). On tooth after missing tooth(teeth) fire O/P 1, then // fire O/P 2 after skip teeth (= next tach), etc. // Option = 5 same as 4 except wheel on cam, so use 1/2 crank skip teeth. // Option = 6 same as 1 except timing signal from 1 cam tooth, 2 cyl only. // Options 7,8 same as 2,3 but with crank synch on 2nd input. 1400, // FastIdle, fast idle temperature (degx10) (idle_ctl = 1 only) 1600, // EgoTemp, min clt temp where ego active, degx10 1300, // RPMOXLimit, Min rpm where O2 closed loop is active 15500, // ReqFuel; fuel pulsewidth (usec) at wide open throttle 4, // Divider, divide factor for input tach pulses 1, // Alternate, option to alternate injector banks 10, // InjOpen, Injector open time (.1 ms units) 255, // InjPWMTim, Time (.1 ms units) after opening to start pwm 66, // InjPWMPd, Injector PWM period (us) 75, // InjPWMDty, Injector PWM duty cycle (%) 12, // BatFac, Battery fuel pw correction factor (msx10) 0, // EngStroke, 0 = 4 stroke, 1 = 2 stroke 0, // InjType, 0 = port injection, 1 = throttle body 8, // NoInj, no. of injectors (1-12) 0, // OddAng: spark advance offset(degx10) for dual spark outputs. This offset // added to the 2nd output to adv(+) or retard(-) relative to o/p 1. // Must use 'calculated' for cranking trigger if OddAng != 0.. 50,50,75, // Lag filter coefficients (1-100%) for Rpm,Map/Maf,Tps, 60, // ego1,2 50,80, // Lag filter coefficients for other adc(clt,mat,batt), knock. 0, // AMC option 0, // dual table option 0, // fuel alpha-N, map blend option 39, // alpha_baro_spkrtd: alphaN spark retard vs baro factor (deg/ baro kPa // above 100 kPa)x100 0, // WBO2 AFR alpha-N, map blend option(not used); 45, // AFRStoich, stoich afr (afrx10 or Vx100) for fuel used. If AFRMult= // 1, this input used to normalize AFRtarget or afr_table multiplier // in fuel pw eq. 0, // lo rpm for alpha-N, map blend option 0, // alpha-N hi rpm {{1000,600,300,350,400,400}, // alpha_map_table[tps no =0][RPM nos 0-5] (kpa x 10) {1000,650,350,400,450,450}, // alpha_map_table[tps no =1][RPM nos 0-5] {1000,700,400,450,500,500}, // alpha_map_table[tps no =2][RPM nos 0-5] {1000,750,450,500,550,550}, // alpha_map_table[tps no =3][RPM nos 0-5] {1000,800,500,550,600,600}, // alpha_map_table[tps no =4][RPM nos 0-5] {1000,800,500,550,600,600}}, // alpha_map_table[tps no =5][RPM nos 0-5] {0,120,160,300,600,600}, // amap_tps[0 - 5] (% x 10); {0,500,1000,1500,2000,2000}, // amap_rpm[0 - 5] 115200, // baud rate 900, // MAPOXLimit, Max MAP value (kPax10) where O2 closed loop active 1, // board_type 0, // can_id of this board. Always 0 for ECU, 1 for all others until configured. 0, // fet_delay, delay in us from time ignition FET line goes active to spark. 0, // vr_delay, delay of VR sensor in us / 1000 rpm from tooth present to 0 crossing. 255, // ECUType: 255 = Unknown; 1 = MS II; 2 = microsquirt. 60, // MapThreshXTD2: see ...XTD below 0, // pwImpulse: Addition (msx10) to be added to normal PW every impulseSec while // impulseSec > 0. If triggered while pin E0 low, it represents a % 0, // of time between squirts to be added to the normal fuel pulsewidth // (latter not allowed with FlexFuel option). Set both 0 to inhibit. 0, // nImpulse: Number of injections to be made every impulseSec. This and // impulseSec apply only to first option. 25, // mapdotSample: Minimum time between map samples (ms) to compute mapdot. // If time shorter, wait for next tach pulse. 25, // tpsdotSample: Time between map samples (ms) to compute mapdot. 1, // ASEHot: option, =1 to implement ASE on warm restarts. 0, // InjStart: Specifies when after tach pulse to start fuel injection. // Value in % of crank angle between tach pulses. For example, // for 8 cyl this is 90 deg, so to fire at 30 deg later, set // InjStart to 30/90 = 33%. 40, // MapThreshXTD: mapdot threshhold for the start of the switch from X-Tau Acc // to Decel parameters(change in kPax10 per .1 s). The switch will // be complete when mapdot < -MapThreshXTD2. 35, // MapThreshXTA: mapdot threshhold for the start of Map Trending for faster // AE (in kPax10 per .1 s). 5, // trendmapLimit: max amount of change in map at any given sample (kPa). {0,0,0,0,0,0,0}, // spr_port {'<','<','<','<','<','<','<'}, // condition1 {'<','<','<','<','<','<','<'}, // condition2 {' ',' ',' ',' ',' ',' ',' '}, // cond12 {0,0,0,0,0,0,0}, {0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0}, {0,0,0,0,0,0,0},{0,0,0,0,0,0,0}, {0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0}, // spr pin params 1, // accel tail duration (sec x 10). 0, // AFRMult,= 1 means multiply fuel pw eq by AFRStoich/AFRTgt(NB) // = 2 means multiply fuel pw eq by AFRStoich/afr_table(WB). 20, // accel end pwidth enrichment (ms x 10) 0, // EgoAlg, 0=simple algorithm; 1=Prop err alg;2=PID w. Smith pred 100,20,0, // KP,KI,KD, PID coefficients in % 10,4000, // egoKdly1,2 = coefficients used to calculate ego transport // delay (ms) = Kdly1 + Kdly2*120000 / (map(kPax10)*rpm). // Defaults based on xpt delay of .1 s at wot, 1 s at idle 0, // Flex fuel option - modifies pw and spk adv based on freq signal // for % alcohol. 0, // prime_delay, (sec) {50, 150}, // Table of fuel sensor freq(Hz) vs {100,163}, // fuel pw corr in %; 1st element is pure gas, 2nd is pure Alcohol. 10, // AMCStep, % of AMC correction to be applied when ramve updated. 2, // AMCdve, AMCcorrection must be > AMCdve % to be applied for burn 200,100, // AMCve_drpm,AMCve_dmap, current rpm, map(kPax10) must be within // this tolerance of a ve table vertex for 20, // AMCramve_dt secs before AMC correction is applied 1800, // AMCT_thresh, min time (secs) between flash burns of ram ve table 2, // AMCupdate_thresh, min number of AMC ram ve updates before burn table. 0, // CWOption, cold warmup option - 0 = linear, 1 = custom table. 0x00, // knk_option: Bits 0-3: 0=no knock detection;1=operate at table value or 1 // step below knock; 2=operate at table value or edge of knock. // Bits 4-7: 0/1 = knock signal < / > knk_thresh indicates knock occurred. 100, // knk_maxrtd, max total retard when knock, (degx10). 30,10, // knk_step1, _step2, ign retard/ adv steps when 1st knock or after stopped, // (degx10); step1 large to quickly retard/ stop knock 2,20, // knk_trtd,_tadv, time between knock retard, adv corrections, (secx10); // allows short time step to quickly retard, longer to try advancing. 30, // knk_dtble_adv, change in table advance required to restart adv til knock // or reach table value (0 knock retard) process, deg x10. // This only applies with knk_option = 1. 2,0, // knk_ndet, number of knock detects required for valid detection; pad byte. 700, // knk_maxmap, no knock retard above this map (kPax10). 700,3500, // knk_lorpm,knk_hirpm, no knock retard below, above these rpms. 500, // knk_rpm[0-NO_KNKRPMS], tables of rpm vs knock threshhold. 1000, 2000, 3000, 4000, 5000, 200, //knk_thresh[NO_KNKRPMS], Vx100 200, 200, 200, 200, 200, 0, // No_Teeth, nominal (include missing) teeth for wheel decoding. (0= // no wheel decoding). 2, // No_Miss_Teeth, number of consecutive missing teeth. 9, // No_Skip_Teeth, number of teeth (between tach pulses) to be skipped. 0, // Delay_Teeth, number of teeth to delay after 1st tooth after the missing // teeth before 1st tach synch declared (< No_Teeth- Miss_Teeth) 0, // ICISR_tmask, time (msx10) after tach input capture during which further // interrupts are inhibited to mask coil ring or VR noise. 50, // ICISR_pmask, % of dtpred after tach input capture during which further // interrupts are inhibited to mask coil ring or VR noise. // This and prior mask not applicable with wheel decoding. 0, // InjTestOption; Puts program in injector test mode: program fires both // injectors according to parameters further below. when done // program halts until hard or soft reset issued. 2500, 5000, // ae_lorpm,ae_hirpm, lorpm is rpm at which normal accel enrichment just // starts to scale down, and is reduced to 0 at ae_hirpm. To omit scaling, set // _lorpm = _hirpm= very large number. {0,-130}, // flex fuel spk corr (degx10); 1st element for pure gas, 2nd for pure // Alcohol; last is normally - to retard spk since Alch burns faster. 400, // InjTestNoSqrts, No of squirts in test/clean mode. 20000, // InjTestPW, Pulse width(usec) for testing/ cleaning injectors. 600, // InjTestOff, Time(msx10) between inj test squirts. 350, // CID, engine cubic in displacement. 90,80,10, // alpha,beta,gamma: coefficients for ignition time prediction(%).pad byte. 3 // acc_synchk: if =0, don't check for tach timing synch if accel/ decel // if =1, always check for extra pulse; check for missing pulse only // if steady or accel rpm // if =2,check for extra pulse only if steady or decel rpm; // always check for missing pulse; // if =3, always check for tach timing synch (like 2.36 code) }; const unsigned char in1padding[N_PADDING(inputs1)] EEPROM_ATTR ={0}; const inputs2 in2flash EEPROM_ATTR = { {{{26, // ve_table[inj1][MAP/tps no =0][RPM no = 0] 26,29,34,40,46,50,55,58,61,61,60}, {29, // ve_table[inj1][MAP/tps no =1][RPM no = 0] 29,32,38,46,53,56,60,63,66,65,62}, {38, // ve_table[inj1][MAP/tps no =2][RPM no = 0] 37,39,44,53,61,65,67,69,73,71,66}, {43, // ve_table[inj1][MAP/tps no =3][RPM no = 0] 42,43,45,54,63,66,69,71,75,73,67}, {48, // ve_table[inj1][MAP/tps no =4][RPM no = 0] 46,48,50,58,65,69,71,73,77,75,69}, {52, // ve_table[inj1][MAP/tps no =5][RPM no = 0] 51,52,55,62,67,71,73,75,79,77,71}, {57, // ve_table[inj1][MAP/tps no =6][RPM no = 0] 59,61,65,69,72,76,78,81,85,85,80}, {61, // ve_table[inj1][MAP/tps no =7][RPM no = 0] 62,65,69,72,75,79,82,85,89,88,84}, {65, // ve_table[inj1][MAP/tps no =8][RPM no = 0] 66,69,73,76,78,82,86,90,93,92,88}, {68, // ve_table[inj1][MAP/tps no =9][RPM no = 0] 70,73,78,81,83,86,90,94,98,97,93}, {72, // ve_table[inj1][MAP/tps no =10][RPM no = 0] 77,82,87,90,93,95,100,105,109,108,103}, {74, // ve_table[inj1][MAP/tps no =11][RPM no = 0] 81,86,91,95,97,100,105,111,114,113,108}}, {{26, // ve_table[inj2][MAP/tps no =0][RPM no = 0] 26,29,34,40,46,50,55,58,61,61,60}, {29, // ve_table[inj2][MAP/tps no =1][RPM no = 0] 29,32,38,46,53,56,60,63,66,65,62}, {38, // ve_table[inj2][MAP/tps no =2][RPM no = 0] 37,39,44,53,61,65,67,69,73,71,66}, {43, // ve_table[inj2][MAP/tps no =3][RPM no = 0] 42,43,45,54,63,66,69,71,75,73,67}, {48, // ve_table[inj2][MAP/tps no =4][RPM no = 0] 46,48,50,58,65,69,71,73,77,75,69}, {52, // ve_table[inj2][MAP/tps no =5][RPM no = 0] 51,52,55,62,67,71,73,75,79,77,71}, {57, // ve_table[inj2][MAP/tps no =6][RPM no = 0] 59,61,65,69,72,76,78,81,85,85,80}, {61, // ve_table[inj2][MAP/tps no =7][RPM no = 0] 62,65,69,72,75,79,82,85,89,88,84}, {65, // ve_table[inj2][MAP/tps no =8][RPM no = 0] 66,69,73,76,78,82,86,90,93,92,88}, {68, // ve_table[inj2][MAP/tps no =9][RPM no = 0] 70,73,78,81,83,86,90,94,98,97,93}, {72, // ve_table[inj2][MAP/tps no =10][RPM no = 0] 77,82,87,90,93,95,100,105,109,108,103}, {74, // ve_table[inj2][MAP/tps no =11][RPM no = 0] 81,86,91,95,97,100,105,111,114,113,108}}}, {{157, // adv_table[MAP/tps no=0][RPM no=0], deg x 10 175,200,286,328,375,370,375,380,380,380,380}, {157, // adv_table[MAP/tps no=1][RPM no=0], deg x 10 178,201,282,324,370,370,370,375,380,380,380}, {156, // adv_table[MAP/tps no=2][RPM no=0], deg x 10 180,202,278,324,368,370,375,375,380,380,380}, {155, // adv_table[MAP/tps no=3][RPM no=0], deg x 10 182,204,274,323,364,370,370,375,375,380,380}, {155, // adv_table[MAP/tps no=4][RPM no=0], deg x 10 184,206,272,322,360,368,374,374,376,376,380}, {157, // adv_table[MAP/tps no=5][RPM no=0], deg x 10 186,207,268,321,360,366,372,375,375,375,375}, {158, // adv_table[MAP/tps no=6][RPM no=0], deg x 10 188,208,258,320,360,365,365,365,370,370,370}, {160, // adv_table[MAP/tps no=7][RPM no=0], deg x 10 185,205,250,317,358,360,362,362,362,362,362}, {160, // adv_table[MAP/tps no=8][RPM no=0], deg x 10 183,203,241,308,353,360,360,360,360,360,360}, {155, // adv_table[MAP/tps no=9][RPM no=0], deg x 10 175,200,235,299,348,360,360,360,360,360,360}, {151, // adv_table[MAP/tps no=10][RPM no=0], deg x 10 172,195,228,295,343,360,360,360,360,360,360}, {148, // adv_table[MAP/tps no=11][RPM no=0], deg x 10 168,190,216,282,335,360,360,360,360,360,360}}, 0, // AMCNBurns { 60, 56, 52, 48, 44, 40, 36, 32, 26, 20}, // CWPrime, (msx10) {110,102,94, 86, 78, 70, 62, 54, 42, 30}, // CWCPW, (msx10) { 45, 43, 41, 39, 37, 35, 33, 31, 28, 25}, // CWAWEV, % {350,330,310,290,270,250,230,210,180,150}, // CWAWC, cycles {1600,1800,2000,2200,2400,2600}, // MatTemps, degx10 F or C {0,0,20,40,60,80}, // MatSpkRtd, degx10 0, // XTauOption: 0 = no XTau algorithm; 1 = XTau for accel/ decel only; // 2 = XTau for AE/DE during warmup also, based on coolant temp. 0, // spkN2O: spark retard subtracted from normal advance while N2O is on // (degx10) Only used with pwImpulse option.. { 300, 300, 300, 300, 300}, // XAccTable[NO_XTRPMS], %x10 { 410, 400, 390, 370, 350}, // TauAccTable[NO_XTRPMS], ms { 200, 200, 200, 200, 200}, // XDecTable[NO_XTRPMS], %x10 { 510, 500, 490, 470, 450}, // TauDecTable[NO_XTRPMS], ms { 600, 1000, 2000, 3000, 6000 }, // XTrpms[NO_XTRPMS]:rpms for above tables (idle to max) {160,150,140,130,125,120,115,110,105,100}, // XClt[NO_TEMPS], % {200,190,180,180,170,160,140,125,110,100}, // TauClt[NO_TEMPS], % { 0 }, // ipada[48]: pad integers { // BaroVals[NO_BARS]: barometric pressures, (kPa x 10)) for baro correction table 800, 900, 1000, 1050, 1100, 1200 }, { // MatVals[NO_MATS]: air temperatures (degx10) for air density correction table 400, 600, 1000, 1300, 1600, 1800 }, { // BaroCorDel[NO_BARS], barometric correction table(%) - added to eq. value 0, 0, 0, 0, 0, 0 }, { // AirCorDel[NO_MATS], air density correction table(%) - added to eq. value 0, 0, 0, 0, 0, 0 }, { // MAFFlow[NO_MAFS], MAF flows (mg/ secx10) for below corrections. 100, 2000, 4000, 6000, 8000, 10000, 12000, 14000, 16000, 18000, 20000, 25000 }, { // MAFCor[NO_MAFS], Corrections to maf factor table (%) for real time tuning/ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }, // intake mods.as // function of MAFFlow. 100% = no correction. 1200, 3000, // MAFRPM1,MAFRPM2. For MAFOption=2, if rpm < rpm1 use MAP only; // > rpm2 use MAF only. In between, use MAP/ MAF blend.(see next). 0, // MAFDir: If = 0, use above for MAP/ MAF blend; =1, reverse // - use MAF only below rpm1 and MAP only above rpm2. 0, // VEIXOptn =1 means use (map*1000)/baro instead of map as the index // into ve, afr, spk tables. 1500, 800, //MatRtdRPMHi,MatRtdRPMLo: Full MAT spark retard applied above Hi rpm, none // applied below Lo rpm, interpolate in between. 0,0, // FuelAdj,SpkAdj, Adjustments for fuel(% +/- to gamma),spark(degx10),idle step,spare 0,0 // IdleAdj,SprAdj for GPIO boards. }; const unsigned char in2padding[N_PADDING(inputs2)] EEPROM_ATTR ={0}; // 1024 bytes = 1 block flash #ifndef GCC_BUILD #pragma ROM_VAR DEFAULT #pragma ROM_VAR OVF_ROM #endif // IAC stepper motor sequence const unsigned char IACCoilA[8] = {0,0,1,1,0,0,1,1}; const unsigned char IACCoilB[8] = {1,0,0,1,1,0,0,1}; // array of TC overflow numbers at which to increment secs cntr. const unsigned int TC_ovfla[60] = { 23, 46, 69, 92, 114, 137, 160, 183, 206, 229, 252, 275, 298, 320, 343, 366, 389, 412, 435, 458, 481, 504, 526, 549, 572, 595, 618, 641, 664, 687, 710, 732, 755, 778, 801, 824, 847, 870, 893, 916, 938, 961, 984, 1007, 1030, 1053, 1076, 1099, 1122, 1144, 1167, 1190, 1213, 1236, 1259, 1282, 1305, 1328, 1350, 1373 }; const char RevNum[20] = { // revision no: // only change for major rev and/or interface change. "MSII Rev 2.89000 " }, Signature[32] = { // program title. // Change this every time you tweak a feature. "** V2.89Embedded Code by B&G **" }; #ifndef GCC_BUILD #pragma ROM_VAR DEFAULT #endif // ram copy of inputs inputs1 inpram; inputs2 in2ram; // sensor variables int last_tps,last_map,last_trendmap; // fuel variables unsigned int pw_open,PrimeP,AWEV,AWC; unsigned int Fl1OCt_overflow,Fl2OCt_overflow; unsigned long Fl1TimerComp,Fl2TimerComp; unsigned char pwm1_on,pwm2_on,cut_fuel,PrimeDone; unsigned long pwm1_time,pwm2_time; int wrmtmp; // ignition variables char ICtt_ovflo; unsigned char SPK,CHG, pulse_no,ign_state[2],ign_setpin[2],ICintmask[2], OCintmask[2],IgnOCpinstate,first_edis,PulseTol,cyl_no,mask_teeth, last_tooth,tooth_no,tach_tooth,tach_seq[2],first_start,ic_ret_flg, stop_ecorr[2],kluge1[2],kluge2[2]; char ICint,OCint,cam_sync; long adv_us,charge_time,coil_dur_set,coil_dur_chan[2]; int spk_delay,ICt_overflow[2],IgnOCt_overflow[2],coil_dur,ic_adv_deg; int tooth_spark_x100[2]; // tooth_nox100 unsigned int nbuf_teethx100,no_skipx100; // tooth_nox100 volatile unsigned short *pTIC[2],*pTOC[2]; unsigned long IgnTimerComp[2],dtpred,setlmms[2],setlmms2[2]; // IAC variables unsigned long motor_time; int motor_step, IACmotor_pos,last_iacclt,tble_motor_pos; char IAC_moving,IACmotor_reset,IdleCtl; // General variables unsigned int mms,millisec,burn_flag,iacpwmctr; unsigned int TC_ovflow,TC_ov_ix; unsigned long lmms,t_enable_IC[2],t_chgoff[2],Rpm_Coeff,ltch_lmms, rcv_timeout,adc_lmms,lastTPS_lmms,lastMAP_lmms; unsigned int asecount,mafraw; unsigned char flocker,tpsaclk,egocount,igncount,altcount,next_adc,first_adc, txmode,tble_idx,burn_idx,synch,trig_ret_mode,crank_trig_mode,reinit_flag, egopstat,afrSL, FSensStat,knk_clk,knk_clk_test,knk_stat,knk_count, amc_burn_flag,mafblend,pump_delay,first_reset,reset_xtau; int ego1err,ego1errm1,ego2err,ego2errm1,sego1err,sego2err,egoKPX, blendve1,blendve2; unsigned int afrdl1,afrdl2,afrtgt_skip, FSens_Pd,FSensFreq; unsigned long MAFCoef; /* Clocks: - igncount: counts up each tach pulse, cleared when hit Divider pulses (and injection occurs). - asecount: counts up each time igncount = 0 (each Divider pulses). - egocount: counts up each tach pulse, cleared when hits EgoCountCmp - tpsaclk: counts every .1 sec - altcount: flips 0,1,0,1... on each injection, resulting in firing alternate injector banks if Alternate option. */ unsigned int txcnt,txgoal,rxoffset,rxnbytes,rxcnt,kill_ser_t,vfy_fail; char AMCmode,AMCdve_change,ego_cloop,bad_ego_flag,bad_ego_ltch,PWcalc, ltch_PWcalc,MAFPin,kill_ser,vfy_flg; unsigned char impulse_count,spkN2O; unsigned int AMCclk,AMCve_clk,AMCNSum,AMCburn_clk,AMCram_updates, XTX,XTm,XTf,burn_tooth_no,lastImpulseSec; int knk_tble_adv,ffspkdel,mafmap,trendmap,kpadiv; unsigned long Tau,sum_dltau[2],PWcalc_lmms,lmms_lostSynch; long XTM[2]; // CAN variables unsigned long cansendclk,ltch_CAN=0xFFFFFFFF; unsigned short can_status; unsigned char can_clr_stat,can_reset,can_id,getCANdat=0,burnCANdat=0; struct canmsg { /* CAN Xmt mssge ring buffer: can[0] is to hold Rx,TxISR messages, can[1] for main loop messages (so don't get clobbered by ISR) cxno = no msgs in queue waiting to be sent out cxno_in = index for inserting a msg in queue (incr after insert) cxno_out = index for sending out a msg (incr after load CAN buf) msg_type = CMD,REQ,RESP,XSUB (= set value, request value, respond to a request for value, execute a subroutine) varblk,varoffset,varbyte = blk no of data structure, byte offset from start of structure, no. bytes of data datbuf = the actual data (max of 8 bytes) dest = id no. of device to which msg being sent. */ unsigned char cxno,cxno_in,cxno_out; unsigned char cx_msg_type[NO_CANMSG], cx_myvarblk[NO_CANMSG], cx_destvarblk[NO_CANMSG], cx_dest[NO_CANMSG],cx_varbyt[NO_CANMSG]; unsigned short cx_myvaroff[NO_CANMSG],cx_destvaroff[NO_CANMSG]; unsigned char cx_datbuf[NO_CANMSG][8]; // max msg data = 8 bytes } can[2]; // pointers for spare port pins volatile unsigned char *pPTMpin[8], *pPTTpin[8], *pPTApin0, *pTacho; unsigned char dummyReg,lst_pval[NPORT]; // allocate space in ram for flash burner core volatile unsigned char RamBurnPgm[36]; // rs232 Outputs to pc typedef struct { unsigned int seconds,pw1,pw2,rpm; // pw in usec int adv_deg; // adv in deg x 10 unsigned char squirt,engine,afrtgt1,afrtgt2; // afrtgt in afr x 10 (or Vx100) /* ; Squirt Event Scheduling Variables - bit fields for "squirt" variable above inj1: equ 0 ; 0 = no squirt; 1 = inj squirting inj2: equ 1 ; Engine Operating/Status variables - bit fields for "engine" variable above ready: equ 0 ; 0 = engine not ready; 1 = ready to run (fuel pump on or ign plse) crank: equ 1 ; 0 = engine not cranking; 1 = engine cranking startw: equ 2 ; 0 = not in startup warmup; 1 = in startw enrichment warmup: equ 3 ; 0 = not in warmup; 1 = in warmup tpsaen: equ 4 ; 0 = not in TPS acceleration mode; 1 = TPS acceleration mode tpsden: equ 5 ; 0 = not in deacceleration mode; 1 = in deacceleration mode */ unsigned char wbo2_en1,wbo2_en2; // from wbo2 - indicates whether wb afr valid int baro,map,mat,clt,tps,batt,ego1,ego2,knock, // baro - kpa x 10 // map - kpa x 10 // mat, clt deg(C/F)x 10 // tps - % x 10 // batt - vlts x 10 // ego1,2 - afr x 10 // knock - volts x 100 egocor1,egocor2,aircor,warmcor, // all in % tpsaccel,tpsfuelcut,barocor,gammae, // tpsaccel - acc enrich(.1 ms units) // tpsfuelcut - % // barocor,gammae - % vecurr1,vecurr2,iacstep,cold_adv_deg, // vecurr - % // iacstep - steps // cold_adv_deg - deg x 10 tpsdot,mapdot, // tps, map rate of change - %x10/.1 sec, // kPax10 / .1 sec coil_dur; // msx10 coil chge set by ecu unsigned int maf; // maf (mg/secx10) int kpa, // kpa (=map,mafmap or tps) fuelcor; // fuel composition correction - % unsigned char port_status, // Bits indicating spare port status. knk_rtd; // amount of ign retard (degx10) subtracted from normal advance. int XTfcor0; // X,tau fuel correction,%, for inj bank 1, to ensure correct amount // of fuel gets to the combustion chamber. int egoV1,egoV2; // ego sensor readbacks in Vx100 unsigned int amc_updates; int kpaix, XTfcor1; int spare[10]; unsigned int tachCount; // counts tach pulses for use in correlating // datalog values with tach pulses (at lo rpm) unsigned char ospare,cksum; unsigned long dt3; // delta t bet. rpm pulses (us) } variables; variables outpc, txbuf; #ifndef GCC_BUILD #pragma ROM_VAR OVF_ROM #endif typedef struct { unsigned int *addrRam; unsigned int *addrFlash; unsigned int n_bytes; } tableDescriptor; const tableDescriptor tables[NO_TBLES] = { { NULL, (unsigned int *)cltfactor_table, sizeof(cltfactor_table) }, { NULL, (unsigned int *)matfactor_table, sizeof(matfactor_table) }, { NULL, (unsigned int *)egofactor_table, sizeof(egofactor_table) }, { NULL, (unsigned int *)maffactor_table, sizeof(maffactor_table) }, { (unsigned int *)&inpram, (unsigned int *)&in1flash, sizeof(inputs1) }, { (unsigned int *)&in2ram, (unsigned int *)&in2flash, sizeof(inputs2) }, { (unsigned int *)&txbuf, NULL, sizeof(txbuf) }, { (unsigned int *)&outpc, NULL, sizeof(outpc) }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, (unsigned int *)&RevNum, sizeof(RevNum) } }; #ifndef GCC_BUILD #pragma ROM_VAR DEFAULT #endif #define tableInit(iTable) (void)memcpy(tables[iTable].addrRam, tables[iTable].addrFlash, tables[iTable].n_bytes) #define tableByteRam(iTable, iByte) ((unsigned char *)tables[iTable].addrRam + iByte) #define tableWordRam(iTable, iWord) (tables[iTable].addrRam + iWord) #define tableByteFlash(iTable, iByte) ((unsigned char *)tables[iTable].addrFlash + iByte) #define tableWordFlash(iTable, iWord) (tables[iTable].addrFlash + iWord) #define tableBytes(iTable) (tables[iTable].n_bytes) #define tableWords(iTable) ((tables[iTable].n_bytes+1)/2) // Round up // Prototypes - Note: ISRs prototyped above. void main(void) FAR_TEXT1_ATTR; void get_adc(char chan1, char chan2); int move_IACmotor(void) TEXT1_ATTR; void fburner(unsigned int* progAdr, unsigned int* bufferPtr, unsigned int no_words) TEXT1_ATTR; int barocor_eq(int baro) TEXT1_ATTR; int aircor_eq(int mat) TEXT1_ATTR; void set_spr_port(char port, char val) TEXT1_ATTR; int coil_dur_table(int delta_volt) TEXT1_ATTR; int knk_thresh_calc(unsigned int rpm) TEXT1_ATTR; int intrp_2ditable(unsigned int x, int y, unsigned char nx, unsigned char ny, unsigned int * x_table, int * y_table, int * z_table) TEXT1_ATTR; void CanInit(void) TEXT1_ATTR; void can_xsub01(void) FAR_TEXT1_ATTR; void Flash_Init(unsigned long oscclk) TEXT1_ATTR; void Flash_Erase_Sector(unsigned int *address) FAR_TEXT1_ATTR; void Flash_Write_Word(unsigned int *address, unsigned int data) TEXT1_ATTR; #ifndef GCC_BUILD #pragma CODE_SEG OTHER_ROM #endif void tpsaen(void); void ego_calc(void); void amc(void); unsigned char afrLF_calc(long t) TEXT1_ATTR; void Ign_TimerIn(void); #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #endif extern void reboot(void) FAR_TEXT1_ATTR; extern void monitor(void) FAR_TEXT1_ATTR; extern void SpSub(void); extern void NoOp(void); void main(void) { int ix; int tmp1,tmp2,tmp3,tmp4; unsigned int utmp1,utmp2,ppw[2]; long lsum,lsum1,lsum2,beta,ltmp; unsigned long ultmp,ultmp2; int start_clt; unsigned int tcrank_done,tcold_pos,tASTol; char ctmp1,ctmp2,Tau0,firstIAC; unsigned char uctmp,inj_sqrt; PPAGE = 0x3C; // initalize PLL - reset default is Oscillator clock // 8 MHz oscillator, PLL freq = 48 MHz, 24 MHz bus, // divide by 16 for timer of 2/3 usec tic PLLCTL &= 0xBF; // Turn off PLL so can change freq SYNR = 0x02; // set PLL/ Bus freq to 48/ 24 MHz REFDV = 0x00; PLLCTL |= 0x40; // Turn on PLL // wait for PLL lock while (!(CRGFLG & 0x08)); CLKSEL = 0x80; // select PLL as clock // wait for clock transition to finish for (ix = 0; ix < 60; ix++); // open flash programming capability Flash_Init(8000); //inp_spare used to force inpflash, flashve_table into sectors. // Must use in program or it won't use up the entire sector. POS CW // ignores the pragma making this stupid statement necessary. ix = in1padding[0]; ix = in2padding[0]; if((int)RamBurnPgm & 0x0001) { // odd address - cpy to even one (void)memcpy((void *)RamBurnPgm,NoOp,1); // cpy noop to 1st location (void)memcpy((void *)&RamBurnPgm[1],SpSub,32); // cpy flashburn core pgm to ram } else (void)memcpy((void *)RamBurnPgm,SpSub,32); // cpy flashburn core pgm to ram // load all user inputs from Flash to RAM tableInit(4); tableInit(5); // set up i/o ports // - port M2 is fast idle solenoid // - port M3 is inj led // - port M4 is accel led // - port M5 is warmup led // - port E0 is flex fuel sensor input // - port E4 is fuel pump // - port P5 is bootload pin (input) // - port T6 is IAC Coil A // - port T7 is IAC Coil B // - port B4 is IAC Enable // - port A0 is Knock Enable (if set, means retard timing) DDRM |= 0xFC; // port M - all outputs, full drive by default DDRE |= 0x10; // port E4 - output, E0 is input DDRT |= 0xC0; // port T6-7 - outputs DDRB |= 0x10; // port B4 - output DDRA |= 0x01; // port A0 - output // Set pointers to real port addresses for(ix = 0; ix < 8; ix++) { pPTMpin[ix] = pPTM; pPTTpin[ix] = pPTT; } pPTApin0 = pPORTA; // reset those pointers to pins which are to be used as alternate outputs outpc.port_status = 0; for(ix = 0;ix < NPORT; ix++) { if(inpram.spr_port[ix]) { // pin output from normal function goes to dumy reg in ram if(ix < 4) pPTMpin[ix + 2] = &dummyReg; else if(ix < 6) pPTTpin[ix + 2] = &dummyReg; else pPTApin0 = &dummyReg; set_spr_port((char)ix, inpram.init_val[ix]); lst_pval[ix] = inpram.init_val[ix]; } } if(inpram.ECUType >= 2) { pPTMpin[3] = &dummyReg; // inj led in usquirt/tacho in sequencer // both reflect tach pulse, that is, pTacho // controls pin but it also lights led pTacho = pPTM; *pTacho |= 0x08; // Turn off tach pulse - active Lo } else { pTacho = &dummyReg; } // turn off fidle solenoid, leds *pPTMpin[2] &= ~0x04; *pPTMpin[3] &= ~0x08; *pPTMpin[4] &= ~0x10; *pPTMpin[5] &= ~0x20; *pPTTpin[6] &= ~0xC0; // turn off IAC coils (do both pins[6,7] in 1 cmd) if(inpram.IdleCtl == 2) { PORTB |= 0x10; // disable current to motor (set bit= 1) } else { // IdleCtl = 3, 5 or no stepper motor (set bit = 0) PORTB &= ~0x10; // enable current to motor always(set bit= 0) } *pPTApin0 &= ~0x01; // no knock signal // set all unused (even unbonded) ports to inputs with pullups DDRA &= 0x01; DDRB &= 0x10; DDRE &= 0x10; PUCR |= 0x13; // enable pullups for ports E, B and A DDRP = 0x00; PERP = 0xFF; // enable pullup resistance for port P DDRJ &= 0x3F; PERJ |= 0xC0; // enable pullup resistance for port J6,7 DDRS &= 0xF3; PERS |= 0x0C; // enable pullup resistance for port S2,3 reinit_flag = 0; // set up CRG RTI Interrupt for .128 ms clock. CRG from 8MHz oscillator. mms = 0; // .128 ms tics millisec = 0; // 1.024 ms clock (8 tics) for adcs lmms = 0; cansendclk = 7812; ltch_lmms = 0; lastTPS_lmms = 0; lastMAP_lmms = 0; outpc.seconds = 0; // (1.0035) secs lastImpulseSec = 0; burn_flag = 0; amc_burn_flag = 0; RTICTL = 0x10; // load timeout register for .128 ms (smallest possible) CRGINT |= 0x80; // enable interrupt CRGFLG = 0x80; // clear interrupt flag (0 writes have no effect) // Set up SCI (rs232): SCI BR reg= BusFreq(=24MHz)/16/baudrate //inpram.baud = 115200; // Use this for debugging corrupted flash sectors. SCI0BDL = (unsigned char)(1500000/inpram.baud); ltmp = (150000000/inpram.baud) - ((long)SCI0BDL*100); if(ltmp > 50)SCI0BDL++; // round up //inpram.baud = in1flash.baud; // See above. SCI0CR1 = 0x00; SCI0CR2 = 0x24; // TIE=0,RIE = 1; TE=0,RE =1 txcnt = 0; rxcnt = 0; txmode = 0; txgoal = 0; kill_ser = 0; vfy_flg = 0; rcv_timeout = 0xFFFFFFFF; SPK = 1 - inpram.spkout_hi_lo; // 0=spk low, but inverted logic CHG = 1 - SPK; // after transistor // Initialize timers: // Note: Inj OC and PWM are Nanded. // -when turn on inj1 i/o, also enable pwm1 @ 100 % duty & // set pwm1 timer = 0 (.1 ms units). // -When pwm1 timer = InjPWMTim, set duty cycle to InjPWMDty. // -when done injecting, turn off inj1 i/o and pwm1. // TC0: Input capture (tach) - no pullup (default) // TC1: Output compare for injector output - bank 1 // TC2: PWM2 for injector bank 1 (or IC for 2nd ignition input) // TC3: Output compare for injector output - bank 2 // TC4: PWM4 for injector bank 2 (or OC for 2nd ignition output) // TC5: Ouput compare for 1st ignition output // Initially put timer pins TC2,TC4, TC5 in safe state // disable PWM MODRR = 0; PWME = 0; // disconnect timer TCTL2 &= ~0x30; // IC2 TCTL1 &= ~0x0F; // IgnOC1,2 // set TC2 as input DDRT &= ~0x04; PERT &= ~0x04; // no pullup on TC2 // Set TC4, 5 as outputs, value = 0, rather than inputs with pullups, // since this can cause Ign1 On when power up after a bootload, // and ECUType has not yet been set. DDRT |= 0x30; PTT &= ~0x30; // Set prescaler to 16. This divides bus clk (= PLLCLK/2 = 24 MHz) // by 16. This gives 1.5 MHz timer, 1 tic = 2/3 us. TSCR2 = 0x04; TSCR2 |= 0x80; // enable timer overflow interrupt (TOI) pTIC[0] = pTC0; ICintmask[0] = 0x01; pTOC[0] = pTC5; OCintmask[0] = 0x20; t_chgoff[0] = 0xFFFFFFFF; t_chgoff[1] = 0xFFFFFFFF; first_reset = 1; ign_reset(); first_reset = 0; lmms_lostSynch = 0; // override setting in ign_reset the very first time first_edis = 1; first_start = 1; // IACStart = enough steps to set IAC wide open. // Set current IAC position to IACStart (all way closed), then move // to the 0 position (wide open - fast idle) and zero out. After // this all subsequent IAC commands will gradually close air passage // as clt temp rises. // Note: Ignore all move commands until finished current move. IdleCtl = inpram.IdleCtl; firstIAC = 1; IAC_moving = 0; motor_step = -1; if(IdleCtl == 4) IACmotor_reset = 1; else IACmotor_reset = 0; outpc.iacstep = inpram.IACStart; // set current motor step position // to closed since don't have actual position. IACmotor_pos = 0; // command motor step position to wide open tble_motor_pos = 0; if((IdleCtl > 1) && (IdleCtl != 4)) (void)move_IACmotor(); last_iacclt = -3200; iacpwmctr = 0; // set up ADC processing // ATD0 // - AN0 is MAP or MAF (MAFOption = 1, MAFPin = 0) // - AN1 is MAT // - AN2 is CLT // - AN3 is TPS // - AN4 is BAT // - AN5 is EGO1 (NB or WB) // - AN6 is Spare or BARO (BaroOption =2) or EGO2 (EgoOption = 2 or 4) // or MAF (MAFOption = 2, MAFPin = 1). // - AN7 is Spare or KNOCK or MAF (MAFOption = 2, MAFPin = 2). // Set up ADCs so they continuously convert, then read result registers // every millisecond MAFPin = (inpram.MAFOption & 0xF0) >> 4; next_adc = 0; // specifies next adc channel to be read ATD0CTL2 = 0x40; // leave interrupt disabled, set fast flag clear ATD0CTL3 = 0x00; // do 8 conversions/ sequence ATD0CTL4 = 0x67; // 10-bit resoln, 16 tic cnvsn (max accuracy), // prescaler divide by 16 => 2/3 us tic x 18 tics ATD0CTL5 = 0xB0; // right justified,unsigned, continuous cnvsn, // sample 8 channels starting with AN0 ATD0CTL2 |= 0x80; // turn on ADC0 // wait for ADC engine charge-up or P/S ramp-up for(ix = 0; ix < 160; ix++) { while(!(ATD0STAT0 >> 7)); // wait til conversion complete ATD0STAT0 = 0x80; } // get all adc values first_adc = 1; get_adc(0,7); first_adc = 0; if((inpram.BaroOption == 0) || (inpram.MAFOption == 1)) outpc.baro = 1000; // kPa x 10 else if(inpram.BaroOption == 1) { outpc.baro = outpc.map; // kPa x 10 // limit baro in case reset while car running if(outpc.baro < 800) outpc.baro = 800; else if(outpc.baro > 1200) outpc.baro = 1200; } adc_lmms = lmms; // Initialize variables flocker = 0; if((inpram.DualSpkOptn != 1) && (inpram.DualSpkOptn != 6)) { if(inpram.EngStroke == 0) // 4 stroke if((inpram.OddAng != 0) && (inpram.no_cyl == 2) && ((inpram.DualSpkOptn == 4) || (inpram.DualSpkOptn > 6))) Rpm_Coeff = 60000000; // need since wasted + oddfire requires // 2 coils with option 4 else { if(inpram.no_cyl > 1) Rpm_Coeff = 120000000; else { // 1 cyl if((inpram.DualSpkOptn == 4) || (inpram.DualSpkOptn > 6)) Rpm_Coeff = 60000000; else Rpm_Coeff = 120000000; } } else // 2 stroke Rpm_Coeff = 60000000; } else { // Dual In+Out (no_cyl = 2 or 4) if(inpram.DualSpkOptn == 1) { // crank signal if(inpram.EngStroke == 0) // 4 stroke if(inpram.no_cyl == 2) // 2 cyl Rpm_Coeff = 120000000; else // 4 cyl Rpm_Coeff = 240000000; // crank signal else // 2 stroke Rpm_Coeff = 120000000; } else { // cam signal (4 stroke) Rpm_Coeff = 240000000; } } Rpm_Coeff /= inpram.no_cyl; nbuf_teethx100 = (unsigned char)((inpram.No_Teeth * 3) / 60); if(nbuf_teethx100 < inpram.No_Miss_Teeth + 1) nbuf_teethx100 = inpram.No_Miss_Teeth + 1; nbuf_teethx100 *= 100; if((inpram.No_Teeth) && (inpram.DualSpkOptn > 1) && (inpram.DualSpkOptn != 6)) no_skipx100 = 200 * inpram.No_Skip_Teeth; // tooth x 100 pw_open = inpram.InjOpen; ppw[0] = 0; // us outpc.pw1 = ppw[0]; ppw[1] = 0; // us outpc.pw2 = ppw[1]; in2ram.FuelAdj = 0; in2ram.SpkAdj = 0; in2ram.IdleAdj = 0; in2ram.SprAdj = 0; pump_delay = 2 + inpram.prime_delay; // sec XTM[0] = 0; XTM[1] = 0; outpc.adv_deg = 0; // crank deg x 10 spkN2O = 0; ic_adv_deg = outpc.adv_deg - inpram.adv_offset; coil_dur = inpram.max_coil_dur; // msx10 coil_dur_set = coil_dur * 100; // us outpc.coil_dur = coil_dur; // msx10 tASTol = 0; PulseTol = inpram.CrnkTol; outpc.kpa = outpc.map; // kPa x 10 outpc.kpaix = outpc.kpa; if(!in2ram.VEIXOptn) kpadiv = outpc.baro; else kpadiv = 1000; last_tps = outpc.tps; // % x 10 if((inpram.MAFOption & 0x0F) != 1) last_map = outpc.map; // kPa x 10 else last_map = 1000; // kpa x 10 - can't get mafmap at rpm=0 last_trendmap = last_map; outpc.mapdot = 0; outpc.tpsdot = 0; if(inpram.EgoOption <= 2) outpc.afrtgt1 = 45; // Vx100 else outpc.afrtgt1 = 147; // afr x 10 outpc.afrtgt2 = outpc.afrtgt1; outpc.aircor = 100; outpc.vecurr1 = 100; outpc.vecurr2 = outpc.vecurr1; outpc.barocor = 100; outpc.warmcor = 100; outpc.cold_adv_deg = 0; // crank deg x 10 outpc.wbo2_en1 = 1; outpc.wbo2_en2 = 1; outpc.egocor1 = 100; outpc.egocor2 = 100; outpc.tpsfuelcut = 100; outpc.gammae = 100; outpc.XTfcor0 = 100; outpc.XTfcor1 = 100; outpc.tpsaccel = 0; tcrank_done = 0xFFFF; tcold_pos = 0xFFFF; FSensStat = 0; FSens_Pd = 0; outpc.fuelcor = 100; // % ffspkdel = 0; // degx10 AMCmode = 0; AMCclk = 0; ego_cloop = 0; bad_ego_flag = 0; AMCram_updates = 0; outpc.amc_updates = 0; AMCburn_clk = outpc.seconds; tooth_no = 0; last_tooth = inpram.No_Teeth - inpram.No_Miss_Teeth; // best tooth to do flash erase if(inpram.No_Teeth > 30) burn_tooth_no = (3 * inpram.No_Teeth) / 4; else if(inpram.No_Teeth > 0) burn_tooth_no = last_tooth; else burn_tooth_no = 0; spk_delay = inpram.fet_delay; MAFCoef = 6184075; if(inpram.CID) MAFCoef /= inpram.CID; /* Initialize CAN comms */ can_reset = 0; can_id = inpram.mycan_id; CanInit(); // make IC highest priority interrupt HPRIO = 0xEE; // enable global interrupts ENABLE_INTERRUPTS // Check HW configuration compatible with ECU Type // Pertains to Timer pins TC2, TC4 ix = 1; while((inpram.ECUType < 1) || (inpram.ECUType > NO_ECUS)) { // Unknown ECU - wait until ECU type is defined DISABLE_INTERRUPTS ultmp = lmms + 7812; // 1 sec rpm, f/pump toggle ENABLE_INTERRUPTS // flip rpm to indicate user must input ECU type outpc.rpm = ix * 8000; if(ix) PORTE |= 0x10; // turn on fuel pump else PORTE &= ~0x10; // turn off fuel pump ix = 1 - ix; for (;;) { DISABLE_INTERRUPTS if(lmms > ultmp) break; ENABLE_INTERRUPTS } ENABLE_INTERRUPTS } outpc.rpm = 0; PORTE &= ~0x10; // turn off fuel pump if(inpram.ECUType == 1) { // MS II TIOS |= 0x3E; // Timer ch 0 = IC, ch 1-5 = OC & PWM, // ch 6,7 = I/O output DDRT |= 0x20; // ch 5 = o/p TCTL2 |= 0x88; // bit OM1,3,5 = 1. OC output line high or // low iaw OL1,3,5 (not toggle or disable) TCTL1 |= 0x08; // ensure not charging ignition coil TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); // set OC o/p in OL5 CFORC |= 0x20; // force output in OL5 onto OC pin // Set up injector PWMs - PWM2, 4 MODRR = 0x14; // Make Port T pins 2,4 be PWM, but // leave pwms disabled initially PWMPOL = 0x14; // polarity = 1, => go hi when start PWMCLK = 0x14; // select scaled clocks SB, SA PWMPRCLK = 0x00; // prescale A,B clocks = bus = 24 MHz PWMSCLA = 0x0C; // pwm clk = SA clk/(2*SCLA) = 24MHz/24 = 1 us clk PWMSCLB = 0x0C; // pwm clk = SB clk/(2*SCLB) = 24MHz/24 = 1 us clk PWMCAE = 0x00; // standard left align pulse: ----- // | |______ // duty // <---period---> PWMPER2 = inpram.InjPWMPd; // set PWM period (us) PWMPER4 = inpram.InjPWMPd; // set PWM period (us) pTIC[1] = pTC0; ICintmask[1] = ICintmask[0]; pTOC[1] = pTC5; OCintmask[1] = OCintmask[0]; } else if(inpram.ECUType == 2) { // Microsquirt PWME = 0; // No pwms pwm1_time = 0xFFFFFFFF; pwm2_time = 0xFFFFFFFF; if(!inpram.DualSpkOptn) { TIOS |= 0x2A; // Timer ch 0 = IC, ch 1,3, 5 = OC, // ch 6,7 = I/O output DDRT |= 0x20; // ch 5 = o/p TCTL2 |= 0x88; // bit OM1,3,5 = 1. OC output line high or // low iaw OL1,3,5 (not toggle or disable) TCTL1 |= 0x08; // ensure not charging ignition coils TCTL1 = (TCTL1 & 0xF8) | SPK | (SPK << 2); // set OC o/p in OL5 CFORC |= 0x20; // force output in OL5 onto OC pin pTIC[1] = pTIC[0]; ICintmask[1] = ICintmask[0]; pTOC[1] = pTOC[0]; OCintmask[1] = OCintmask[0]; } else { // Dual Spark mode TIOS |= 0x3A; // Timer ch 0,2 = IC, ch 1,3,4,5 = OC, // ch 6,7 = I/O output DDRT |= 0x30; // ch 4,5 = o/p TCTL2 |= 0x88; // bit OM1,3,4,5 = 1. OC output line high or // low iaw OL1,3,4,5 (not toggle or disable) TCTL1 |= 0x0A; // ensure not charging ignition coils TCTL1 = (TCTL1 & 0xFA) | SPK | (SPK << 2); // set OC o/p in OL4, OL5 CFORC |= 0x30; // force output in OL4,OL5 onto OC pin if((inpram.DualSpkOptn < 4) || (inpram.DualSpkOptn > 5)) { // Dual ICs and OCs pTIC[1] = pTC2; ICintmask[1] = 0x04; pTOC[1] = pTC4; OCintmask[1] = 0x10; } else { // One IC and Dual OCs pTIC[1] = pTIC[0]; ICintmask[1] = ICintmask[0]; pTOC[1] = pTC4; OCintmask[1] = 0x10; } } } // Check for Injector Test Mode: Squirt n pulses of duration pw us, then // wait until: MT repeat cmd (will repeat the squirts) or user clears // InjTestOption in MT, which will cause reboot back to normal operation. // To remain in test mode, leave Test Mode on, burn ecu, and turn off power. RESTRT_IT: if(inpram.InjTestOption) { utmp1 = 0; outpc.pw1 = inpram.InjTestPW; outpc.pw2 = outpc.pw1; outpc.engine = 0x01; PORTE |= 0x10; // Turn on fuel pump IT_SCHED: TIE &= 0xFE; // disable ign IC // Turn On Inj1 and 2 TCTL2 |= 0x44; // set output hi in OL1,OL3 CFORC |= 0x0A; // force high ltmp = TCNT; // grab now to get max accuracy in pw *pPTMpin[3] |= 0x08; // turn on inj led outpc.squirt |= 0x03; // inj1,2 squirting if(!inpram.DualSpkOptn) { uctmp = (unsigned char)(((int)inpram.InjPWMTim*8)/10); DISABLE_INTERRUPTS pwm1_time = lmms + uctmp; pwm2_time = pwm1_time; // .128 ms ENABLE_INTERRUPTS pwm1_on = 0; pwm2_on = 0; PWMPER2 = inpram.InjPWMPd; // set PWM period (us) PWMPER4 = inpram.InjPWMPd; // set PWM period (us) PWMDTY2 = PWMPER2; // set PWM duty to 100 % PWMDTY4 = PWMPER4; // set PWM duty to 100 % PWMCNT2 = 0x00; // clear counter PWMCNT4 = 0x00; // clear counter PWME |= 0x14; // enable PWM2, 4 } // Set up to turn Off Inj1,2 when get to pw us ltmp += ((3*(long)inpram.InjTestPW)>>1); // 2/3 us utmp2 = (unsigned short)(ltmp >> 16); DISABLE_INTERRUPTS Fl1TimerComp = ltmp; Fl2TimerComp = Fl1TimerComp; Fl1OCt_overflow = utmp2; Fl2OCt_overflow = Fl1OCt_overflow; TC1 = (unsigned short)(Fl1TimerComp & 0xFFFF); // load OC compare reg TC3 = TC1; if(Fl1OCt_overflow <= 1) { // don't need to handle <= 1 overflow because TCNT will not // = TimerComp until after the overflow as long as pw < ~43 ms Fl1OCt_overflow = 0; Fl2OCt_overflow = 0; TCTL2 &= ~0x44; // set output lo in OL1, OL3 TIE |= 0x0A; // Enable Inj1,2 OC interrupt TFLG1 = 0x0A; // clear OC (pending) interrupt flags } else { // Disable Inj1,2 OC interrupt until ovflows done // do below to allow enough time to set OC pin in overflow isr if(TC1 < 30)TC1 = 30; if(TC3 < 30)TC3 = 30; TIE &= ~0x0A; } ENABLE_INTERRUPTS // wait til injections done - indicated when pins go low while((PTIT & 0x0A)); // wait specified time off DISABLE_INTERRUPTS ultmp = lmms; ENABLE_INTERRUPTS ltch_lmms = ultmp; for (;;) { while(lmms == ultmp); // wait til increments if((((lmms - ltch_lmms) * 128) / 100) > inpram.InjTestOff) // msx10 break; ultmp = lmms; } utmp1++; if(utmp1 < inpram.InjTestNoSqrts) goto IT_SCHED; PORTE &= ~0x10; // Turn off fuel pump while(inpram.InjTestOption == 1); // wait for user command if(inpram.InjTestOption > 1) { // repeat test inpram.InjTestOption = 1; goto RESTRT_IT; } else { // Test mode turned Off - want to reboot for norm operation // if flash changed, reburn to 0 which is the ram value if(in1flash.InjTestOption) { // burn flash 512 byte(256 word) sector(s) burn_flag = 1; flocker = 0xCC; while(burn_flag <= tableWords(4)) { fburner(tableWordFlash(4, 0), tableWordRam(4, 0), tableWords(4)); burn_flag++; } (void)reboot(); } } } // Prime Pulse - shoot 1 prime pulse of length PrimeP ms x 10 start_clt = outpc.clt; if(!inpram.CWOption) { if(outpc.clt >= inpram.temp_table[NO_TEMPS-1]) { PrimeP = (unsigned short)inpram.PrimePH; AWEV = (unsigned short)inpram.AWEVH; AWC = (unsigned short)inpram.AWCH; } else if(outpc.clt <= inpram.temp_table[0]) { PrimeP = (unsigned short)inpram.PrimePU; AWEV = (unsigned short)inpram.AWEVU; AWC = (unsigned short)inpram.AWCU; } else { ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.PrimePH - inpram.PrimePU); PrimeP = (unsigned short)(inpram.PrimePU + (ltmp / (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // msx10 ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.AWEVH - inpram.AWEVU); AWEV = (unsigned short)(inpram.AWEVU + (ltmp / (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // % ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.AWCH - inpram.AWCU); AWC = (unsigned short)(inpram.AWCU + (ltmp / (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // cycles } } else { // custom warmup tables PrimeP = (unsigned short)CW_table(outpc.clt,in2ram.CWPrime); // msx10 AWEV = (unsigned short)CW_table(outpc.clt,in2ram.CWAWEV); // % AWC = (unsigned short)CW_table(outpc.clt,in2ram.CWAWC); // cycles } if(PrimeP) { if(PrimeP < 655) outpc.pw1 = PrimeP * 100; // us else outpc.pw1 = 65535; // output railed, but primep can go > seconds outpc.pw2 = outpc.pw1; // Turn on fuel pump PORTE |= 0x10; // wait prime delay secs unless start cranking while((outpc.seconds < inpram.prime_delay) && !outpc.rpm); // Turn On injectors & PWMs TCTL2 |= 0x44; // set outputs hi in OL1,3 CFORC |= 0x0A; // force high PrimeDone = 0; // turn on inj led *pPTMpin[3] |= 0x08; outpc.squirt |= 0x03; // both injectors squirting outpc.engine |= 0x01; // engine in ready to run status // set PWM duty (on time) to 100 % if(!inpram.DualSpkOptn) { PWMDTY2 = PWMPER2; PWMDTY4 = PWMDTY2; pwm1_on = 0; pwm1_time = 0; pwm2_on = 0; pwm2_time = 0; PWMCNT2 = 0x00; PWMCNT4 = 0x00; PWME |= 0x14; // enable PWMs } // Set up to turn Off injectors when get to pw us Fl1TimerComp = TCNT + ((300*(long)PrimeP)>>1); // 2/3 us Fl1OCt_overflow = Fl1TimerComp >> 16; Fl1TimerComp &= 0xFFFF; Fl2TimerComp = Fl1TimerComp; Fl2OCt_overflow = Fl1OCt_overflow; TC1 = (unsigned short)Fl1TimerComp; // load OC compare register TC3 = (unsigned short)Fl2TimerComp; // load OC compare register if(Fl1OCt_overflow <= 1) { Fl1OCt_overflow = 0; Fl2OCt_overflow = 0; TCTL2 &= ~0x44; // set outputs lo in OL1,3 // Enable OC interrupt TIE |= 0x0A; TFLG1 = 0x0A; // clear OC (pending) interrupt flag } else { // Disable OC interrupt TIE &= ~0x0A; } // ensure prime pulse over before cranking starts while(!PrimeDone); } if((IdleCtl == 1) && (outpc.clt < inpram.FastIdle)) *pPTMpin[2] |= 0x04; // turn on fast idle solenoid // Set up COP timeout for main loop COPCTL = 0x44; // 131 ms timeout for reboot // enable ICs if(!inpram.DualSpkOptn || ((inpram.DualSpkOptn == 4) || (inpram.DualSpkOptn == 5))) { TIE |= 0x01; } else { // dual inputs TIE |= 0x05; } // main loop for (;;) { // clear COP timeout counter ARMCOP = 0x55; ARMCOP = 0xAA; // Force No_Teeth = 0 if EDIS if((inpram.ICIgnOption & 0x10) || (inpram.ICIgnOption & 0x20)) inpram.No_Teeth = 0; // read sensors from ADCs DISABLE_INTERRUPTS ultmp = lmms; ENABLE_INTERRUPTS if((ultmp - adc_lmms) > 78) { // every 10 ms (78 x .128 ms clk) adc_lmms = ultmp; // read 10-bit ADC results, convert to engineering units and filter next_adc++; if(next_adc > 7)next_adc = 1; switch(next_adc) { case 0: // skip map/ maf (get at tach pulse + InjStart) case 3: // skip tps (get every tpsdotSample) case 5: // do ego1 in ego algorithm break; case 1: case 2: case 4: get_adc(next_adc,next_adc); // get one channel on each pass if(next_adc == 1) { // airdensity correction (%) outpc.aircor = aircor_eq(outpc.mat) + intrp_1dctable(1, outpc.mat, NO_MATS, in2ram.MatVals, 1, (unsigned char *)in2ram.AirCorDel); } break; case 6: // do ego2 in ego algorithm, get maf when tach pulse if(inpram.BaroOption == 2) get_adc(next_adc,next_adc); // get one channel on each pass if(inpram.BaroOption == 0) { outpc.barocor = 100; } else { // barometric correction (%) outpc.barocor = barocor_eq(outpc.baro) + intrp_1dctable(1, outpc.baro, NO_BARS, in2ram.BaroVals, 1, (unsigned char *)in2ram.BaroCorDel); } break; case 7: // if knock option selected, do in knock algorithm, // get maf when tach pulse if(!(inpram.knk_option & 0x0F) && (inpram.MAFOption != 0x22)) get_adc(next_adc,next_adc); // get one channel on each pass } // end switch if(synch != END_SYNCH) { PWcalc = 1; // calculate all parameters up to but not PW PWcalc_lmms = 0xFFFFFFFF; } } // end 10 ms check DISABLE_INTERRUPTS ultmp = lmms; // .128 tics ultmp2 = ltch_lmms; // .128 tics ENABLE_INTERRUPTS ultmp2 = ultmp - ultmp2; // .128 tics // check rpm for stall condition (< 50 rpm) after engine synch if(synch == END_SYNCH) { if(ultmp2 > (18750/inpram.no_cyl)) { // Engine is stalled, reset ign_reset(); } } // if no tach pulse within pump_delay sec else if(ultmp2 > pump_delay * (unsigned long)7812) { PORTE &= ~0x10; // Turn off fuel pump *pPTMpin[2] &= ~0x04; // Turn off fast idle ** Bug Fix By Guy Hill ** } // check for overdwell if(ultmp > t_chgoff[0]) { // cease charging ignition coil after fuel has dispersed TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); // set OC o/p in OL5 CFORC |= 0x20; // force output in OL5 onto OC pin t_chgoff[0] = 0xFFFFFFFF; } if(ultmp > t_chgoff[1]) { TCTL1 = (TCTL1 & 0xFE) | SPK; // set OC o/p in OL4 CFORC |= 0x10; // force output in OL4 onto OC pin t_chgoff[1] = 0xFFFFFFFF; } // check idle control if(IdleCtl == 1) { if(outpc.clt < inpram.FastIdle - inpram.IdleHyst) *pPTMpin[2] |= 0x04; // turn on fast idle solenoid else if(outpc.clt > inpram.FastIdle) *pPTMpin[2] &= ~0x04; // turn off fast idle solenoid } else if(IACmotor_reset && ((IdleCtl >= 2) && (IdleCtl <= 5))) { // In cranking mode. The very first time we crank we want to // open to crankpos, but after cranking we don't want to do // this because of an ignition reset (in which rpm = 0) if((outpc.rpm + firstIAC) && (outpc.rpm < inpram.crank_rpm)) { tmp1 = CW_table(outpc.clt,inpram.iacstep_table); if(tmp1 > inpram.IACcrankpos) { tmp1 = inpram.IACcrankpos; // want IAC open at // least this much during cranking regardless of temp } tble_motor_pos = tmp1; } // after cranking flare back to normal temperature dependent pos else if((outpc.seconds >= tcrank_done) && (outpc.seconds <= tcrank_done + inpram.IACcrankxt)) { // 0 steps is fully open (fast idle) and IACStart steps is fully closed tmp1 = CW_table(outpc.clt,inpram.iacstep_table); if(tmp1 > inpram.IACcrankpos) { tmp2 =(int)((long)(inpram.IACcrankpos - tmp1) * (tcrank_done + inpram.IACcrankxt - outpc.seconds) / inpram.IACcrankxt); tmp1 += tmp2; } tble_motor_pos = tmp1; } // switch to time based control if cold enough at startup else if((outpc.seconds >= tcold_pos) && (outpc.seconds <= tcold_pos + inpram.IACcoldxt)) { tmp1 = CW_table(outpc.clt,inpram.iacstep_table); if(tmp1 > inpram.IACcoldpos) { tmp2=(int)((long)(inpram.IACcoldpos - inpram.iacstep_table[NO_TEMPS-1]) *(tcold_pos + inpram.IACcoldxt - outpc.seconds) /inpram.IACcoldxt); tmp1 = inpram.iacstep_table[NO_TEMPS-1] + tmp2; } tble_motor_pos = tmp1; } // check if there has been a significant change in clt temp else if((outpc.clt < last_iacclt - inpram.IdleHyst) || (outpc.clt > last_iacclt)) { tble_motor_pos = CW_table(outpc.clt,inpram.iacstep_table); } tmp1 = tble_motor_pos - in2ram.IdleAdj; if(tmp1 < 0)tmp1 = 0; IACmotor_pos = tmp1; if(outpc.iacstep != IACmotor_pos) { // move IAC motor to new step position if(IdleCtl != 4) { if(move_IACmotor()) last_iacclt = outpc.clt; } else last_iacclt = outpc.clt; } // check if/ when to start extended time-based idle control if(start_clt < inpram.IACcoldtmp) { if((outpc.seconds > tcrank_done) && (tcold_pos == 0xFFFF) && (IACmotor_pos > inpram.IACcoldpos)) tcold_pos = outpc.seconds; } if((inpram.IdleCtl == 5) && (outpc.seconds > 900)) { IdleCtl = 2; if(!IAC_moving) PORTB |= 0x10; // disable current to stepper motor(bit=1) } if((tcrank_done != 0xFFFF) && (outpc.seconds > tcrank_done + inpram.IACcrankxt))firstIAC = 0; } // check if time to calculate tpsdot DISABLE_INTERRUPTS ultmp = lmms; ENABLE_INTERRUPTS tmp3 = (short)(((ultmp - lastTPS_lmms) * 128)/100); // ms x 10 if(tmp3 > inpram.tpsdotSample * (short)10) { lastTPS_lmms = ultmp; tmp1 = ATD0DR3 - inpram.tps0; // tps tmp2 = inpram.tpsmax - inpram.tps0; asm { ldy tmp1 ldd #1000 EMULS ldx tmp2 EDIVS sty tmp1 } // tmp1 is raw tps (% x 10) - apply lag filter tmp1 = tmp1 - outpc.tps; tmp2 = (short)inpram.tpsLF; asm { ldy tmp1 ldd tmp2 EMULS ldx #100 EDIVS sty tmp1 } outpc.tps += tmp1; // save last tps and calculate tpsdot // tpsdot = % x 10 change per .1 sec outpc.tpsdot = (short)(((long)1000 * (outpc.tps - last_tps)) / tmp3); last_tps = outpc.tps; } // PWcalc = 0, bypass all calcs and goto KNK // PWcalc = 1, set when no synch; do calcs every 10 ms, no sched squirt // PWcalc = 2, set when synch; do calcs every tach plse + injstart x dt, // that is, lmms >= PWcalc_lmms, but no x-tau, no sched squirt. // PWcalc = 3, same as =2, but do x-tau (if selected) and do sched squirt uctmp = 0; DISABLE_INTERRUPTS // need uctmp because CW can't correctly handle negating below conditions if((PWcalc == 1) || ((PWcalc >= 2) && (lmms >= PWcalc_lmms))) uctmp = 1; if(!uctmp) { ENABLE_INTERRUPTS goto KNK; } ltch_PWcalc = PWcalc; PWcalc = 0; // clear flag - will be reset on next tach pulse or 10 ms PWcalc_lmms = 0xFFFFFFFF; ENABLE_INTERRUPTS // calculate map/ maf once every tach pulse,if synch, or every 10 ms if not. // save map/maf reading at same point in each cyl cycle if((inpram.MAFOption & 0x0F) != 1) { // map tmp1 = inpram.mapmax - inpram.map0; tmp2 = ATD0DR0; asm { ldy tmp1 ldd tmp2 EMULS ldx #1023 EDIVS sty tmp1 } tmp1 += inpram.map0; // adcval in kPa x 10 // apply lag(IIR) filter tmp1 = tmp1 - outpc.map; tmp2 = (short)inpram.mapLF; asm { ldy tmp1 ldd tmp2 EMULS ldx #100 EDIVS sty tmp1 } outpc.map += tmp1; // kpa x 10 } if((inpram.MAFOption & 0x0F) != 0) { // maf if(MAFPin == 0) utmp1 = ATD0DR0; else if(MAFPin == 1) utmp1 = ATD0DR6; else utmp1 = ATD0DR7; utmp1 = maffactor_table[utmp1]; // apply lag filter tmp1 = utmp1 - mafraw; tmp2 = (short)inpram.mapLF; asm { ldy tmp1 ldd tmp2 EMULS ldx #100 EDIVS sty tmp1 } mafraw += tmp1; // mg/ sec x 10 // Calculate corrected maf utmp1 = intrp_1dctable(0, (int)mafraw, NO_MAFS, (int *)in2ram.MAFFlow, 0, in2ram.MAFCor); asm { ldy mafraw ldd utmp1 EMUL ldx #100 EDIV sty outpc.maf }; // Calculate equivalent MAF pressure, mafmap (kpax10) if(outpc.rpm > 0) { mafmap = (int)(((MAFCoef / outpc.aircor) * outpc.maf) / outpc.rpm); if(mafmap < 100) // limit min to 10 kPa mafmap = 100; } else { mafmap = 1000; } } // calculate mapdot DISABLE_INTERRUPTS ultmp = lmms; ENABLE_INTERRUPTS tmp1 = (short)(((ultmp - lastMAP_lmms) * 128)/100); // ms x 10 if(tmp1 > inpram.mapdotSample * (short)10) { // want elapsed time // large enough to avoid excessive fluctuation in mapdot. // Wait for next tach if not. lastMAP_lmms = ultmp; // save last map and calculate mapdot if((inpram.MAFOption & 0x0F) != 1) { // mapdot (always use map if available) // mapdot = kPa x 10 change per .1 sec outpc.mapdot = (short)(((long)1000 * (outpc.map - last_map)) / tmp1); last_map = outpc.map; } else { // mafmapdot (map not available) // mafmapdot = kPa x 10 change per .1 sec outpc.mapdot = (short)(((long)1000 * (mafmap - last_map)) / tmp1); last_map = mafmap; } } // do map trending if((inpram.MAFOption & 0x0F) != 1) { // always use map if available tmp1 = outpc.map; } else { // only maf available tmp1 = mafmap; } if(outpc.mapdot > inpram.MapThreshXTA) { tmp2 = tmp1 - last_trendmap; if(tmp2 < (inpram.trendmapLimit*10)) // assume map delayed and increment trendmap = tmp1 + tmp2; // at same rate as previous increase else trendmap = tmp1 + (inpram.trendmapLimit*10); // Limit in kPa, map= kPax10 } else trendmap = tmp1; last_trendmap = tmp1; // Calculate blended Maf/ Map if((inpram.MAFOption & 0x0F) == 2) { if(in2ram.MAFDir == 0) { if(outpc.rpm <= in2ram.MAFRPM1) { mafmap = trendmap; // use map at low rpm mafblend = 1; } else if(outpc.rpm < in2ram.MAFRPM2) { // use maf, map blend tmp1 = (in2ram.MAFRPM2 - outpc.rpm); tmp2 = (outpc.rpm - in2ram.MAFRPM1); tmp3 = (in2ram.MAFRPM2 - in2ram.MAFRPM1); mafmap = (int)((((long)tmp1 * trendmap) + ((long)tmp2 * mafmap)) / tmp3); mafblend = 2; } else { // else leave mafmap as calculated mafblend = 3; } } else { if(outpc.rpm > in2ram.MAFRPM2) { mafmap = trendmap; // use map at high rpm mafblend = 1; } else if(outpc.rpm > in2ram.MAFRPM1) { // use maf, map blend tmp1 = (outpc.rpm - in2ram.MAFRPM1); tmp2 = (in2ram.MAFRPM2 - outpc.rpm); tmp3 = (in2ram.MAFRPM2 - in2ram.MAFRPM1); mafmap = (int)((((long)tmp1 * trendmap) + ((long)tmp2 * mafmap)) / tmp3); mafblend = 2; } else { // else leave mafmap as calculated mafblend = 3; } } } // end MAFOptn = 2 (blend) else { // map only or maf only mafmap = trendmap; // needed for alpha-N processing mafblend = 0; } /* Determine if in Speed-density or Alpha-N mode. If in Alpha-N mode, set the variable "kpa" = map-like function(rpm,tps). */ if(!(inpram.FuelAlpha & 0x0F)) { outpc.kpa = mafmap; // kpa x 10 } else { switch((inpram.FuelAlpha & 0xF0) >> 4) { case 0: // use alphaN if rpm < lorpm, map or maf if rpm > hirpm if(outpc.rpm < inpram.alpha_lorpm) { tmp4 = intrp_2ditable(outpc.rpm,outpc.tps, NO_ARPMS, NO_ATPSS, &inpram.amap_rpm[0],&inpram.amap_tps[0],&inpram.alpha_map_table[0][0]); // kpa x 10 // multiply kpa x baro to emulate effect of altitude on map asm { ldy tmp4 ldd outpc.baro EMULS ldx #1000 EDIVS sty outpc.kpa } } else if(outpc.rpm > inpram.alpha_hirpm) { outpc.kpa = mafmap; // kpa x 10 } else { beta = ((long)100 * (outpc.rpm - inpram.alpha_lorpm)) / (inpram.alpha_hirpm - inpram.alpha_lorpm); tmp4 = intrp_2ditable(inpram.alpha_lorpm,outpc.tps,NO_ARPMS, NO_ATPSS,&inpram.amap_rpm[0],&inpram.amap_tps[0], &inpram.alpha_map_table[0][0]); asm { ldy tmp4 ldd outpc.baro EMULS ldx #1000 EDIVS sty tmp4 } tmp4 = (short)((tmp4 * (100 - beta)) / 100); outpc.kpa = (short)(tmp4 + ((mafmap * beta) / 100)); } break; case 1: // use alphaN if rpm > hirpm, map or maf if rpm < lorpm if(outpc.rpm > inpram.alpha_hirpm) { tmp4 = intrp_2ditable(outpc.rpm,outpc.tps, NO_ARPMS, NO_ATPSS, &inpram.amap_rpm[0],&inpram.amap_tps[0],&inpram.alpha_map_table[0][0]); // kpa x 10 // multiply kpa x baro to emulate effect of altitude on map asm { ldy tmp4 ldd outpc.baro EMULS ldx #1000 EDIVS sty outpc.kpa } } else if(outpc.rpm < inpram.alpha_lorpm) { outpc.kpa = mafmap; // kpa x 10 } else { beta = ((long)100 * (outpc.rpm - inpram.alpha_lorpm)) / (inpram.alpha_hirpm - inpram.alpha_lorpm); tmp4 = intrp_2ditable(inpram.alpha_hirpm,outpc.tps,NO_ARPMS, NO_ATPSS,&inpram.amap_rpm[0],&inpram.amap_tps[0], &inpram.alpha_map_table[0][0]); // multiply kpa x baro to emulate effect of altitude on map asm { ldy tmp4 ldd outpc.baro EMULS ldx #1000 EDIVS sty tmp4 } tmp4 = (short)((tmp4 * beta) / 100); outpc.kpa =(short)(tmp4 + ((mafmap * (100 - beta)) / 100)); } break; } // end switch } if(!in2ram.VEIXOptn) { outpc.kpaix = outpc.kpa; // baro correction not in index switch(inpram.MAFOption & 0x0F) { case 0: // use pw= ...../baro kpadiv = outpc.baro; break; case 1: // maf only or baro, use pw= ..../1000 kpadiv = 1000; break; case 2: if(mafblend <= 1) kpadiv = outpc.baro; else if(mafblend == 2) kpadiv = (int)((((long)tmp1 * outpc.baro) + ((long)tmp2 * 1000)) / tmp3); else kpadiv = 1000; break; }; } else { // baro correction in index asm { ldy outpc.kpa ldd #1000 EMULS ldx outpc.baro EDIVS sty tmp4 }; if(mafblend <= 1) // use (kpa*1000)/baro as index to ve, spk, tau tbles + AMC indx outpc.kpaix = tmp4; else if(mafblend == 2) // blend (kpa*1000)/baro and kpa index outpc.kpaix = (int)((((long)tmp1 * tmp4) + ((long)tmp2 * outpc.kpa)) / tmp3); else outpc.kpaix = outpc.kpa; kpadiv = 1000; } /************************************************************************** ** ** Cranking Mode ** ** Pulsewidth is directly set by the coolant temperature to a value of ** CWU (at temp_table[0]) and CWH (at temp_table[NO_TEMPS -1]). ** The value is interpolated at clt. ** **************************************************************************/ if(outpc.rpm > 0) { if(outpc.rpm < inpram.crank_rpm) { // engine cranking PulseTol = inpram.CrnkTol; tcrank_done = 0xFFFF; outpc.engine |= 0x02; // set cranking bit outpc.engine &= ~0x0C; // clr starting warmup bit & warmup bit outpc.tpsaccel = 0; if(outpc.tps > inpram.TPSWOT) { ppw[0] = 300; // usec (.3 ms for Flood Clear) ppw[1] = ppw[0]; inj_sqrt = 3; altcount = 1 - altcount; goto SCHED_SQUIRT; } if(!inpram.CWOption) { // KJW if outside the interpolation range, rail to CWU or CWH as // appropriate, else interpolate if(outpc.clt >= inpram.temp_table[NO_TEMPS-1]) lsum1 = (unsigned short)inpram.CWH * 100; // usec else if(outpc.clt <= inpram.temp_table[0]) lsum1 = (unsigned short)inpram.CWU * 100; // usec else { ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.CWH - inpram.CWU) * 100; lsum1 = (unsigned short)(inpram.CWU * (long)100 + (ltmp / (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // usec } } else { // custom warmup table lsum1 = (unsigned short)CW_table(outpc.clt,in2ram.CWCPW) * 100; // usec } // add barocor and aircor lsum1 = (((lsum1 * outpc.aircor) / 100) * outpc.barocor) / 100; lsum2 = lsum1; goto BATCOR; } else if(tcrank_done == 0xFFFF) { // engine running tcrank_done = outpc.seconds; pump_delay = 2; } } else { // engine stopped goto BURN_FLASH; } /************************************************************************** ** ** Warm-up and After-start Enrichment Section ** ** The Warm-up enrichment is a linear interpolated value for the current clt ** temperature from warmen_table[NO_TEMPS] vs temp_table[NO_TEMPS]. ** ** Also, the after-start enrichment value is calculated and applied here - it ** is an added percent value on top of the warmup enrichment, and it is applied ** for the number of ignition cycles specified in AWC. This enrichment starts ** at a value of AWEV at first, then it linearly interpolates down to zero ** after AWC cycles. ** ** If (startw, engine is set) then: ** compare if (AWC > 0) then: ** interpolate for warmup enrichment ** else clear startw bit in engine ** **************************************************************************/ wrmtmp = outpc.warmcor; if((outpc.engine & 0x02) || first_start) { // if engine cranking first_start = 0; // needed in case first rpm > crank_rpm outpc.engine &= ~0x02; // clear crank bit outpc.engine |= 0x0C; // set starting warmup bit & warmup bit asecount = 0; PulseTol = inpram.ASTol; tASTol = outpc.seconds; } wrmtmp = intrp_1dctable(1, outpc.clt, NO_TEMPS, inpram.temp_table, 0, inpram.warmen_table); // % if((wrmtmp == 100) && (!inpram.ASEHot || (asecount > AWC))) { // done warmup outpc.engine &= ~0x0C; // clear start warmup bit & warmup bit *pPTMpin[5] &= ~0x20; // clear warmup led if(outpc.seconds > tASTol + 5) PulseTol = inpram.PulseTol; goto END_WRM; } *pPTMpin[5] |= 0x20; // set warmup led outpc.engine |= 0x08; // set warmup bit if(!(outpc.engine & 0x04)) // if starting warmup bit clear goto END_WRM; if(asecount > AWC) { outpc.engine &= ~0x04; // clear start warmup bit if(outpc.seconds > tASTol + 5) PulseTol = inpram.PulseTol; goto END_WRM; } if(AWC > 0) { utmp1 = AWEV * asecount; wrmtmp += (AWEV - (utmp1 / AWC)); } END_WRM: outpc.warmcor = wrmtmp; /************************************************************************** ** ** Throttle Position Acceleration Enrichment ** **************************************************************************/ switch_page(3,0,0); // call tpsaen in page 3D /************************************************************************** ** ** Exhaust Gas Oxygen Sensor Measurement Section ** **************************************************************************/ switch_page(0,0,0); // call ego_calc in page 3D /************************************************************************** ** ** Computation of Fuel Parameters ** Note that GAMMAE only includes Warmcor, Barocor, and Aircor ** (EGO, Tpsfuelcut no longer included) ** **************************************************************************/ lsum = (outpc.warmcor * outpc.aircor) / 100; lsum = (lsum * outpc.barocor) / 100; outpc.gammae = (int)lsum; lsum = (lsum * outpc.tpsfuelcut) / 100; if((inpram.FuelAlpha & 0x0F) != 2) lsum2 = (long)outpc.kpa; else lsum2 = 1000; // normalizes to ~1 when divide by baro or 1000 if((inpram.MAFOption & 0x0F) == 1) { // pure maf blendve1 = 100; blendve2 = 100; } else { // pure map or maf/ map blend // Look up volumetric efficiency as function of rpm and map/ mafmap/ tps // (in alpha N mode) or (kpa * 1000) / baro blendve1 = intrp_2dctable(outpc.rpm, outpc.kpaix, NO_FRPMS, NO_FMAPS, &inpram.frpm_table[0], &inpram.fmap_table[0], &in2ram.ve_table[0][0][0]); // % if(inpram.dual_tble_optn) blendve2 = intrp_2dctable(outpc.rpm, outpc.kpaix, NO_FRPMS, NO_FMAPS, &inpram.frpm_table[0], &inpram.fmap_table[0], &in2ram.ve_table[1][0][0]); // % else blendve2 = blendve1; if((inpram.MAFOption & 0x0F) == 2) { // maf/ map blend ve if(in2ram.MAFDir == 0) { // map at lo rpm if((outpc.rpm > in2ram.MAFRPM1) && (outpc.rpm < in2ram.MAFRPM2)) { // use blend blendve1 = (int)((((long)(in2ram.MAFRPM2 - outpc.rpm) * blendve1) + ((long)(outpc.rpm - in2ram.MAFRPM1) * 100)) / (in2ram.MAFRPM2 - in2ram.MAFRPM1)); blendve2 = (int)((((long)(in2ram.MAFRPM2 - outpc.rpm) * blendve2) + ((long)(outpc.rpm - in2ram.MAFRPM1) * 100)) / (in2ram.MAFRPM2 - in2ram.MAFRPM1)); } else if(outpc.rpm > in2ram.MAFRPM2) { blendve1 = 100; blendve2 = 100; } } else { // map at hi rpm if(outpc.rpm < in2ram.MAFRPM1) { blendve1 = 100; blendve2 = 100; } else if((outpc.rpm > in2ram.MAFRPM1) && (outpc.rpm < in2ram.MAFRPM2)) { // use blend blendve1 = (int)((((long)(in2ram.MAFRPM2 - outpc.rpm) * 100) + ((long)(outpc.rpm - in2ram.MAFRPM1) * blendve1)) / (in2ram.MAFRPM2 - in2ram.MAFRPM1)); blendve2 = (int)((((long)(in2ram.MAFRPM2 - outpc.rpm) * 100) + ((long)(outpc.rpm - in2ram.MAFRPM1) * blendve2)) / (in2ram.MAFRPM2 - in2ram.MAFRPM1)); } } } // end maf/ map blendve } // end pure map or maf/ map blend outpc.vecurr1 = blendve1 + in2ram.FuelAdj; // Adjustment from GPIO board outpc.vecurr2 = blendve2 + in2ram.FuelAdj; // Adjustment from GPIO board lsum1 = (lsum * ((outpc.egocor1 * lsum2) / kpadiv)/100); lsum1 = (lsum1 * ((outpc.vecurr1 * (long)inpram.ReqFuel)/ 100)/ 100); // usec lsum2 = (lsum * ((outpc.egocor2 * lsum2) / kpadiv)/100); lsum2 = (lsum2 * ((outpc.vecurr2 * (long)inpram.ReqFuel)/ 100)/ 100); // usec if(inpram.AFRMult) { // factor in AFRTgt if this facilitates tuning lsum1 = (lsum1 * inpram.AFRStoich) / outpc.afrtgt1; lsum2 = (lsum2 * inpram.AFRStoich) / outpc.afrtgt2; } /************************************************************************** ** ** Calculation of Battery Voltage Correction for Injector Opening Time ** ** Injector open time is implemented as a linear function of ** battery voltage, from 7.2 volts to 19.2 volts, ** with 13.2 volts being the nominal operating voltage ** ** INJOPEN = injector open time at 13.2 volts in ms x 10 ** BATTFAC = injector open adjustment factor 6 volts from 13.2V in ms x 10 ** ** ** + (INJOPEN + BATTFAC) ** + * ** + (INJOPEN) ** + * ** + (INJOPEN - BATTFAC) ** + * ** + ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** 7.2V 13.2V 19.2 ** **************************************************************************/ BATCOR: tmp1 = (inpram.InjOpen + inpram.BatFac) - ((inpram.BatFac * (outpc.batt - 72)) / 60); // ms x 10 if(tmp1 > 0) pw_open = (unsigned int)tmp1; // if < 0, => bat spike, use prior val. /************************************************************************** ** ** Calc. of Flex Fuel Sensor %alcohol and PW,spk correction (fuelcor,ffspkdel) ** **************************************************************************/ if(inpram.FlexFuel && (FSens_Pd > 0)) { FSensFreq = (int)(7812 / FSens_Pd); // Hz, (FSens_Pd in .128 tics) if((FSensFreq >= 10) || (FSensFreq <= 300)) { outpc.fuelcor = inpram.fuelCorr[0] + (((short)(FSensFreq - inpram.fuelFreq[0]) * (inpram.fuelCorr[1] - inpram.fuelCorr[0])) / (inpram.fuelFreq[1] - inpram.fuelFreq[0])); // % ffspkdel = inpram.ffSpkDel[0] + (((char)(FSensFreq - inpram.fuelFreq[0]) * (inpram.ffSpkDel[1] - inpram.ffSpkDel[0])) / (inpram.fuelFreq[1] - inpram.fuelFreq[0])); // degx10 } else { // sensor reading bad - use default outpc.fuelcor = 100; // % ffspkdel = 0; // degx10 } lsum1 = (lsum1 * outpc.fuelcor)/ 100; // usec lsum2 = (lsum2 * outpc.fuelcor)/ 100; // usec } else { // no flex fuel or fuel sensor not yet ready/ broken outpc.fuelcor = 100; // % ffspkdel = 0; // degx10 } /************************************************************************** ** ** Calculation of Fuel Pulse Width ** **************************************************************************/ if(ltch_PWcalc == 3) { if(((outpc.engine & 0x02) && (inpram.no_cyl <= 8)) || !inpram.Alternate) { // if cranking or no alternate option inj_sqrt = 3; } else { inj_sqrt = altcount + 1; altcount = 1 - altcount; } } if(inpram.RevLimOption == 2) { if(outpc.rpm > inpram.RevLimRpm2) // Cut fuel for Over Rev cut_fuel = 1; else if(outpc.rpm < inpram.RevLimRpm1) // restore fuel cut_fuel = 0; if(cut_fuel) { ppw[0] = 0; ppw[1] = 0; goto SCHED_SQUIRT; } } // calculate additions to base pw lsum = pw_open + outpc.tpsaccel; // msx10 // Impulse to pw for N2O or x-tau tuning ultmp = 0; spkN2O = 0; if(!inpram.FlexFuel && !(PORTE & 0x01)) { ultmp = (inpram.pwImpulse * dtpred) / (unsigned short)100; // add // to lsum after convert to us. Note: user must include whatever // factor needed in pwImpulse % to convert dtpred (time bet tach // pulses) to time between squirts (eg, x2 for most dual spk // options and whatever else needed for no squirts / eng cycle). spkN2O = in2ram.spkN2O; // retard spark if desired } else if(impulse_count && (ltch_PWcalc == 3)) { impulse_count--; lsum += inpram.pwImpulse; } else if(inpram.impulseSec && ((outpc.seconds - lastImpulseSec) > inpram.impulseSec)) { lastImpulseSec = outpc.seconds; if(inpram.impulseSec && inpram.nImpulse) impulse_count = inpram.nImpulse - 1; lsum += inpram.pwImpulse; } lsum = (100 * lsum) + ultmp; // convert to usec // keep cut fuel here in case want to set elsewhere than rev lim if((lsum1 > 0) && !cut_fuel) { lsum1 += lsum; // usec if(lsum1 > 32000)lsum1 = 32000; // rail at 32 ms ppw[0] = (unsigned int)lsum1; } else { ppw[0] = 0; } if((lsum2 > 0) && !cut_fuel) { lsum2 += lsum; // usec if(lsum2 > 32000)lsum2 = 32000; ppw[1] = (unsigned int)lsum2; } else { ppw[1] = 0; } if((ppw[0] + ppw[1]) == 0) { // no fuel - skip x-tau outpc.XTfcor0 = 100; outpc.XTfcor1 = 100; goto SCHED_SQUIRT; } /************************************************************************** ** ** X,Tau Transient Enrichment Section: ** ** fi = [ mi - (Mi / (tau / dltau)) ] / (1 - X) ** ** Mi+1 = Mi + X * fi - (Mi / (tau / dltau)) ** ** where, ** fi = total fuel injected ** ** mi = total fuel going directly into combustion chamber (want this ** to be the calculated fuel) ** ** Mi = net fuel entering/leaving port wall puddling ** ** dltau = time (msx10) between squirts ** ** X = fraction of fuel injected which goes into port wall puddling ** tau = puddle fuel dissipation time constant (ms) as function ** of rpm and coolant temp. ** ** XTfcor = % correction to calculated fuel to ensure the calculated ** amount gets into the comb chamber. ** **************************************************************************/ if(in2ram.XTauOption && (ltch_PWcalc == 3)) { // calculate X(%x10), tau(ms) as function of rpm, accel/decel, clt tmp1 = -(int)inpram.MapThreshXTD; tmp2 = -(int)inpram.MapThreshXTD2; if(outpc.mapdot > tmp1) { // use accel tables XTX = intrp_1ditable(outpc.rpm, NO_XTRPMS, in2ram.XTrpms, in2ram.XAccTable); Tau = intrp_1ditable(outpc.rpm, NO_XTRPMS, in2ram.XTrpms, in2ram.TauAccTable); } else if(outpc.mapdot < tmp2) { // use decel tables XTX = intrp_1ditable(outpc.rpm, NO_XTRPMS, in2ram.XTrpms, in2ram.XDecTable); Tau = intrp_1ditable(outpc.rpm, NO_XTRPMS, in2ram.XTrpms, in2ram.TauDecTable); } else { // transition from accel -> decel utmp1 = intrp_1ditable(outpc.rpm, NO_XTRPMS, in2ram.XTrpms, in2ram.XAccTable); utmp2 = intrp_1ditable(outpc.rpm, NO_XTRPMS, in2ram.XTrpms, in2ram.XDecTable); tmp3 = outpc.mapdot - tmp2; tmp4 = tmp1 - tmp2; asm { ldy tmp3 ldd #100 EMULS ldx tmp4 EDIVS sty tmp3 }; // save tmp3 = ((outpc.mapdot - tmp2)* 100) / (tmp1 - tmp2) tmp4 = utmp1 - utmp2; asm { ldy tmp3 ldd tmp4 EMULS ldx #100 EDIVS sty tmp4 }; XTX = utmp2 + tmp4; utmp1 = intrp_1ditable(outpc.rpm, NO_XTRPMS, in2ram.XTrpms, in2ram.TauAccTable); utmp2 = intrp_1ditable(outpc.rpm, NO_XTRPMS, in2ram.XTrpms, in2ram.TauDecTable); tmp4 = (short)(utmp1 - utmp2); asm { ldy tmp3 ldd tmp4 EMULS ldx #100 EDIVS sty tmp4 }; Tau = utmp2 + tmp4; } if(in2ram.XTauOption == 2) { // use X-Tau for warmup based on clt temp utmp1 = (unsigned short)CW_table(outpc.clt,(int *)in2ram.XClt); // % asm { ldy XTX ldd utmp1 EMUL ldx #100 EDIV sty XTX }; // use X-Tau for warmup based on clt temp utmp1 = (unsigned short)CW_table(outpc.clt,(int *)in2ram.TauClt); // % asm { ldy Tau ldd utmp1 EMUL ldx #100 EDIV sty Tau }; } if(Tau < 1) { Tau = 1; Tau0 = 1; // continue x-tau calc with tau= 1, but make no correction. } else Tau0 = 0; utmp1 = pw_open * 100; // us for(ix = 0; ix < 2; ix++) { if((inj_sqrt != (ix+1)) && (inj_sqrt != 3))continue; XTm = ppw[ix] - utmp1; DISABLE_INTERRUPTS ultmp = sum_dltau[ix]; sum_dltau[ix] = 0; ENABLE_INTERRUPTS ltmp = (Tau * 1000) / ultmp; // tau x 1000 (us) / dltau(us) if(ltmp < 1) { // port wall is totally dry ltmp = 0; // These derived from setting (1-X)fi = mi XTM[ix] = 0; } else ltmp = XTM[ix] / ltmp; if(ltmp > XTm) ltmp = XTm; // must leave beta and (int)cast below because of pos CW beta = ((XTm - ltmp) * 1000) / (int)(1000 - XTX); if(beta > 65000)beta = 65000; XTf = (unsigned int)beta; asm { ldy XTX ldd XTf EMUL ldx #1000 EDIV sty utmp2 }; XTM[ix] = XTM[ix] + utmp2 - ltmp; if((XTm > 0) && !Tau0) { asm { ldy XTf ldd #100 EMUL ldx XTm EDIV sty tmp1 }; } else tmp1 = 100; if(ix == 0) outpc.XTfcor0 = tmp1; else outpc.XTfcor1 = tmp1; if(!Tau0) ppw[ix] = XTf + utmp1; if(ppw[ix] > 32000)ppw[ix] = 32000; // rail at 32 ms } } // end X-Tau if(!in2ram.XTauOption) { sum_dltau[0] = 0; sum_dltau[1] = 0; outpc.XTfcor0 = 100; outpc.XTfcor1 = 100; } SCHED_SQUIRT: if(ltch_PWcalc < 3) { goto KNK; } // Turn on fuel pump PORTE |= 0x10; outpc.engine |= 0x01; // set engine running if(inj_sqrt == 2) goto SCHED2; // SCHED1: if(ppw[0] == 0) { outpc.pw1 = 0; if(inj_sqrt == 3) goto SCHED2; else goto KNK; } // Turn On Inj1 TCTL2 |= 0x04; // set output hi in OL1 CFORC |= 0x02; // force high ltmp = TCNT; // grab now to get max accuracy in pw if(!inpram.DualSpkOptn) { uctmp = (unsigned char)(((int)inpram.InjPWMTim*8)/10); DISABLE_INTERRUPTS pwm1_time = lmms + uctmp; // .128 ms ENABLE_INTERRUPTS pwm1_on = 0; PWMPER2 = inpram.InjPWMPd; // set PWM period (us) PWMDTY2 = PWMPER2; // set PWM duty to 100 % PWMCNT2 = 0x00; // clear counter PWME |= 0x04; // enable PWM2 } // Set up to turn Off Inj1 when get to pw us outpc.pw1 = ppw[0]; ltmp += ((3*(long)outpc.pw1)>>1); // 2/3 us utmp1 = (unsigned short)(ltmp >> 16); DISABLE_INTERRUPTS Fl1TimerComp = ltmp; Fl1OCt_overflow = utmp1; TC1 = (unsigned short)(Fl1TimerComp & 0xFFFF); // load OC compare reg if(Fl1OCt_overflow <= 1) { // don't need to handle <= 1 overflow because TCNT will not // = TimerComp until after the overflow as long as pw < ~43 ms Fl1OCt_overflow = 0; TCTL2 = TCTL2 & 0xFB; // set output lo in OL1 TIE |= 0x02; // Enable Inj1 OC interrupt TFLG1 = 0x02; // clear OC interrupt flag } else { // Disable Inj1 OC interrupt until ovflows done // do below to allow enough time to set OC pin in overflow isr if(TC1 < 30)TC1 = 30; TIE &= ~0x02; } ENABLE_INTERRUPTS *pPTMpin[3] |= 0x08; // turn on inj led outpc.squirt |= 0x01; // inj1 squirting if(inj_sqrt != 3) goto KNK; SCHED2: if(ppw[1] == 0) { outpc.pw2 = 0; goto KNK; } // Turn On Inj2 TCTL2 |= 0x40; // set output hi in OL3 CFORC |= 0x08; // force high ltmp = TCNT; // grab now to get max accuracy in pw if(!inpram.DualSpkOptn) { uctmp = (unsigned char)(((int)inpram.InjPWMTim*8)/10); DISABLE_INTERRUPTS pwm2_time = lmms + uctmp; // .128 ms ENABLE_INTERRUPTS pwm2_on = 0; PWMPER4 = inpram.InjPWMPd; // set PWM period (us) PWMDTY4 = PWMPER4; // set PWM duty to 100 % PWMCNT4 = 0x00; // clear counter PWME |= 0x10; // enable PWM4 } // Set up to turn Off Inj2 when get to pw us outpc.pw2 = ppw[1]; ltmp += ((3*(long)outpc.pw2)>>1); // 2/3 us utmp1 = (unsigned short)(ltmp >> 16); DISABLE_INTERRUPTS Fl2TimerComp = ltmp; Fl2OCt_overflow = utmp1; TC3 = (unsigned short)(Fl2TimerComp & 0xFFFF); // load OC compare reg if(Fl2OCt_overflow <= 1) { // don't need to handle <= 1 overflow because TCNT will not // = TimerComp until after the overflow as long as pw < ~43 ms Fl2OCt_overflow = 0; TCTL2 = TCTL2 & 0xBF; // set output lo in OL3 TIE |= 0x08; // Enable Inj2 OC interrupt TFLG1 = 0x08; // clear OC interrupt flag } else { // Disable Inj2 OC interrupt until ovflows done // do below to allow enough time to set OC pin in overflow isr if(TC3 < 30)TC3 = 30; TIE &= ~0x08; } ENABLE_INTERRUPTS *pPTMpin[3] |= 0x08; // turn on inj led outpc.squirt |= 0x02; // inj2 squirting /************************************************************************** ** ** Calculation of Knock retard, Distributor Advance & coil charge time correction ** **************************************************************************/ KNK: // check for stall if(((outpc.engine & 0x01) == 0) || (outpc.rpm == 0))goto BURN_FLASH; if(!(inpram.knk_option & 0x0F) || (outpc.kpa > inpram.knk_maxmap) || (outpc.rpm < inpram.knk_lorpm) || (outpc.rpm > inpram.knk_hirpm)) { // no knock correction outpc.knk_rtd = 0; goto DIST_ADV; } else { // latch any knocks that occur during the knk_clk_test interval // calculate knk_thresh get_adc(7,7); tmp1 = knk_thresh_calc(outpc.rpm); ix = (inpram.knk_option & 0xF0) >> 4; if(((ix == 0) && (outpc.knock < tmp1)) || ((ix == 1) && (outpc.knock > tmp1))) { // signal(Vx100) indicates knock occurred if(knk_count < 255) // don't want to overflow to 0 knk_count++; if((knk_stat == 0) && (knk_count > inpram.knk_ndet)) knk_clk = knk_clk_test; // just caught knock - force 1st retard // immediately (as fast as main loop allows) } if(knk_clk >= knk_clk_test) { // time to check for retard/ adv spark if(knk_count > inpram.knk_ndet) { // got valid knock. req ndet consec // knocks to be sure knk_clk_test = inpram.knk_trtd; if(knk_stat == 4) // thought had enough retard, but need more knk_stat = 2; if(knk_stat < 2) uctmp = inpram.knk_step1; // haven't stopped knock yet else uctmp = inpram.knk_step2; // stopped once - use smaller step uctmp = outpc.knk_rtd + uctmp; if(uctmp < inpram.knk_maxrtd) outpc.knk_rtd = uctmp; else outpc.knk_rtd = inpram.knk_maxrtd; if(knk_stat == 0) // 1st knock knk_stat = 1; else if(knk_stat == 2) { // this is knk returning due to too much adv if((inpram.knk_option & 0x0F) == 1) // operate one step below knock knk_stat = 3; } } else { // did not get unambiguous knock knk_clk_test = inpram.knk_tadv; // change time interval switch (knk_stat) { case 0: break; case 1: case 2: if((knk_stat == 2) && (outpc.knk_rtd == 0)) knk_stat = 0; // back to table value & no knock- restart process else { if(outpc.knk_rtd >= inpram.knk_step2) outpc.knk_rtd -= inpram.knk_step2; // remove some retard else outpc.knk_rtd = 0; knk_stat = 2; // had knock, eliminated it, now try get closer // to threshhold } break; case 3: // had knock, eliminated it, couldn't get closer to thresh // This is as good as can get; save tps, rpm knk_tble_adv = outpc.adv_deg; knk_stat = 4; break; case 4: // maintain status until knock or change in tps or map tmp1 = outpc.adv_deg - knk_tble_adv; if(tmp1 < 0)tmp1 = -tmp1; if(tmp1 > inpram.knk_dtble_adv) { outpc.knk_rtd = 0; // conditions changed- restart process knk_stat = 0; } break; } // end status switch } knk_count = 0; knk_clk = 0; } } DIST_ADV: // cold advance outpc.cold_adv_deg = CW_table(outpc.clt,inpram.cold_adv_table); // deg x 10 // spark delay(us) due to VR sensor delay(rpm) + FET driver delay spk_delay = (int)(((inpram.vr_delay * (long)outpc.rpm)/1000) + inpram.fet_delay); // Calculate ignition advance lsum = intrp_2ditable(outpc.rpm, outpc.kpaix, NO_SRPMS, NO_SMAPS, &inpram.srpm_table[0], &inpram.smap_table[0], &in2ram.adv_table[0][0]) + outpc.cold_adv_deg - outpc.knk_rtd - spkN2O; // degx 10 // Subtract retard vs manifold air temp tmp1 = intrp_1dctable(1, outpc.mat,NO_MAT_TEMPS,in2ram.MatTemps,0,in2ram.MatSpkRtd); // % // apply MAT spk retard rpm-based scaler if(outpc.rpm <= in2ram.MatRtdRPMLo) // no retard below this rpm tmp2 = 0; else if(outpc.rpm >= in2ram.MatRtdRPMHi) // full table retard above this rpm tmp2 = 100; else { tmp2 = (short)(((long)100 * (outpc.rpm - in2ram.MatRtdRPMLo)) / (in2ram.MatRtdRPMHi - in2ram.MatRtdRPMLo)); } tmp1 = (tmp1 * tmp2) / 100; lsum -= tmp1; // correct for flex fuel; lsum += ffspkdel; // degx10 // adjustment from GPIO board lsum += in2ram.SpkAdj; // degx10 // alphaN spark retard if(inpram.FuelAlpha & 0x0F) { tmp1 = inpram.alpha_baro_spkrtd; // (((degx10/kPax10) x 100) * (baro - 1000)kPax10)/100 tmp2 = outpc.baro - 1000; asm { ldy tmp1 ldd tmp2 EMULS ldx #100 EDIVS sty tmp1 }; lsum -= tmp1; // degx10 } // rev limit if(inpram.RevLimOption == 1) { if(outpc.rpm > inpram.RevLimRpm2) lsum -= inpram.RevLimMaxRtd; // deg x 10 else if(outpc.rpm > inpram.RevLimRpm1) { lsum -= (((long)inpram.RevLimMaxRtd*(outpc.rpm- inpram.RevLimRpm1))/ (inpram.RevLimRpm2 - inpram.RevLimRpm1)); // deg x 10 } } // reference relative to IC ic_adv_deg = (int)(lsum - inpram.adv_offset); if((outpc.engine & 0x02) && // cranking & trigger rise/ return (crank_trig_mode || trig_ret_mode)) outpc.adv_deg = inpram.adv_offset; // relative to true TDC else outpc.adv_deg = (int)lsum; // relative to true TDC // Base dwell tmp1 = inpram.max_coil_dur; // Add correction if accelerating if((outpc.tpsaccel > 20) || (outpc.XTfcor0 > 110) || (outpc.XTfcor1 > 110)) tmp1 += inpram.DurAcc; // Correct for battery voltage tmp1 += coil_dur_table(outpc.batt - 120); utmp1 = outpc.rpm; if((inpram.DualSpkOptn > 1) && (inpram.DualSpkOptn != 6) && (synch == END_SYNCH)) { // scale back dwell at high rpm if(inpram.OddAng <= 0) ltmp = (long)16667 * ic_adv_deg; else ltmp = ((long)16667 * ic_adv_deg) + inpram.OddAng; // worst case ltmp /= utmp1; // tot adv in us rel to tach n utmp1 = tmp1 * 100; ltmp += (utmp1 + spk_delay); // add dwell, account for vr + FET delays ltmp += 30; // add 30 us slop ultmp = dtpred << 1; // dual mode time bet tachs // tach (n-2) chge---dwell--spk--adv--adv_offst tach (n) // |____________~________|____________|____________|____________| // |------------------------2 * dtpred--------------------------| if(ltmp > ultmp) { // scale down chg time to fit time available coil_dur = (short)((ultmp - (ltmp - utmp1)) / 100); // msx10 } else coil_dur = tmp1; // msx10 } else coil_dur = tmp1; // msx10, will be used in IC Isr. // following belongs in Input Capture ISR, but this saves long div in ISR DISABLE_INTERRUPTS ltmp = coil_dur_set; ENABLE_INTERRUPTS outpc.coil_dur = (int)(ltmp / 100); // msx10 /************************************************************************** ** ** Automatic Mixture Control - enter once a second ** ***************************************************************************/ if(inpram.AMCOption) { switch_page(2,0,0); // call amc in page 3D } // AMC option selected /*************************************************************************** ** ** Check whether to burn flash ** **************************************************************************/ BURN_FLASH: if(amc_burn_flag && (outpc.rpm < AMCBURN_RPM_THRESH)) { amc_burn_flag = 0; burn_flag = 1; in2ram.AMCNBurns++; // increment cumulative no. of AMC flash burns flocker = 0xCC; // set sem to prevent burn flash thru runaway code } if(burn_flag) { // burn flash 512 byte(256 word) sector(s) if((burn_flag > 1) || (outpc.rpm <= 50) || (tooth_no == burn_tooth_no)) { fburner(tableWordFlash(burn_idx, 0), tableWordRam(burn_idx, 0), tableWords(burn_idx)); if(burn_flag >= tableWords(burn_idx)) { burn_flag = 0; flocker = 0; } else burn_flag++; } } /*************************************************************************** ** ** Check for reinit command ** **************************************************************************/ if(reinit_flag) { reinit_flag = 0; // update initialization of variables/ registers dependent on // user config inputs SPK = 1 - inpram.spkout_hi_lo; // 0=spk low, but inverted logic CHG = 1 - SPK; // after transistor if(!(inpram.ICIgnOption & 0x02)) trig_ret_mode = 0; else if(outpc.rpm < inpram.crank_rpm) trig_ret_mode = 2; TCTL4 &= ~0x33; // clear bits 0,1 and 4.5(won't hurt if not dual spk) if((inpram.DualSpkOptn < 2) || (inpram.DualSpkOptn == 6)) { if(inpram.ICIgnOption & 0x01) { // normal rising edge input capture if(!trig_ret_mode) // not trig ret mode TCTL4 |= 0x11; // rising edge input capture else // reverse in trigger return mode TCTL4 |= 0x22; // falling edge input capture } else { // normal falling edge input capture if(!trig_ret_mode) // not trig ret mode TCTL4 |= 0x22; // falling edge input capture else // reverse in trigger return mode TCTL4 |= 0x11; // rising edge input capture } } else { if(inpram.ICIgnOption & 0x01) // tach in is rising edge TCTL4 |= 0x01; else // tach in is falling edge TCTL4 |= 0x02; if((inpram.DualSpkOptn == 3) || (inpram.DualSpkOptn == 8)) TCTL4 |= 0x10; // cam/crnk_sync is rising edge else if((inpram.DualSpkOptn == 2) || (inpram.DualSpkOptn == 7)) TCTL4 |= 0x20; // cam/crnk_sync is falling edge } if(!inpram.CWOption) { if(outpc.clt >= inpram.temp_table[NO_TEMPS-1]) { AWEV = (unsigned short)inpram.AWEVH; AWC = (unsigned short)inpram.AWCH; } else if(outpc.clt <= inpram.temp_table[0]) { AWEV = (unsigned short)inpram.AWEVU; AWC = (unsigned short)inpram.AWCU; } else { ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.AWEVH - inpram.AWEVU); AWEV = (unsigned short)(inpram.AWEVU + (ltmp / (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // % ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.AWCH - inpram.AWCU); AWC = (unsigned short)(inpram.AWCU + (ltmp / (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // cycles } } else { // custom warmup tables AWEV = (unsigned short)CW_table(outpc.clt,in2ram.CWAWEV); // % AWC = (unsigned short)CW_table(outpc.clt,in2ram.CWAWC); // cycles } crank_trig_mode = (inpram.ICIgnOption & 0x04) >> 2; // Set up SCI (rs232): SCI BR reg= BusFreq(=24MHz)/16/baudrate SCI0BDL = (unsigned char)(1500000/inpram.baud); ltmp = (150000000/inpram.baud) - ((long)SCI0BDL*100); if(ltmp > 50)SCI0BDL++; // round up can_id = inpram.mycan_id; can_reset = 1; // do this to reset id in CAN message mask register } /*************************************************************************** ** ** Determine spare port settings ** **************************************************************************/ for(ix = 0;ix < NPORT; ix++) { if(inpram.spr_port[ix]) { // Evaluate first condition if(inpram.out_byte1[ix] == 1) tmp1 = *((char *)(&outpc) + inpram.out_offset1[ix]); else tmp1 = *( int *)((char *)(&outpc) + inpram.out_offset1[ix]); tmp1 = tmp1 - inpram.thresh1[ix]; if(inpram.condition1[ix] == '<') tmp1 = -tmp1; // convert < condition to same as > condition if(inpram.condition1[ix] == '=') { if((tmp1 >= -inpram.hyst1[ix]) && (tmp1 <= inpram.hyst1[ix])) ctmp1 = 1; // 1st condition true } else if(tmp1 > 0) ctmp1 = 1; // 1st condition true else ctmp1 = 0; // 1st condition false // Evaluate second condition if there is one if(inpram.cond12[ix] != ' ') { if(inpram.out_byte2[ix] == 1) tmp2 = *((char *)(&outpc) + inpram.out_offset2[ix]); else tmp2 = *( int *)((char *)(&outpc) + inpram.out_offset2[ix]); tmp2 = tmp2 - inpram.thresh2[ix]; if(inpram.condition2[ix] == '<') tmp2 = -tmp2; // convert < condition to same as > condition if(inpram.condition2[ix] == '=') { if((tmp2 >= -inpram.hyst2[ix]) && (tmp2 <= inpram.hyst2[ix])) ctmp2 = 1; // 2nd condition true } else if(tmp2 > 0) ctmp2 = 1; // 2nd condition true else ctmp2 = 0; // 2nd condition false } // Evaluate final condition if(((inpram.cond12[ix] == '&') && (ctmp1 && ctmp2)) || ((inpram.cond12[ix] == '|') && (ctmp1 || ctmp2)) || ((inpram.cond12[ix] == ' ') && ctmp1)) { if(lst_pval[ix] != inpram.port_val[ix]) { set_spr_port((char)ix, inpram.port_val[ix]); lst_pval[ix] = inpram.port_val[ix]; } } else { // Evaluate hysteresis conditions if((inpram.condition1[ix] == '>') || (inpram.condition1[ix] == '<')) tmp1 = -tmp1 - inpram.hyst1[ix]; if(inpram.condition1[ix] == '=') { ctmp1 = 1 - ctmp1; // 1st hyst. condition opposite of set cond } else if(tmp1 > 0) ctmp1 = 1; // 1st hysteresis condition true else ctmp1 = 0; // 1st hysteresis condition false if(inpram.cond12[ix] != ' ') { if((inpram.condition2[ix] == '>') || (inpram.condition2[ix] == '<')) tmp2 = -tmp2 - inpram.hyst2[ix]; if(inpram.condition2[ix] == '=') { ctmp2 = 1 - ctmp2; // 2nd hyst. condition opposite of set cond } else if(tmp2 > 0) ctmp2 = 1; // 2nd hysteresis condition true else ctmp2 = 0; // 2nd hysteresis condition false } // Evaluate final hysteresis condition if(((inpram.cond12[ix] == '&') && (ctmp1 || ctmp2)) || ((inpram.cond12[ix] == '|') && (ctmp1 && ctmp2)) || ((inpram.cond12[ix] == ' ') && ctmp1)) { if(lst_pval[ix] != 1 - inpram.port_val[ix]) { set_spr_port((char)ix, 1 - inpram.port_val[ix]); lst_pval[ix] = 1 - inpram.port_val[ix]; } } } // end eval of hysteresis conditions } // end if spr_port } // end for ix loop /*************************************************************************** ** ** Check for serial, CAN receiver timeout ** **************************************************************************/ DISABLE_INTERRUPTS ultmp = lmms; ultmp2 = rcv_timeout; ENABLE_INTERRUPTS if(ultmp > ultmp2) { txmode = 0; // break out of current receive sequence rcv_timeout = 0xFFFFFFFF; } DISABLE_INTERRUPTS ultmp2 = ltch_CAN; ENABLE_INTERRUPTS if(ultmp > ultmp2) { getCANdat = 0; // break out of current receive sequence ltch_CAN = 0xFFFFFFFF; } if(kill_ser) { if(outpc.seconds > kill_ser_t) { kill_ser = 0; SCI0CR2 |= 0x24; // rcv, rcvint re-enable } } if(vfy_flg) { vfy_flg = 0; for(ix = 0; ix < tables[tble_idx].n_bytes; ix++) { if(*tableByteFlash(tble_idx,ix) != *tableByteRam(tble_idx,ix)) vfy_fail++; } txcnt = 0; txgoal = 2; SCI0DRL = *(char *)&vfy_fail; SCI0CR2 &= ~0x24; // rcv, rcvint disable SCI0CR2 |= 0x88; // xmit enable & xmit interrupt enable } /*************************************************************************** ** ** Check for CAN reset ** **************************************************************************/ if(can_reset) { /* Re-initialize CAN comms */ CanInit(); can_reset = 0; } #ifdef CAN_TEST // sample code to send periodic messages DISABLE_INTERRUPTS ultmp = lmms; ENABLE_INTERRUPTS if(ultmp > cansendclk) { cansendclk = ultmp + 7812; // 1 sec(7812 x .128 ms) clk for(ix = 0;ix < 6;ix++) { // load ring buffer - send rpm utmp1 = outpc.rpm; can[1].cx_datbuf[can[1].cxno_in][0] = *((char *)&utmp1); can[1].cx_datbuf[can[1].cxno_in][1] = *((char *)&utmp1 + 1); can[1].cx_msg_type[can[1].cxno_in] = MSG_CMD; can[1].cx_destvarblk[can[1].cxno_in] = 6; // Told to put rpm in outpc.spare[3]; below is offset can[1].cx_destvaroff[can[1].cxno_in] = (unsigned short)(&outpc.spare[3]) - (unsigned short)(&outpc); can[1].cx_dest[can[1].cxno_in] = 1; // send to device 1 can[1].cx_varbyt[can[1].cxno_in] = 2; // 2 bytes // This is where (in xmt ring buffer) to put next message if(can[1].cxno_in < (NO_CANMSG - 1)) can[1].cxno_in++; else can[1].cxno_in = 0; // increment counter if(can[1].cxno < NO_CANMSG) can[1].cxno++; else can[1].cxno = NO_CANMSG; } if(!(CANTIER & 0x07)) { // Following will cause entry to TxIsr without sending msg // since when CANTIER = 0, CANTFLG left as buff empty(>0). // If CANTIER has at least 1 int buf enabled, will enter // TxIsr automatically. CANTBSEL = CANTFLG; CANTIER = CANTBSEL; } } #endif // end sample CAN send code } // END Main while(1) Loop } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #pragma CODE_SEG NON_BANKED #endif void ign_reset(void) { // enter here at init and when in stall condition // Disable IC, Ign,Inj OC interrupts TIE &= 0xC0; TSCR1 &= 0x7F; // disable all timers // reinitialize outpc.spare[1]++; synch = 0; cam_sync = 0; tach_tooth = 255; *pTacho |= 0x08 ; // Turn off tacho - active Lo outpc.engine = 0; outpc.rpm = 0; ICt_overflow[0] = 0; ICt_overflow[1] = 0; ICtt_ovflo = 0; IgnOCt_overflow[0] = 0; IgnOCt_overflow[1] = 0; kluge1[0] = 0; kluge1[1] = 0; kluge2[0] = 0; kluge2[1] = 0; Fl1OCt_overflow = 0; Fl2OCt_overflow = 0; tach_seq[0] = 0; tach_seq[1] = 0; lmms_lostSynch = lmms; impulse_count = 0; TC_ovflow = 0; TC_ov_ix = 0; pulse_no = 0; // This will inhibit overflow counters t_enable_IC[0] = 0xFFFFFFFF; t_enable_IC[1] = 0xFFFFFFFF; PWcalc = 1; PWcalc_lmms = 0xFFFFFFFF; if(inpram.Divider > 0) igncount = inpram.Divider - 1; // faster (re)synch if inject immed; // if go thru cranking, this will get wiped out and will wait // divider pulses to smooth transition to larger run pw else igncount = 0; outpc.tachCount = 0; altcount = 0; egocount = 0; egopstat = 0; tpsaclk = 0; cut_fuel = 0; knk_clk = 0; knk_clk_test = inpram.knk_trtd; knk_stat = 0; knk_count = 0; outpc.knk_rtd = 0; if(!(inpram.ICIgnOption & 0x02)) trig_ret_mode = 0; else trig_ret_mode = 2; TCTL4 &= ~0x33; // clear bits 0,1 and 4,5(won't hurt if not dual spk) if((inpram.DualSpkOptn < 2) || (inpram.DualSpkOptn == 6)) { if(inpram.ICIgnOption & 0x01) { // normal rising edge input capture if(!trig_ret_mode) // not trig ret mode TCTL4 |= 0x11; // rising edge input capture else // reverse in trigger return mode TCTL4 |= 0x22; // falling edge input capture } else { // normal falling edge input capture if(!trig_ret_mode) // not trig ret mode TCTL4 |= 0x22; // falling edge input capture else // reverse in trigger return mode TCTL4 |= 0x11; // rising edge input capture } } else { if(inpram.ICIgnOption & 0x01) // tach in is rising edge TCTL4 |= 0x01; else // tach in is falling edge TCTL4 |= 0x02; if((inpram.DualSpkOptn == 3) || (inpram.DualSpkOptn == 8)) TCTL4 |= 0x10; // cam/crnk_sync is rising edge else if((inpram.DualSpkOptn == 2) || (inpram.DualSpkOptn == 7)) TCTL4 |= 0x20; // cam/crnk_sync is falling edge } crank_trig_mode = (inpram.ICIgnOption & 0x04) >> 2; // Turn Off injectors TCTL2 &= ~0x44; // set outputs lo in OL1,3 CFORC |= 0x0A; PWME &= ~0x14; // turn off pwms *pPTMpin[3] &= ~0x08; // turn off inj led outpc.squirt = 0; // injectors off IgnOCpinstate = SPK; // clear timer interrupt flags TFLG1 = 0x3F; // enable IC timer(s) & interrupt(s) TSCR1 |= 0x80; if(!first_reset) { if(!inpram.DualSpkOptn || ((inpram.DualSpkOptn == 4) || (inpram.DualSpkOptn == 5))) { TIE |= 0x01; } else { // dual inputs TIE |= 0x05; } } return; } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #endif void get_adc(char chan1, char chan2) { char chan; long adcval; int adcvalv,tmp1,tmp2; for (chan = chan1; chan <= chan2; chan++) { switch(chan) { case 0: if((inpram.MAFOption & 0x0F) != 1) { // map adcval = (long)inpram.map0 + ((long)(inpram.mapmax - inpram.map0) * ATD0DR0) / 1023; // kPa x 10 if(first_adc) outpc.map = (short)adcval; else outpc.map += (short)((inpram.mapLF * (adcval - outpc.map)) / 100); } else { // maf only if(first_adc) mafraw = maffactor_table[ATD0DR0]; // mg/ secx10 else { adcval = maffactor_table[ATD0DR0]; mafraw += (short)((inpram.mapLF * (adcval - mafraw)) / 100); // mg/secx 10 } } break; case 1: adcval = (long)inpram.mat0 + ((long)inpram.matmult * matfactor_table[ATD0DR1]) / 100; // deg F or C x 10 if(first_adc) outpc.mat = (short)adcval; else outpc.mat += (short)((inpram.adcLF * (adcval - outpc.mat)) / 100); break; case 2: adcval = (long)inpram.clt0 + ((long)inpram.cltmult * cltfactor_table[ATD0DR2]) / 100; // deg F or C x 10 if(first_adc) outpc.clt = (short)adcval; else outpc.clt += (short)((inpram.adcLF * (adcval - outpc.clt)) / 100); break; case 3: adcval = (ATD0DR3 - (long)inpram.tps0) * 1000 / (inpram.tpsmax - inpram.tps0); // % x 10 if(first_adc) outpc.tps = (short)adcval; else outpc.tps += (short)((inpram.tpsLF * (adcval - outpc.tps)) / 100); break; case 4: adcval = (long)inpram.batt0 + ((long)(inpram.battmax - inpram.batt0) * ATD0DR4) / 1023; // V x 10 if(first_adc) outpc.batt = (short)adcval; else outpc.batt += (short)((inpram.adcLF * (adcval - outpc.batt)) / 100); break; case 5: if(inpram.EgoOption >= 1) { // check if sensor bad (near limits) if((ATD0DR5 < 3) || (ATD0DR5 > 1020)) bad_ego_flag |= 0x01; else bad_ego_flag &= ~0x01; adcval = (long)inpram.ego0 + ((long)inpram.egomult * egofactor_table[ATD0DR5]) / 100; // afr x 10 tmp1 = (int)ATD0DR5; asm { ldy tmp1 ldd #500 EMULS ldx #1023 EDIVS sty adcvalv }; if(first_adc) { outpc.ego1 = (short)adcval; outpc.egoV1 = adcvalv; // Vx100 } else { outpc.ego1 += (short)((inpram.egoLF * (adcval - outpc.ego1)) / 100); tmp1 = (int)inpram.egoLF; tmp2 = adcvalv - outpc.egoV1; asm { ldy tmp1 ldd tmp2 EMULS ldx #100 EDIVS sty adcvalv }; outpc.egoV1 += adcvalv; // Vx100 } } break; case 6: if(inpram.BaroOption == 2) { adcval = (long)inpram.baro0 + ((long)(inpram.baromax - inpram.baro0) * ATD0DR6) / 1023; // kPa x 10 if(first_adc) outpc.baro = (short)adcval; else outpc.baro += (short)((inpram.adcLF * (adcval - outpc.baro)) / 100); outpc.ego2 = outpc.ego1; outpc.egoV2 = outpc.egoV1; } else if((inpram.EgoOption == 2) || (inpram.EgoOption == 4)) { // check if sensor bad (near limits) if((ATD0DR6 < 3) || (ATD0DR6 > 1020)) bad_ego_flag |= 0x02; else bad_ego_flag &= ~0x02; adcval = (long)inpram.ego0 + ((long)inpram.egomult * egofactor_table[ATD0DR6]) / 100; // afr x 10 tmp1 = (int)ATD0DR6; asm { ldy tmp1 ldd #500 EMULS ldx #1023 EDIVS sty adcvalv }; if(first_adc) { outpc.ego2 = (short)adcval; outpc.egoV2 = adcvalv; // Vx100 } else { outpc.ego2 += (short)((inpram.egoLF * (adcval - outpc.ego2)) / 100); tmp1 = (int)inpram.egoLF; tmp2 = adcvalv - outpc.egoV2; asm { ldy tmp1 ldd tmp2 EMULS ldx #100 EDIVS sty adcvalv }; outpc.egoV2 += adcvalv; // Vx100 } } else if(inpram.MAFOption == 0x12) { if(first_adc) mafraw = maffactor_table[ATD0DR6]; // mg/ secx10 else { adcval = maffactor_table[ATD0DR6]; mafraw += (short)((inpram.mapLF * (adcval - mafraw)) / 100); } outpc.ego2 = outpc.ego1; outpc.egoV2 = outpc.egoV1; } else { outpc.ego2 = outpc.ego1; outpc.egoV2 = outpc.egoV1; } break; case 7: if(inpram.MAFOption != 0x22) { adcval = (long)inpram.knock0 + ((long)(inpram.knockmax - inpram.knock0) * ATD0DR7) / 1023; // V x 100 if(first_adc) outpc.knock = (short)adcval; else outpc.knock += (short)((inpram.knkLF * (adcval - outpc.knock)) / 100); } else { if(first_adc) mafraw = maffactor_table[ATD0DR7]; // mg/ secx10 else { adcval = maffactor_table[ATD0DR7]; mafraw += (short)((inpram.mapLF * (adcval - mafraw)) / 100); } } break; default: break; } // end of switch } // end of for loop return; } #ifndef GCC_BUILD #pragma CODE_SEG ROM_7400 #endif void switch_page(unsigned char sub_no, char carg1, char carg2) { // This resides in non-banked memory and is used to call subroutines // from another page. switch(sub_no) { case 0: PPAGE = 0x3D; ego_calc(); PPAGE = 0x3C; break; case 1: PPAGE = 0x3C; get_adc(carg1,carg2); PPAGE = 0x3D; break; case 2: PPAGE = 0x3D; amc(); PPAGE = 0x3C; break; case 3: PPAGE = 0x3D; tpsaen(); PPAGE = 0x3C; break; default: break; } return; } int intrp_1dctable(char sgnx, int x, unsigned char n, int * x_table, char sgnz, unsigned char * z_table) { int ix; long interp, interp3; // bound input arguments if((sgnx && (x > x_table[n-1])) || (!sgnx && ((unsigned int)x > (unsigned int)x_table[n-1]))) { if(!sgnz) return((int)z_table[n -1]); else return((int)((char)z_table[n -1])); } if((sgnx && (x < x_table[0])) || (!sgnx && ((unsigned int)x < (unsigned int)x_table[0]))) { if(!sgnz) return((int)z_table[0]); else return((int)((char)z_table[0])); } for(ix = n - 2; ix > -1; ix--) { if((sgnx && (x > x_table[ix])) || (!sgnx && ((unsigned int)x > (unsigned int)x_table[ix]))) { break; } } if(ix < 0)ix = 0; if(sgnx) interp = x_table[ix + 1] - x_table[ix]; else interp = (unsigned int)x_table[ix + 1] - (unsigned int)x_table[ix]; if(interp != 0) { if(sgnx) interp3 = x - x_table[ix]; else interp3 = (unsigned int)x - (unsigned int)x_table[ix]; interp3 = (100 * interp3); interp = interp3 / interp; } if(!sgnz) return((int)(z_table[ix] + interp * (z_table[ix+1] - z_table[ix])/ 100)); else return((int)((char)z_table[ix] + interp * ((char)z_table[ix+1] - (char)z_table[ix])/ 100)); } int intrp_2dctable(unsigned int x, int y, unsigned char nx, unsigned char ny, unsigned int * x_table, int * y_table, unsigned char * z_table) { int ix,jx; long interp1, interp2, interp3; // bound input arguments if(x > x_table[nx-1])x = x_table[nx-1]; else if(x < x_table[0])x = x_table[0]; if(y > y_table[ny-1])y = y_table[ny-1]; else if(y < y_table[0])y = y_table[0]; // Find bounding indices in table for(ix = ny - 2; ix > -1; ix--) { // Start w highest index // because will generally have least time for calculations at hi y if(y > y_table[ix]) { break; } } if(ix < 0)ix = 0; for(jx = nx - 2; jx > -1; jx--) { // Start w highest index // because will generally have least time for calculations at hi x if(x > x_table[jx]) { break; } } if(jx < 0)jx = 0; // do 2D interpolate interp1 = y_table[ix + 1] - y_table[ix]; if(interp1 != 0) { interp3 = (y - y_table[ix]); interp3 = (100 * interp3); interp1 = interp3 / interp1; } interp2 = x_table[jx + 1] - x_table[jx]; if(interp2 != 0) { interp3 = (x - x_table[jx]); interp3 = (100 * interp3); interp2 = interp3 / interp2; } return((int)(((100 - interp1) * (100 - interp2) * z_table[ix*nx+jx] + interp1 * (100 - interp2) * z_table[(ix+1)*nx+jx] + interp2 * (100 - interp1) * z_table[ix*nx+jx+1] + interp1 * interp2 * z_table[(ix+1)*nx+jx+1]) / 10000)); } unsigned int intrp_1ditable(unsigned int x, unsigned char nx, unsigned int * x_table, unsigned int * z_table) { int ix; long interp, interp3; // bound input arguments if(x > x_table[nx-1])return(z_table[nx-1]); else if(x < x_table[0])return(z_table[0]); // Find bounding indiex in table for(ix = nx - 2; ix > -1; ix--) { // Start w highest index // because will generally have least time for calculations at hi y if(x > x_table[ix]) { break; } } if(ix < 0)ix = 0; // do 1D interpolate interp = (long)(x_table[ix + 1] - x_table[ix]); if(interp != 0) { interp3 = (long)(x - x_table[ix]); interp3 = (100 * interp3); interp = interp3 / interp; } return((unsigned int)(z_table[ix] + interp * (int)(z_table[ix+1] - z_table[ix])/ 100)); } int CW_table(int clt, int *table) { int ix; long interp, interp3; // returns values for various cold warmup table interpolations // bound input arguments if(clt > inpram.temp_table[NO_TEMPS-1]) { return(table[NO_TEMPS -1]); } if(clt < inpram.temp_table[0]) { return(table[0]); } for(ix = NO_TEMPS - 2; ix > -1; ix--) { if(clt > inpram.temp_table[ix]) { break; } } if(ix < 0)ix = 0; interp = inpram.temp_table[ix + 1] - inpram.temp_table[ix]; if(interp != 0) { interp3 = (clt - inpram.temp_table[ix]); interp3 = (100 * interp3); interp = interp3 / interp; } return((int)(table[ix] + interp * (table[ix+1] - table[ix])/ 100)); } #ifndef GCC_BUILD #pragma CODE_SEG OTHER_ROM #endif void tpsaen(void) { int tmp1,tmp2,tmp3,tmp4,ae_scaler; static int tpsatmp,tpsaccel_end; /************************************************************************** ** ** Throttle Position Acceleration Enrichment ** ** Method is the following: ** ** ** ACCELERATION ENRICHMENT: ** If (tpsdot < 0) goto DEACCELERATION_ENRICHMENT ** If tpsdot > tpsthresh and TPSAEN bit = 0 then (acceleration enrichment): ** { ** 1) Set acceleration mode ** 2) Continuously determine rate-of-change of throttle, and perform ** interpolation of table values to determine acceleration ** enrichment amount to apply. ** } ** If (TPSACLK > TpsAsync) and TPSAEN is set then: ** { ** 1) Clear TPSAEN bit in engine ** 2) Set TPSACCEL to 0 ** 3) Go to EGO Delta Step Check Section ** } ** Enrichment tail-off pulsewidth: ** ** ------------------ tpsaccel ** | |\ ** | | \ ** | | \ ** | | \ ____ TpsAccel2 ** | | | ** | | | ** --------------------------- ** <--TpsAsync--><-TpsAsync2-> ** ** ** DEACCELERATION ENRICHMENT: ** If (-tpsdot) > tpsthresh then (deceleration fuel cut) ** { ** If (TPSAEN = 1) then: ** { ** 1) TPSACCEL = 0 (no acceleration) ** 2) Clear TPSAEN bit in ENGINE ** 3) Go to EGO Delta Step ** } ** If (RPM > 1500 then (fuel cut mode): ** { ** 1) Set TPSACCEL value to TPSDQ ** 2) Set TPSDEN bit in ENGINE ** 3) Go to EGO Delta Step Check Section ** } ** } ** else ** { ** If (TPSDEN = 1) then ** { ** 1) Clear TPSDEN bit in ENGINE ** 2) TPSACCEL = 0 ** 3) Go to EGO Delta Step Check Section ** } ** } ** **************************************************************************/ tpsatmp = outpc.tpsaccel; if(outpc.tpsdot < 0)goto TDE; // decelerating if(outpc.engine & 0x10)goto AE_COMP_SHOOT_AMT; // if accel enrich bit set if(((inpram.Tps_acc_wght == 0) || (outpc.tpsdot < inpram.TpsThresh)) && ((inpram.Tps_acc_wght == 100) || (outpc.mapdot < inpram.MapThresh))) goto TAE_CHK_TIME; // start out using first element - determine actual next time around tpsatmp = (((short)inpram.tpsen_table[0] * inpram.Tps_acc_wght) + ((short)inpram.mapen_table[0] * (100 - inpram.Tps_acc_wght))) / 100; tpsaccel_end = tpsatmp; // latch last tpsaccel before tailoff tpsaclk = 0; // incremented in .1 sec timer outpc.engine |= 0x10; // set tpsaen bit outpc.engine &= ~0x20; // clear tpsden bit *pPTMpin[4] |= 0x10; // set accel led goto END_TPS; /* First, calculate Cold temperature add-on enrichment value from coolant value TPSACOLD at min temp & 0 at high temp. Then determine cold temperature multiplier value ACCELMULT (in percent). Next, Calculate Shoot amount (quantity) for acceleration enrichment from table. Find bins (between) for corresponding TPSDOT and MAPDOT, and linear interpolate to find enrichment amount from table. This is continuously checked every time thru main loop while in acceleration mode, and the highest value is latched and used. The final acceleration enrichment (in .1 ms units) applied is AE = (((Alookup(TPSDOT)*Tps_acc_wght + Alookup(MAPDOT)*(100 - Tps_acc_wght)) /100) * ACCELMULT/100) + TPSACOLD. */ AE_COMP_SHOOT_AMT: if(tpsaclk <= inpram.TpsAsync) { tmp1 = (short)inpram.Tpsacold - (short)((inpram.Tpsacold * (long)(outpc.clt - inpram.temp_table[0]) / (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // in .1 ms tmp2 = (short)inpram.AccMult + (short)(((100 - inpram.AccMult) * (long)(outpc.clt - inpram.temp_table[0]) / (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // in % if(inpram.Tps_acc_wght > 0) { tmp3 = intrp_1dctable(1, outpc.tpsdot, NO_TPS_DOTS, inpram.tpsdot_table, 0, inpram.tpsen_table); // .1 ms units } else tmp3 = 0; if(inpram.Tps_acc_wght < 100) { tmp4 = intrp_1dctable(1, outpc.mapdot, NO_MAP_DOTS, inpram.mapdot_table, 0, inpram.mapen_table); // .1 ms units } else tmp4 = 0; tmp3 = ((tmp3 * inpram.Tps_acc_wght) + (tmp4 * (100 - inpram.Tps_acc_wght))) / 100; tmp3 = (tmp3 * tmp2) / 100; if(tmp3 > 200) tmp3 = 200; // rail at 20 ms tmp3 += tmp1; if(tmp3 > tpsatmp) tpsatmp = tmp3; // make > tps/mapen_table entry for lowest tps/mapdot // plus latch and hold largest pw tpsaccel_end = tpsatmp; // latch last tpsaccel before tailoff } else { // tailoff enrichment pulsewidth TAILOFF: if(inpram.TpsAsync2 > 0) { tpsatmp = tpsaccel_end + (short)(((inpram.TpsAccel2 - tpsaccel_end) * (long)(tpsaclk - inpram.TpsAsync)) / inpram.TpsAsync2); } else tpsatmp = 0; } TAE_CHK_TIME: // check if accel is done // if tps decel bit not set, accel bit is if(!(outpc.engine & 0x20) && (outpc.engine & 0x10)) { if(tpsaclk < (inpram.TpsAsync + inpram.TpsAsync2))goto END_TPS; } goto KILL_ACCEL; TDE: // tpsdot < 0 if(outpc.engine & 0x10) { // if tps accel bit set if((-outpc.tpsdot) > inpram.TpsThresh) goto KILL_ACCEL; if(tpsaclk < inpram.TpsAsync) tpsaclk = inpram.TpsAsync; // just tail off AE goto TAILOFF; } // decel if((-outpc.tpsdot) > inpram.TpsThresh) { if(outpc.rpm < 1500)goto END_TPS; outpc.tpsfuelcut = inpram.TPSDQ; // in % outpc.engine |= 0x20; // set tps decel bit goto END_TPS; } // ** Jedrik bug fix if(outpc.engine & 0x30) { // if decel or just finished accel KILL_ACCEL: outpc.engine &= ~0x30; // clear tps decel, accel bits outpc.tpsfuelcut = 100; *pPTMpin[4] &= ~0x10; // clear accel led tpsatmp = 0; } END_TPS: // apply AE rpm-based scaler if(outpc.rpm <= inpram.ae_lorpm) ae_scaler = 100; else if(outpc.rpm >= inpram.ae_hirpm) ae_scaler = 0; else { ae_scaler = (short)(((long)100 * (inpram.ae_hirpm - outpc.rpm)) / (inpram.ae_hirpm - inpram.ae_lorpm)); } outpc.tpsaccel = (tpsatmp * ae_scaler) / 100; return; } void ego_calc(void) { /************************************************************************** ** ** Exhaust Gas Oxygen Sensor Measurement Section ** **************************************************************************/ long ltmp; unsigned long ultmp; int ego1step,ego2step; static unsigned long tegoclk,tegoXpt,tegoupdate; // calculate target AFR if(((inpram.EgoOption > 0) && (inpram.EgoOption <= 2)) || (inpram.AFRMult == 1)) { // NBO2 outpc.afrtgt1 = inpram.AFRTarget; outpc.afrtgt2 = inpram.AFRTarget; } else if((inpram.EgoOption >= 3) || (inpram.AFRMult == 2)) { // WBO2 outpc.afrtgt1 = (unsigned char)intrp_2dctable(outpc.rpm, outpc.kpaix, NO_FRPMS, NO_FMAPS, &inpram.frpm_table[0], &inpram.fmap_table[0], &inpram.afr_table[0][0][0]); // afr x 10 if(inpram.dual_tble_optn) outpc.afrtgt2 = (unsigned char)intrp_2dctable(outpc.rpm, outpc.kpaix, NO_FRPMS, NO_FMAPS, &inpram.frpm_table[0], &inpram.fmap_table[0], &inpram.afr_table[1][0][0]); // afr x 10 else outpc.afrtgt2 = outpc.afrtgt1; } if( (inpram.EgoOption == 0) || (outpc.rpm < inpram.RPMOXLimit) || (outpc.engine & 0x30) || // engine accel/ decel (outpc.clt < inpram.EgoTemp) || (outpc.tps > inpram.TPSOXLimit) || (outpc.kpa > inpram.MAPOXLimit) || (outpc.seconds < 30)) { outpc.egocor1 = 100; outpc.egocor2 = outpc.egocor1; // get ego1,2 from sensor switch_page(1,5,6); // get_adc ego_cloop = 0; egopstat = 0; // out of ego closed PID loop goto EGO_RETURN; } ego_cloop = 1; if(inpram.EgoAlg == 0) { // simple (non-pid) ego closed loop if(egocount < inpram.EgoCountCmp)goto EGO_RETURN; // check if rich/ lean after every EgoCountCmp ignition pulses // get ego1,2 from sensor switch_page(1,5,6); // get_adc egocount = 0; if(inpram.EgoOption <= 2) { // NBO2 if(outpc.egoV1 < outpc.afrtgt1) { // O2_IS_LEAN ego1step = (int)inpram.EgoStep; } else { // O2_IS_RICH ego1step = -(int)inpram.EgoStep; } ego2step = ego1step; } else if(inpram.EgoOption >= 3) { // WBO2 // calculate error ego1step = (inpram.egoKP * (outpc.ego1 - outpc.afrtgt1)) / outpc.afrtgt1; ego2step = (inpram.egoKP * (outpc.ego2 - outpc.afrtgt2)) / outpc.afrtgt2; } // end EgoOption >= 3 } // end EgoAlg = 0 else { // Prop err corr with transport delay or // PID algorithm with Smith predictor if(egopstat == 0) { // set up for ego closed P/PID loop egopstat = 1; tegoclk = 0; ego1err = 0; sego1err = 0; ego1errm1 = ego1err; ego2err = 0; sego2err = 0; ego2errm1 = ego2err; outpc.egocor1 = 100; outpc.egocor2 = outpc.egocor1; afrtgt_skip = 0; afrdl1 = outpc.afrtgt1; afrdl2 = outpc.afrtgt2; } DISABLE_INTERRUPTS ultmp = lmms; ENABLE_INTERRUPTS if(ultmp < tegoclk)goto EGO_RETURN; // get ego1,2 from sensor switch_page(1,5,6); // get_adc // update correction // go through pid/Smith pred algorithm DISABLE_INTERRUPTS ultmp = lmms; ENABLE_INTERRUPTS tegoclk = ultmp + EGODT; // .128 tics if(outpc.rpm > 0) { ltmp = inpram.egoKdly1 + (((long)(inpram.egoKdly2 * 120000) / outpc.kpa) / outpc.rpm); // sensor + transport delay (ms) tegoXpt = (ltmp * 1000) / 128; // .128 tics } else { tegoXpt = 7812; // = 1 sec in .128 tics } if(inpram.EgoAlg == 2) { if(afrtgt_skip) afrtgt_skip--; else { if(tegoXpt > 0) { // Calculate slow lag coeff(%) for delayed afr target afrSL = afrLF_calc(tegoXpt); if(afrSL < 34) { // From eq for afrdly (below) any SL < 34 and (afrtgt - afrdly) // < 3 (afrx10) means the update for afrdly will be 0. In this // case keep afrSL =34, but skip (tegoXpt/txpt(SL=34)) updates. afrSL = 34; afrtgt_skip = (unsigned short)(tegoXpt / (unsigned short)468); } else { afrtgt_skip = 0; if(afrSL > 100)afrSL = 100; } } else { afrtgt_skip = 0; afrSL = 100; } // Smith Predictor delay free model outputs = outpc.afrtgt1,2 // delayed model outputs = afrdl1,2 afrdl1 += (int)((afrSL * (int)(outpc.afrtgt1 - afrdl1)) / 100); afrdl2 += (int)((afrSL * (int)(outpc.afrtgt2 - afrdl2)) / 100); } } // calc err from model o/ps: egoerr = ego + (afrtgt - afrdly) - afrtgt if(inpram.EgoOption <= 2) { // NB ego1err = -(int)(outpc.egoV1 - afrdl1); ego2err = -(int)(outpc.egoV2 - afrdl2); } else { // WB ego1err = outpc.ego1 - afrdl1; ego2err = outpc.ego2 - afrdl2; } if(egopstat == 1) { // 1st time just correct and wait tegoXpt delay ego1step = ((int)(inpram.egoKP * ego1err) / (int)afrdl1); ego2step = ((int)(inpram.egoKP * ego2err) / (int)afrdl2); if(inpram.EgoAlg == 1) { // for this alg. correct based on ego measurement (which is delayed) // and the delayed afrtgt - so both at approx. same time. tegoupdate = tegoclk; afrdl1 = outpc.afrtgt1; afrdl2 = outpc.afrtgt2; } egopstat = 2; } else if(egopstat == 2) { ego1step = 0; // initially don't do anything til tegoXpt delay ego2step = 0; if(inpram.EgoAlg == 1) { if(tegoclk - tegoupdate > tegoXpt) egopstat = 1; // make another correction next time thru } else { if(tegoclk > tegoXpt) egopstat = 3; } } else { // egopstat=3 = normal error correction for EgoAlg = 2 egoKPX = (int)(((int)EGODT * inpram.egoKP) / tegoXpt); if(egoKPX < 1)egoKPX = 1; if(ego1err * ego1errm1 <= 0)sego1err = 0; sego1err += ego1err; if(sego1err > 16000)sego1err = 16000; if(sego1err < -16000)sego1err = -16000; ego1step = (int)(egoKPX * (ego1err + (int)((inpram.egoKI* sego1err)/ 100) + (int)(inpram.egoKD * (ego1err - ego1errm1) / 100)) / (int)afrdl1); if(ego1step != 0)sego1err = 0; ego1errm1 = ego1err; if(ego2err * ego2errm1 <= 0)sego2err = 0; sego2err += ego2err; if(sego2err > 16000)sego2err = 16000; if(sego2err < -16000)sego2err = -16000; ego2step = (int)(egoKPX * (ego2err + (int)((inpram.egoKI* sego2err)/ 100) + (int)(inpram.egoKD * (ego2err - ego2errm1) / 100)) / (int)afrdl2); if(ego2step != 0)sego2err = 0; ego2errm1 = ego2err; } } // end p/pid loop - EgoAlg = 1,2 // implement correction - update egocor1,2 if(!outpc.wbo2_en1) { // no valid ego rdbk outpc.egocor1 = 100; } else { if(ego1step > 0) { // O2_IS_LEAN if((outpc.egocor1 + ego1step) <= (100 + inpram.EgoLimit)) outpc.egocor1 += ego1step; // egocor not railed else outpc.egocor1 = 100 + inpram.EgoLimit; // railed } else { // O2_IS_RICH if((outpc.egocor1 + ego1step) >= (100 - inpram.EgoLimit)) outpc.egocor1 += ego1step; // egocor not railed else outpc.egocor1 = 100 - inpram.EgoLimit; // railed } } if(!outpc.wbo2_en2) { // no valid ego rdbk outpc.egocor2 = 100; } else { if(ego2step > 0) { // O2_IS_LEAN if((outpc.egocor2 + ego2step) <= (100 + inpram.EgoLimit)) outpc.egocor2 += ego2step; // egocor not railed else outpc.egocor2 = 100 + inpram.EgoLimit; // railed } else { // O2_IS_RICH if((outpc.egocor2 + ego2step) >= (100 - inpram.EgoLimit)) outpc.egocor2 += ego2step; // egocor not railed else outpc.egocor2 = 100 - inpram.EgoLimit; // railed } } EGO_RETURN: return; } /************************************************************************** ** ** Automatic Mixture Control - enter once a second ** ***************************************************************************/ void amc(void) { int ix,jx,tmp1; static char ve_mix,ve_rjx; static unsigned char ve_save[NO_INJ]; static long sumegocor1,sumegocor2; if(outpc.seconds > AMCclk) { AMCclk = outpc.seconds; // check criteria for being in AMC mode if(ego_cloop && (bad_ego_flag < 3) && (outpc.warmcor == 100) && (outpc.tpsfuelcut == 100) && (outpc.tpsaccel == 0) && (outpc.fuelcor == 100)) { if(!AMCmode) { AMCmode =1; // initialize for entry to AMC mode AMCve_clk = 0; AMCNSum = 0; ve_mix = -1; ve_rjx = -1; sumegocor1 = 0; sumegocor2 = 0; AMCdve_change = 0; bad_ego_ltch = 0; } // latch occurence of bad ego sensor bad_ego_ltch |= bad_ego_flag; // check if near a map/rpm node for(ix = 0;ix < NO_FMAPS; ix++) { if(outpc.kpaix < inpram.fmap_table[ix]) break; } if(ix == NO_FMAPS) { ix--; } else if(ix > 0) { tmp1 = outpc.kpaix - inpram.fmap_table[ix-1]; if(tmp1 < inpram.AMCve_dmap) { if((inpram.fmap_table[ix] - outpc.kpaix) >= tmp1)ix--; } else if((inpram.fmap_table[ix] - outpc.kpaix) >= inpram.AMCve_dmap) ix = -1; } if(ix == -1) goto NOT_VRTX; // no need to evaluate rpm vertex for(jx = 0;jx < NO_FRPMS; jx++) { if(outpc.rpm < inpram.frpm_table[jx]) break; } if(jx == NO_FRPMS) { jx--; } else if(jx > 0) { tmp1 = outpc.rpm - inpram.frpm_table[jx-1]; if(tmp1 < inpram.AMCve_drpm) { if((inpram.frpm_table[jx] - outpc.rpm) >= tmp1)jx--; } else if((inpram.frpm_table[jx] - outpc.rpm) >= inpram.AMCve_drpm) jx = -1; } if(jx == -1) { NOT_VRTX: ve_mix = -1; ve_rjx = -1; AMCve_clk = outpc.seconds; AMCNSum = 0; sumegocor1 = 0; sumegocor2 = 0; goto CHK_BURN; } if((ix != ve_mix) || (jx != ve_rjx)) { // moved to a new vertex ve_mix = (char)ix; ve_rjx = (char)jx; ve_save[0] = in2ram.ve_table[0][ve_mix][ve_rjx]; ve_save[1] = in2ram.ve_table[1][ve_mix][ve_rjx]; AMCve_clk = outpc.seconds; AMCNSum = 0; sumegocor1 = 0; sumegocor2 = 0; } sumegocor1 += outpc.egocor1; // 100% = no correction sumegocor2 += outpc.egocor2; AMCNSum++; // check for how long at same vertex if(outpc.seconds - AMCve_clk >= inpram.AMCramve_dt) { // update ram ve by a % of ave ego correction tmp1 = 100 - (int)(sumegocor1 / AMCNSum); tmp1 = 100 - ((inpram.AMCStep * tmp1) / 100); if(!(bad_ego_ltch & 0x01)) in2ram.ve_table[0][ve_mix][ve_rjx] = (unsigned char)((tmp1 * in2ram.ve_table[0][ve_mix][ve_rjx]) / 100); tmp1 = 100 - (int)(sumegocor2 / AMCNSum); tmp1 = 100 - ((inpram.AMCStep * tmp1) / 100); if(!(bad_ego_ltch & 0x02)) in2ram.ve_table[1][ve_mix][ve_rjx] = (unsigned char)((tmp1 * in2ram.ve_table[1][ve_mix][ve_rjx]) / 100); if((bad_ego_ltch & 0x03) != 0x03) { AMCram_updates++; // send back to MT so knows when amc update and can fetch data outpc.amc_updates++; tmp1 = in2ram.ve_table[0][ve_mix][ve_rjx] - ve_save[0]; if((tmp1 > inpram.AMCdve) || (-tmp1 > inpram.AMCdve)) AMCdve_change = 1; tmp1 = in2ram.ve_table[1][ve_mix][ve_rjx] - ve_save[1]; if((tmp1 > inpram.AMCdve) || (-tmp1 > inpram.AMCdve)) AMCdve_change = 1; } AMCve_clk = outpc.seconds; AMCNSum = 0; sumegocor1 = 0; sumegocor2 = 0; } } // in AMC mode else { // leave AMC mode AMCmode = 0; } CHK_BURN: // Check if should burn ve tables in flash if((inpram.AMCOption > 1) && ((outpc.seconds - AMCburn_clk) > inpram.AMCT_thresh) && (AMCram_updates > inpram.AMCupdate_thresh) && AMCdve_change) { AMCburn_clk = outpc.seconds; AMCram_updates = 0; AMCdve_change = 0; burn_idx = 5; amc_burn_flag = 1; } } // seconds clock tic return; } unsigned char afrLF_calc(long t) { // afrtgt slow lag filter for Smith Prediction const int txpt[10] = { // xpt time, .128 ms tics 78,156,234,390,468,858,1716,3510,8892,17940}; const unsigned char SL[10] = { // Lag filter coeff to produce txpt delay 90,80,60,40,34,20,10,5,2,1}; // % int ix,interp; long interp3; if(t <= txpt[0]) return(SL[0]); if(t >= txpt[9]) return(SL[9]); for(ix = 1;ix <= 9; ix++) { if(t < txpt[ix])break; } interp = txpt[ix] - txpt[ix - 1]; interp3 = t - txpt[ix - 1]; return((char)(SL[ix-1] + (interp3*(SL[ix] - SL[ix-1]))/interp)); } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #pragma CODE_SEG NON_BANKED #endif INTERRUPT void ISR_Timer_Clock(void) { unsigned char coils,fsensdat; static unsigned char last_fsensdat; static unsigned short FPdcounter; unsigned char ix; // .128 ms clock interrupt - clear flag immediately to resume count CRGFLG = 0x80; // clear RTI interrupt flag // also generate 1.024 ms, .10035 sec and 1.0035 sec clocks lmms++; // free running clock(.128 ms tics) good for ~ 150 hrs mms++; // in .128 ms tics - reset every 8 tics = 1.0024 ms for(ix = 0; ix < 2;ix++) { if(kluge1[ix] == 1) { if(lmms > setlmms[ix] + 1) { if(ix == 0) TCTL1 = (TCTL1 & 0xFB) | (CHG << 2); // set OC o/p in OL5 else TCTL1 = (TCTL1 & 0xFE) | CHG; // set OC o/p in OL4 CFORC |= OCintmask[ix]; // force output in OLn onto OC pin t_chgoff[ix] = lmms + 156; // 20 ms timeout kluge1[ix] = 0; kluge2[ix] = 2; tach_seq[ix] = 2; ign_setpin[ix] = SPK; if(ign_state[ix] == 0) { // disable ign OC interrupt - will set up spk when get to spk tooth TIE &= ~OCintmask[ix]; continue; } else { // spark on same tooth as dwell IgnTimerComp[ix] = *pTOC[ix] + ((3 * coil_dur_chan[ix]) >> 1); // 2/3 us ign_state[ix] = 0; stop_ecorr[ix] = 2; if(IgnTimerComp[ix] < TCNT + 22)IgnTimerComp[ix] = TCNT + 22; if(IgnTimerComp[ix] > (long)65535) { IgnOCt_overflow[ix] = IgnTimerComp[ix] >> 16; IgnTimerComp[ix] &= 0xFFFF; } else IgnOCt_overflow[ix] = 0; if(!IgnOCt_overflow[ix]) { *pTOC[ix] = (unsigned short)IgnTimerComp[ix]; // load ign OC register setlmms2[ix] = lmms + (coil_dur_chan[ix] / 128); kluge2[ix] = 1; // enable Ign OC pin if(ix == 0) TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); else TCTL1 = (TCTL1 & 0xFE) | SPK; } else { // Disable Ign OC intrpt til ovflow done TIE &= ~OCintmask[ix]; } // clear ign OC interrupt flag TFLG1 = OCintmask[ix]; } } } else if(kluge2[ix] == 1) { if(lmms > setlmms2[ix] + 1) { if(ix == 0) TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); // set OC o/p in OL5 else TCTL1 = (TCTL1 & 0xFE) | SPK; // set OC o/p in OL4 CFORC |= OCintmask[ix]; // force output in OLn onto OC pin t_chgoff[ix] = 0xFFFFFFFF; kluge2[ix] = 0; IgnOCt_overflow[ix] = 0; TIE &= ~OCintmask[ix]; // Disable Ign OC interrupt tach_seq[ix] = 3; // clear ign OC interrupt flag TFLG1 = OCintmask[ix]; stop_ecorr[ix] = 2; } } } // end for ix loop // check for re-enabling IC interrupt if(lmms > t_enable_IC[0]) { TFLG1 = ICintmask[0]; // clear Ignition IC interrupt flag TIE |= ICintmask[0]; // enable Ignition IC interrupt t_enable_IC[0] = 0xFFFFFFFF; } if(lmms > t_enable_IC[1]) { TFLG1 = ICintmask[1]; // clear Ignition IC2 interrupt flag TIE |= ICintmask[1]; // enable Ignition IC2 interrupt t_enable_IC[1] = 0xFFFFFFFF; } // check for turning on pwm duty cycle if(!(outpc.engine & 0x02)) { // if engine not cranking if(!pwm1_on) { // and haven't started pwm if(lmms > pwm1_time) { pwm1_on = 1; PWMPER2 = inpram.InjPWMPd; // set PWM period (us) // set PWM duty (on time) PWMDTY2 = (unsigned char)((inpram.InjPWMDty * PWMPER2) / 100); } } if(!pwm2_on) { // and haven't started pwm if(lmms > pwm2_time) { pwm2_on = 1; PWMPER4 = inpram.InjPWMPd; // set PWM period (us) // set PWM duty (on time) PWMDTY4 = (unsigned char)((inpram.InjPWMDty * PWMPER4) / 100); } } } // check for IAC step pulses if(IdleCtl == 4) { iacpwmctr += inpram.IACpwm_step; if (iacpwmctr >= 100) iacpwmctr=0; // 100 - IACmotor_pos = duty cycle if (IACmotor_pos > iacpwmctr) *pPTMpin[2] &= ~0x04; // turn off fast idle solenoid else *pPTMpin[2] |= 0x04; // turn on fast idle solenoid outpc.iacstep = IACmotor_pos; // Dual purpose as idle DC - E.Fahlgren fix } else { if(IAC_moving > 1) { if(lmms > motor_time) { motor_time = lmms + ((inpram.IACtstep*8)/10); // .128 ms motor_step++; if(motor_step > 7)motor_step = 0; if(IACmotor_pos > outpc.iacstep) { // cw outpc.iacstep++; coils = (IACCoilA[7-motor_step] << 6) | (IACCoilB[7-motor_step] << 7); } else if(IACmotor_pos < outpc.iacstep) { //ccw outpc.iacstep--; coils = (IACCoilA[motor_step] << 6) | (IACCoilB[motor_step] << 7); } *pPTTpin[6] = (PTT & ~0xC0) | coils; if(outpc.iacstep == IACmotor_pos) { if(!IACmotor_reset) { IACmotor_reset = 1; last_iacclt = -3200; // this will cause to go to // temperature based position after initial reset IAC_moving = 0; } else { motor_time += 32000; // delay next move for ~4 secs IAC_moving = 1; } if(IdleCtl == 2) PORTB |= 0x10; // disable current to stepper motor(bit=1) } } } else if(IAC_moving) { if(lmms > motor_time) { IAC_moving = 0; } } } // read fuel sensor data, determine freq(proportional to %alcohol) if(inpram.FlexFuel) { fsensdat = PORTE & 0x01; switch(FSensStat) { case 0: // first time - save pin value last_fsensdat = fsensdat; FPdcounter = 0; FSens_Pd = 0; FSensStat = 1; break; case 1: // wait for pin hi -> lo transition to start period(freq) count if(last_fsensdat && !fsensdat) { FPdcounter++; // .128 ms tics FSensStat = 2; } break; case 2: // continue period count if(last_fsensdat && !fsensdat) { // transition hi -> lo // save present count = period FSens_Pd = FPdcounter; // .128 ms tics FPdcounter = 0; // start new period count } FPdcounter++; break; default: break; } // end of switch last_fsensdat = fsensdat; } // check mms to generate other clocks if(mms < 8)goto CLK_DONE; mms = 0; millisec++; // actually 1.024 ms if(millisec > 97) { millisec = 0; tpsaclk++; // .10035 sec clocks knk_clk++; } CLK_DONE: return; } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #pragma CODE_SEG NON_BANKED #endif INTERRUPT void ISR_Ign_TimerIn(void) { char save_page; unsigned long ltmp1; save_page = PPAGE; PPAGE = 0x3D; Ign_TimerIn(); if(ic_ret_flg == 1) { // IC_EXIT if(inpram.No_Teeth) { PPAGE = save_page; // Check if any teeth have come in during isr processing if(TFLG1 & 0x01) { TFLG1 = 0x01; // clear IC interrupt flag mask_teeth++; } if(TFLG1 & 0x01) { TFLG1 = 0x01; // clear IC interrupt flag mask_teeth++; } if(TFLG1 & 0x01) { TFLG1 = 0x01; // clear IC interrupt flag mask_teeth++; } *pTacho |= 0x08; // turn off tach pulse return; } else { // Set up to renable IC interrupt in Timer ISR after a part of the time // to next IC has elapsed to avoid coil ringing noise false interrupts ltmp1 = ((inpram.ICISR_pmask * dtpred) / 100) + ((unsigned long)inpram.ICISR_tmask * 100); t_enable_IC[ICint] = ltch_lmms + ((ltmp1 * 8) / 1000); // .128 ms units TIE &= ~ICintmask[ICint]; // disable interrupt } } // IC_RET TFLG1 = ICintmask[ICint]; // clear IC interrupt flag PPAGE = save_page; *pTacho |= 0x08; // turn off tach pulse return; } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #pragma CODE_SEG OTHER_ROM #endif void Ign_TimerIn(void) { static unsigned long t_in3[2],dt2[2],dt3[2],t3t,dt3t; static unsigned long dts; static long tddts,tddtpred,t2dddts; unsigned long t_in2,t2t,dt2t; static unsigned short st3t,sdt3t,sdtprdt; unsigned short st2t,sdt2t; long errt,tcnt,tcnt2; short stcnt; static unsigned long dtprdt; static unsigned char missed_pulse[2],xtra_pulse[2],no_chk_pulses, no_wheel_teeth,nsynch,savovflo,just_synch,last_cam; unsigned int rpm_pred=0,rpm=0; unsigned long ltmp1,ltmp2,dtpredx,tooth_ltme; unsigned short stmp1,stmp2,TCICus,tcIC,tooth_tme; short tooth_dwell_x100; unsigned short spk_dly_t100,coil_dur_t100; char sync_flg; unsigned char ctmp1,mask_save,ix,jx,chk_tach; long old_charge; union ultyp { unsigned long ltmp; unsigned int pltmp[2]; } ul1, ul2; // Just received new input pulse - 1/ cyl / 2 crank revs (1 for 2 stroke). // In wheel decode mode will throw out pulses to achieve this. if(TFLG1 & 0x01) { // interrupt from 1st IC sensor TFLG1 = 0x01; // clear IC interrupt flag ICint = 0; if((inpram.DualSpkOptn == 1) || (inpram.DualSpkOptn == 6)) OCint = 0; tcIC = *pTIC[0]; TCICus = (tcIC/3)<<1; if(pulse_no == 0) { // 1st input pulse pulse_no++; OCint = 0; ICt_overflow[0] = 0; ICtt_ovflo = 0; t_in3[0] = TCICus; dt2[0] = 0; dt3[0] = 0; // time difference between pulses, us outpc.dt3 = dt3[0]; // for HP calculations dtpred = dt3[0]; // predicted time difference for next pulse tddtpred = 0; t2dddts = 0; ign_state[0] = 0; missed_pulse[0] = 0; xtra_pulse[0] = 0; mask_teeth = 0; nsynch = 0; if(inpram.no_skip_pulses > 2) no_chk_pulses = inpram.no_skip_pulses; else no_chk_pulses = 2; if((inpram.DualSpkOptn == 1) || (inpram.DualSpkOptn == 6)) no_chk_pulses++; // else have to use pulse_no[ICint] goto IC_RET; } } else if((inpram.DualSpkOptn == 1) || (inpram.DualSpkOptn == 6)) { // interrupt from 2nd IC sensor (Dual Spark mode) TFLG1 = ICintmask[1]; // clear IC2 interrupt flag ICint = 1; OCint = 1; tcIC = *pTIC[1]; TCICus = (tcIC/3)<<1; // wait til synch on 1st IC if(!synch) { ICt_overflow[1] = 0; t_in3[1] = TCICus; dt2[1] = 0; dt3[1] = dt3[0]; ign_state[1] = 0; missed_pulse[1] = 0; xtra_pulse[1] = 0; goto IC_RET; } } else { // interrupt from 2nd IC sensor (cam/crnk_sync) ICint = 1; cam_sync = 1; goto IC_RET; } if(synch != END_SYNCH) { ICtt_ovflo = 0; } else { // synched // if synched in wheel decode - process pulses if(inpram.No_Teeth) { // incr absol tooth no (1 = 1st tooth after missing teeth) ctmp1 = 1 + mask_teeth; tooth_no = tooth_no + ctmp1; if(tooth_no > last_tooth) tooth_no = tooth_no - last_tooth; // reset tooth number if(tooth_no == tach_tooth) { *pTacho &= ~0x08; // output tach pulse - active Lo tach_tooth = tooth_no + inpram.No_Skip_Teeth; if(tach_tooth > inpram.No_Teeth) tach_tooth = tach_tooth - inpram.No_Teeth; } if(tooth_no == 1) { // tooth 1 = 1st tooth after missing teeth ctmp1 += inpram.No_Miss_Teeth; // check cam synch if((inpram.DualSpkOptn == 2) || (inpram.DualSpkOptn == 3)) { if(cam_sync == (1 - last_cam)) { last_cam = cam_sync; cam_sync = 0; } else { ign_reset(); goto IC_RET; } } else if(inpram.DualSpkOptn > 6) { if(cam_sync) { cam_sync = 0; } else { ign_reset(); goto IC_RET; } } } stmp1 = ctmp1 * 100; tooth_spark_x100[0] -= stmp1; tooth_spark_x100[1] -= stmp1; // no. teeth, including missing teeth, passed since last tach IC no_wheel_teeth = no_wheel_teeth + ctmp1; // calculate time since last entered here savovflo = ICtt_ovflo; if(inpram.No_Teeth > 40) { // use shorts to speed cpu time for many-teeth wheels st2t = st3t; st3t = TCICus; sdt2t = sdt3t; if(!(TFLG2 & 0x80) || (TCNT < tcIC)) { // check for t overflow isr pending // no pending overflow isr or IC occurred < overflow if(!ICtt_ovflo) sdt3t = st3t - st2t; else if(ICtt_ovflo > 1) { ign_reset(); goto IC_RET; } else { sdt3t = st3t + (21845 + (int)(21845 - st2t)); ICtt_ovflo = 0; } } else { // pending overflow isr and IC after overflow if(ICtt_ovflo > 0) { ign_reset(); goto IC_RET; } sdt3t = st3t + (21845 + (int)(21845 - st2t)); ICtt_ovflo = -1; } } else { // use longs to hold large times for few-tooth wheels at lo rpm t2t = t3t; t3t = TCICus; dt2t = dt3t; // Need to test for overflow because overflow ISR < priority than // IC ISR and might be pending, so ICtt_ovflo not up to date. if(!(TFLG2 & 0x80) || (TCNT < tcIC)) { // check for t overflow isr pending // no pending overflow isr or IC occurred < overflow if(!ICtt_ovflo) dt3t = t3t - t2t; else { asm { ldab ICtt_ovflo clra ldy #43690 EMUL sty ul1.pltmp[0] std ul1.pltmp[1] }; dt3t = t3t + ul1.ltmp - t2t; ICtt_ovflo = 0; } } else { // pending overflow isr and IC after overflow ICtt_ovflo++; asm { ldab ICtt_ovflo clra ldy #43690 EMUL sty ul1.pltmp[0] std ul1.pltmp[1] }; dt3t = t3t + ul1.ltmp - t2t; ICtt_ovflo = -1; } } mask_save = mask_teeth; mask_teeth = 0; if(no_wheel_teeth < inpram.No_Skip_Teeth) { // non-tach tooth if(inpram.No_Teeth > 40) { // check non-tach tooth error stmp2 = sdt2t * ctmp1; if(sdt3t > stmp2) { stmp1 = sdt3t - stmp2; ix = 1; } else { stmp1 = stmp2 - sdt3t; ix = 2; } //ltmp1 = 100 * stmp1; asm { ldd #100 clra ldy stmp1 EMUL sty ul1.pltmp[0] std ul1.pltmp[1] }; //ltmp2 = PulseTol * stmp2; asm { ldab PulseTol clra ldy stmp2 EMUL sty ul2.pltmp[0] std ul2.pltmp[1] }; if(ul1.ltmp > ul2.ltmp) { // teeth out of synch if(ix == 1) { xtra_pulse[0] = 0; // missing pulse missed_pulse[0]++; outpc.spare[2]--; tooth_no++; no_wheel_teeth++; if((missed_pulse[0] > 1) || (tooth_no > last_tooth) || (stmp1 > (3*sdt2t))) { ign_reset(); goto IC_RET; } // add missing pulse - set dt3t to last dt2t sdt3t = sdt2t; ctmp1 = 1; if(no_wheel_teeth >= inpram.No_Skip_Teeth)goto TACH_T; } else { missed_pulse[0] = 0; // false trigger xtra_pulse[0]++; outpc.spare[2]++; if(xtra_pulse[0] > 1) { ign_reset(); goto IC_RET; } // reject false trigger tooth_no--; if(tooth_no < 1)tooth_no = last_tooth; mask_teeth = mask_save; no_wheel_teeth -= ctmp1; st3t = st2t; sdt3t = sdt2t; ICtt_ovflo = savovflo; goto IC_RET; // wait for next (true) pulse } } else { missed_pulse[0] = 0; xtra_pulse[0] = 0; } // compute prediction error // normalize sdt3 to 1 tooth stmp1 = sdt3t; if(ctmp1 > 1) sdt3t = sdt3t / ctmp1; // refine prediction for output(s) based on newest tooth datum if(!inpram.DualSpkOptn) { stcnt = stmp1 - (ctmp1 * sdtprdt); // = error in us tcnt = IgnTimerComp[0] + (3*(stcnt>>1)); // = 2/3 us tics // only update if not very close (within 40 us) to OC if((IgnOCt_overflow[0] > 0) || (tcnt > TCNT + 60)) { if(tcnt < 0) { IgnOCt_overflow[0]--; tcnt += 65535; } else if(tcnt > (long)65535) { IgnOCt_overflow[0]++; tcnt -= 65535; } IgnTimerComp[0] = tcnt; *pTOC[0] = (unsigned short)IgnTimerComp[0]; } goto DONE_ECORR; } else if(just_synch) jx = 1; else jx = 2; if(spk_delay > 0) spk_dly_t100 = (spk_delay * 100) / sdt3t; else spk_dly_t100 = 0; chk_tach = 0; for(ix = 0; ix < jx; ix++) { if(stop_ecorr[ix] >= 2)continue; SCHK_TACH: if(!stop_ecorr[ix] && (tach_seq[ix] < 2)) { // check for at/ near dwell tooth // tooth // |-excess-| // tooth ________|________| // |-------nbuf_teeth--------|--excess-| coil_dur_t100 | // ---x-------------------------x---------| x |--- // (tooth_dwell tooth_dwell dwell tooth_ spark // - nbuf_teeth) (tooth_ spark (tooth_ // dwell_x100) spark_x100) // coil_dur_t100 = (short)((coil_dur_chan[ix] * 100) / sdt3t); //us -> toothx100 if(coil_dur_t100 < 3)coil_dur_t100 = 3; // calculate no teethx100 remaining until dwell stcnt = tooth_spark_x100[ix] - spk_dly_t100; // tooth_no x 100 tooth_dwell_x100 = stcnt - (short)coil_dur_t100; // tooth_no x 100 if(tooth_dwell_x100 > (short)nbuf_teethx100) { // have not passed dwell - nbuf if(chk_tach)goto RET_CHK_TACH; continue; } if(tooth_dwell_x100 > 0) { // translate toothx100 into us based on last tooth delta_t asm { ldy tooth_dwell_x100 ldd sdt3t EMUL ldx #100 EDIV sty tooth_tme }; } else tooth_tme = 0; tcnt = 3*(tooth_tme >> 1); // = 2/3 us tics tcnt2 = TCNT - tcIC; if(tcnt2 < 0)tcnt2 += 65535; // elapsed time to now if(tcnt > tcnt2 + 45) { // enough time to get out of ISR tcnt = tcIC + (unsigned long)tcnt; // = 2/3 us tics if(tcnt > (long)65535) { IgnOCt_overflow[ix] = (unsigned long)tcnt >> 16; IgnTimerComp[ix] = tcnt & 0xFFFF; // Disable Ign OC interrupt until ovflow done TIE &= ~OCintmask[ix]; } else { IgnOCt_overflow[ix] = 0; // Set Ign OC pin & enable interrupt if(ix == 0) { TCTL1 = (TCTL1 & 0xFB) | (CHG << 2); } else { TCTL1 = (TCTL1 & 0xFE) | CHG; } IgnTimerComp[ix] = tcnt; *pTOC[ix] = (unsigned short)IgnTimerComp[ix]; if(kluge1[ix]) { kluge1[ix] = 1; setlmms[ix] = lmms + (tooth_tme / 128); } TIE |= OCintmask[ix]; TFLG1 = OCintmask[ix]; // clear ign OC interrupt flag } // check if spark will occur on the same tooth if(stcnt <= (short)nbuf_teethx100) ign_state[ix] = 2; } else { // not enough time to get out of ISR // Force charge now if(ix == 0) { TCTL1 = (TCTL1 & 0xFB) | (CHG << 2); } else { TCTL1 = (TCTL1 & 0xFE) | CHG; } CFORC |= OCintmask[ix]; // force output in OLn onto OC pin if(kluge1[ix]) { kluge1[ix] = 0; kluge2[ix] = 2; } t_chgoff[ix] = lmms + 156; // 20 ms timeout IgnOCt_overflow[ix] = 0; TIE &= ~OCintmask[ix]; // Disable Ign OC interrupt tach_seq[ix] = 2; ign_setpin[ix] = SPK; if(stcnt <= (short)nbuf_teethx100) goto SSET_SPK; } stop_ecorr[ix] = 1; } else { // monitor til start dwell or reach spark tooth // check for at/ near spark tooth // calculate no teethx100 remaining until spark stcnt = tooth_spark_x100[ix] - spk_dly_t100; // tooth_no x 100 if(stcnt > (short)nbuf_teethx100) { // have not passed spark - nbuf if(chk_tach)goto RET_CHK_TACH; continue; } if(tach_seq[ix] < 2) { // haven't set dwell yet but spark will also occur on this tooth ign_state[ix] = 2; if(chk_tach)goto RET_CHK_TACH; continue; } SSET_SPK: if(stcnt > 0) { // translate toothx100 into us based on last tooth delta_t asm { ldy stcnt ldd sdt3t EMUL ldx #100 EDIV sty tooth_tme }; } else tooth_tme = 0; tcnt = 3*(tooth_tme >> 1); // = 2/3 us tics tcnt2 = TCNT - tcIC; if(tcnt2 < 0)tcnt2 += 65535; // elapsed time to now if(tcnt > tcnt2 + 45) { // enough time to get out of ISR tcnt = tcIC + (unsigned long)tcnt; // = 2/3 us tics if(tcnt > (long)65535) { IgnOCt_overflow[ix] = (unsigned long)tcnt >> 16; IgnTimerComp[ix] = tcnt & 0xFFFF; // Disable Ign OC interrupt until ovflow done TIE &= ~OCintmask[ix]; } else { IgnOCt_overflow[ix] = 0; // Set Ign OC pin & enable interrupt if(ix == 0) { TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); } else { TCTL1 = (TCTL1 & 0xFE) | SPK; } IgnTimerComp[ix] = tcnt; *pTOC[ix] = (unsigned short)IgnTimerComp[ix]; if(kluge2[ix]) { setlmms2[ix] = lmms + (tooth_tme / 128); kluge2[ix] = 1; } TIE |= OCintmask[ix]; TFLG1 = OCintmask[ix]; // clear ign OC interrupt flag } } else { // not enough time to get out of ISR // Force spark now if(ix == 0) { TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); } else { TCTL1 = (TCTL1 & 0xFE) | SPK; } CFORC |= OCintmask[ix]; // force output in OLn onto OC pin t_chgoff[ix] = 0xFFFFFFFF; kluge2[ix] = 0; IgnOCt_overflow[ix] = 0; TIE &= ~OCintmask[ix]; // Disable Ign OC interrupt tach_seq[ix] = 3; } stop_ecorr[ix] = 2; } if(chk_tach)goto RET_CHK_TACH; } // end for loop } else { // <= 40 teeth // check non-tach tooth error if(ctmp1 > 1) ltmp2 = dt2t * ctmp1; else ltmp2 = dt2t; if(dt3t > ltmp2) { ltmp1 = dt3t - ltmp2; ix = 1; } else { ltmp1 = ltmp2 - dt3t; ix = 2; } ltmp1 = 100 * ltmp1; if(ltmp1 > PulseTol * ltmp2) { // teeth out of synch if(ix == 1) { xtra_pulse[0] = 0; // missing pulse missed_pulse[0]++; outpc.spare[2]--; tooth_no++; no_wheel_teeth++; if((missed_pulse[0] > 1) || (tooth_no > last_tooth) || (ltmp1 > (150*ltmp2))) { ign_reset(); goto IC_RET; } // add missing pulse - set dt3t to last dt2t dt3t = dt2t; ctmp1 = 1; if(no_wheel_teeth >= inpram.No_Skip_Teeth)goto TACH_T; } else { missed_pulse[0] = 0; // false trigger xtra_pulse[0]++; outpc.spare[2]++; if(xtra_pulse[0] > 1) { ign_reset(); goto IC_RET; } // reject false trigger tooth_no--; if(tooth_no < 1)tooth_no = last_tooth; mask_teeth = mask_save; no_wheel_teeth -= ctmp1; t3t = t2t; dt3t = dt2t; ICtt_ovflo = savovflo; goto IC_RET; // wait for next (true) pulse } } else { missed_pulse[0] = 0; xtra_pulse[0] = 0; } // compute prediction error // normalize dt3 to 1 tooth ltmp1 = dt3t; if(ctmp1 > 1) dt3t = dt3t / ctmp1; // refine prediction for output(s) based on newest tooth datum if(!inpram.DualSpkOptn) { if(ctmp1 > 1) tcnt2 = ltmp1 - (ctmp1 * dtprdt); // = error in us else tcnt2 = ltmp1 - dtprdt; // = error in us tcnt = IgnTimerComp[0] + ((3*tcnt2)>>1); // = 2/3 us tics // only update if not very close (within 40 us) to OC if((IgnOCt_overflow[0] > 0) || (tcnt > TCNT + 60)) { if(tcnt < 0) { IgnOCt_overflow[0]--; tcnt += 65535; } else if(tcnt > (long)65535) { IgnOCt_overflow[0]++; tcnt -= 65535; } IgnTimerComp[0] = tcnt; *pTOC[0] = (unsigned short)IgnTimerComp[0]; } goto DONE_ECORR; } else if(just_synch) jx = 1; else jx = 2; if(spk_delay > 0) spk_dly_t100 = (unsigned short)(((long)spk_delay * 100) / dt3t); else spk_dly_t100 = 0; chk_tach = 0; for(ix = 0; ix < jx; ix++) { if(stop_ecorr[ix] >= 2)continue; CHK_TACH: if(!stop_ecorr[ix] && (tach_seq[ix] < 2)) { // check for at/ near dwell tooth // tooth // |-excess-| // tooth ________|________| // |-------nbuf_teeth--------|--excess-| coil_dur_t100 | // ---x-------------------------x---------| x |--- // (tooth_dwell tooth_dwell dwell tooth_ spark // - nbuf_teeth) (tooth_ spark (tooth_ // dwell_x100) spark_x100) // coil_dur_t100 = (short)((coil_dur_chan[ix] * 100) / dt3t); //us -> toothx100 if(coil_dur_t100 < 3)coil_dur_t100 = 3; // calculate no teethx100 remaining until dwell stcnt = tooth_spark_x100[ix] - spk_dly_t100; // tooth_no x 100 tooth_dwell_x100 = stcnt - (short)coil_dur_t100; // tooth_no x 100 if(tooth_dwell_x100 > (short)nbuf_teethx100) { // have not passed dwell - nbuf if(chk_tach)goto RET_CHK_TACH; continue; } if(tooth_dwell_x100 > 0) // translate toothx100 into us based on last tooth delta_t tooth_ltme = (tooth_dwell_x100 * dt3t) / 100; else tooth_ltme = 0; tcnt = (3*tooth_ltme) >> 1; // = 2/3 us tics tcnt2 = TCNT - tcIC; if(tcnt2 < 0)tcnt2 += 65535; // elapsed time to now if(tcnt > tcnt2 + 45) { // enough time to get out of ISR tcnt = tcIC + (unsigned long)tcnt; // = 2/3 us tics if(tcnt > (long)65535) { IgnOCt_overflow[ix] = (unsigned long)tcnt >> 16; IgnTimerComp[ix] = tcnt & 0xFFFF; // Disable Ign OC interrupt until ovflow done TIE &= ~OCintmask[ix]; } else { IgnOCt_overflow[ix] = 0; // Set Ign OC pin & enable interrupt if(ix == 0) { TCTL1 = (TCTL1 & 0xFB) | (CHG << 2); } else { TCTL1 = (TCTL1 & 0xFE) | CHG; } IgnTimerComp[ix] = tcnt; *pTOC[ix] = (unsigned short)IgnTimerComp[ix]; if(kluge1[ix]) { kluge1[ix] = 1; setlmms[ix] = lmms + (tooth_ltme / 128); } TIE |= OCintmask[ix]; TFLG1 = OCintmask[ix]; // clear ign OC interrupt flag } // check if spark will occur on the same tooth if(stcnt <= (short)nbuf_teethx100) ign_state[ix] = 2; } else { // not enough time to get out of ISR // Force charge now if(ix == 0) { TCTL1 = (TCTL1 & 0xFB) | (CHG << 2); } else { TCTL1 = (TCTL1 & 0xFE) | CHG; } CFORC |= OCintmask[ix]; // force output in OLn onto OC pin if(kluge1[ix]) { kluge1[ix] = 0; kluge2[ix] = 2; } t_chgoff[ix] = lmms + 156; // 20 ms timeout IgnOCt_overflow[ix] = 0; TIE &= ~OCintmask[ix]; // Disable Ign OC interrupt tach_seq[ix] = 2; ign_setpin[ix] = SPK; if(stcnt <= (short)nbuf_teethx100) goto SET_SPK; } stop_ecorr[ix] = 1; } else { // monitor til start dwell or reach spark tooth // check for at/ near spark tooth // calculate no teethx100 remaining until spark stcnt = tooth_spark_x100[ix] - spk_dly_t100; // tooth_no x 100 if(stcnt > (short)nbuf_teethx100) { // have not passed spark - nbuf if(chk_tach)goto RET_CHK_TACH; continue; } if(tach_seq[ix] < 2) { // haven't set dwell yet but spark will also occur on this tooth ign_state[ix] = 2; if(chk_tach)goto RET_CHK_TACH; continue; } SET_SPK: if(stcnt > 0) // translate toothx100 into us based on last tooth delta_t tooth_ltme = (stcnt * dt3t) / 100; else tooth_ltme = 0; tcnt = (3*tooth_ltme) >> 1; // = 2/3 us tics tcnt2 = TCNT - tcIC; if(tcnt2 < 0)tcnt2 += 65535; // elapsed time to now if(tcnt > tcnt2 + 45) { // enough time to get out of ISR tcnt = tcIC + (unsigned long)tcnt; // = 2/3 us tics if(tcnt > (long)65535) { IgnOCt_overflow[ix] = (unsigned long)tcnt >> 16; IgnTimerComp[ix] = tcnt & 0xFFFF; // Disable Ign OC interrupt until ovflow done TIE &= ~OCintmask[ix]; } else { IgnOCt_overflow[ix] = 0; // Set Ign OC pin & enable interrupt if(ix == 0) { TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); } else { TCTL1 = (TCTL1 & 0xFE) | SPK; } IgnTimerComp[ix] = tcnt; *pTOC[ix] = (unsigned short)IgnTimerComp[ix]; if(kluge2[ix]) { setlmms2[ix] = lmms + (tooth_ltme / 128); kluge2[ix] = 1; } TIE |= OCintmask[ix]; TFLG1 = OCintmask[ix]; // clear ign OC interrupt flag } } else { // not enough time to get out of ISR // Force spark now if(ix == 0) { TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); } else { TCTL1 = (TCTL1 & 0xFE) | SPK; } CFORC |= OCintmask[ix]; // force output in OLn onto OC pin t_chgoff[ix] = 0xFFFFFFFF; kluge2[ix] = 0; IgnOCt_overflow[ix] = 0; TIE &= ~OCintmask[ix]; // Disable Ign OC interrupt tach_seq[ix] = 3; } stop_ecorr[ix] = 2; } if(chk_tach)goto RET_CHK_TACH; } // end for loop } // End non-tach teeth DONE_ECORR: // Check if any teeth have come in during isr processing if(TFLG1 & 0x01) { TFLG1 = 0x01; mask_teeth++; } goto IC_RET; } // end non-tach tooth processing TACH_T: // at a tach pulse - normalize dlt_t to 1 tooth for next time if(ctmp1 > 1) { dt3t = dt3t / ctmp1; sdt3t = sdt3t / ctmp1; } just_synch = 0; no_wheel_teeth = 0; // reset counter } // end wheel decode synched processing else { // non-wheel synched processing *pTacho &= ~0x08; // output tach pulse - active Lo } if((inpram.DualSpkOptn > 1) && (inpram.DualSpkOptn != 6)) { // first do check for charge/spark tooth for other channel if(stop_ecorr[OCint] < 2) { chk_tach = 1; ix = OCint; if(spk_delay > 0) { if(inpram.No_Teeth > 40) { spk_dly_t100 = (spk_delay * 100) / sdt3t; goto SCHK_TACH; } else { spk_dly_t100 = (unsigned short)(((long)spk_delay * 100) / dt3t); goto CHK_TACH; } } else { spk_dly_t100 = 0; if(inpram.No_Teeth > 40) goto SCHK_TACH; else goto CHK_TACH; } } RET_CHK_TACH: // wasted spark/ COP synched tach pulse processing // alternate outputs (note: this avoids rpm limit due to coil dwell) OCint = 1 - OCint; // kluges for unexplained low rpm misses: // kluge1: =2 at tach; =1 when set TC4/5/pTOC for CHG; // =0 when actually reach dwell state (CFORC or OC) // kluge2: =0 at tach; =2 when get to dwell state(CFORC/ OC) // =1 when set TC4/5/pTOC for SPK; // =0 when actually spark (CFORC or OC) if(outpc.rpm < inpram.crank_rpm) kluge1[OCint] = 2; else kluge1[OCint] = 0; kluge2[OCint] = 0; if((tach_seq[OCint] != 3) && (tach_seq[OCint] != 0)) { // Force spark NOW if(OCint == 0) { // Ign O/P 1 TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); // set OC o/p in OL5 } else { // Ign O/P 2 TCTL1 = (TCTL1 & 0xFE) | SPK; // set OC o/p in OL4 } CFORC |= OCintmask[OCint]; // force output in OLn onto OC pin t_chgoff[OCint] = 0xFFFFFFFF; IgnOCt_overflow[OCint] = 0; TIE &= ~OCintmask[OCint]; // Disable Ign OC interrupt } if((inpram.DualSpkOptn == 2) || (inpram.DualSpkOptn == 3) || (inpram.DualSpkOptn > 6)) { // increment cylinder no. cyl_no++; if(inpram.DualSpkOptn < 7) ctmp1 = inpram.no_cyl; else ctmp1 = inpram.no_cyl >> 1; if(cyl_no >= ctmp1) cyl_no = 0; } } // end wasted spark/ COP synched processing } // end synched processing ltch_lmms = lmms; // latch RTI .128 ms clk at each tach pulse // calculate new dt between tach pulses. All times in us. t_in2 = t_in3[ICint]; dt2[ICint] = dt3[ICint]; t_in3[ICint] = TCICus; if(trig_ret_mode == 1) { // transitioning out of trigger return mode if(!(TFLG2 & 0x80) || (TCNT < tcIC)) // check for t overflow isr pending ICt_overflow[ICint] = 0; else ICt_overflow[ICint] = -1; // pending, so -1 will cause to clear in isr. // use last dt3 as prediction for next IC for this one time transition; // also leave rpm as is. dtpred = dt3[ICint]; trig_ret_mode = 0; // done with trig return til ign_reset goto AVE; } // Need to test for overflow because overflow ISR < priority than // IC ISR and might be pending, so ICt_overflow not up to date. if(!(TFLG2 & 0x80) || (TCNT < tcIC)) { // check for t overflow isr pending // no pending overflow isr or IC occurred < overflow if(!ICt_overflow[ICint]) dt3[ICint] = t_in3[ICint] - t_in2; else { dt3[ICint] = t_in3[ICint] + ((long)ICt_overflow[ICint] * 43690) - t_in2; ICt_overflow[ICint] = 0; } } else { // pending overflow isr and IC after overflow dt3[ICint] = t_in3[ICint] + ((long)(ICt_overflow[ICint] + 1) * 43690) - t_in2; ICt_overflow[ICint] = -1; // pending, so -1 will cause to clear in isr } if(inpram.No_Teeth) { sync_flg = 0; switch(synch) { // Establish synch in wheel decode mode case 0: if(PulseTol != inpram.CrnkTol) { /* was previously not in cranking - check if still the case */ if(dt3[0] * inpram.crank_rpm > Rpm_Coeff) PulseTol = inpram.CrnkTol; } if(dt3[0] > dt2[0]) ltmp1 = dt3[0] - dt2[0]; else ltmp1 = dt2[0] - dt3[0]; ltmp1 = 100 * ltmp1; if(ltmp1 < PulseTol * dt2[0]) { nsynch++; if(nsynch >= 1) { // have consecutive normal teeth if(((inpram.DualSpkOptn == 2) || (inpram.DualSpkOptn == 3) || (inpram.DualSpkOptn > 6)) && (inpram.No_Miss_Teeth == 0)) { // look for cam sync - in between last tooth and tooth 1 if(cam_sync) { last_cam = 1; cam_sync = 0; tooth_no = 1; // 1st tooth after cam_sync OCint = 0; // fire on 1st output if(!inpram.Delay_Teeth) { sync_flg = 1; // at adv_offset degx10 btdc break; } else synch = 2; // wait delay teeth } goto IC_RET; // keep looking for cam_sync } else { synch = 1; // look for missing teeth } } } else // not normal tooth nsynch = 0; cam_sync = 0; goto IC_RET; // Look for missing teeth case 1: if(dt3[0] > dt2[0]) ltmp1 = dt3[0] - dt2[0]; else ltmp1 = dt2[0] - dt3[0]; ltmp1 = 100 * ltmp1; if(ltmp1 > PulseTol * dt2[0]) { // not normal tooth - check if missing tooth ltmp2 = (inpram.No_Miss_Teeth + 1) * dt2[0]; if(dt3[0] > ltmp2) ltmp1 = dt3[0] - ltmp2; else ltmp1 = ltmp2 - dt3[0]; if(ltmp1 < PulseTol * ltmp2) { // passed exact no. missing teeth and got pulse from next normal tooth tooth_no = 1; // 1st tooth after missing teeth // dt for normal tooth dt3[0] = dt3[0] / (inpram.No_Miss_Teeth + 1); if((inpram.DualSpkOptn == 2) || (inpram.DualSpkOptn == 3)) { // cam sync must be set so as to occur during missing teeth if(!cam_sync && ((inpram.no_cyl == 2) || (inpram.no_cyl == 6))) { // no cam - fire 2nd output first OCint = 1; } else { // cam sync, cop or wasted spark - fire 1st output first OCint = 0; } last_cam = cam_sync; cam_sync = 0; } else // no cam signal OCint = 0; if(!inpram.Delay_Teeth) { sync_flg = 1; // at adv_offset degx10 btdc break; } else synch = 2; // wait delay teeth } else { // not normal or missing tooth ign_reset(); // something out of synch - start over goto IC_RET; } } // if get here => normal tooth or synched but want Delay_Teeth cam_sync = 0; goto IC_RET; // Wait for Delay_Teeth normal teeth to start 1st tach case 2: if(dt3[0] > dt2[0]) ltmp1 = dt3[0] - dt2[0]; else ltmp1 = dt2[0] - dt3[0]; ltmp1 = 100 * ltmp1; if(ltmp1 < PulseTol * dt2[0]) { tooth_no++; if(tooth_no > inpram.Delay_Teeth) { sync_flg = 1; break; } } else { ign_reset(); // something out of synch - start over goto IC_RET; } // have not passed Delay_Teeth yet goto IC_RET; // Have synch case END_SYNCH: break; default: break; } // end synch switch if(sync_flg) { synch = END_SYNCH; just_synch = 1; dt3t = dt3[0]; dtprdt = dt3t; sdt3t = (unsigned short)dt3t; t3t = t_in3[0]; st3t = (unsigned short)t3t; cyl_no = OCint; altcount = OCint; no_wheel_teeth = 0; if(inpram.ECUType >= 2) { tach_tooth = tooth_no + inpram.No_Skip_Teeth; if(tach_tooth > inpram.No_Teeth) tach_tooth = tach_tooth - inpram.No_Teeth; *pTacho &= ~0x08; // both active Lo } // calculate dt3 for tach pulse and set up for predictor dt3[0] = inpram.No_Skip_Teeth * dt3[0]; dt2[0] = dt3[0]; dtpred = dt3[0]; reset_xtau = 1; } // Check if any teeth have come in during isr processing if(TFLG1 & 0x01) { TFLG1 = 0x01; // clear IC interrupt flag mask_teeth++; } } // End synch check for wheel decode else { // Do synch check for non-wheel mode if(pulse_no >= no_chk_pulses) { // after 1st few pulses, start checking for missed/ extra pulses if(((outpc.tpsdot < (inpram.TpsThresh >> 1)) || (inpram.acc_synchk == 1) || (inpram.acc_synchk == 3)) && (PulseTol < 100)) { // require not strongly accelerating for unambiguous false trigger if(100 * dt3[ICint] < ((100 - PulseTol) * dt2[ICint])) { xtra_pulse[ICint]++; outpc.spare[2]++; if(xtra_pulse[ICint] > 2) { ign_reset(); goto IC_RET; } // reject false trigger t_in3[ICint] = t_in2; dt3[ICint] = dt2[ICint]; outpc.dt3 = dt3[ICint]; goto IC_RET; // wait for next (true) pulse } else { // no extra pulses xtra_pulse[ICint] = 0; } } if((outpc.tpsdot > -(int)(inpram.TpsThresh >> 1)) || (inpram.acc_synchk >= 2)) { // require not strongly decelerating for unambiguous missing pulse if(100 * dt3[ICint] > (100 + PulseTol) * dt2[ICint]) { missed_pulse[ICint]++; outpc.spare[2]--; if(missed_pulse[ICint] > 2) { ign_reset(); goto IC_RET; } // make up for missing pulse - set dt3 to last dt2 dt3[ICint] = dt2[ICint]; } else { // no missed pulses missed_pulse[ICint] = 0; } } } // end pulse_no >= no_chk_pulse } // end synch check for non-wheel mode outpc.dt3 = dt3[ICint]; if(synch != END_SYNCH) { dtpred = dt3[ICint]; } // Predict next tach delta_t errt = dt3[ICint] - dtpred; outpc.spare[0] = (int)((outpc.rpm * errt) / 16667); // degx10 if(!inpram.PredOpt) { // last interval pred. dtpred = dt3[ICint]; } else { // alpha-beta-gamma filter prediction dts = dtpred + ((inpram.alpha * errt) / 100); tddts = tddtpred + ((inpram.beta * errt) / 100); t2dddts = t2dddts + ((inpram.gamma * errt) / 100); dtpred = dts + tddts + (t2dddts >> 1); tddtpred = tddts + t2dddts; } if(inpram.No_Teeth) { // calculate predicted time per tooth for comparison with measured // delta_t between teeth to correct OC register dtprdt = dtpred / inpram.No_Skip_Teeth; sdtprdt = (unsigned short)dtprdt; } if(pulse_no < inpram.no_skip_pulses) { // skip 1st few (>=1) pulses pulse_no++; goto IC_RET; } else { if(pulse_no < 3) pulse_no++; if((inpram.DualSpkOptn <= 1) || (inpram.DualSpkOptn == 6)) { if(synch != END_SYNCH) { reset_xtau = 1; synch = END_SYNCH; } } } if(synch != END_SYNCH)goto IC_RET; // calculate rpm halfway between now and next ign pulse if(dtpred > 100) rpm_pred = (unsigned int)(Rpm_Coeff / dtpred); else { // Noise or beyond rev limit, clear all in engine ign_reset(); goto IC_RET; } rpm = (unsigned int)(Rpm_Coeff / dt3[ICint]); AVE: // lag (iir) filter if(synch < END_SYNCH) outpc.rpm = rpm; else outpc.rpm += (int)((inpram.rpmLF * ((long)rpm - outpc.rpm)) / 100); if(inpram.No_Teeth) { if(TFLG1 & 0x01) { // Check any teeth have come in during isr processing TFLG1 = 0x01; // clear IC interrupt flag mask_teeth++; } } // Calculate ignition parameters if(trig_ret_mode == 2) { /* Example tach signal used in trigger return mode // n-2 n-1 n n+1 n+2 // lead edge(70 btdc) TDC trail edge(5 btdc) // \/ | \/ //--- -------- -------- -------- -------- // | | | | | | | | | // |__| |__| |__| |__| |_ // /\ /\ /\ /\ /\ /\ // IC & Spk IC&Spk IC&Spk IC Spk IC // |----dt-----| |----dt-----| // Cranking------->|<--------Not Cranking // /\ // transition(trig_ret_mode=1) // NOTE: This mode NOT used with Dual Spark or wheel decode */ // Force spark NOW TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); // set OC o/p in OL5 CFORC |= 0x20; // force output in OL5 onto OC pin t_chgoff[0] = 0xFFFFFFFF; IgnOCpinstate = SPK; adv_us = 0; if(outpc.rpm > inpram.crank_rpm) { // finished cranking // transition to other edge for future ICs and out of trig ret mode if((TCTL4 & 0x03) == 0x01) TCTL4 = (TCTL4 & 0xFC) | 0x02; else if((TCTL4 & 0x03) == 0x02) TCTL4 = (TCTL4 & 0xFC) | 0x01; trig_ret_mode = 1; // don't want to do anything else til next IC on opposite edge, // then will set up to charge & spark in normal way, so disable // Ign OC interrupt. TIE &= ~0x20; goto IC_EXIT; } else { coil_dur_set = (coil_dur * 100) << 1; // us, double to be sure coil // sufficiently charged before next, possibly erratic IC charge_time = dtpred - adv_us - coil_dur_set; // us // want to start charging coil_dur_set usec before next predicted IC, // but don't want to discharge til next IC, so will disable OC // when start charging. } tcnt = TCNT - tcIC; // *pTIC is latched time at entry to ISR if(tcnt < 0)tcnt += 65535; tcnt = tcIC + tcnt + 90; // add some extra time for final reg setting IgnTimerComp[0] = tcIC + ((3*charge_time)>>1); // 2/3 us if(IgnTimerComp[0] < tcnt) IgnTimerComp[0] = tcnt; ign_state[0] = 0; ign_setpin[0] = CHG; // use to set OC o/p in OL5 goto CHECK_OVFLO; } // end trig return mode else if(crank_trig_mode) { // trigger rise cranking // Force spark NOW if(OCint == 0) { // Ign O/P 1 TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); // set OC o/p in OL5 } else { // Ign O/P 2 TCTL1 = (TCTL1 & 0xFE) | SPK; // set OC o/p in OL4 } CFORC |= OCintmask[OCint]; // force output in OLn onto OC pin t_chgoff[OCint] = 0xFFFFFFFF; IgnOCpinstate = SPK; if(outpc.rpm > inpram.crank_rpm) { // finished cranking crank_trig_mode = 0; goto NORM_IGN; } else { adv_us = 0; coil_dur_set = coil_dur * 100; // us ltmp1 = coil_dur_set + (inpram.max_spk_dur * 100); // leave next condition in case allow trigger rise with wasted/COP Dual Spk if((inpram.DualSpkOptn > 1) && (inpram.DualSpkOptn != 6)) dtpredx = dtpred << 1; // wasted spk/ COP use 2 cycles for dwell, spk // so when cyl 1 tach arrives, set up spk for cyl3, etc. else dtpredx = dtpred; if(ltmp1 > dtpredx) { // scale down chg/ dischg time to fit time available coil_dur_set = (coil_dur_set * dtpredx) / ltmp1; } else { // double dwell to be sure coil sufficiently charged before // next, possibly erratic, IC coil_dur_set = coil_dur_set << 1; ltmp1 = coil_dur_set + (inpram.max_spk_dur * 100); if(ltmp1 > dtpredx) { // reduce charge time to fit time available coil_dur_set = dtpredx - (inpram.max_spk_dur * 100); } } charge_time = dtpredx - adv_us - coil_dur_set; // us // want to start charging coil_dur_set usec before next predicted IC, // but don't want to discharge til next IC, so will disable OC // when start charging. } tcnt = TCNT - tcIC; // *pTIC is latched time at entry to ISR if(tcnt < 0)tcnt += 65535; tcnt = tcIC + tcnt + 90; // add some extra time for final reg setting IgnTimerComp[OCint] = tcIC + ((3*charge_time)>>1); // 2/3 us if(IgnTimerComp[OCint] < tcnt) IgnTimerComp[OCint] = tcnt; ign_state[OCint] = 0; ign_setpin[OCint] = CHG; // use to set OC o/p in OL5 or OL4 goto CHECK_OVFLO; } // end trigger rise cranking NORM_IGN: if((inpram.ICIgnOption >> 4) < 1) { // Normal Ignition Output if((inpram.DualSpkOptn > 1) && (inpram.DualSpkOptn != 6)) { // calculate tooth no for spark (tooth x 100) stcnt = ic_adv_deg; if(OCint == 1) stcnt += inpram.OddAng; if(inpram.DualSpkOptn != 5) stmp1 = 36; else stmp1 = 72; // cam wheel rev is 720 crank deg // toothx100 = degx10 * NTeeth (* 100 to incr resoln) // -------------------------------------- // 3600 or 7200 degx10 asm { ldy inpram.No_Teeth ldd stcnt EMULS ldx stmp1 EDIVS sty stcnt }; // no_skip accounts for dual tach interval tooth_spark_x100[OCint] = no_skipx100 - stcnt; // teethx100 coil_dur_set = coil_dur * 100; // us // Note: coil_dur_chan is us in this mode, in tics in all others coil_dur_chan[OCint] = coil_dur_set; ign_state[OCint] = 0; ign_setpin[OCint] = CHG; // use to set OC o/p in OL5 or OL4 dtpredx = dtpred << 1; // wasted spk/ COP use 2 cycles for dwell, spk // so when cyl 1 tach arrives, set up spk for cyl3, etc. stop_ecorr[OCint] = 0; tach_seq[OCint] = 1; // Turn off OC interrupt TIE &= ~OCintmask[OCint]; goto SET_FUEL; } // Note: tdc is really input capture (IC) (or tach pulse if toothed wheel) // cyl n cyl n+1 // tdc tdc // |--------------- dtpred -------------------| // | __________ | // | | | | // | |-coil_dur-|----adv_us-----| // |--charge_time--| | | // |_______________| |_______________| // *pTIC Spk *pTIC // // ic_adv_deg = table adv - trigger offset if(OCint == 0) adv_us = (long)16667 * ic_adv_deg; else adv_us = (long)16667 * (ic_adv_deg + inpram.OddAng); adv_us = adv_us / rpm_pred; adv_us += spk_delay; // account for vr + FET delays: // // | | |<--tble adv_us-->| | // | VR | _________ FET| | VR| // | dly| | dwell | dly| |dly| // | | | | | | | // ___|____|________| |____|_________________|___|_ // Tooth *pTIC *pTOC Spk Tooth *pTIC // dtpredx = dtpred; // The following limits max adv to 60 deg at 10krpm for 8 cyl and // much more for < rpm, < no. cyl. It provides enough calculation // time to do a prediction before the spark event. if(adv_us > (long)(dtpredx - 500)) adv_us = (long)(dtpredx - 500); // calculate time to start charging coil coil_dur_set = coil_dur * 100; // us if(!inpram.DualSpkOptn) ltmp1 = coil_dur_set + (inpram.max_spk_dur * 100); else { if(adv_us < 10)adv_us = 10; // don't want -adv - set sensor to avoid ltmp1 = coil_dur_set + adv_us; } if(ltmp1 > dtpredx) { // scale down chg/ dischg time to fit time available coil_dur_set = (coil_dur_set * dtpredx) / ltmp1; } if(inpram.No_Teeth) { if(TFLG1 & 0x01) { // Check any teeth have come in during isr processing TFLG1 = 0x01; // clear IC interrupt flag mask_teeth++; } } charge_time = (long)(dtpredx - adv_us - coil_dur_set); // us if(!inpram.DualSpkOptn) { old_charge = 0; IgnOCpinstate = (PTIT & 0x20) >> 5; // present pin state (OCint = 0) ltmp1 = dtpred - (dtpred >> 4); if(charge_time > (long)ltmp1) { // charge time is > 6% of next IC; due to normal fluctuation // this could result in starting charge after IC. if(charge_time > dtpred) { // In this case switch to 'this cylinder' mode (spark on // this cylinder) adv_us += dtpred; if(IgnOCpinstate == SPK) { charge_time -= dtpred; } else { ign_state[0] = 2; } } else { if(IgnOCpinstate == CHG) { // extend coil dur to ensure we start charging before dtpred coil_dur_set += charge_time - ltmp1; charge_time = ltmp1; ign_state[0] = 0; } else { old_charge = charge_time; charge_time = 0; adv_us = dtpred + adv_us; } } } } } else { // EDIS Ignition Output // cyl n cyl n+1 // tdc tdc // |-------- dtpred ---------------------------| // |-delay- __________ | // | | | | // | |---SAW----| | // | | | | // |_______| |________________________| // *pTIC // NOTE: This mode NOT used with Dual Spark or wheel decode // adv_us = 0; // send SAW pulse after delay, so we don't send // SAW while still sparking (send at 64us atdc) charge_time = 64; //us // SAW pulse calculation ltmp1 = 1536 - ((long)256 * ic_adv_deg) / 100; // multispk EDIS if((inpram.ICIgnOption >> 4) == 2) { if(first_edis) { // 1st SAW for multispk is 2048 us first_edis = 0; ltmp1 = 2048; // 1024 if ever have 10 cyl } else if(outpc.rpm < 1200) { ltmp1 += 2048; // 1024 if ever have 10 cyl } } coil_dur_set = ltmp1; IgnOCpinstate = SPK; tcnt = TCNT - tcIC; // *pTIC is latched time at entry to ISR if(tcnt < 0)tcnt += 65535; tcnt = tcIC + tcnt + 90; // add some extra time for final reg setting IgnTimerComp[0] = tcIC + ((3*charge_time)>>1); // 2/3 us if(IgnTimerComp[0] < tcnt) IgnTimerComp[0] = tcnt; ign_state[0] = 0; ign_setpin[0] = CHG; // use to set OC o/p in OL5 goto CHECK_OVFLO; } // Only get here for normal ignition output if(inpram.No_Teeth) { if(TFLG1 & 0x01) { // Check any teeth have come in during isr processing TFLG1 = 0x01; // clear IC interrupt flag mask_teeth++; } } if(!inpram.DualSpkOptn) { if(IgnOCpinstate == SPK) { // OC output is sparking tcnt = TCNT - tcIC; // *pTIC is latched time at entry to ISR if(tcnt < 0)tcnt += 65535; tcnt = tcIC + tcnt + 20; // add extra time for final reg setting IgnTimerComp[OCint] = tcIC + ((3*charge_time) >> 1); // 2/3 us if(IgnTimerComp[OCint] < tcnt) { // transitioning to early chge (large adv) state ign_state[OCint] = 2; // Charge NOW if(OCint == 0) { TCTL1 = (TCTL1 & 0xFB) | (CHG << 2); // set OC o/p in OL5 } else { TCTL1 = (TCTL1 & 0xFE) | CHG; // set OC o/p in OL4 } t_chgoff[OCint] = lmms + 156; // 20 ms timeout CFORC |= OCintmask[OCint]; // force output in OLn onto OC pin IgnOCpinstate = CHG; // Set time to spark ltmp1 = (dtpred - adv_us); IgnTimerComp[OCint] = tcIC + ((3*ltmp1)>>1); // 2/3 us ign_setpin[OCint] = SPK; // use to set OC o/p in OL5 or OL4 if(old_charge > 0) { charge_time = old_charge - ltmp1; } else { // next chge time - predicted to be prior to next pulse input charge_time += adv_us; // rel to spk event } } else { // normal state - wait til time to charge ign_state[OCint] = 0; ign_setpin[OCint] = CHG; // use to set OC o/p in OL5 or OL4 } } // end OC output is sparking else { // OC output is charging if(charge_time <= 0) { // => early chge, OC interrupt enabled // in early chge (large adv) state - stay there ign_state[OCint] = 2; // Reset OC reg to update to latest spk time IgnTimerComp[OCint] = tcIC + ((3*(dtpred - adv_us))>>1); // 2/3 us // next chge time charge_time += adv_us; // rel to spk event ign_setpin[OCint] = SPK; } else { if(ign_state[OCint] == 2) { // transitioning to normal charge state ign_state[OCint] = 0; // Reset OC reg to update to latest spk time IgnTimerComp[OCint] = tcIC + ((3*(dtpred - adv_us))>>1); // 2/3 us ign_setpin[OCint] = SPK; } else { if(ic_adv_deg >= 0) { // IC came earlier than predicted and haven't sparked yet. FORCE_SPK: // Force spark NOW if(OCint == 0) { TCTL1 = (TCTL1 & 0xFB) | (SPK << 2); // set OC o/p in OL5 } else { TCTL1 = (TCTL1 & 0xFE) | SPK; // set OC o/p in OL4 } CFORC |= OCintmask[OCint]; // force output in OLn onto OC pin t_chgoff[OCint] = 0xFFFFFFFF; IgnOCpinstate = SPK; IgnTimerComp[OCint] = tcIC + ((3*charge_time)>>1); // 2/3 us ign_setpin[OCint] = CHG; ign_state[OCint] = 0; } else { // desired advance is atdc - update register to latest spk time IgnTimerComp[OCint] = tcIC - ((3 * adv_us)>>1); // 2/3 us tcnt = TCNT - tcIC; // *pTIC is latched time at entry to ISR if(tcnt < 0)tcnt += 65535; tcnt = tcIC + tcnt + 90; // add some extra time to exit isr if(IgnTimerComp[OCint] < tcnt) goto FORCE_SPK; charge_time += adv_us; // rel to spk event ign_setpin[OCint] = SPK; ign_state[OCint] = 1; } } } } // end OC output is charging } else { // dual ign outputs simplifies since double the // period means entire dwell/spark sequence over by next IC for this o/p IgnTimerComp[OCint] = tcIC + ((3*charge_time) >> 1); // 2/3 us ign_state[OCint] = 0; ign_setpin[OCint] = CHG; // use to set OC o/p in OL5 or OL4 } // check if v. short OC reg occurs after TCNT (=free running, common // timer). tcnt = TCNT - tcIC; // tcIC is latched time at entry to ISR if(tcnt < 0)tcnt += 65535; tcnt = tcIC + tcnt + 90; // add some extra time to get out of isr if(IgnTimerComp[OCint] < tcnt) { if(ign_setpin[OCint] == CHG) { // make spk time in OC come out exact ltmp1 = ((tcnt - IgnTimerComp[OCint])<<1)/3; // us coil_dur_set -= ltmp1; if(coil_dur_set < 2)coil_dur_set = 2; } IgnTimerComp[OCint] = tcnt; } if(inpram.No_Teeth) { if(TFLG1 & 0x01) { // Check any teeth have come in during isr processing TFLG1 = 0x01; // clear IC interrupt flag mask_teeth++; } } CHECK_OVFLO: coil_dur_chan[OCint] = (3 * coil_dur_set) >> 1; // 2/3 us tics if(IgnTimerComp[OCint] > (long)65535) { IgnOCt_overflow[OCint] = IgnTimerComp[OCint] >> 16; IgnTimerComp[OCint] &= 0xFFFF; } else IgnOCt_overflow[OCint] = 0; if(!IgnOCt_overflow[OCint]) { // Set Ign OC pin & enable interrupt if(OCint == 0) { TCTL1 = (TCTL1 & 0xFB) | (ign_setpin[OCint] << 2); } else { TCTL1 = (TCTL1 & 0xFE) | ign_setpin[OCint]; } *pTOC[OCint] = (unsigned short)IgnTimerComp[OCint]; // load OC compare register TIE |= OCintmask[OCint]; TFLG1 = OCintmask[OCint]; // clear ign OC interrupt flag } else { // Disable Ign OC interrupt until ovflow done TIE &= ~OCintmask[OCint]; } SET_FUEL: if(inpram.No_Teeth) { if(TFLG1 & 0x01) { // Check any teeth have come in during isr processing TFLG1 = 0x01; // clear IC interrupt flag mask_teeth++; } } // Set up for Injector squirt(s) PWcalc = 2; // just got tach pulse - set flag for map/ PW calc in main loop. if(reset_xtau) { // 1st startup or re-synch if(lmms_lostSynch == 0) { sum_dltau[0] = 0xFFFFFFFF; // at 1st startup assume > largest possible Tau sum_dltau[1] = 0xFFFFFFFF; } else { // re-synch, add time since lost synch tcnt = (lmms - lmms_lostSynch) * 128; // us (.128 tic *128/100)*100 sum_dltau[0] += tcnt; sum_dltau[1] += tcnt; } reset_xtau = 0; } else { sum_dltau[0] += dt3[ICint]; sum_dltau[1] += dt3[ICint]; } outpc.tachCount++; tcnt = (inpram.InjStart * dtpred) / 100; // inj must occur at least 2 ms < next predicted tach to allow enough time // for pred error + detection of PW calculation time + calculation of PW if((dtpred - tcnt) < 2000)tcnt = dtpred - 2000; if(tcnt < 0)tcnt = 0; tcnt = tcnt >> 7; // div by 128 to convert to .128 tics PWcalc_lmms = lmms + tcnt; egocount++; if(outpc.rpm < inpram.crank_rpm) { // if engine cranking igncount = 0; // when go from crank to run, want // to wait divider pulses before inject, since run pw >> // crank pw and causes x-tau jump unless dltau also large PWcalc = 3; // calc map, PW and sched squirt } else { igncount++; if(igncount >= inpram.Divider) { igncount = 0; // skip Divider tach pulses if(asecount < 65535) asecount++; PWcalc = 3; // calc map, PW and sched squirt } } IC_EXIT: ic_ret_flg = 1; return; IC_RET: ic_ret_flg = 2; return; } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #pragma CODE_SEG NON_BANKED #endif INTERRUPT void ISR_TimerOverflow(void) { // Get display seconds from continuously running TCNT TC_ovflow++; if(TC_ovflow >= TC_ovfla[TC_ov_ix]) { // update seconds to send back to PC outpc.seconds++; if(TC_ov_ix >= 59) { TC_ov_ix = 0; TC_ovflow = 0; } else TC_ov_ix++; } // Handle Ign IC stuff if(pulse_no == 0) { if(PrimeDone) { // clear Timer Overflow interrupt flag (TOF) TFLG2 = 0x80; return; } else goto INJ_OC; } else { ICt_overflow[0]++; ICt_overflow[1]++; ICtt_ovflo++; } // Handle Ign OC stuff // Example: *pTIC = 30K us, dt = 80K, *pTIC + dt = 110k // OC reg = 110K - 65K = 45K => overflow = 1 // Timer counter goes from 30K to 65K/ 0 to 45K, // elapsed time = (65 - 30 = 35K) + 45K = 80K = dt if(IgnOCt_overflow[0] > 0) { IgnOCt_overflow[0]--; if(IgnOCt_overflow[0] == 0) { // check if v. short OC reg occurs after TCNT (=free running, // common timer). Enter this isr with TCNT=0, so if needed, // add some extra time to get out of isr before OC time reached if(IgnTimerComp[0] < TCNT + 18) IgnTimerComp[0] = TCNT + 18; TC5 = (unsigned short)IgnTimerComp[0]; // load OC compare register // set/ enable ign OC pin and interrupt if(kluge1[0] == 2) { kluge1[0] = 1; setlmms[0] = lmms + (IgnTimerComp[0] / 192); // (2/3)*I/128 } if(kluge2[0] == 2) { kluge2[0] = 1; setlmms2[0] = lmms + (IgnTimerComp[0] / 192); // (2/3)*I/128 } TCTL1 = (TCTL1 & 0xFB) | (ign_setpin[0] << 2); TIE |= 0x20; TFLG1 = 0x20; // clear ign OC interrupt flag } } if(inpram.DualSpkOptn && (IgnOCt_overflow[1] > 0)) { IgnOCt_overflow[1]--; if(IgnOCt_overflow[1] == 0) { // check if v. short OC reg occurs after TCNT (=free running, // common timer). Enter this isr with TCNT=0, so if needed, // add some extra time to get out of isr before OC time reached if(IgnTimerComp[1] < TCNT + 18) IgnTimerComp[1] = TCNT + 18; TC4 = (unsigned short)IgnTimerComp[1]; // load OC compare register // set/ enable ign OC pin and interrupt if(kluge1[1] == 2) { kluge1[1] = 1; setlmms[1] = lmms + (IgnTimerComp[1] / 192); // (2/3)*I/128; } if(kluge2[1] == 2) { kluge2[1] = 1; setlmms2[1] = lmms + (IgnTimerComp[1] / 192); // (2/3)*I/128; } TCTL1 = (TCTL1 & 0xFE) | ign_setpin[1]; TIE |= 0x10; TFLG1 = 0x10; // clear ign OC interrupt flag } } INJ_OC: // Handle Inj OC stuff if(Fl1OCt_overflow > 0) { Fl1OCt_overflow--; if(Fl1OCt_overflow == 0) { // set/ enable inj1 OC pin and interrupt TCTL2 = TCTL2 & 0xFB; // set output lo in OL1 TIE |= 0x02; TFLG1 = 0x02; // clear OC interrupt flag } } if(Fl2OCt_overflow > 0) { Fl2OCt_overflow--; if(Fl2OCt_overflow == 0) { // set/ enable inj2 OC pin and interrupt TCTL2 = TCTL2 & 0xBF; // set output lo in OL3 TIE |= 0x08; TFLG1 = 0x08; // clear OC interrupt flag } } // clear timer overflow interrupt flag (TOF) TFLG2 = 0x80; return; } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #pragma CODE_SEG NON_BANKED #endif INTERRUPT void ISR_Ign_TimerOut(void) { long tcnt; char save_page,OCchan; // Enter here when ign OC reg match if((TIE & 0x20) && (TFLG1 & 0x20)) { OCchan = 0; IgnOCpinstate = (TCTL1 & 0x04) >> 2; } else if((TIE & 0x10) && (TFLG1 & 0x10)) { OCchan = 1; IgnOCpinstate = TCTL1 & 0x01; } else { TFLG1 = OCintmask[0]; TFLG1 = OCintmask[1]; return; } save_page = PPAGE; PPAGE = 0x3C; // if call any subs on another page have to swtch pge if(IgnOCpinstate == CHG) { t_chgoff[OCchan] = lmms + 156; // 20 ms timeout if(kluge1[OCchan]) { kluge1[OCchan] = 0; kluge2[OCchan] = 2; } // Coil has just started charging if(trig_ret_mode || crank_trig_mode) { // disable ign OC interrupt - will spk on next IC TIE &= ~OCintmask[OCchan]; goto OC_RET; } tach_seq[OCchan] = 2; ign_setpin[OCchan] = SPK; if((inpram.DualSpkOptn > 1) && (inpram.DualSpkOptn != 6)) { if(ign_state[OCchan] == 0) { // disable ign OC interrupt - will set up spk when get to spk tooth TIE &= ~OCintmask[OCchan]; goto OC_RET; } else { // spark on same tooth as dwell IgnTimerComp[OCchan] = *pTOC[OCchan] + ((3 * coil_dur_chan[OCchan]) >> 1); // 2/3 us ign_state[OCchan] = 0; stop_ecorr[OCchan] = 2; } } else { IgnTimerComp[OCchan] = *pTOC[OCchan] + coil_dur_chan[OCchan]; // 2/3 us } } else { // Coil has just started discharging (sparking) kluge2[OCchan] = 0; t_chgoff[OCchan] = 0xFFFFFFFF; if(ign_state[OCchan] == 0) { // disable ign OC interrupt - chge after next IC pulse TIE &= ~OCintmask[OCchan]; IgnOCt_overflow[OCchan] = 0; tach_seq[OCchan] = 3; goto OC_RET; } else { // want to charge after done with spark IgnTimerComp[OCchan] = *pTOC[OCchan] + ((3*charge_time)>>1); // 2/3 us } ign_setpin[OCchan] = CHG; // use to set OC o/p in OLn } // check if v. short OC reg occurs after TCNT tcnt = TCNT - *pTOC[OCchan]; // pTOC is OC time at entry to ISR if(tcnt < 0)tcnt += 65535; tcnt = *pTOC[OCchan] + tcnt + 12; if(IgnTimerComp[OCchan] < tcnt)IgnTimerComp[OCchan] = tcnt; if(IgnTimerComp[OCchan] > (long)65535) { IgnOCt_overflow[OCchan] = IgnTimerComp[OCchan] >> 16; IgnTimerComp[OCchan] &= 0xFFFF; } else IgnOCt_overflow[OCchan] = 0; if(!IgnOCt_overflow[OCchan]) { if(kluge2[OCchan]) { setlmms2[OCchan] = lmms + ((IgnTimerComp[OCchan] - *pTOC[OCchan]) / 192); // (2/3)*I/128 kluge2[OCchan] = 1; } *pTOC[OCchan] = (unsigned short)IgnTimerComp[OCchan]; // load ign OC register // enable Ign OC pin if(OCchan == 0) TCTL1 = (TCTL1 & 0xFB) | (ign_setpin[OCchan] << 2); else TCTL1 = (TCTL1 & 0xFE) | ign_setpin[OCchan]; } else { // Disable Ign OC intrpt til ovflow done TIE &= ~OCintmask[OCchan]; } OC_RET: // clear ign OC interrupt flag TFLG1 = OCintmask[OCchan]; PPAGE = save_page; return; } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #pragma CODE_SEG NON_BANKED #endif INTERRUPT void ISR_Inj1_TimerOut(void) { // Enter here when OC match to cease firing Inj1. // At entry here the pin will automatically turn off injector; // also turn off pwm1 (this not necessary since nanded) PWME &= ~0x04; outpc.squirt &= ~0x01; // inj1 stopped squirting if(!(outpc.squirt & 0x03)) // neither inj squirting *pPTMpin[3] &= ~0x08; // turn off inj led // Disable Inj1 OC interrupt - will re-enable on next tach pulse TIE &= ~0x02; // clear OC interrupt flag TFLG1 = 0x02; return; } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #pragma CODE_SEG NON_BANKED #endif INTERRUPT void ISR_Inj2_TimerOut(void) { // Enter here when OC match to cease firing Inj2. // At entry here the pin will automatically turn off injector; // also turn off pwm2 (this not necessary since nanded) PWME &= ~0x10; outpc.squirt &= ~0x02; // inj2 stopped squirting if(!(outpc.squirt & 0x03)) // neither inj squirting *pPTMpin[3] &= ~0x08; // turn off inj led PrimeDone = 1; // Disable Inj2 OC interrupt - will re-enable on next tach pulse TIE &= ~0x08; // clear OC interrupt flag TFLG1 = 0x08; return; } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #endif int move_IACmotor(void) { unsigned char coils; unsigned long ultmp; short del; if(IAC_moving)return 0; del = IACmotor_pos - outpc.iacstep; if(del < 0)del = -del; if(del < inpram.IACminstep)return 0; // set up the motor move DISABLE_INTERRUPTS ultmp = lmms; ENABLE_INTERRUPTS motor_time = ultmp + ((inpram.IACtstep*8)/10); // .128 ms motor_step++; if(motor_step > 7)motor_step = 0; if(IACmotor_pos > outpc.iacstep) { // cw outpc.iacstep++; coils = (IACCoilA[7-motor_step] << 6) | (IACCoilB[7-motor_step] << 7); } else { // ccw outpc.iacstep--; coils = (IACCoilA[motor_step] << 6) | (IACCoilB[motor_step] << 7); } PORTB &= ~0x10; // enable current to motor(bit=0) DISABLE_INTERRUPTS *pPTTpin[6] = (PTT & ~0xC0) | coils; ENABLE_INTERRUPTS IAC_moving = 2; return 1; } /************************************************************************** ** ** SCI Communications ** ** Communications is established when the PC communications program sends ** a command character - the particular character sets the mode: ** ** "a" = send all of the realtime display variables (outpc structure) via txport. ** "w"++++ = ** receive updated data parameter(s) and write into offset location ** relative to start of data block ** "e" = same as "w" above, followed by "r" below to echo back value ** "r"++++ = read and ** send back value of a data parameter or block in offset location ** "y" = verify inpram data block = inpflash data block, return no. bytes different. ** "b" = jump to flash burner routine and burn a ram data block into a flash ** data block. ** "t" = receive new data for clt/mat/ego/maf tables ** "T" = receive new table data for CAN re-transmission to GPIO ** "c" = Test communications - echo back Seconds ** "Q" = Send over Embedded Code Revision Number ** "S" = Send program title. ** **************************************************************************/ #ifndef GCC_BUILD #pragma CODE_SEG NON_BANKED #endif INTERRUPT void ISR_SCI_Comm(void) { char dummy,save_page; int ix; static int rd_wr,xcntr; static unsigned char CANid,ibuf,sendCANdat=0,next_txmode,cksum,Tcntr; unsigned char sect,nsect; static unsigned int txptr,tble_word,ntword; #define getCANid 40 #define getTableId 41 #define setBurningParameters 99 // if RDRF register not set, => transmit interrupt if(!(SCI0SR1 & 0x20))goto XMT_INT; // Receive Interrupt // Clear the RDRF bit by reading SCISR1 register (done above), then read data // (in SCIDRL reg). // Check if we are receiving new input parameter update txgoal = 0; rcv_timeout = 0xFFFFFFFF; if(SCI0SR1 & 0x08) { // check for rcv Overrun error txmode = 0; dummy = SCI0DRL; SCI0CR2 &= ~0xAC; // rcv, xmt disable, interrupt disable kill_ser = 1; kill_ser_t = outpc.seconds + SER_TOUT; return; } switch(txmode) { case 0: switch(SCI0DRL) { case 'a': // send back all real time ram output variables next_txmode = 1; txmode = getCANid; cksum = 0; break; case 'w': // receive new ram input data and write into offset location; // also used for forwarding CAN msgs bet. MT & auxilliary boards. // In this CAN mode, no_bytes must be >0 and <=8. next_txmode = 5; txmode = getCANid; rd_wr = 1; break; case 'e': // same as 'w', but verify by echoing back values. Don't use with CAN next_txmode = 5; txmode = getCANid; rd_wr = 2; break; case 'r': // read and send back ram input data from offset location; // also used for forwarding CAN msgs bet. MT & auxilliary boards next_txmode = 5; txmode = getCANid; rd_wr = 0; cksum = 0; break; case 'y': // Verify that a flash data block matches a next_txmode = 2; // corresponding ram data block txmode = getCANid; break; case 'b': // burn a block of ram input values into flash; // also used for forwarding CAN msgs bet. MT & auxilliary boards next_txmode = setBurningParameters; txmode = getCANid; break; case 't': // update a flash table with following serial data txmode = 20; CANid = can_id; cksum = 0; break; case 'T': // grab following serial table data & send to GPIO via CAN next_txmode = 24; txmode = getCANid; // Must NOT = can_id cksum = 0; break; case '!': // start receiving reinit/reboot command txmode = 30; break; case 'c': // send back seconds to test comms txcnt = 0; txmode = 1; txgoal = 2; // seconds is 1st 2 bytes of outpc structure txbuf.seconds = outpc.seconds; SCI0DRL = *(char *)&txbuf; SCI0CR2 &= ~0x24; // rcv, rcvint disable SCI0CR2 |= 0x88; // xmit enable & xmit interrupt enable break; case 'Q': // send code rev no. txcnt = 0; txmode = 4; txgoal = 20; SCI0DRL = RevNum[0]; SCI0CR2 &= ~0x24; // rcv, rcvint disable SCI0CR2 |= 0x88; // xmit enable & xmit interrupt enable break; case 'S': // send program title txcnt = 0; txmode = 5; txgoal = 32; SCI0DRL = Signature[0]; SCI0CR2 &= ~0x24; // rcv, rcvint disable SCI0CR2 |= 0x88; // xmit enable & xmit interrupt enable break; case 'k': // request checksum. txcnt = 0; txmode = 0; txgoal = 1; SCI0DRL = cksum; SCI0CR2 &= ~0x24; // rcv, rcvint disable SCI0CR2 |= 0x88; // xmit enable & xmit interrupt enable break; default: break; } // End of switch for received command break; case getCANid: // Get CAN id for current command. CANid = SCI0DRL; if (CANid < MAX_CANBOARDS) txmode = getTableId; else { // CANid wrong - kill comms since don't know where to send data txmode = 0; SCI0CR2 &= ~0xAC; // rcv, xmt disable, interrupt disable kill_ser = 1; kill_ser_t = outpc.seconds + SER_TOUT; return; } break; case getTableId: // Get table id for current command. tble_idx = SCI0DRL; if(CANid != can_id) { txmode = next_txmode; } else if(tble_idx >= NO_TBLES) { // tble index wrong txmode = 0; // kill comms since don't know which/ how much data SCI0CR2 &= ~0xAC; // rcv, xmt disable, interrupt disable kill_ser = 1; kill_ser_t = outpc.seconds + SER_TOUT; return; } else txmode = next_txmode; next_txmode = 0; if (txmode == 1) { txcnt = 0; txgoal = tableBytes(tble_idx); // load output variables into txbuf to avoid incoherent word data. // To work all words in structure must be word aligned, all longs // aligned on long boundaries. xcntr = 0; txptr = 0; *tableWordRam(tble_idx,0) = *((unsigned int *)(&outpc) + 0); *tableWordRam(tble_idx,1) = *((unsigned int *)(&outpc) + 1); SCI0DRL = *tableByteRam(tble_idx, 0); cksum += SCI0DRL; SCI0CR2 &= ~0x24; // rcv, rcvint disable SCI0CR2 |= 0x88; // xmit enable & xmit interrupt enable } else if (txmode == setBurningParameters) { // Burn command if(CANid != can_id) { // set up single CAN message & forward to aux board ix = can[0].cxno_in; can[0].cx_msg_type[ix] = MSG_BURN; can[0].cx_destvarblk[ix] = tble_idx; can[0].cx_destvaroff[ix] = 0; can[0].cx_dest[ix] = CANid; can[0].cx_varbyt[ix] = 0; // no data bytes // This is where (in xmt ring buffer) to put next message if(can[0].cxno_in < (NO_CANMSG - 1)) can[0].cxno_in++; else can[0].cxno_in = 0; // overwrite oldest msg in queue // increment counter if(can[0].cxno < NO_CANMSG) can[0].cxno++; else can[0].cxno = NO_CANMSG; if(!(CANTIER & 0x07)) { // Following will cause entry to TxIsr without sending msg // since when CANTIER = 0, CANTFLG left as buff empty(>0). // If CANTIER has at least 1 int buf enabled, will enter // TxIsr automatically. CANTBSEL = CANTFLG; CANTIER = CANTBSEL; } } else if(flocker == 0) { flocker = 0xCC; // set semaphore to prevent burning flash thru runaway code burn_flag = 1; burn_idx = tble_idx; } txmode = 0; // handle burning in main loop } else if(txmode == 2) { vfy_flg = 1; vfy_fail = 0; } break; case 5: if(CANid != can_id) { // MT wants to send data to aux board via CAN if(rd_wr == 1) { sendCANdat = 1; } // MT wants to request data from aux board via CAN else if(rd_wr == 0) { getCANdat = 1; ltch_CAN = lmms + 3906; // 0.5 sec timeout to get CAN data } else { sendCANdat = 0; getCANdat = 0; } } rxoffset = (SCI0DRL << 8); // byte offset(msb) from start of inpram txmode++; break; case 6: rxoffset |= SCI0DRL; // byte offset(lsb) from start of inpram txmode++; break; case 7: // nbytes must be <= 8 for CAN interaction rxnbytes = (SCI0DRL << 8); // no. bytes (msb) txmode++; break; case 8: rxnbytes |= SCI0DRL; // no. bytes (lsb) rxcnt = 0; // check won't blow table arrays if(CANid == can_id) { if(rxoffset + rxnbytes > tableBytes(tble_idx)) { // kill comms since don't know how much data being sent txmode = 0; SCI0CR2 &= ~0xAC; // rcv, xmt disable, interrupt disable kill_ser = 1; kill_ser_t = outpc.seconds + SER_TOUT; return; } } if(rd_wr == 0) { // read & send back input data if(getCANdat) { txmode = 0; // done receiving/writing all the data // set up single CAN message & forward to aux board // MT requesting data from aux board. // Note: MT must only deal with 1 board at a time and wait til // data back or timeout. ix = can[0].cxno_in; can[0].cx_msg_type[ix] = MSG_REQ; can[0].cx_destvarblk[ix] = tble_idx; can[0].cx_myvarblk[ix] = tble_idx; // MT will have duplicate // varblks for the aux boards can[0].cx_destvaroff[ix] = rxoffset; can[0].cx_myvaroff[ix] = rxoffset; can[0].cx_dest[ix] = CANid; can[0].cx_varbyt[ix] = (unsigned char)rxnbytes; // This is where (in xmt ring buffer) to put next message if(can[0].cxno_in < (NO_CANMSG - 1)) can[0].cxno_in++; else can[0].cxno_in = 0; // overwrite oldest msg in queue // increment counter if(can[0].cxno < NO_CANMSG) can[0].cxno++; else can[0].cxno = NO_CANMSG; if(!(CANTIER & 0x07)) { // Following will cause entry to TxIsr without sending msg // since when CANTIER = 0, CANTFLG left as buff empty(>0). // If CANTIER has at least 1 int buf enabled, will enter // TxIsr automatically. CANTBSEL = CANTFLG; CANTIER = CANTBSEL; } } else { txcnt = 0; txmode = 3; txgoal = rxnbytes; SCI0DRL = *tableByteRam(tble_idx, rxoffset); cksum += SCI0DRL; SCI0CR2 &= ~0x24; // rcv, rcvint disable SCI0CR2 |= 0x88; // xmit enable & xmit interrupt enable } } else { // write data to input data block // buffer to maintain coherence of inputs while awaiting serial bytes if((rxnbytes > 1) && (rxnbytes < sizeof(txbuf))) ibuf = 1; else ibuf = 0; txmode++; } break; case 9: // Check for data overrun. Note: this // still not bullet proof because could write half the bytes // then have overrun; need to store and write all at once if // all is valid. if((SCI0SR1 & 0x08) == 0) { // none yet- write data. if(sendCANdat) *((char *)&txbuf + rxcnt) = SCI0DRL; else if(!ibuf) *tableByteRam(tble_idx, rxoffset + rxcnt) = SCI0DRL; else *((char *)&txbuf + rxcnt) = SCI0DRL; } rxcnt++; if(rxcnt >= rxnbytes) { if(sendCANdat) { txmode = 0; // done receiving/writing all the data sendCANdat = 0; // set up single CAN message & forward to aux board // MT sending data to aux board for(ix = 0; ix < rxnbytes; ix++) { can[0].cx_datbuf[can[0].cxno_in][ix] = *((char *)&txbuf + ix); } ix = can[0].cxno_in; can[0].cx_msg_type[ix] = MSG_CMD; can[0].cx_destvarblk[ix] = tble_idx; can[0].cx_destvaroff[ix] = rxoffset; can[0].cx_dest[ix] = CANid; can[0].cx_varbyt[ix] = (unsigned char)rxnbytes; // This is where (in xmt ring buffer) to put next message if(can[0].cxno_in < (NO_CANMSG - 1)) can[0].cxno_in++; else can[0].cxno_in = 0; // overwrite oldest msg in queue // increment counter if(can[0].cxno < NO_CANMSG) can[0].cxno++; else can[0].cxno = NO_CANMSG; if(!(CANTIER & 0x07)) { // Following will cause entry to TxIsr without sending msg // since when CANTIER = 0, CANTFLG left as buff empty(>0). // If CANTIER has at least 1 int buf enabled, will enter // TxIsr automatically. CANTBSEL = CANTFLG; CANTIER = CANTBSEL; } } else if(ibuf) { for(ix = 0; ix < rxnbytes; ix++) { *tableByteRam(tble_idx,rxoffset + ix) = *((char *)&txbuf + ix); } } dummy = SCI0DRL; // clear OR bit (read sci0sr1(done), sci0drl) if(rd_wr == 1) txmode = 0; // done receiving/writing all the data else { txcnt = 0; // send back data just written txmode = 3; // for verification txgoal = rxnbytes; SCI0DRL = *tableByteRam(tble_idx, rxoffset); SCI0CR2 &= ~0x24; // rcv, rcvint disable SCI0CR2 |= 0x88; // xmit enable & xmit interrupt enable } } // finished all rxnbytes break; case 20: // d/load & burn tables - *** Do while engine NOT turning *** // Note: this type table has no ram copy - its not intended for tuning tble_idx = SCI0DRL; // type of table if(tble_idx > NO_TBLES) { // tble index wrong - kill comms since don't know how much data being sent txmode = 0; SCI0CR2 &= ~0xAC; // rcv, xmt disable, interrupt disable kill_ser = 1; kill_ser_t = outpc.seconds + SER_TOUT; return; } ntword = 0; flocker = 0xCC; // set semaphore to prevent burning flash thru runaway code save_page = PPAGE; PPAGE = 0x3C; // erase table nsect = (unsigned char)(tableWords(tble_idx)/SECTOR_WORDS); if(tableWords(tble_idx) > (nsect*SECTOR_WORDS))nsect++; for (sect = 0; sect < nsect; sect++) { Flash_Erase_Sector(tableWordFlash(tble_idx, 0) + sect * SECTOR_WORDS); } PPAGE = save_page; flocker = 0; txmode++; break; case 21: cksum += SCI0DRL; tble_word = (SCI0DRL << 8); // msb txmode++; break; case 22: cksum += SCI0DRL; tble_word |= SCI0DRL; // lsb flocker = 0xCC; // set semaphore to prevent burning flash thru runaway code save_page = PPAGE; PPAGE = 0x3C; // write table word Flash_Write_Word(tableWordFlash(tble_idx, ntword), tble_word); flocker = 0; ntword++; if(ntword < tableWords(tble_idx)) txmode = 21; else { txmode = 0; ign_reset(); } PPAGE = save_page; break; case 24: // nbytes must be <= 8 for CAN interaction rxnbytes = (SCI0DRL << 8); // no. bytes (msb) txmode++; break; case 25: rxnbytes |= SCI0DRL; // no. bytes (lsb) rxcnt = 0; Tcntr = 0; txmode++; break; case 26: // get 4 table words (8 bytes). Receive msb,then lsb for each word cksum += SCI0DRL; *((char *)&txbuf + Tcntr) = SCI0DRL; Tcntr++; rxcnt++; if((Tcntr >= 8) || (rxcnt >= rxnbytes)) { // set up single CAN message & forward to aux board // MT sending data to aux board for(ix = 0; ix < Tcntr; ix++) { can[0].cx_datbuf[can[0].cxno_in][ix] = *((char *)&txbuf + ix); } ix = can[0].cxno_in; can[0].cx_msg_type[ix] = MSG_CMD; can[0].cx_destvarblk[ix] = tble_idx; can[0].cx_destvaroff[ix] = 0; can[0].cx_dest[ix] = CANid; can[0].cx_varbyt[ix] = Tcntr; // This is where (in xmt ring buffer) to put next message if(can[0].cxno_in < (NO_CANMSG - 1)) can[0].cxno_in++; else can[0].cxno_in = 0; // overwrite oldest msg in queue // increment counter if(can[0].cxno < NO_CANMSG) can[0].cxno++; else can[0].cxno = NO_CANMSG; if(!(CANTIER & 0x07)) { // Following will cause entry to TxIsr without sending msg // since when CANTIER = 0, CANTFLG left as buff empty(>0). // If CANTIER has at least 1 int buf enabled, will enter // TxIsr automatically. CANTBSEL = CANTFLG; CANTIER = CANTBSEL; } Tcntr = 0; if(rxcnt >= rxnbytes) // done txmode = 0; } break; case 30: if(SCI0DRL == '!') // receive 2nd ! for reboot signal txmode++; else if(SCI0DRL == 'x') { // received reinit signal (!x) reinit_flag = 1; txmode = 0; } else txmode = 0; break; case 31: if(SCI0DRL == 'x') { // received complete reboot signal(!!x) if(outpc.rpm < inpram.crank_rpm) { // check not rebooting at hi rpm PPAGE = 0x3C; // go to start of bootload pgm //asm (jmp $F800); (void)reboot(); } } else if(SCI0DRL == '!') { // received complete reload signal(!!!) /* if(outpc.rpm < inpram.crank_rpm) { // check not reloading code at hi rpm PPAGE = 0x3C; SCI0CR1 = 0x00; SCI0CR2 = 0x00; // disable Tx, Rx // go to start of monitor code //asm (jmp $F842); (void)monitor(); } */ } txmode = 0; break; default: dummy = SCI0DRL; // dummy read of reg to clear int flag break; } // End of case switch for received data if((txgoal == 0) && (txmode > 0)) rcv_timeout = lmms + 3906; // 0.5 sec timeout return; // Transmit Interrupt XMT_INT: // Clear the TDRE bit by reading SCISR1 register (done), then write data txcnt++; if(txcnt < txgoal) { if(txmode == 3) { SCI0DRL = *tableByteRam(tble_idx, rxoffset + txcnt); if(rd_wr == 0) cksum += SCI0DRL; } else if(txmode == 4) { SCI0DRL = RevNum[txcnt]; } else if(txmode == 5) { SCI0DRL = Signature[txcnt]; } else if(txmode == 2) { SCI0DRL = *((char *)&vfy_fail + txcnt); } else if(txmode == 6) { // pass on CAN msg to MT SCI0DRL = *((char *)&txbuf + txcnt); } else { xcntr++; SCI0DRL = *tableByteRam(tble_idx, xcntr); cksum += SCI0DRL; if(xcntr >= 3) { // transmitted 4 bytes, load 2 new words xcntr = -1; txptr += 2; *tableWordRam(tble_idx,0) = *((unsigned int *)(&outpc) + txptr); *tableWordRam(tble_idx,1) = *((unsigned int *)(&outpc) + txptr+1); } } } else { // done transmitting if(txmode == 1) outpc.cksum = cksum; txcnt = 0; txgoal = 0; txmode = 0; SCI0CR2 &= ~0x88; // xmit disable & xmit interrupt disable SCI0CR2 |= 0x24; // rcv, rcvint re-enable } return; } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #endif void CanInit(void) { unsigned char ix; /* Set up CAN communications */ /* Enable CAN, set Init mode so can change registers */ CANCTL1 |= 0x80; CANCTL0 |= 0x01; /* clear ring buffers */ for(ix = 0;ix < 2;ix++) { can[ix].cxno = 0; can[ix].cxno_in = 0; can[ix].cxno_out = 0; } can_status = 0; getCANdat = 0; while(!(CANCTL1 & 0x01)); // make sure in init mode /* Set Can enable, use IPBusclk (24 MHz),clear rest */ CANCTL1 = 0xC0; /* Set timing for .5Mbits/ sec */ CANBTR0 = 0xC2; /* SJW=4,BR Prescaler= 3(24MHz CAN clk) */ CANBTR1 = 0x1C; /* Set time quanta: tseg2 =2,tseg1=13 (16 Tq total including sync seg (=1)) */ CANIDAC = 0x00; /* 2 32-bit acceptance filters */ /* CAN message format: Reg Bits: 7 <-------------------- 0 IDR0: |---var_off(11 bits)----| (Header bits 28 <-- 21) IDR1: |cont'd 1 1 --msg type--| (Header bits 20 <-- 15) IDR2: |---From ID--|--To ID---| (Header bits 14 <-- 7) IDR3: |--var_blk-|--spare--rtr| (Header bits 6 <-- 0,rtr) */ /* Set identifier acceptance and mask registers to accept messages only for can_id or device #15 (=> all devices) */ /* 1st 32-bit filter bank-to mask filtering, set bit=1 */ CANIDMR0 = 0xFF; // anything ok in IDR0(var offset) CANIDAR1 = 0x18; // 0,0,0,SRR=IDE=1 CANIDMR1 = 0xE7; // anything ok for var_off cont'd, msgtype CANIDAR2 = can_id; // rcv msg must be to can_id, but CANIDMR2 = 0xF0; // can be from any other device CANIDMR3 = 0xFF; // any var_blk, spare, rtr /* 2nd 32-bit filter bank */ CANIDMR4 = 0xFF; // anything ok in IDR0(var offset) CANIDAR5 = 0x18; // 0,0,0,SRR=IDE=1 CANIDMR5 = 0xE7; // anything ok for var_off cont'd, msgtype CANIDAR6 = 0x0F; // rcv msg can be to everyone (id=15), and CANIDMR6 = 0xF0; // can be from any other device CANIDMR7 = 0xFF; // any var_blk, spare, rtr /* clear init mode */ CANCTL0 &= 0xFE; /* wait for synch to bus */ while(!(CANCTL0 & 0x10)); /* no xmit yet */ CANTIER = 0x00; /* clear RX flag to ready for CAN recv interrupt */ CANRFLG = 0xC3; /* set CAN rcv full interrupt bit */ CANRIER = 0x01; return; } #ifndef GCC_BUILD #pragma CODE_SEG NON_BANKED #endif INTERRUPT void CanTxIsr(void) { unsigned char ix,jx,kx; /* CAN Xmit Interrupt */ CANTBSEL = CANTFLG; // select MSCAN xmit buffer // Check ring buffers and xfer to MSCAN buffer for(ix = 0;ix < 2;ix++) { if(can[ix].cxno) { jx = can[ix].cxno_out; /* Set up identifier registers */ CAN_TB0_IDR0 = (unsigned char)(can[ix].cx_destvaroff[jx] >> 3); // 8 high bits in IDR0, 3 low bits in IDR1 CAN_TB0_IDR1 = (unsigned char)((can[ix].cx_destvaroff[jx] & 0x0007) << 5) | 0x18 | // SRR=IDE=1 can[ix].cx_msg_type[jx]; // 3 bits CAN_TB0_IDR2 = (can_id << 4) | can[ix].cx_dest[jx]; CAN_TB0_IDR3 = (can[ix].cx_destvarblk[jx] << 4); /* Set xmt buffer priorities (lower is > priority) */ CAN_TB0_TBPR = 0x02; /* set data in buffer */ switch(can[ix].cx_msg_type[jx]) { case MSG_CMD: // msg for dest ecu to set a variable to val in msg case MSG_RSP: // msg in reply to dest ecu's request for a var val CAN_TB0_DLR = can[ix].cx_varbyt[jx]; for(kx = 0;kx < CAN_TB0_DLR;kx++) { *(&CAN_TB0_DSR0 + kx) = can[ix].cx_datbuf[jx][kx]; } break; case MSG_REQ: // msg to send back current value of variable(s) // this 1st byte holds var blk for where to put rcvd data // 2nd,3rd bytes hold var offset rel. to var blk and how // many consecutive bytes to be sent back CAN_TB0_DLR = 3; CAN_TB0_DSR0 = can[ix].cx_myvarblk[jx]; CAN_TB0_DSR1 = (unsigned char)(can[ix].cx_myvaroff[jx] >> 3); CAN_TB0_DSR2 = (unsigned char)((can[ix].cx_myvaroff[jx] & 0x0007) << 5) | can[ix].cx_varbyt[jx]; break; case MSG_XSUB: CAN_TB0_DLR = 0; break; case MSG_BURN: CAN_TB0_DLR = 0; break; } // This is where (in xmt buffer) to get next outgoing message if(can[ix].cxno_out < (NO_CANMSG - 1)) can[ix].cxno_out++; else can[ix].cxno_out = 0; if(can[ix].cxno > 0) can[ix].cxno--; // TB buf loaded for xmit - decrement ring buf count can_status &= CLR_XMT_ERR; /* set CAN xmt interrupt bit and clear flag to initiate transmit */ CANTIER |= CANTBSEL; CANTFLG = CANTBSEL; // 1 clears(buffer full), 0 is ignored break; // only handle 1 buffer entry at time } if(ix == 1) { // nothing left in either ring buffer CANTIER = 0x00; // leave CANTFLG as buffer empty, but disable interrupt // Note: if this last xmt in buf, will re-neter ISR, but // then exit because can[0,1].cxno = 0. break; } } // end for loop if((CANRFLG & 0x0C) != 0) { // Xmt error count can_status |= XMT_ERR; //can_reset = 1; } return; } INTERRUPT void CanRxIsr(void) { unsigned char rcv_id,msg_type,var_blk,var_byt,jx,kx; unsigned short var_off,dvar_off,tble_word,ntword; static unsigned short canrxbytes; unsigned char save_page,sect,nsect; /* CAN Recv Interrupt */ if(CANRFLG & 0x01) { var_off = ((unsigned short)CAN_RB_IDR0 << 3) | ((CAN_RB_IDR1 & 0xE0) >> 5); msg_type = (CAN_RB_IDR1 & 0x07); rcv_id = CAN_RB_IDR2 >> 4; // message from device rcv_id var_blk = CAN_RB_IDR3 >> 4; var_byt = (CAN_RB_DLR & 0x0F); switch(msg_type) { case MSG_CMD: // msg for this ecu to set a variable to val in msg case MSG_RSP: // msg in reply to this ecu's request for a var val // value in the data buffer in recvd msg if(getCANdat) { // set up for serial xmit of the recvd CAN bytes (<= 8) // MT getting back data requested from aux board txcnt = 0; txgoal = var_byt; if((txgoal > 0) && (txgoal <= 8)) { txmode = 6; for(jx = 0;jx < var_byt;jx++) { *((char *)&txbuf + jx) = *(&CAN_RB_DSR0 + jx); } SCI0DRL = *((char *)&txbuf); SCI0CR2 &= ~0x24; // rcv, rcvint disable SCI0CR2 |= 0x88; // xmit enable & xmit interrupt enable } else { txgoal = 0; txmode = 0; } getCANdat = 0; ltch_CAN = 0xFFFFFFFF; break; } else { if(tables[var_blk].addrRam != NULL) { // update ram variables with received data for(jx = 0;jx < var_byt;jx++) { *tableByteRam(var_blk, var_off + jx) = *(&CAN_RB_DSR0 + jx); } } else { if(!burnCANdat) { // erase flash sector(s) burnCANdat = 1; canrxbytes = 0; flocker = 0xCC; // set semaphore to prevent burning flash thru runaway code save_page = PPAGE; PPAGE = 0x3C; // erase table nsect = (unsigned char)(tableWords(tble_idx)/SECTOR_WORDS); if(tableWords(tble_idx) > (nsect*SECTOR_WORDS))nsect++; for (sect = 0; sect < nsect; sect++) { Flash_Erase_Sector(tableWordFlash(tble_idx, 0) + sect * SECTOR_WORDS); } PPAGE = save_page; flocker = 0; } // update flash table with received data flocker = 0xCC; save_page = PPAGE; PPAGE = 0x3C; for(jx = 0; jx < 8;jx += 2) { tble_word = (*(&CAN_RB_DSR0 + canrxbytes) << 8) | *(&CAN_RB_DSR0 + (canrxbytes + 1)); // write table word ntword = canrxbytes>>1; Flash_Write_Word(tableWordFlash(var_blk, ntword), tble_word); canrxbytes += 2; if(canrxbytes >= tableBytes(var_blk)) { burnCANdat = 0; break; } } flocker = 0; PPAGE = save_page; if(burnCANdat) { // still more data - set 1 sec timeout ltch_CAN = lmms + 7812; } else { // all done ign_reset(); } } } break; case MSG_REQ: // msg to send back current value of variable(s) // Update related parameters for xmt ring buffer jx = can[0].cxno_in; can[0].cx_msg_type[jx] = MSG_RSP; // destination var blk can[0].cx_destvarblk[jx] = CAN_RB_DSR0; dvar_off = ((unsigned short)CAN_RB_DSR1 << 3) | ((CAN_RB_DSR2 & 0xE0) >> 5); can[0].cx_destvaroff[jx] = dvar_off; can[0].cx_dest[jx] = rcv_id; var_byt = CAN_RB_DSR2 & 0x1F; can[0].cx_varbyt[jx] = var_byt; // put variable value(s) in xmit ring buffer for(kx = 0;kx < var_byt;kx++) { can[0].cx_datbuf[jx][kx] = *tableByteRam(var_blk, var_off + kx); } // This is where (in xmt buffer) to put next messge if(can[0].cxno_in < (NO_CANMSG - 1)) can[0].cxno_in++; else can[0].cxno_in = 0; // increment counter if(can[0].cxno < NO_CANMSG) can[0].cxno++; else can[0].cxno = NO_CANMSG; if(!(CANTIER & 0x07)) { // Following will cause entry to TxIsr without sending msg // since when CANTIER = 0, CANTFLG left as buff empty. // If CANTIER has at least 1 int buf enabled, will enter // TxIsr automatically. CANTBSEL = CANTFLG; CANTIER = CANTBSEL; } break; case MSG_XSUB: // msg to execute a subroutine switch(rcv_id) { case 1: // message to this ecu from device 1 can_xsub01(); // execute sub immediately here (set // flag if can execute in main loop) break; } break; case MSG_BURN: // msg to burn ram data table into flash data table flocker = 0xCC; // set to prevent burning flash thru runaway code burn_idx = var_blk; burn_flag = 1; break; } // end msg_type switch can_status &= CLR_RCV_ERR; } if((CANRFLG & 0x72) != 0) { // Rcv error or overrun on receive can_status |= RCV_ERR; //can_reset = 1; } /* clear RX buf full, err flags to ready for next rcv int */ /* (Note: can't clear err count bits) */ CANRFLG = 0xC3; return; } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #endif void can_xsub01(void) { return; } int barocor_eq(int baro) { // returns baro correction in % (100 is no correction) // baro in kPa x 10 return((int)(inpram.bcor0 + (((long)inpram.bcormult * baro) / 1000))); } int aircor_eq(int mat) { // returns air density correction from equation in % (100 is no correction) // mat in deg F x10 or deg Cx10 /* Following equations used: For Farenheit, air density (pounds/cubic feet) = .0391568 x map (kPa x 10) ------------------------- ((mat(degFx10)/10) + 459.7) If we take 70 degF as standard temperature (no density correction) and ignore map, which is already accounted for elsewhere,then air density correction(%) = (70 + 459.7) x 100 ------------------ ((mat/10) + 459.7) For Celsius, with a 20 deg C reference and mat in deg C x 10 air density correction(%) = (20 + 273.2) x 100 ------------------ ((mat/10) + 273.2) */ if(inpram.Temp_Units == 0) { // deg F return((int)(529700 / (mat + 4597))); } else { // deg C return((int)(293200 / (mat + 2732))); } } int coil_dur_table(int delta_volt) { int ix; long interp, interp3; // returns correction for coil duration vs battery voltage deviation // from 12 V from table lookup in .1 ms units // bound input arguments if(delta_volt > inpram.deltV_table[NO_COILCHG_PTS-1]) { return((int)inpram.deltDur_table[NO_COILCHG_PTS-1]); } if(delta_volt < inpram.deltV_table[0]) { return((int)inpram.deltDur_table[0]); } // delta_volt in V x 10 for(ix = NO_COILCHG_PTS - 2; ix > -1; ix--) { if(delta_volt > inpram.deltV_table[ix]) { break; } } if(ix < 0)ix = 0; interp = inpram.deltV_table[ix + 1] - inpram.deltV_table[ix]; if(interp != 0) { interp3 = (delta_volt - inpram.deltV_table[ix]); interp3 = (100 * interp3); interp = interp3 / interp; if(interp < 0)interp = 0; } return((int)(inpram.deltDur_table[ix] + interp * (inpram.deltDur_table[ix+1] - inpram.deltDur_table[ix])/ 100)); } int intrp_2ditable(unsigned int x, int y, unsigned char nx, unsigned char ny, unsigned int * x_table, int * y_table, int * z_table) { int ix,jx; long interp1, interp2, interp3; // bound input arguments if(x > x_table[nx-1])x = x_table[nx-1]; else if(x < x_table[0])x = x_table[0]; if(y > y_table[ny-1])y = y_table[ny-1]; else if(y < y_table[0])y = y_table[0]; // Find bounding indices in table for(ix = ny - 2; ix > -1; ix--) { // Start w highest index // because will generally have least time for calculations at hi y if(y > y_table[ix]) { break; } } if(ix < 0)ix = 0; for(jx = nx - 2; jx > -1; jx--) { // Start w highest index // because will generally have least time for calculations at hi x if(x > x_table[jx]) { break; } } if(jx < 0)jx = 0; // do 2D interpolate interp1 = y_table[ix + 1] - y_table[ix]; if(interp1 != 0) { interp3 = (y - y_table[ix]); interp3 = (100 * interp3); interp1 = interp3 / interp1; } interp2 = x_table[jx + 1] - x_table[jx]; if(interp2 != 0) { interp3 = (x - x_table[jx]); interp3 = (100 * interp3); interp2 = interp3 / interp2; } return((int)(((100 - interp1) * (100 - interp2) * z_table[ix*nx+jx] + interp1 * (100 - interp2) * z_table[(ix+1)*nx+jx] + interp2 * (100 - interp1) * z_table[ix*nx+jx+1] + interp1 * interp2 * z_table[(ix+1)*nx+jx+1]) / 10000)); } int knk_thresh_calc(unsigned int rpm) { int ix; long interp, interp3; // returns values for knock signal threshhold vs rpm. // bound input arguments if(rpm > inpram.knk_rpm[NO_KNKRPMS-1]) { return(inpram.knk_thresh[NO_KNKRPMS -1]); } if(rpm < inpram.knk_rpm[0]) { return(inpram.knk_thresh[0]); } for(ix = NO_KNKRPMS - 2; ix > -1; ix--) { if(rpm > inpram.knk_rpm[ix]) { break; } } if(ix < 0)ix = 0; interp = inpram.knk_thresh[ix + 1] - inpram.knk_thresh[ix]; if(interp != 0) { interp3 = (rpm - inpram.knk_rpm[ix]); interp3 = (100 * interp3); interp = interp3 / interp; } return((int)(inpram.knk_thresh[ix] + interp * (inpram.knk_thresh[ix+1] - inpram.knk_thresh[ix])/ 100)); } void set_spr_port(char port, char val) { unsigned char pval; if (val) { outpc.port_status |= (0x01 << port); } else { outpc.port_status &= ~(0x01 << port); // JL fix } if(port < 4) { pval = (0x01 << (port + 2)); if(val) { DISABLE_INTERRUPTS PTM |= pval; ENABLE_INTERRUPTS } else { DISABLE_INTERRUPTS PTM &= ~pval; ENABLE_INTERRUPTS } } else if(port < 6) { pval = (0x01 << (port + 2)); if(val) { DISABLE_INTERRUPTS PTT |= pval; ENABLE_INTERRUPTS } else { DISABLE_INTERRUPTS PTT &= ~pval; ENABLE_INTERRUPTS } } else { if(val) PORTA |= 0x01; else PORTA &= ~0x01; } } #ifndef GCC_BUILD #pragma CODE_SEG NON_BANKED #endif INTERRUPT void UnimplementedISR(void) { /* Unimplemented ISRs trap.*/ return; } #ifndef GCC_BUILD #pragma CODE_SEG DEFAULT #endif /****************************************************************************** Arguments: progAdr Pointer to the start of the destination Flash location to be programmed bufferPtr Pointer to the start of the source data size Number of WORDS to be programmed This function will program non-paged flash only ******************************************************************************/ void fburner(unsigned int* progAdr, unsigned int* bufferPtr, unsigned int no_words) { unsigned char sect,no_sectors; if(flocker != 0xCC)return; // if not set, we got here unintentionally // sector = 1024 bytes (512 words) if(burn_flag == 1) { no_sectors = (unsigned char)(no_words / SECTOR_WORDS); if(no_words > (no_sectors * SECTOR_WORDS))no_sectors++; for (sect = 0; sect < no_sectors; sect++) { Flash_Erase_Sector(progAdr + sect * SECTOR_WORDS); } } Flash_Write_Word(progAdr + burn_flag-1, *(bufferPtr + burn_flag-1)); return; } //***************************************************************************** //* Function Name: Flash_Init //* Description : Initialize Flash NVM for HCS12 by programming //* FCLKDIV based on passed oscillator frequency, then //* uprotect the array, and finally ensure PVIOL and //* ACCERR are cleared by writing to them. //* //***************************************************************************** void Flash_Init(unsigned long oscclk) { unsigned char fclk_val; unsigned long temp; /* Next, initialize FCLKDIV register to ensure we can program/erase */ temp = oscclk; if (oscclk >= 12000) { fclk_val = oscclk/8/200 - 1; /* FDIV8 set since above 12MHz clock */ FCLKDIV = FCLKDIV | fclk_val | FDIV8; } else { fclk_val = oscclk/8/200 - 1; FCLKDIV = FCLKDIV | fclk_val; } FPROT = 0xFF; /* Disable all protection (only in special modes)*/ FSTAT = FSTAT | (PVIOL|ACCERR);/* Clear any errors */ return; } void Flash_Erase_Sector(unsigned int *address) { FSTAT = (ACCERR | PVIOL); // clear errors (*address) = 0xFFFF;/* Dummy store to page to be erased */ FCMD = ERASE; /********************************************************************* ;* Allow final steps in a flash prog/erase command to execute out ;* of RAM while flash is out of the memory map ;* This routine can be used for flash word-program or erase commands ;* ;********************************************************************/ /* Execute the sub out of ram */ asm { ldaa #CBEIF jsr RamBurnPgm }; return; } void Flash_Write_Word(unsigned int *address, unsigned int data) { FSTAT = (ACCERR | PVIOL); // clear errors (*address) = data; // Store desired data to address being programmed FCMD = PROG; // Store programming command in FCMD /* Execute the sub out of ram */ asm { ldaa #CBEIF jsr RamBurnPgm }; return; }