Ignition Control and the '747 ecm Robert Rauscher, 5/26/99, Updated 4/9/00 Areas preceded with a '|' (vertical bar), are updated. The spk747al.bin file is also updated. It is based on the ACSC bin. This write-up is on the spark control only. It also does not include any egr spark conditions. I have been testing an ignition only '747 setup, and do not have an egr engine. |First off, I'd like to thank EcmGuy for providing the referance materials that made this write-up possible. (originally credit was given to EcmNut, apologies to both, 4/00, rr). Second, this material is as accurate as I believe, but may contain errors. If any, they would most likely be in areas that use bit mapped status words. It can be difficult to understand the exact meaning of these bits. /* ------------------------------------- */ For sensors, I've been using: a) vss b) coolant temp c) map d) knock There is also a power brake vacuum line restrictor. This is a stock GM piece that slows the rate that the p/b's 'get vacuum' from the intake manifold. This reduces the effects that using the p/b's have on the MAP sensor value. If your not planning on doing the fuel also, try to mount the tps to the carb. It would be worth it, it will allow pe/wot to be determined, along with proper map test being done. Without it, there was intermittent error 33's: map sensor high codes. /* ------------------------------------- */ Ecm error codes in use: 12 drp 14 coolant 15 coolant 24 vss 34 map 42 est 43 esc 51 prom 52 cal pack 55 adu /* ------------------------------------- */ Some notes and associated files: 1) I recommend using promedit for an editor, I have provided a .ecu file for use with promedit: spk747.ecu I'm using the version with the build date of: Aug 05, 1998. 2) spk747al.bin, a bin file with high timing for an alum headed engine. Use at your own risk! But study it. /* ------------------------------------- */ (SA => Spark Advance) (wot => wide open throttle) (PE => power enrich, same as pe) (map => manifold absolute pressure) (esc => electronic spark control) (est => electronic spark timing) (vss => vehicle speed sensor) (tdc => top dead center) (btdc => before top dead center) (atdc => after top dead center) (aldl => Assembly LIne Diagnostic Link) /* ------------------------------------- */ If you have an aldl scanner, you can monitor some of the spark advance values with some small modifications to the bin. SA values can be output at locations normally used for other functions. Then using a little math, the SA in degrees can be ascertained. I use locations that the scanner does no math on, the displayed value will then be as it is in the ecm. Then for SA values, just multiply the displayed scanner value by 0.35 (90/256 = 0.3515625). The result will be degrees of advance. Any initial timing will also need to be added to this advance (see 0x009). So, if the aldl INT value is 91: 91 * 0.35 = 31.8 Degrees. With the initial advance set to 6 degrees: 31.8 + 6 = 37.8 degrees at the crank. I do have a caution on the use of the aldl on the '747 ecm, it is Slow. Hence, the entire data output during one frame, may not be relevant to that frame. IE: If your capturing a frame, while accellerating, things are changing, and the data is not a 'snapshot' of one point in time. The data output is gotten from memory as it is being output. Ok, locations I use: BLM, INT, IAC, (0x509, 0x4f7, 0x4eb). I output RAM locations: 0x0067 (total spark advance) 0x006E (PE/WOT spark advance) 0x0077 (Retard spark amount, only half this value is sub'd from the total SA) The value of location 0x0067 will include the SA of the PE/WOT and Retard spark also. This is nearly the final value used to program the spark control counters. Only a spark latency correction is made after this. /* ------------------------------------- */ Here are the more important spark locations in the bin: The left hand column are the eprom offset location for each. 0x009: Initial spark advance. This value should be what the physical distributor timing is set to. This value gets subtracted from the Total Spark Advance. (but is physically added back in by the position of the distributor relative to piston tdc). This is also called 'static' timing. 0x00C: Max RPM used in the spark slope calculation. This is maximum value at which additional slope spark will be calculated to. Once this value is reached (you have it floored, right)?, the slope increase will be held at this value. 0x00D: Slope in degrees per 1K rpm. This is the rate that additional SA will be added. --- These last two work together to add additional spark at a linear rate as the rpm increases. This additional spark starts at 3600 rpm, and increases until the max rpm value is reached. 3600 rpm is where the main SA table ends. Above the 'max rpm' the total slope will be held at the value calc'd for 'max' rpm. 0x014: Warm spark bias. This value is subtracted from the Total SA. It's purpose is to bias the main SA table, so that negative advance values can be in the table as a positive number. 0x015: Cold spark bias. This value is subtracted from the Total SA. It's purpose is to bias the Coolant compensation spark table. Again, to allow for a negative advance in this table. 0x019: Maximum spark (2 bytes). This value is the highest total advance that can be programmed into the distributor. Any Calculated Total SA above this value gets clipped to this value. --- It's purpose is due to the physical limitations of the distributor. You can only advance the timing so far until you are sparking on the previous | cylinder. With eight spark firings per distributor | revolution, there are only 45 degrees between each | set of plug wire terminals on the cap. GM uses the | value of 42 degrees as the maximum advance. This | always a safety margin to direct the spark to the | proper terminal. | | However, this is not the limit that can be run at | the crank. If you set the static timing to 10 degrees | BTDC, a maximum of 52 degrees can be had at the crank. | | The 42 degree limit is only the distributor limit. | Crank timing is the total of the static setting, and | the amount programmed by the ECM into the distributor. | It just becomes a balance of how much total crank advance | is required, versa, how much static (inital), timing can | be used (and still hot-start). (if it becomes a limitation, you need a DIS system). 0x01B: Maximum Retard (2 bytes). Maximum total amount of retard allowed. For the same reasons as max SA above. 0x023: aldl/closed loop added spark. This spark is added when in aldl diagnostic closed loop mode. 0x024: Hi-way spark mph threshold. Minimum MPH to start timer. 0x025: Hi-way spark time threshold: Amount of time required before hi-way spark is added. Must be above MPH threshold for this amount of time, in order to get hi-way spark. --- Note: The ecm does an ESC test once each time after an engine start. This will typically cause two (2) knock counts to occur (see loc 0x555). Until this test occurs, and passes, hi-way spark won't be added. Sooner or later, these counts should show up on the aldl scanner, under 'knock counts'. Note: Another parameter (hard coded), is that no err43 (est) must have existed since power up(?). Or, is that the test for err43 hasn't yet been run(?). 0x026: Startup added SA is reduced (re-calc'd), on this time period. Every 'n' seconds, any added startup spark is decayed. 0x02B: Knock retard mph cutoff threshold. 0x02C: Knock retard rpm cutoff threshold. 0x02D: Knock retard temperature cutoff threshold. --- For knock retard to take place, the engine temp needs to be greater then the temperature threshold and the vehicle voltage must be greater than 9.0 volts, then, either the VSS or the RPM threshold must be exceeded. 0x02E: Burst knock MAP threshold, in Kpa. 0x02F: Burst knock TPS threshold, in percent. 0x030: Burst knock differential TPS threshold, in percent. 0x031: Burst knock, duration of retard, in seconds. --- The last four, plus Knock Temp Threshold (0x02D), determine whether burst knock retard takes place. There is also an enable bit at location 0x006, bit zero, that must be set, if burst knock is desired. Right off, the engine temp is compared to the knock temp threshold (0x02D). If below this temp, burst knock can take place, if above this temp, only regular knock retard can take place. The burst knock qualification is as follows: Option byte @ 0x006, bit 0 set (= 1) Engine temp below knock retard threshold Not in closed loop Map value above threshold TPS % above threshold TPS differential above threshold TPS differential is for more throttle opening If all of the above qualifies, then a lookup is done based on current engine temperature. This value is subtracted from the total timing. It is not available for output to the aldl link, but is reflected in the value at location 0x0067. Burst Knock is an all or nothing deal, no attack rate or decay rate. Once started, it exists for the entire duration set at 0x031. The actual retard value is adjusted each pass through (80 times/second), relative to the engine temperature (from the lookup table). Once the Duration is met, the retard instantly ceases. 0x035: Main SA table. This is the beginning for what the Total Calc'd SA will be. This is a 3-dimensional table with rpm and map in Kpa as the look up parameters. The value at that location is the third dimension, which is the base or starting SA. The warm spark bias is reflected in these table values. (ie: actual SA will be the lookup value minus the warm spark bias). 0x10A: Coolant Compensation SA table. This table is used to add or subtract SA, based on engine temperature, and less so on manifold vacuum. Typically, more SA at low temp's, less SA at high temp's, and no change at normal operating temperature. This is a 3 dimensional table, with coolant temp and map VAC (inverse of map Kpa), as the look up parameters. The cold spark bias is reflected in these table values. 0x157: Hi-way Spark Added. This spark is added while in a steady state cruise. See 0x024 & 0x025 for qualifiers. If qualified, a new value is calulated each pass through the code. It is based on engine vacuum. 0x15E: PE Spark. This spark is added when at WOT. It is based on engine rpm. I believe that a TPS is required for the code to recognize a WOT condition. 0x16E: Startup Spark Added. This SA is added after an engine startup. It then decays at a rate defined in two other parameters (shown next). The amount of spark added is based on coolant temp at startup. If the engine is startup while in clear flood mode, no statup SA is added. 0x174: Startup spark decay. This is the initial amount of time, in seconds that the added spark will decay over. The decay is set once at each start time. 0x17a: Startup spark decay multiplier. This value is a multiplier of the Spark Decay parameter. Since this value is updated on a regular basis, as the engine warms up, this increases the initial decay rate. So, on hot day, the added SA will decay faster than on a cold day. 0x17f: PE Knock Retard. In degrees, the amount of retard to use based on engine rpm, during WOT knock detection. 0x188: Knock Retard. In degrees, the amount of retard to use during normal engine operation. Based on map VAC. 0x18d: Retard attack rate. Amount of degrees per millisecond that any required retard will occur at, based on rpm. --- When knock is detected, a count is available to the ecm code. This is read from the ecm hardware. This count is the number of milliseconds that knock has been occurring. This is the 'knock count' displayed on a scanner. The total amount of retard is calculated by: 1). Multiplying the attack rate by the 'count'. 2) Lookup on either PE retard table if WOT, if not in PE, then it uses the normal table. The amount of added retard will be the lessor of the above two values; the attack rate, or the table value. It is then divided by two. Yes, it appears that the code divides the retard value in half before subtracting it out from the total spark advance. Of course, a maximum retard check is then done using the value in location 0x01b. The ecm will not retard the total timing any more than this value. 0x196: Retard recovery rate. Percent per millisecond that any retard recovers back to normal at, based on rpm. The decay routine runs 10 times a second. The decay starts immediately upon the addition of retard. Again, required for a smooth transition? /* ------------------------------------- */ OK, so what do I do with the above, and how does it all fit together? Note: These calc's are completed 80 times a second, asynchronous to the actual timing signal from the distributor. First, a quick run down of what the code does. Remember, just because a value is calc'd, doesn't mean that it has to be greater than zero in value. 1) A look up is done on the Main SA table, the value retrieved is based on the current rpm and map values. Call this Total Spark Advance, or TSA for now. 2) The Slope, if any, is calculated and added to the TSA. 3) A look up is done on the Coolant Compensation table using the current engine temp and manifold vacuum. Add this to TSA. |4) (adjust for any egr spark, SA is added if egr is active). | (a lag filter calculation is done here also, but appears | to be egr related only. The lag filter allows for the smooth | change of SA whenever the EGR is enabled/disabled). 5) Add in any PE spark to TSA. Calc'd in a separate routine. 6) Add in any aldl/closed loop spark, to TSA. 7) Add in any Start-up Spark, to TSA. 8) Add in any Hi-way Spark, to TSA. 9) Subtract the Warm Bias spark value, from TSA. 10) Subtract the Cold Bias spark value, from TSA. 11) Subtract the Initial spark value, from TSA. 12) At this point, the TSA value is tested against the Max Allowed Spark Value. If it is greater, it will be set to the maximum allowed SA value. 13) Subtract any Burst Knock, from TSA. 13) Subtract any Knock Retard, from TSA. 14) The TSA value is tested against the Minimum Allowed Spark, and adjusted to this minimum value if required. 15) A Spark Latency correction is made and the end value is sent to counters in the ecm. An output from the counters control when the distributor will spark, relative to the incoming distributor pulse. /* ------------------------------------- */ As I explain this, I'll describe how I set up this engine. I can't say whether the same setup will work for you. The engine is as follows: 327 ci SBC '91 alum corvette heads, no heat riser flat top forged pistons ~ 9.75:1 cr Isky 280 Mega cam, .465 lift, 224 deg @ .050, 108 deg LDA Edelbrock Performer RPM dual plane intake Carter 625cfm afb headers & duels 4-spd w/3.55 rear This engine idles at 850 to 900 rpm. Keep this in mind as I discuss the changes I made to the bin. Note: I don't recommend this cam for an efi engine, it idles rough, along with a highly fluctuating map. It works OK with the ignition system, but have my doubts once I get a TBI on it. /* ------------------------------------- */ 1). I set the Initial Timing to 6 deg. This is both in the bin and at the distributor. (disconnect the bypass and with the engine running, time it with a light to 6 BTDC). 2). Set the Warm Spark Bias to 0 degrees. Between these two, the values you see in the Main SA table |will be what you have at the crank. The initial 6 deg will be subtracted from the main SA look-up value, but will be physically added back in by the distributor timing. This makes it both easy to start the engine, and easy to read the main SA table. 3). Set the cold spark bias to 10 degrees. 4) Set each value in the coolant compensation spark table 10 degrees higher than the actual desired value. This is due to the cold spark bias being subtracted from each of the values of this table. If you set a value to 15 degrees, the spark will be advanced only an additional 5 degrees. If set to 7 degrees, this will in effect subtract 3 degrees from the total SA, (7 - 10 = -3). 5). Spark Slope values. I've extended the Max RPM to 6375, along with reducing the slope to 1 degree per 1k rpm. This will add a maximum of 2.7 deg over 3600 rpm through 6375 rpm. |6). Set the Maximum Spark value to 42 degrees. The initial SA |will be subtracted from this; this allows a max of 48 degrees |of timing at the crank (with the 6 degrees inital). 7). aldl spark. Just set it to zero and forget about it. 8). Startup Spark Added. At the lowest temp, I added about three additional degrees. Changed from 5 to 8 degrees. 9). Retard Recovery Rate. Note that at 400 rpm, the recovery rate is high (on some bin's). With an idle speed of 900 rpm, I brought the recovery rate higher at 600 and 800 RPM also. This is an anti-stall tactic. 10). For the following I have not changed anything: 0x157: Hi-way Spark Added. Typically disabled. 0x15E: PE Spark. (Need tps to hit PE). 0x174: Startup Spark Decay. 0x17a: Startup Spark Decay Multiplier. 0x17f: PE Knock Retard (Again, tps needed). 0x188: Knock Retard vs VAC. 0x18d: Retard Attack Rate. 11). This leaves the main SA table and the coolant compensation table. Set the main spark table values to what you want to see at the crank during normal engine operation. At the low rpm idle area, I level out the SA so that fluctuations in rpm and map don't cause the timing to change. This gives a more stable idle. Along the low map values (foot off the pedal, slowing down), you can put less timing. This will allow the car to slow down better, as you are pulling power from the engine. Change the timing up and down until you like the way it drives. Pulling a lot out can put your nose into the windshield, and then light the tires when you re-apply the gas. For the cruise areas, map between 35 and 50 Kpa, along with the rpm range of 1800 to 3000 rpm, adding timing helps. As the map rises to 100Kpa (wot), reduce the timing to prevent detonation. Keep an eye on the knock counts while tuning. With aluminum heads, you can run more timing throughout. This is reflected in the current bin. 12). For the coolant compensation table, enter the Cold Spark Bias value throughout the normal operation areas. This will be around the temperature range of the thermostat. You don't want to use this table to change the spark in this area. So by using the cold bias value, will in effect, cause no change to the TSA during normal engine operation. With a cold engine you want more SA, as the engine starts to run a hot (overheat), pull some timing out. Then blend in from the edges to the normal range for a smooth transition. /* ------------------------------------- */ /* ------------------------------------- */ All in all, this should get you started, examine these bin's, and some stock ones with a promedit'r. You will soon get a feel for how to do spark timing. /* ------------------------------------- */ /* ------------------------------------- */ | New section describing how loookups are done into 3D tables: It's a 3-way, 16-point interpolation between the table values. Think of the point of 53kpa map & 1700 rpm as being someplace inside of a box. The four corners of that box, are the four table values that surround the defined point. The distance from that point, to each of the four walls is estimated, with the final result based on those four values. Works something like this... Part of main SA table: map> 50 60 rpm 1600 20 24 1800 22 26 If the map is 53 and the rpm is 1700, first lookup gets the 20 & 24 degree values @ 1600 rpm, and interpolates between them. The difference between these two are divided into 16 points: 24deg - 20deg = 4deg, 4deg / 16points = .25 deg per point 60map - 50map = 10map, 10map / 16points = .625 kpa per point. 53 map @ 1600rpm => 20deg + (3map / .625) * .25 = 21.2 deg. So, 21.2 deg is the 16 point interpolation of 53 map @ 1600 rpm. (Note: On a 2D table, we would be done, and this value returned). The 3D lookup, however, calls the 2D routine a total of three times. In this example, the first call we already completed. The routine then advances a full row to interpolate between the 22 deg and 26 deg values found at 1800rpm. So, using the same math as above, 23.2 deg is the 16 point interpolation of 53 map @ 1800 rpm. The third and final interp. is done between the answers of the first two lookups: 21.2 deg & 23.2 degrees (@ 1600 & 1800 rpm). The rpm is divided into 16 points the same. For this example, it's right in the middle of table values 1600rpm and 1800rpm, with 1700 actual. So, 21.2 deg & 23.2 deg => 22.2 deg as the actual returned, looked up finalized interpolated value to be used. /* ------------------------------------- */ /* ------------------------------------- */