The Lucas 14CUX is an electronic engine management system designed to control fuel injection on the Rover V8 engine. It was used primarily in Land Rover products from its introduction in 1990 until 1995, and also by low-volume sports car manufacturers until the engine ceased production in 2005-2006. More information about the high-level design and applications of the 14CUX can be found in its Wikipedia article.
The Rover V8 has a tuning potential that made it popular with specialist sports car builders, and it exists in a variety of configurations from 3.5L to 5.0L displacement and beyond. The fuel maps stored in the 14CUX each represent a two-dimensional array of fueling values, indexed by engine speed and engine load. There are at least two reasons that a person may want to design a custom fuel map:
- The fuel maps are usually specific to a particular engine displacement; a 3.5L fuel map will not work well with a 5.0L engine. Appropriate fuel maps could be used to allow retrofitting the 14CUX system to later Rover V8 configurations -- these may include the 4.0L and 4.6L units, which were sold in the United States with a different engine management system after the 14CUX had been discontinued.
- Engines being tuned for motorsport have different performance requirements than those that were designed for road use. Fuel maps can be modified to increase power at the expense of fuel economy.
The purpose of this project is to explore and document the internal function of the 14CUX. Exploration of the 14CUX firmware has also uncovered a software protocol that can be used to communicate with the ECU and retrieve diagnostic information over its serial link. (See 14CUX Serial Interface)
In Land Rover vehicles, the ECU will have a Land Rover part number starting with either "PRC" (for earlier units) or "AMR" (for later units).
The microprocessor used by the 14CUX is a Motorola 6803U4, which is an uncommon variation of the 6803. The "U4" has three built-in timers as opposed to the standard 6803's single timer; this is important, as the fuel injectors are batch-fired an entire bank at a time, and each of the left and right banks requires its own timing.
The code and data is stored in a 27256 PROM, which seems to be soldered in place in earlier (PRC) units and socketed in later units (PRC and AMR). Only half of the 32KB space is used, so the 16KB of code and data is duplicated in the upper half.
The 64KB address space is not fully utilized and some devices are mapped into memory in multiple locations. For example, the 16KB ROM contents appears in $C000-$FFFF, and again in $8000-$BFFF. The four ADC registers repeat from $4000 to $7FFF. If an empty memory location is read, the low byte of the address is returned as the data. (Thus, if displaying a block of memory read from an empty location, it will appear as an incrementing count.)
The low area of the ROM ($C000-$C83F) contains the fuel maps and other data. The area from $C840 to $FFFF contains code, jump tables, and a few fixed data tables. Code stops around $FA50, and is followed by an unprogrammed section. The unprogrammed area of the ROM continues until near the end, where a vector table is located.
|$0000-$0020||MPU||6803U4 internal register block|
|$0040-$00FF||MPU||6803U4 RAM; first 32 bytes ($40 to $5F) are battery-backed and used to hold fault codes|
|$2000-$207F||PAL?||External RAM (repeats from $2000 to $3FFF)|
|$6000-$6004||ADC||A/D converter registers (repeats from $6000 to $7FFF)|
|$C000-$FFFF||ROM||The contents of the ROM appear both here and in $8000-$BFFF|
|$C000||ROM image (16KB)|
|$0049-$004E||Fault codes (see fault bits table, below)|
|$0055/56||Main battery voltage (16 bits). This is used as input to the fueling value since it affects the opening time of the injectors.|
|$0059/5A||Mass airflow sensor voltage (16 bits). The A/D converter does a 10-bit reading of the MAF voltage and stores it as a 16-bit value in these locations.|
|$005B||Fuel map row index. This is calculated mainly from engine load (determined by air flow) with some scaling from engine speed. The value will be from 0x00 to 0x70. The upper nibble can have eight different values (0 thru 7). This will indicate the row in the 16-column by 8-row fuel map.|
|$005C||Fuel map column index. The firmware looks up one of sixteen engine RPM brackets and stores the corresponding number (from 0x0 thru 0xF) in the upper nibble of this memory location.|
|$005F/60||Throttle position (16 bits). The 10-bit throttle pot value is stored here. The pot is spring-loaded to its minimum position, but is mounted with the wiper just off the minimum setting. If the pot is removed from its mounting and allowed to read below a certain threshold, the firmware will detect a fault.|
|$00A4/A5||The throttle pot reading is copied to this location before the throttle pot routine finishes. The next time the routine runs, the new reading (at $005F/60) is compared with this. If the new reading is greater than the old reading by a certain threshold, it means that the throttle is opening quickly. In this case, the fueling is immediately increased. This can be thought of as a software equivalent of the carburetor accelerator pump.|
|$006D||Idle bypass motor position. On startup, this will read 0, which is fully open. Fully closed is 180.|
|$007A-$007B||Engine speed (instantaneous) (16 bits). This is the instantaneous engine ignition pulse width measured in 2-microsecond increments. This is the time between two consecutive ignition pulses from the ignition coil. To obtain RPM, divide 7,500,000 by this number.|
|$007C-$007D||Engine speed (filtered) (16 bits). This is a running average of the ignition pulse width, and is calculated by averaging three old values and one newly-measured value. The result is a value that changes more smoothly, though it does not precisely represent the most current measurement. Since this filtered speed lags the instantaneous value, it's possibly useful to indicate whether the engine is revving up or slowing down.|
|$007E-$007F||Engine speed (RPM) (16 bits). This is the actual engine RPM, which the firmware calculated by dividing 7,500,000 by the above pulse width. This calculation is bypassed when the engine speed goes above 1950 RPM. This was probably done because the microprocessor gets very busy at higher RPMs; the processor doesn't have a divide instruction, and it is computationally expensive to calculate the RPM without one.|
|$2000||Transmission gear / neutral switch. With an automatic transmission, this will read zero in park and a full 8-bit count (0xFF) when in drive. Testing with a Defender 90 manual (NAS) showed that the value stayed at half count (0x7F hex), probably indicating a resistor on this ADC input to put the ECU in manual-gearbox mode.|
|$2003||Road speed in kilometers per hour.|
|$2006||Fuel temperature. This value will start about the same as the coolant for a completely cooled engine but will not drop as much as the engine warms since the fuel does not get nearly as hot as the coolant.|
|$202C||Currently selected fuel map. (This is always 5 on NAS Land Rover vehicles.)|
|$2041||Checksum of the ROM. This must always be 0x01, or the code will register a fault. (When the ROM image was being finalized, a particular byte was modified to force the contents of the ROM to produce an 8-bit checksum value of 0x01.)|
Fault bits are packed into memory locations $0049 through $004E, as shown below. Shaded cells represent an extended set of fault codes. Update, 2013-12-24: There seems to be a contradiction between the Land Rover documentation and the firmware, with regard to the bank-specific fault codes. I've updated the table below with the correct data. Note that I'm now using the terms 'odd' (cylinders 1-3-5-7) and 'even' (cylinders 2-4-6-8) to differentiate the banks, as this is less ambiguous than left/right or A/B.
|$0049||(21) Tune resistor out of range||(12) Airflow meter||(50) Misfire (odd)||(40) Misfire (even)||(spare)||(45) O2 sensor (odd)||(44) O2 sensor (even)||(29) ECU checksum error|
|$004A||(88) Purge valve leak||(19) Throttle pot low with MAF high||(18) Throttle pot high with MAF low||(17) Throttle pot||(14) Coolant temp sensor||(36) Injector (even)||(spare)||(34) Injector (odd)|
|$004B||(spare)||(28) Intake air leak||(spare)||(26) Mixture too lean||(spare)|
|$004C||(69) Neutral (gear selector) switch||(68) Road speed sensor||(spare)||(48) Idle valve stepper motor||(spare)||(23) Low fuel pressure|
|$004D||(spare)||(15) Fuel temperature sensor||(58) Ambiguous: low fuel pressor or intake air leak||(spare)|
|$004E||(02) RAM contents unreliable (battery disconnected)||(03) Bad checksum on battery-backed RAM||(spare)|
The analog-to-digital converter used by the 14CUX is a Hitachi HD46508. It is a 16-channel device that is capable of 8-bit conversions, 10-bit conversions, and comparator operations. The following table shows the assignment of channels:
|1||Heated screen sense||8-bit|
|4||Coolant temperature thermistor||8-bit|
|5||Gearbox neutral switch||8-bit|
|6||Air conditioner load input||8-bit|
|7||Road speed transducer||8-bit|
|9||MAF sensor voltage (open-loop fuel maps only; not used for NAS Land Rovers)||10-bit|
|10||Tune resistor (disabled in NAS Land Rovers)||8-bit|
|11||Fuel temperature thermistor||8-bit|
|12||Left O2 sensor||8-bit|
|13||O2 sensor reference voltage||8-bit|
|15||Right O2 sensor||8-bit|