2011年7月4日作成 2011年7月4日更新
点火はECUの仕事の中で、一番正確さが必要になる制御です。燃料の噴射は噴射時間だけ正確であればよく、噴射タイミングはばらついても問題ありませんが、点火信号はタイミングが重要になります。他の処理よりも優先して処理するのが良いでしょう。
10,000RPM で回転しているときに、クランクが1度回る時間が (60[秒] / 10,000[RPM]) / 360[度] = 16.7マイクロ秒 になりますが、これは今回使ったマイコンから見れば命令を400回も実行出来る、十分長い時間なので楽勝です。
点火信号が正しく出るようになれば、もうECUが完成したも同然です。
純正ECUの回路とほぼ同じで、トランジスタをFETに置き換えました。もっと小さなFETでよいのですが、使用する部品の種類を減らすために、インジェクタの駆動FETと同じものを使っています。
電源投入時など、マイコンの出力がハイインピーダンスになっている時に点火信号を L に固定するために、FETのゲート端子はプルダウンではなく、プルアップしておくのが良いでしょう。
になります。
エンジンの回転数と負荷(吸気量)に応じて点火時期を調整するために、点火マップ(テーブル)を用意しています。エンジンの制御では一般的な手法かと思います。
ign_map.h
// 点火時期の初期値の例 エンジンシミュレーターを使って純正ECUを動かし観測した点火時期です
const unsigned char IGN_MAP_INITIAL[16][25]=
{ /* 空気量小 空気量大 */
/* 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*/ 13, 13, 13, 13, 13, 13, 14, 14, 16, 19, 19, 17, 16, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 9
/*1000*/, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 16, 19, 20, 19, 17, 10, 4, 3, 3, 3, 3, 3, 9, 9
/*1500*/, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 22, 27, 27, 26, 20, 11, 11, 11, 11, 11, 8, 8
/*2000*/, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 25, 28, 29, 28, 24, 15, 15, 15, 15, 8, 8
/*2500*/, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 30, 28, 26, 27, 34, 34, 31, 24, 19, 19, 19, 8, 8
/*3000*/, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 36, 32, 23, 23, 23, 7, 7
/*3500*/, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 33, 29, 26, 26, 7, 7
/*4000*/, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 37, 32, 23, 22, 7, 7
/*4500*/, 39, 40, 40, 39, 39, 40, 40, 39, 39, 40, 40, 39, 39, 40, 40, 39, 39, 40, 41, 39, 34, 29, 29, 7, 6
/*5000*/, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 33, 29, 29, 6, 6
/*5500*/, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 36, 33, 30, 28, 6, 6
/*6000*/, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 39, 36, 31, 30, 5, 5
/*6500*/, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 37, 32, 30, 5, 5
/*7000*/, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 38, 33, 30, 4, 3
/*7500*/, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 38, 33, 30, 4, 3
/*8000*/, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 38, 33, 30, 4, 3
};
吸気量 | |||
---|---|---|---|
30 | 40 | ||
回転数 | 1000 | 10 | 4 |
1500 | 26 | 20 |
例として上のようなテーブルがあった時、吸気量が33、回転数が1200だった場合の値を求めてみましょう。
まず吸気量30から40の間を補完します
1000[RPM]の時
吸気量が10増えると点火時期は 4-10 = -6増える(6減る)
吸気量が1増えると点火時期は (4-10)/10 = -0.6増える(0.6減る)
1000[RPM]で吸気量が33なら、10+(((4-10)/10)*3) = 8.2
同じように
1500[RPM]で吸気量が33なら、26+(((20-26)/10)*3) = 24.2
吸気量が33の時、1000[RPM]と1500[RPM]それぞれの点火時期を求めることが出来ました。
吸気量 | ||
---|---|---|
33 | ||
回転数 | 1000 | 8.2 |
1500 | 24.2 |
次に回転数1000と1500の間を補完します
回転数が500増えると点火時期は 24.2-8.2 = 16増える
回転数が1増えると点火時期は (24.2-8.2)/500 = 0.032増える
1200[RPM]の時の点火時期は 8.2+(((24.2-8.2)/500)*200)=14.6
吸気量が33、回転数が1200の時の点火時期は 14.6 と求めることが出来ました。
実際にコンピュータで計算させる場合、小数点以下を無視すると処理が速くなるので、計算の順番を少し入れ替えて、小数点以下を無視しても誤差が少なくなるようにしています。
ignition.c
static unsigned char getign(unsigned short rpm, unsigned char air)
{
unsigned char index_a1,index_a2,index_r1,index_r2;
int a,b,c,d;
air=255-air; // テーブルを見やすくするため空気量の並びを逆にしている
if(rpm>8000) rpm=8000; // 範囲のチェック
if(air>10)
{
index_a1=air/10-1;
if(index_a1==24)index_a2=index_a1;
else index_a2=index_a1+1;
}
else
{
index_a1=0;
index_a2=0;
}
if(rpm>500)
{
index_r1=rpm/500-1;
if(index_r1==15)index_r2=index_r1;
else index_r2=index_r1+1;
}
else
{
index_r1=0;
index_r2=0;
}
a=IGN_MAP[index_r1][index_a1];
b=IGN_MAP[index_r1][index_a2];
a=a+((b-a)*(air%10)/10); // 2点間の補完
c=IGN_MAP[index_r2][index_a1];
d=IGN_MAP[index_r2][index_a2];
b=c+((d-c)*(air%10)/10); // 2点間の補完
a=a+((b-a)*(rpm%500)/500); // 2点間の補完
if(IN_TEN==0)
{
a=10; // TEN端子がLo(テストモード)だったら点火時期BTDC10度固定
}
MONIGN=a; // モニタ用
return a;
}
Neの立ち下がりを基準にして、点火信号を作っています。
通電時間は回転数が変化しても一定にしています。回転が上昇して通電時間を確保できなくなった場合にだけ通電時間を減少させます。
ignition.c
dwell=IOCLK/320000*DWELL;
c=getign(RPM,(AIR>>8)); // 点火マップから点火時期を読み出す
// 点火時期
//IOCLK/RPM/384; // クランクが1度回るカウンタ値
// 点火までの待ち時間を計算
//b=(IOCLK/384)*(190-c)/RPM; // Φ/64
b=(IOCLK/192)*(190-c)/RPM; // Φ/32
b=b-116; // Ne信号の積分回路での遅れと処理に掛かった時間分(116)を引いておく
// a=コイル通電開始までの待ち時間
if((dwell+50)>b)
{
a=50; // ドエルタイムよりも点火信号の山が短い時
dwell=b-a;
}
else
{
a=b-dwell; // 通常時
}
switch(CYLINDERS)
{
case 1:
case 3:
IGN2_PIN=1;
CMT1.CMCOR=a;
IGN2_BUFF=dwell;
CMT.CMSTR.BIT.STR1=1; // タイマースタート
break;
case 2:
case 4:
IGN1_PIN=1;
CMT0.CMCOR=a;
IGN1_BUFF=dwell;
CMT.CMSTR.BIT.STR0=1; // タイマースタート
break;
}
始動時にはクランクの回転が安定しないのと、タイマーで長い時間を計るのが面倒くさいので、タイマーを使わずに
としています。上死点前10度での点火になりますね。
通電時間が長すぎると感じますが、純正ECUでも同じような処理のようですし、始動時の短時間のみなので問題ないかと思います。
rotetion_speed.c
// Ne立ち上がり
if(RPM<START_RPM) start_up(); // 低速の時、始動モードの点火処理(通電開始)
ignition.c
// Neの立ち下がりで呼ばれる
void ignition(void)
{
if(RPM<START_RPM) start(); // 低速の時、始動モードの点火処理(通電終了)
else normal();
}
ignition.c
// 始動モードNe立ち上がり
void start_up(void)
{
CMT.CMSTR.BIT.STR0=0; // タイマー停止
CMT.CMSTR.BIT.STR1=0; // タイマー停止
// Ne立ち上がり
if(CRANK_G==1) IGN1_PIN=0;
if(CRANK_G==0) IGN2_PIN=0;
}
// 始動モードNe立ち下がり
static void start(void)
{
CMT.CMSTR.BIT.STR0=0; // タイマー停止
CMT.CMSTR.BIT.STR1=0; // タイマー停止
IGN1_PIN=1;
IGN2_PIN=1;
MONIGN=10;
}
Ne が H の状態でエンジンが止まると、イグナイタに通電し続けてしまうので、回転数の計測でエンジンが回転していないと判断したら、イグナイタの通電を停止するようにしています。
rotetion_speed.c
回転数計測のオーバーフロー割り込み
void interrupt_mtu21overflow(void)
{
RPM=0;
ignition_pinreset(); // クランクが止まっている時にイグナイタに電流が流れ続けるのを防ぐ
while(MTU21.TSR.BIT.TCFV==1) MTU21.TSR.BIT.TCFV=0; // オーバーフローフラグクリア
}
('A`)