Saturday, December 28, 2013

Working like a regular car

In order to be practical I need the final version of my ECU to work with no thought or effort from the driver (me). I want to simply turn the key and have my car start. For the ECU to work it must have the engine tune data available. Where it previously was only provided by my laptop over a serial interface, it can now be written and read from the built in EEPROM memory. EEPROM memory will hold data even when power is disconnected from the ECU, but is much slower than RAM. When power is applied to the ECU, the data in the EEPROM is loaded into RAM so it can be quickly accessed when needed.

This works fine for now, but there is only 1KB of storage and I'm already using half. Later I'll need more data recorded, which might not fit in 1KB. The only other non volatile storage in the chip is the Flash program memory. It's much bigger at 64KB. The Flash storage is where the program is recorded, but I should be able record tune data into the unused areas.

Closed Loop Mode
Nearly all fuel injected cars will use a slightly rich air/fuel ratio while accelerating, under heavy load, or high RPM. This is based on values recorded in a fuel map and is called open loop mode. This allows for fast adjustments as engine load and speed changes, gives better power and helps prevent pinging(knocking). The disadvantage here is decreased fuel efficiency and more pollution.

While under light, steady load, such as when idling or cruising at a constant speed, closed loop mode is used. In closed loop mode the air/fuel ratio is continuously adjusted as close as possible to the ideal ratio. Using an oxygen sensor, the current ratio is determined by measuring the amount of oxygen in the exhaust gases. Voltage goes up when the engine is running rich, and down when lean. Small adjustments are made to the injector duty cycle based on measurements of this voltage. The end result is that less fuel is required and the function of the catalytic converter is improved. Unfortunately it does not function if it's cold, so cannot be used as soon as the car is started. And compared to a MAP sensor is much slower to respond to changes, which makes it unsuitable for situations where a sudden change in engine speed or load occurs.

Wednesday, December 4, 2013

When I grow up, I want to be a real ECU.

Mmmm sparodic updates...

I've made a lot of changes since my last post and I'm much closer to having something I can drive daily.

MAP Sensor
I've ditched the hotwire setup for a 2 bar MAP sensor. What this means is that instead of measuring the air flow, I am measuring the air pressure. It complicates things, but there's a few reasons I went for the MAP sensor. With the hotwire sensor I had trouble figuring out how it'd be properly mounted and how I'd fit the existing (or make a replacement) air filter box. I also had the problem of very high air flows not being measured accurately. Higher voltage from the hotwire signal = more airflow and I was getting close to 5v (max voltage) before the turbo was even fitted. The MAP sensor can be anywhere as long as I can reach a vacuum line to it. It's also much smaller and easier to mount.
MAP sensor

The difficult part comes in software. In order to properly calculate the injector pulse width, you need to know how much air is flowing into the engine (the total mass of air). The MAP sensor only gives me the manifold air pressure. An increase in air pressure does mean more air is flowing into the engine, but an increase in RPM also means more air is flowing into the engine. Now instead of a 1D fuel map, I have a 2D fuel map. The first dimension related to air pressure and the 2nd related to RPM. I now have 256 data points where I used to have 16. The code needs to calculate the injector pulse width based on how far the current values (pressure and RPM) are from their nearest data points. It does a sort of "proportional averaging" or "local linearisation" (seriously I don't know what to call it) to get the final result. I'm using a lot of floating point numbers and there are a lot of calculations to be done!
Small portion of tuning data (top/left numbers). Also, yes! I know RPM isn't a percentage!

I'll also be doing the same thing for ignition timing. More advance as RPM increases, and less advance as air pressure increases.

Processing time
My complicated fuel map gave me a new problem. And that's required processing time. Engine peaked at 2200 rpm now, not because the tuning was poor, but because the next injector pulse width could not be calculated in time. I ended up confirming the duration of processing time by outputting the timer values at the beginning and end up the interrupt handler. The difference was the total processing time.

I'm using the free XC8 compiler to compile my C code. The free version has "limited optimisations" which result in the compiled code being slow to execute. The "standard" and "pro" editions are far too expensive for me to buy. Finally, there is SDCC a free and open source C compiler for PIC (and other) microcontollers, but is appears that my micro PIC18F46K80 is not properly supported yet. That leaves the following options:
  • Write optimised in-line assembly for the slowest sections of code
  • Crank up the MHz
I took the easy way out. Using the 4x PLL clock multiplier i'm now running the clock at 64MHz from 16MHz. This had the side effect of speeding up the timing for all the mcu peripherals (timers, USART). But I could fix this by adjusting some of the timer clock dividers and switching to the now 4x faster baud rate. Finally, in the case of the timer user for cam position timing, I used a seperate microcontroller PIC16F88 who's sole purpose was to provide a 500kHz signal for just that timer. Otherwise, only a 32.768 kHz watch crystal can be used (too slow). There's probably a way to provide a 500kHz clock with a few discreet components as well but it's much easier to write a couple of lines of code than it is to make up a new circuit.

The second micro could also be used for for secondary functions like turbo boost controller, or controlling the EGR valve. By removing these functions away from the main mcu I can simplify the code that it runs. Simpler code generally runs faster and is less bug-prone.

I've also moved the entire circuit from the broadboard to a veroboard. Soldered connections seem to be more reliable, except kynar wire is frikin fragile! There were some changes to the electrical characteristics of the circuit, especially with the 7805 regulator. I had to add a cap on the input side to smooth out the supply voltage. I think this has to do with the relatively high capacitance of the breadboard.
Messy Breadboard

I wasn't really sure of the best way to lay out the board, I just tried to keep the wires short. I made a couple of mistakes (miscounting pins) so I had to make some additional changes and the final result is a little messier than I wanted. Still, it is much better than the breadboard setup.
mmm blobbly solder

I also learned a very important lesson about proper connections. I spent a full day trying to fix an issue with serial communication between the mcu and my laptop. I thought the USB>serial adapter had died. I spent the night configuring a spare old laptop that had a serial port on it, when I should have realised the 4 pin header connecting to the MAX232 chip was damaged!! I would've liked to watch the signals coming in and out of the MAX232 chip with a scope, but I can't tell much with my analog scope and my cheapo USB digital scope is screwed.

So now I can start the car
Nope! for some reason the micro is acting a bit weird. I wasn't sure what version of my program was on it at the time, but I can't reprogram it. I know my programmer (PICkit 3) is working because I can program other chips. I'm hoping it's just a connection issue with the ICSP interface, but it looks OK. Could I have damaged the micro with either heat from the soldering iron or ESD? I'm not looking forward to replacing this micro on the board. So many pins. If only I had gone with a ZIF socket!

Edit on 2013-12-04: mcu is deaded. I can program a spare mcu on a breadboard, but if I replace it with the one desoldered from the veroboard, it will fail to program.