woj
|
|
« on: June 17, 2018, 02:41:13 AM »
|
|
|
... or how to program your own interrupt driven input First and foremost - whatever you do with it, you take full responsibility for your doings. I will try to provide help here (hope others will too), but will not provide personal guidance through PMs (unless you pay me ). Also, there might be errors in the code, I edited it for presentation. I was thinking for a while now if I should publish this or not, came to the conclusion that there is no such big market for this specific ECU and this feature (well OK, maybe in U.S. there is, there are both Fiats 500 and E85 in the same country). Besides, the way it is presented, it is neither complete nor directly reproducible nor easily transferable to other ME-s, you have to be very inclined to repeat the exercise (and if you can - good for you, I would only appreciate if you acknowledge me if you do it commercially). I will not post ready made patches. So to cut this long intro short, this is mostly for education (something that some of you seem to be craving on this forum). Well, OK, I also want to have some of my code scrutinised, I know it works, but in the process of testing and polishing it I have seen bugs. I do not intend to give much guidance on the actual E85/E100 tuning, though I will mention some things. I am simply no specialist here, you should ask pros for help. On the other hand, there is lots of misinformation about this out there (including this forum), I will come back to this. The particular bin I worked with is attached, this is ME7.9.10 software number 1037391689 for Fiat Grande Punto, 120hp t-jet engine. And some links to the ST10 documentation you may find useful (not precisely for the CPU in this particular ECU, but close enough for the purpose at hand): http://www.keil.com/dd/docs/datashts/infineon/c167cr_um.pdfhttp://www.st.com/content/ccc/resource/technical/document/programming_manual/27/c0/48/83/94/9d/4d/45/CD00147146.pdf/files/CD00147146.pdf/jcr:content/translations/en.CD00147146.pdfHardwareSo, the task is to connect the Continental FlexFuel sensor (picture from Amazon): to the Bosch ME7.9.10 that runs the Fiat t-jet FIRE engine (picture from ECU Backup site): And provide a software patch for it. I used the small sensor, part number GM 13577429. When connected according to this scheme: it will produce a square wave 0-5V signal with the following characteristics, the frequency gives the Ethanol content reading, the pulse width gives the fuel temperature: 50Hz (20ms period) = 0% Ethanol, 150Hz (6.6(6)ms period) = 100% Ethanol, 180Hz+ -> contaminated fuel 1ms pulse width = -40 °C, 5ms pulse width = 125 °C (some sources say something about 151.25 °C ?) My first idea was to just go lazy and buy an external control module, like the Zeitronix one ( http://www.zeitronix.com/Products/ECA/ECA.shtml), to get 0-5V analog signal and feed that to the ECU through ADC, but then I though it might be worth investigating if it can be connected directly somehow. To save some money and minimise clutter in the engine bay. For that I looked through the disassembly of my bin to see if there are any unused port pins on the ST10 that could be used to hook up an interrupt procedure. There were some potential candidates, but I also wanted something with a connection to the ECU plug. After taking the board under the magnifying glass I found the following connection: Logic wise, this is exactly what the sensor needs, only there is the additional in-series 100K resistor. I consulted an electronics engineer and was told this should not matter. Out of curiosity I also looked up Fiat t-jet docs I have - the ECU pin is described as "Oil condition sensor input (unused)". OK, let's use it then. Connect the +12V and ground of the FlexFuel sensor to ignition switched power, and the signal line to ECU pin 59 on the small connector (spare ECU plug to get extra connectors might be needed, I did not have one and did a bit of a bodge job there). So, in the end, there was not even any need to open the ECU, just adding one connector to the ECU plug and tapping into some power wires. At the expense of more involved software. And obviously connect the fuel lines. I connected mine on the return line (mostly for the reason of much lower fuel pressures and less likelihood of having pressure related leaks in my experimental set up). Out there you will find advice to do it on the return line, but also on the feed line (BTW, for high power applications the flow limitation of the sensor might be an issue). From the experience so far I already know that the feed line placement would give more stable readings, on the return line air pockets are caught by the sensor in some operating conditions skewing the readings (can be remedied by software). This is the complete hooked up sensor, minus securing the fuel lines (and fastening the sensor, I still have to do this): SoftwareLet's program this thing, the proper Bosch way - let's over-engineer it a bit. I mean, let's measure the CPU clock to get the sensor input precisely (the frequency and pulse width) to one clock tick to get both ethanol content and temperature, do error detection / recovery / reporting, readout smoothing / filtering, etc. The code below has some legacy from my earlier developments, which means things can be simplified and/or omitted, but once they worked I left them as is. In retrospect, to just get this working, things can be probably simplified to a couple of lines of assembly code. But we are here to learn All addresses in the assembly code below are logical CPU addresses (so not the flash bin addresses like the direct addresses in your map pack, but that should be obvious). The memory map of ME7.9.10 is this. Internal flash is at $0000-$7FFF (as with most (all?) ST10 ME-s), the rest of the flash is mapped to $18000-$CFFFF, out of which $A8000-$BFFFF is the data area ( $A0000-$A7FFF also, but it is unused, not sure why). The big RAM is at $F0000. Placing code in some flash areas requires care. For example, $18000-$20000 is the code image for RAM only operation (flashing, recovery boot), but generally can be used if free. The region $90000-$9FFFF, even though tempting (all empty) should be left untouched - this is where the ECU stores the boot code backup during flashing. The DPP registers are initialised as follows: dpp0 is $2A (data area $A8000-$ABFFF), dpp1 is $2B (data area $AC000-$AFFFF), dpp2 is $3C ( $F0000-$F3FFF), dpp3 is $3 ( $C000-$FFFF). So these areas can be addressed through DPP registers, everything else has to be done with extp/exts. The code for the sensor will operate on several layers. To start with, we need three things: (1) initialisation procedure that will set up things, reset variables, and enable our interrupt, (2) the interrupt procedure itself for the square wave signal processing with low-level diagnosis, and then (3) the high level diagnosis / application procedure. (2) and (3) will communicate to better discover errors. This is just to get sensor reading, the map modification (fuel, ignition, etc.) is done separately, and so is extending diagnostics interface to report the Ethanol readings.
|
|
« Last Edit: June 17, 2018, 02:54:11 AM by woj »
|
Logged
|
|
|
|
woj
|
|
« Reply #1 on: June 17, 2018, 02:45:55 AM »
|
|
|
Finding memoryIn terms of free space for variables, code, and data we need the following. A 32 byte register area in IRAM for the new interrupt, an external RAM piece for other variables, flash space for our new procedures, and data area for the calibration data (this does not have to be there by any necessity, but flashing data-only changes later with the right flasher will be quicker). On top of this we will patch a couple of places in the existing code to interface with our new procedures and provide mixture dependant operation of some maps. The flash space and data space are easily found by simply looking at the bin image. Several possibilities there, I decided to bravely stick code things at the end of $18000-$20000 and data after the factory calibration data:
free_code_area const $1F000 ;; this cannot overrun $1FFF0 (boot fingerprint and checksum) free_data_area const $B7390 ;; this cannot overrun $BFB78 (checksum data start) data_area_page const free_data_area / $4000 ;; $2D
The RAM things require looking more carefully at the disassembly. Starting at $F3900 there is a chunk of big RAM available until $F3FFC, and then much larger chunk after $F5000. The first one can be directly DPP addressed, so we can save on extp-s and exts-s. Some of these RAM areas are not cleared during ignition off (I believe, did not check this carefully), but that does not matter, we take care of cleaning / resetting ourselves, so:
free_ram_area const $F3900 ram_area_page const free_ext_ram / $4000 ;; $3C
Then the 32 byte register area. Every interrupt needs its own private register area for r0-r15 (well, not every, but without it things get very hairy). Finding this requires going through the disassembly and looking for " mov $FXXX, r0" instructions (typically followed by " sctx CP, #$FXXX"). This gives you the area where the 32 byte blocks are kept, and then you can identify which 32 blocks there are unused (there is no corresponding write of the quoted form). For our bin that gives at least $F940, $F960, $FAA0, $FAC0 (there are some more). For reasons now unclear to me, I picked the third one:
context_CC9 const $FAA0
The interrupt set upSo we have the P2.9 pin to service. This corresponds to the Capture-Compare (CC) register number 9 and this is what we have to set up. Pin P2.9 needs to be set up for input, but this is already taken care of in the existing code. We want to trigger the interrupt procedure on both edge changes (remember, we want the frequency and the pulse width of the signal from the sensor) and connect it to a clock with suitable resolution - clock with overflow range larger than our longest possible period (20ms, 50Hz), but small enough range to have precision measurements. The only choice we have for CC9 to have a hardware clock capture is timers T0 and T1 (CAPCOM module 1). And we cannot change the timer setup. On this ECU they are configured in the following way in the T01CON register: T0: $4A (counter mode, pre-scaler 2) T1: $42 (duration mode, pre-scaler 2) We want duration kind, so T1 it is. Pre-scaler is 2, which for the 40MHz clock this ECU has means that the total period of this clock is 52.5ms, resolution (one tick) is 0.8us. Just what we need (if it wasn't like this, stuff would get much harder). The CC9 behaviour is set up in the second nibble of the CCM2 register:
and CCM2, #$FF0F ;; clean up config (can be probably skipped) or CCM2, #$00B0 ;; $B = %1011 - T1 timer, both edges
Another important (very!) thing is the interrupt priority. Too low - it will be pushed away by other interrupts and we will miss edge changes. Too high - it will pre-empt other important interrupts. In fact, with very high priorities the ECU crashes after connecting the sensor. Experimentally, this value seems to work fine (essentially mid range priority):
mov CC9IC, #$20
This is also a priority that is not used by any other interrupt (for this you have to scan the disassembly for writes to the other interrupt control registers), this is a good thing to have (though not necessary IIRC). We can then enable the interrupt:
bset CC9IC.6
This code so far should go into our initialisation procedure, but after some variables are initialised, so for now just the assembly statements for the interrupt initialisation. The interruptThe interrupt procedure has only one job - to provide raw sensor reading to the higher level procedure. But, since our high level procedure will execute every 100ms (could be more frequent, but really there is no point), and interrupt at least every 20ms, we can already do some signal pre-processing in the interrupt and the rest elsewhere. In the interrupt we will de-bounce the signal, validate edge alternations, validate edge periods, and do modest stepping average of the periods. The higher level procedure will then convert the period recorded in the interrupt, do filtering, and error processing. The interrupt procedure has to be as concise and quick as possible. So, for example, we will not do MDL/MDH operations in the interrupt to calculate the average, but rather do division by 2/4 with shr. The nice thing about the interrupt procedure is that it has its own private register space (the 32 bytes above). This means that whatever we store in r0 through r15 we can leave it there for the next interrupt round, we can access the registers from the higher level procedure by addressing them explicitly in IRAM (BTW, I do not recall seeing this trick done on Bosch-es, maybe didn't look hard enough, but several times on Marelli systems), and we can have our own private DPP register values to optimise RAM and data access within the interrupt. The registers will serve the following purposes: r0 - this has to be the current local stack pointer, even though we do not use the stack r1 - flags set 1 - status_flagsr7 - flags set 2 - heartbeat_flagsr8 - the clock stamp of the last edge seen (any edge) r9 - the clock stamp of the last high edge seen (the edge that starts both the pulse width and period) Otherwise, we can use them freely for temporary storage. r1/r7 separation is one of the legacy things, the flags could be squeezed into one register. In any case, r1/r7 will be IRAM accessed from the higher-level procedure:
status_flags const context_CC9 + 2*1 ;; r1 heartbeat_flags const context_CC9 + 2*7 ;; r7
Also, the notion of the high/low edge needs care. At first, before I got the actual sensor and simulated it on Arduino instead, I thought the signal is like this: ________ | 5V |_0V_|
where the first chunk is the PW (temperature) and the complete chunk is the period. So all "high" terms in the code refer to the first chunk, the "low" ones to the the second chunk. But, with the actual sensor it is the other way round, freely floating 5V pull-up gets pulled to 0V during the PW chunk. Without rewriting the code I just introduced a calibration flag to account for the other possibility (for example, where the ECU does not allow for direct connection and additional circuitry might be needed). For calculating the period and the pulse time we use two 12 byte / 6 word arrays:
org free_ram_area
period_ptr: db 0, 0 ;; # of correct period (high-to-high) reads in lower byte, wrapping index to reads in higher byte period_val: dw 0 ;; average of the last 1-4 reads dw 0, 0, 0, 0 ;; last 4 reads pulse_ptr: db 0, 0 ;; Same as above but for pulse (high-to-low) reads pulse_val: dw 0 dw 0, 0, 0, 0
|
|
|
Logged
|
|
|
|
woj
|
|
« Reply #2 on: June 17, 2018, 02:47:49 AM »
|
|
|
The interrupt procedureWe are now ready to write the interrupt service procedure. The POFF(X) is X & $3FFF (page offset), DPP(X,Y) is (X*$4000 | Y) (DPP based addressing), the meaning of the bits in r1/r7 is explained as we go:
org free_code_area
CC9_IR: mov context_CC9, r0 scxt CP, #context_CC9 ;; change to our private register area scxt DPP0, #data_area_page ;; access data area with DPP0 scxt DPP2, #ram_area_page ;; access RAM with DPP2 bmov r6.0, P2.9 ;; copy the pin state before it gets a chance to change xorb rl6, DPP(0, POFF(INV_P29_SIG)) ;; flip the edge meaning (see above) mov r2, CC9 ;; CC9 holds the time snapshot when the interrupt happened jnb r1.0, CC9_signal ;; jump if not seen high at least once mov r3, r2 sub r3, r8 ;; r3 holds the time-diff from the last edge / interrupt cmp r3, DPP(0, POFF(DEBOUNCE_TIME)) jmpr cc_ULE, CC9_IR_exit ;; bail out if the interrupt is too quick bcmp r6.0, r1.2 ;; r1.2 is the previous edge jmpr cc_EQ, CC9_IR_wrong_signal ;; wrong signal if the edges do not alternate high-low bclr r1.4 ;; clear the wrong signal flag CC9_signal: mov r11, r2 sub r11, r9 ;; r11 holds the time to the last high edge (check later if there was one) jb r6.0, signal_high ;; jump if edge is currently high jnb r1.0, low_done ;; we have low edge, if there was no high yet, there is nothing to process mov r13, #DPP(2, POFF(pulse_ptr)) mov r14, DPP(0, POFF(PULSE_MIN)) mov r15, DPP(0, POFF(PULSE_MAX)) callr check_avg_signal ;; check the pulse (temp.) to be within specified bounds, average the readings bmov r1.5, r1.7 ;; copy the possible error flag to r1.5 (pulse bound error) low_done: bclr r1.2 ;; the last edge was not high (it was low) jmpr cc_UC, CC9_IR_finish signal_high: jnb r1.0, high_done ;; we have high edge, if there was no high yet, there is nothing to process mov r13, #DPP(2, POFF(period_ptr)) mov r14, DPP(0, POFF(PERIOD_MIN)) mov r15, DPP(0, POFF(PERIOD_MAX)) callr check_avg_signal ;; check the period (E%) to be within specified bounds, average the readings bmov r1.6, r1.7 ;; copy the possible error flag to r1.6 (period bound error) bclr r1.8 ;; clear fuel contamination error flag jnb r1.7, high_done ;; period in normal bounds cmp r11, DPP(0, POFF(PERIOD_BADF)) ;; Period was outside of normal bounds, check for contaminated fuel frequency jmpr cc_UGT, high_done bset r1.8 ;; set fuel contamination error flag high_done: mov r9, r2 ;; store previous high edge time bmov r1.1, r1.0 ;; seen two highs if high seen previously (completed one read) bset r1.0 ;; seen high bset r1.2 ;; the last edge was high jmpr cc_UC, CC9_IR_finish CC9_IR_wrong_signal: bclr r1.0 ;; re-sync of the signal needed - have not seen any highs bclr r1.1 ;; have not seen any highs bset r1.4 ;; signal generally wrong CC9_IR_finish: mov r8, r2 ;; store previous (any) edge time bset r7.2 ;; heartbeat for the high level procedure - there is sensor signal CC9_IR_exit: pop DPP2 pop DPP0 pop CP reti
;; r11 has the pulse / period time to check ;; r14/r15 are the allowed bounds ;; r13 is the pointer to the structure to calculate the stepping average (see above) ;; r1 has the flags check_avg_signal: cmp r11, r14 jmpr cc_ULT, signal_out_of_bounds cmp r11, r15 jmpr cc_ULE, signal_in_bounds signal_out_of_bounds: bset r1.7 ;; set signal out of bounds flag, copied accordingly to r1.5/r1.6 in CC9_IR ret signal_in_bounds: bclr r1.7 ;; clear the signal out of bounds flag mov r14, r13 ;; maintain the original pointer in r13, use r14 as temporary jnb r1.15, check_bit_14 ;; a request from high-level to clean up the period/pulse reading structure bclr r1.15 ;; is made through bits 14 and 15 of the status_flag, check them one at a time jmpr cc_UC, reset_reads check_bit_14: jnb r1.14, no_reset bclr r1.14 reset_reads: ;; reset the reading / averaging structure to all zeros mov r4, #0 ;; this could be done properly with a loop, but for 6 words add r14, #12 ;; the amount of total instructions is roughly the same mov [-r14], r4 mov [-r14], r4 mov [-r14], r4 mov [-r14], r4 mov [-r14], r4 mov [-r14], r4 no_reset: ;; calculate the average of what we already have mov r4, [r14] cmpb rl4, #4 jmpr cc_EQ, max_readouts addb rl4, #1 max_readouts: movbz r6, rl4 ;; get the number of reads so far as a word in r6 movbz r5, rh4 ;; get the index to the read array as a word in r5 shl r5, #1 addb rh4, #1 ;; update the index, wrap if necessary (ring buffer) cmpb rh4, #4 jmpr cc_NE, no_wrap movb rh4, #0 no_wrap: mov [r14], r4 ;; update the number of reads and index add r14, #2 ;; r14 points to the new averaged value to be stored add r13, #4 add r13, r5 ;; r13 points to the new read position in the array mov [r13], r11 ;; store the new read mov r13, r14 add r13, #2 ;; r13 points to the beginning of the array with 4 reads cmp r6, #1 jmpr cc_NE, two_or_more mov [r14], r11 ;; only one read so far, just use this read ret two_or_more: mov r3, r13 ;; use r3 to be able to do post-increment in one instruction mov r15, [r3+] ;; we have at least two reads, r15 stores the average of them add r15, [r3+] ;; they are now checked to be in bounds, no need to check for overflow (or? ) shr r15, #1 cmp r6, #2 jmpr cc_EQ, store_last ;; only two reads, r15 is what we need for the current averaged values cmp r6, #4 jmpr cc_EQ, four_values mov r6, [r3] ;; we have three values, copy the third to r6 jmpr cc_UC, add_last four_values: mov r6, [r3+] ;; we have four values, make r6 hold the average of the last two add r6, [r3] shr r6, #1 add_last: add r15, r6 ;; average r15 (the avg. of the first two) with either the third value shr r15, #1 ;; or the avg. of the last two store_last: mov [r14], r15 ;; store the final result ret
Finally, we can install our interrupt in the interrupt vector (in the factory code this is a self loop - the interrupt is never serviced):
org $0064 CC9INT_handler: jmps CC9_IR
|
|
« Last Edit: June 18, 2018, 01:21:11 AM by woj »
|
Logged
|
|
|
|
woj
|
|
« Reply #3 on: June 17, 2018, 02:48:20 AM »
|
|
|
And of course we need the calibration data that we used in the code:
org free_data_area
INV_P29_SIG: db $01, $00 ;; invert the signal bit, pad DEBOUNCE_TIME: dw 312 ;; ignore consecutive pulses within this time range: X * 0.8us = 249.6 us -> 0.25ms
;; Absolute value ranges for error detection on edge duration PULSE_MIN: dw 1000 ;; ~<1ms PULSE_MAX: dw 6500 ;; ~>5ms PERIOD_MIN: dw 8000 ;; ~>150Hz PERIOD_MAX: dw 27000 ;; ~<50Hz PERIOD_BADF: dw 7000 ;; ~<180Hz
This is first step, it gets us to the point where we have the high-to-high 4-value-averaged time reading stored in period_val, and high-to-low 4-value-averaged time reading stored in pulse_val, effectively raw ethanol content and fuel temperature. And some error flags in r1/ status_flags. This now all needs to be converted to more digestible format and dealt with for error reporting, filtering, and the dynamics of the fuel system. And then for map alterations. But for now, I leave you bunch with this to digest and waiting for opinions whether I should continue
|
|
|
Logged
|
|
|
|
jcsbanks
Full Member
Karma: +19/-3
Offline
Posts: 146
|
|
« Reply #4 on: June 17, 2018, 03:59:50 AM »
|
|
|
Nice work. There is a shortage of these skills and commercially viable stuff to do on later platforms for sure.
|
|
|
Logged
|
|
|
|
370rx
Jr. Member
Karma: +0/-0
Offline
Posts: 39
|
|
« Reply #5 on: October 03, 2018, 06:18:03 AM »
|
|
|
A lot of work has been done!
|
|
|
Logged
|
|
|
|
woj
|
|
« Reply #6 on: October 04, 2018, 02:31:15 PM »
|
|
|
And it continues, I have really shitty time (because of lack of it) to properly calibrate cranking and transients when cold.
|
|
|
Logged
|
|
|
|
370rx
Jr. Member
Karma: +0/-0
Offline
Posts: 39
|
|
« Reply #7 on: October 05, 2018, 09:37:07 AM »
|
|
|
when the outside temperature is from 3 to 15 degrees I'm experiencing the same problems as you (you wrote about them in the topic Re: Porsche Cayenne ME7.1 RE85 half warm start problem), and gasoline 95,98. This problem is many of my friends on 1.4 t-jet, but not as obvious as you.
|
|
|
Logged
|
|
|
|
MyTunes
Full Member
Karma: +9/-5
Offline
Posts: 83
|
|
« Reply #8 on: November 10, 2018, 09:40:28 PM »
|
|
|
And it continues, I have really shitty time (because of lack of it) to properly calibrate cranking and transients when cold.
Does the MED9 have the ability to do multiple spark like the ME7? I thought that this had helped some people on E-85 cold startup?
|
|
|
Logged
|
|
|
|
woj
|
|
« Reply #9 on: November 11, 2018, 02:02:10 AM »
|
|
|
Don't understand me wrong - I get a start on the first key all the time, down to 0*C, we did not have colder days than this yet. It's just that it is not as clean and predictable as the gasoline start. Sometimes it turns a lot without catching and then catches on very nicely hitting the target RPM without hesitation (that's probably the sweet spot I want to have), sometimes it catches on much quicker (too quick?), but hesitates and chokes a bit before hitting the target RPM. My ECU has multi-spark enabled by default (if I checked everything right) and I played back and forth with FKSTT, FKSTT reduce factor, small start ignition offsets (though so far one way, I also have to try small retards). The WORST part of all this is that get one (two if I happen to let the car stay for most of the day) try per day, each day different temp. And my AFR controller shuts down during cranking.
Cold transients are a different story, now I have a tank of E15 out of necessity, and I did several cold logs to see how it should be. My E75 pattern seems to match on negative transients, so I should be fine there, yet on acceleration transients I run horribly lean (and get occasional chokes and hesitation) on E75 compared to E15. So either I still need to go much higher with KFBAKL, or start playing with the reduction factor in ZBAKM. But it's all very suspicious, because the log figures tell me I am *pouring* fuel on accelerations rather than injecting it, perhaps I went too far and the lean condition is caused by flooding. And the same problem of one experiment per day.
It seems that now they have introduced a true winter blend of E85 in the country, I judge this from a visibly increased price I see displayed. What exactly is the blend I will get to see once I empty the tank sufficiently.
|
|
|
Logged
|
|
|
|
Cheekano
Full Member
Karma: +4/-1
Offline
Posts: 60
|
|
« Reply #10 on: August 20, 2023, 09:41:08 PM »
|
|
|
Thank you for this post. It has really helped me with my current plan to implement flex fuel. May I ask how you were able to implement scaling the KRKTE?
|
|
|
Logged
|
|
|
|
prj
|
|
« Reply #11 on: August 21, 2023, 12:55:20 AM »
|
|
|
I'll just leave this comment here, maybe someone thinks before doing it. Zeitronix has this available: https://zeitronix.com/Products/ECA/ECA2CAN.shtmlUsing this piece of hardware you can put the ethanol sensor signal on CAN, and then you can read it from the ECU. Much better idea than messing with the PCB, inputs, interrupts etc on ECU's that have no native flex fuel sensor support. I have done this successfully on MED17, but it can be done just as well on ME7.
|
|
|
Logged
|
|
|
|
Cheekano
Full Member
Karma: +4/-1
Offline
Posts: 60
|
|
« Reply #12 on: August 21, 2023, 07:31:42 AM »
|
|
|
I'll just leave this comment here, maybe someone thinks before doing it. Zeitronix has this available: https://zeitronix.com/Products/ECA/ECA2CAN.shtmlUsing this piece of hardware you can put the ethanol sensor signal on CAN, and then you can read it from the ECU. Much better idea than messing with the PCB, inputs, interrupts etc on ECU's that have no native flex fuel sensor support. I have done this successfully on MED17, but it can be done just as well on ME7. it I have a similar hardware but not the CAN version. Plan is to wire it via O2 sensor uushk/ushk as input to the ECU. I'm more curious how to integrate a factor/multiplier for the KRKTE. I've looked at FRs of some flex fuel vehicles and it seems it is implemented elsewhere as diagram for KRKTE is showing as standard. I've seen ASM on here for map switching which is straight forward but there's no blending/interpolation going on.
|
|
|
Logged
|
|
|
|
prj
|
|
« Reply #13 on: August 21, 2023, 10:35:03 AM »
|
|
|
So write your own blending/interpolation logic... you can use the built in ECU map lookup commands. You're going to need to do a ton of this. On port injected engines cold start needs a huge amount of work. And you need to do 3 axis ignition, target load and constant maps, because ignition does not really change much after E40-E50.
|
|
|
Logged
|
|
|
|
woj
|
|
« Reply #14 on: August 24, 2023, 03:35:59 AM »
|
|
|
I'll just leave this comment here, maybe someone thinks before doing it. Zeitronix has this available: https://zeitronix.com/Products/ECA/ECA2CAN.shtmlUsing this piece of hardware you can put the ethanol sensor signal on CAN, and then you can read it from the ECU. Much better idea than messing with the PCB, inputs, interrupts etc on ECU's that have no native flex fuel sensor support. I have done this successfully on MED17, but it can be done just as well on ME7. I am not here often, recently almost never in fact, but I can throw in my 5 cents, since I started this topic. Doing it the CAN ways indeed seems most appropriate and more scalable across different ECUs, though I am not sure if adding CAN signals to the ECU is that much easier than what I did, in both cases you still do need to know the ECU code quite well and know your way around ASM. As for "messing with PCB", not really sure what you mean, studying the PCB to figure out which of the undocumented ECU connector pins to use is hardly "messing", for implementing the actual project I did not even have to take the ECU out of the car, let alone open it. I did have to wire up a couple of things into the harness, but that still stands for adding CAN based devices. My other idea at the time, should I have failed with my approach, was to use one of the available 0-5V analog inputs on my ECU, do not remember any more what these were originally for, but they were there. Hell, I would have probably gone that route right away if I had the FlexFuel module, not just the sensor. Zeitronix provides 0-5V output too, and I guess, from the programming point of view, that is the easiest way. Anyhow, the project has been put on the shelf long time ago, first, the prices of E85 made it totally pointless, second, if done for increased power reasons, I totally grew out of trying to kill myself in the car stage of life As for the Cheekano questions about scaling / interpolating. What prj says, you need to know what you already have in the ECU and use that. Studying FRs will not give you much, you need get hold of the actual procedures in the code used for all that. For the particular question about krkte, my hookup procedure (replaces write to krkte) is roughly this: mov r13, DPP(fuel_scaler) mov r12, r4 calls mult_factor extp #PAGE(rkte), #1 mov POFF(rkte), r4
Based on the ethanol content and fuel temperature, fuel_scaler is my variable read from maps using stock map reading functions, and mult_factor is a common procedure found all over the ECU that effectively does r4 = (r12*r13)/0xFFFF. As the dependency of krkte on ethanol content is essentially linear, here you could skip the whole E/temp map and just use ethanol content (but properly scaled / shifted) for mult_factor. But like prj said, not for ignition. Long story short, you need to know what you are doing, be fluent with ASM, and there is a lot to be done. Roughly scanning through my source, I have 18 hoop up functions just to modify the original ECU parameters, plus a ton of other things to prepare data for these hook ups.
|
|
|
Logged
|
|
|
|
|