2011年7月4日作成 2011年7月6日更新
噴射処理は燃料の噴射時間が正確であればよく、噴射開始のタイミングには神経質になる必要はありません。タイマーで指定した時間だけインジェクタに通電してやるだけでよいですね。
純正ECUとだいたい同じ回路になっています。スナバ回路に 1uF 50V のセラミックコンデンサを2個直列にして使用したのですが、ここには瞬間的に数百ボルトの電圧が掛かるので、本当ならもっと耐圧の高いコンデンサを使うべきです。
純正ECUでは 684K100
と書かれた、大きなコンデンサが使われていますが耐圧の読み方がよくわかりません。容量は 0.68uF で良いと思います。
このままだとFETが壊れるかもしれませんが、壊れたらFETを交換すれば良いのです。
点火処理と同じように、エンジンの回転数と吸気量に応じて噴射量を調整するために、噴射マップ(テーブル)を用意しています。
inj_map.h
// 噴射マップ
// 噴射時間をマイクロ秒単位でセット 10000 で 10000マイクロ秒 = 10ミリ秒
//
// 格子の間は補完するんだし、マップはミリ秒単位の8ビットでいいんじゃね?とも思うんだけど、
// メモリー不足なわけでもないから16ビットで
const unsigned short INJ_MAP_INITIAL[16][25]=
{ /* 空気量小 空気量大 */
//噴射マップ 2011/06/26 15:48:38
/* 240, 230, 220, 210, 200, 190, 180, 170, 160, 150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0 */
/* 500*/ 2691, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2806, 2815, 3780, 5046, 6003, 6722, 8078, 9518,10360,10360,10467,10467,10571,10467,10467,10467,
/*1000*/ 2137, 2137, 2134, 2172, 2281, 2387, 2300, 2300, 2300, 2182, 2428, 2363, 2897, 3178, 3550, 4474, 5092, 8224, 8170,11431,10465,10571,10467,10467, 9859,
/*1500*/ 2137, 2137, 2137, 2137, 2137, 2137, 2137, 2201, 2245, 2128, 1535, 1977, 1647, 2290, 2748, 2943, 3761, 4294, 6239, 8800,11135,10924,10924,10921,10924,
/*2000*/ 2108, 2108, 2108, 2105, 2108, 2105, 2108, 2108, 2091, 1649, 1217, 1784, 1597, 1823, 2179, 2471, 3083, 3640, 4879, 6593, 9257, 9506,10643,10726,10729,
/*2500*/ 2108, 2108, 2108, 2108, 2105, 2108, 2108, 2108, 2105, 2083, 1842, 1813, 1775, 1890, 1830, 2081, 2526, 3076, 3857, 5330, 7854,10341,10878,10726,10726,
/*3000*/ 2108, 2108, 2108, 2108, 2108, 2108, 2105, 2108, 2108, 2108, 1906, 2003, 1748, 1997, 1862, 1643, 2102, 2522, 3488, 4265, 6299, 9700,11078,10726,10729,
/*3500*/ 2105, 2108, 2105, 2108, 2108, 2105, 2108, 2108, 2108, 2108, 2023, 2002, 1999, 2035, 2031, 2231, 2120, 2254, 2904, 3598, 5709, 9154,10788,10729,10729,
/*4000*/ 2108, 2105, 2108, 2105, 2108, 2105, 2105, 2105, 2108, 2108, 2003, 2065, 2065, 2023, 2140, 2255, 2644, 3157, 4054, 5211, 7000, 8121,11057,10732,10732,
/*4500*/ 2112, 2108, 2112, 2105, 2108, 2108, 2105, 2108, 2108, 2105, 2083, 2048, 2041, 2062, 2064, 2169, 2662, 2949, 3502, 4471, 6471, 9216,10287,10758,10758,
/*5000*/ 2204, 2208, 2208, 2204, 2204, 2204, 2208, 2208, 2208, 2208, 2204, 2119, 2185, 2185, 2208, 2304, 2709, 3072, 3702, 4882, 7020, 9216,10351,11152,11155,
/*5500*/ 2236, 2236, 2233, 2233, 2236, 2233, 2236, 2236, 2233, 2236, 2213, 2236, 2233, 2213, 2168, 2252, 2575, 2926, 3593, 4589, 6104, 9504,10321,11289,11289,
/*6000*/ 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2191, 2233, 2191, 2124, 2413, 2819, 3392, 4355, 5809, 9600,10063,11948,11948,
/*6500*/ 2387, 2384, 2387, 2384, 2384, 2384, 2387, 2384, 2384, 2387, 2384, 2363, 2336, 2358, 2310, 2237, 2461, 2904, 3516, 4518, 6409, 9600, 9592,12012,12012,
/*7000*/ 2432, 2406, 2406, 2406, 2403, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2237, 2444, 2822, 3420, 4387, 6236,10201,12000,11727,12476,
/*7500*/ 2432, 2406, 2406, 2406, 2403, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2444, 2822, 3420, 4387, 6236,10201,12000,12476,12476,
/*8000*/ 2432, 2406, 2406, 2406, 2403, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2406, 2444, 2822, 3420, 4387, 6236,10201,12000,12476,12476
};
(例)純正O2センサーの出力でフィードバック補正を掛けて、全域でほぼ理論空燃比になっているマップ
点火処理と同様に4点間の補完を行って、噴射時間を決定しています。
水温や吸気温が低い時には、ガソリンが気化しにくいため噴射量を増やしてやります。
temp_table.h
// 水温補正
const unsigned char WATER_CORRECT_TABLE[13]=
{
/* -40 -30 -20 -10 0 10 20 30 40 50 60 70 80 */
156,156,156,145,129,121,110,106,103,102,101,100,100
};
temp_table.h
// 吸気温補正
const unsigned char AIR_CORRECT_TABLE[13]=
{
/* -40 -30 -20 -10 0 10 20 30 40 50 60 70 80 */
120,119,117,115,113,111,109,107,105,100,100,100,100
};
intervaltimer.c
一秒ごとに温度を計測してから実行
// 水温補正係数計算
if(WATER_TEMP<(70+50)) // 70℃以上で補正なし
{
b=(WATER_TEMP+40-50)/10;
a=WATER_CORRECT_TABLE[b];
b=WATER_CORRECT_TABLE[b+1];
WATER_CORRECT=a+((b-a)/10*(WATER_TEMP%10)); // 2点間の補完
}
else
{
WATER_CORRECT=100;
}
// 吸気温補正係数計算
if(AIR_TEMP<(70+50)) // 70℃以上で補正なし
{
b=(AIR_TEMP+40-50)/10;
a=AIR_CORRECT_TABLE[b];
b=AIR_CORRECT_TABLE[b+1];
AIR_CORRECT=a+((b-a)/10*(AIR_TEMP%10)); // 2点間の補完
}
else
{
AIR_CORRECT=100;
}
injection.c
a = 噴射時間
// 水温補正
b=WATER_CORRECT; // 水温補正量加算
// 吸気温補正
b=b+AIR_CORRECT-100; // 吸気温補正量加算
a=a*b/100; // 補正量を掛ける
かけ算しているだけなので簡単ですね。
アクセルを踏み込んだ時、エアフローメーターで計測したよりも多くの空気がシリンダーに入って(サージタンクの容量や吸気管の長さやセンサーの反応速度のせいかな?)空燃比が薄くなりますので燃料を増量してやります。キャブレターにも加速ポンプって付いてますよね、それと同じことをプログラムで行います。
理論空燃比付近で運転している場合、加速時に 10% 程度燃料を増量してやるとレスポンスがよくなるようです。
セッティング中で噴射マップを作っている段階では、加速補正を行わないように補正量を 100 にしておくとよいでしょう。
injection.c
a = 噴射時間
// 加速補正
// 前回よりも吸気量が大きかったら、加速増量補正を行う
if((INJAIR-PreviousValue)>ACCELCOMP_TH) ACCELCOMP=ACCELCOMP_VAL;
PreviousValue=INJAIR;
if(ACCELCOMP!=100)
{
a=a*ACCELCOMP/100;
if(ACCELCOMP>100) // 増量補正の時
{
ACCELCOMP=ACCELCOMP-ACCELCOMP_RDC; // 補正量を減らす
if(ACCELCOMP<100)ACCELCOMP=100;
}
if(ACCELCOMP<100) // 減量補正の時
{
ACCELCOMP=ACCELCOMP+ACCELCOMP_RDC; // 補正量を減らす
if(ACCELCOMP>100)ACCELCOMP=100;
}
STATUS|=SFUELPOWINC; // 加速補正フラグセット
}
else
{
STATUS&=~SFUELPOWINC; // 加速補正フラグクリア
}
燃料をカットした後の噴射再開で、徐々に噴射するために減量補正もできるようになっています。
inj_map.h
// 始動時噴射マップ(×10us)
// 吸気量関係無し、水温で変わる
//
const unsigned short INJ_MAP_START[13]=
{/* -40 -30 -20 -10 0 10 20 30 40 50 60 70 80 */
7128,7128,7128,6288,4246,2966,2208,1777,1411,1000, 600, 400, 400
};
injection.c
time = 噴射時間[マイクロ秒]
time=getinj(RPM,(AIR>>8)); // 通常時噴射時間読み出し
if(IN_START==1) // スターターが回っている時
{
starttime=getstart(RPM,(AIR>>8)); // 始動時噴射時間読み出し
timer=TIMER+STARTTIMER; // スターターを止めてから始動モードをやめるまでの時間
STATUS|=SSTART; // スターターが回っていたら、スタートフラグセット
STATUS|=SO2ERR; // O2センサー異常フラグセット センサーの正常動作が確認できてからクリアする
}
if(IN_IDL==1)
{
// スロットルが開いていたら、チョークしない
STATUS&=~SSTART; // スタートフラグクリア
}
if((STATUS&SSTART)!=0) // スタートフラグが立っているとき
{
if(starttime>time)
{
time=starttime; // 通常時の噴射時間を捨てて、始動時の噴射時間をセット
if(TIMER>timer) starttime-=500; // 噴射時間を少しづつ減らす
}
else
{
STATUS&=~SSTART; // スタートフラグクリア
}
}
if(WATER_TEMP>(60+50))
{
// 水温60度以上なら、チョークをすぐに終了する
STATUS&=~SSTART; // スタートフラグクリア
}
// クランク2回転より長い間噴射しない(開弁率100%以内にする)
// 2回転する時間 2*60*1/RPM[s]=2*60*1/RPM*1000000[us]
// =120*1000000/RPM
{
unsigned int temp;
temp=(120000000/RPM)-100; // 噴射しない時間を100usは確保
if(time>temp) time=temp;
}
INJACCUM+=time; // インジェクタ開弁時間積算[us]
MONINJ=time/10; // モニタ用
time=(time*100)/((64*100)/(IOCLK/1000000)); // 秒数からタイマーにセットするカウント数を計算
if(time>0xFFFF) time=0xFFFF; // けっこうでたらめに補正かけてるから、オーバーフロー対策
クランクアングルセンサー Ne の立ち下がりで、点火処理を実行した後に噴射処理を実行しています。
となります。点火処理よりも簡単になります。
injection.c
time = 噴射時間(タイマーにセットするカウント数)
if(((STATUS&
(SFUELCUT // 燃料カットフラグが立っていたら噴射しない
|SREV))==0) // レブリミットフラグが立っていたら噴射しない
&& (IGf!=0)) // 点火確認信号が確認できない時(IGfが0の時)は噴射しない
{
switch(CYLINDERS)
{
case 2: // 1,3 シリンダ燃料噴射
MTU25.TSTR.BIT.CSTU=0; // タイマー停止
INJ1=0;
MTU25.TCNTU=0;
MTU25.TGRU=time;
INJ1=1;
MTU25.TSTR.BIT.CSTU=1; // タイマースタート
//if(IGf>0)IGf--; //次の噴射が近いからここではIGfをいじらないほうがいいかも
break;
case 3: // 2,4 シリンダ燃料噴射
MTU25.TSTR.BIT.CSTV=0; // タイマー停止
INJ2=0;
MTU25.TCNTV=0;
MTU25.TGRV=time;
INJ2=1;
MTU25.TSTR.BIT.CSTV=1; // タイマースタート
//if(IGf>0)IGf--;
IGf=0;
break;
}
STATUS|=SINJF; // 噴射処理しましたフラグセット
}
injection.c
// エンジンブレーキで燃料カット
if(((STATUS&SIDL)!=0) && (REV_FUELCUT<RPM))
{
STATUS|=SFUELCUT; // スロットルIDLで回転が高いと燃料カットフラグセット
ACCELCOMP=INJRESUME; // 噴射再開増減量補正
set_iscv(INJRESUMEISCV); // 噴射再開のためISCVを開いておく
}
if(REV_FUELCUT_CLR>RPM) STATUS&=~SFUELCUT; // 回復回転数以下になったら、フラグクリア
if(IN_IDL==1)
{
STATUS&=~SFUELCUT; // スロットルがアイドル位置から離れても、フラグクリア
STATUS&=~SIDL;
}
injection.c
// レブリミットで燃料カット
if(REVLIMIT<RPM)
{
STATUS|=SREV; // レブリミットを上回ったらフラグセット
ACCELCOMP=INJRESUME; // 噴射再開増減量補正
}
if(REVLIMIT_CLR>RPM) STATUS&=~SREV; // 回転が下がってきたらフラグクリア
パラメーターの設定で、純正ECUと同じように 7200 RPM で燃料カット、7000 RPM で噴射再開であれば、燃料カットから噴射再開まで時間が空くので問題無いと思いますが、燃料カット後すぐに噴射を再開するようにすると、微妙に薄い空燃比で燃焼が続いてしまいピストンが溶けてしまうかもしれません。そのような時には点火もカットするようにすると良いでしょう。
点火をカットすると「マフラーから火を噴くかも」と思って、今のところ燃料だけをカットするようにしています。
点火確認信号が出ていないときには噴射しないようにするために、点火確認信号で割り込みを掛けて信号が出ているかを確認します。
injection.c
void interrupt_IGf(void)
{
unsigned short temp;
IGf=1; // 寿命1 点火確認信号が途切れたら燃料噴射しない
// IRQ割り込みフラグは、割り込みが受け付けられた時点で自動的にクリアされる
// 念のため割り込みフラグクリア
while(IGf_IRQF==1) IGf_IRQF=0;
}
('A`)