2011年7月11日作成 2011年7月11日更新

自作ECU動作説明に戻る

自作ECUの動作説明

クランク非同期の定期処理

クランクの回転に関係無く時々実行すれば良い処理は、タイマーで 100ミリ秒毎に割り込みを掛けて実行しています。

水温・吸気温・油温の計測

温度は急激に変化するものではありませんから、頻繁に計測する必要はありませんね。1秒毎に AD変換を行って、温度センサーの出力電圧を測定しています。

電圧から温度への変換は、いちいち計算するのではなくテーブルを参照して行っています。

また、計測した温度から燃料噴射量の補正係数を決定しておきます。


intervaltimer.c


// 一秒ごとに実行
// 低速側AD変換
// AD変換が終了していたら、値を読んでから次のAD変換開始
// (間隔が長く空いているからADFチェックしなくてもいいと思うけど)

if(AD1.ADCSR.BIT.ADF==1)
{
	int a,b;
	// AD変換終了フラグクリア
	while(AD1.ADCSR.BIT.ADF==1)AD1.ADCSR.BIT.ADF=0;

	// AD変換の結果をテーブルで温度に変換
	WATER_TEMP = WATER_TEMP_TABLE [WATER_TEMP_REG>>8];
	AIR_TEMP   = WATER_TEMP_TABLE [AIR_TEMP_REG>>8];
	OIL_TEMP   = OIL_TEMP_TABLE   [OIL_TEMP_REG>>8];
				

	// 水温補正係数計算
	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;
	}
	
	AD1_start();	// 次のAD変換スタート
}

アイドリング回転制御

0.5秒毎に現在の回転数と目標回転数を比較して、回転が低ければ ISCVを開き回転が高ければ ISCVを閉じることで、アイドリング回転数が一定になるように制御します。


intervaltimer.c


// 0.5秒ごと
// アイドリング回転制御

if((WATER_TEMP<(IDLUP_TEMP+50))// 水温が低い時にもアイドルアップ
||(IN_PS==0)	// パワステが動作していたらアイドルアップ
||(IN_AC==0)	// エアコンが動いていたらアイドルアップ
||(IN_FAN==0)	// ヒーターのブロアモーターが勢い良く回っていたらアイドルアップ
/*||(IN_DEF==1)*/	// リアデフォガが動いていたらアイドルアップ
||(IN_LIGHT==1)) STATUS|=SIDLUP;	// ヘッドライトが点灯していたらアイドルアップ
else STATUS&=~SIDLUP;

// エンジンが回っていてスロットルポジションがIDLでギアがニュートラルで燃料カットしていないなら
// アイドル回転制御
if((RPM!=0)&&(IN_IDL==0)&&(IN_NEUTRAL==0)&&((STATUS&SINJF)!=0))
{
	static unsigned short lastrpm;	// 前回の回転数
	
	// 目標回転数の決定
	if((STATUS&SIDLUP)==0)
	{
		temp=IDL_RPM;
	}
	else
	{
		temp=LOAD_RPM;
	}
	
	temp=(temp-RPM);
	
	if(((signed short)(RPM-lastrpm))<5)	// 回転が上昇中ならISCV開くのを待つ
	{
		if(temp>  20) idl_control(+5);		// 目標より低かったらISCV少し開ける
		if(temp> 120) idl_control(+70);		// 目標よりたくさん低かったらISCVたくさん開ける
	}
	
	if(((signed short)(lastrpm-RPM))<5)	// 回転が下降中ならISCV閉じるのを待つ
	{
		if(temp< -60) idl_control(-5);		// 目標より高かったらISCV少し閉じる
		if(temp<-500) idl_control(-30);		// 目標よりたくさん高かったらISCV多めに閉じる
	}
	
	lastrpm=RPM;
}

ISCV の駆動

ISCVはたぶん「アイドル スピード コントロール バルブ」の頭文字。かな?

タイマーの PWMモードを使っているので、タイマーのレジスタに値をセットするだけで勝手に信号を出してくれてプログラムはとても簡単です。

純正ECUの出力を観測してみると、周期が 6.2ms デューティ比が 43〜78%くらいで変化していました。通電時間が増えるとバルブが開きます。デューティ比がこの値から外れると ISCV が動かなくなってしまうことがあったので、この範囲を外れないようにチェックするようにしています。


iscv.c


void set_iscv(unsigned char a)	// 引数 デューティ比 0-100 値が増えると回転が上がる
{
	unsigned int temp;

	if(a>ISCVMAX) a=ISCVMAX;	// 範囲のチェック
	if(a<ISCVMIN) a=ISCVMIN;	// ディユーティー比 通電時間が43〜78%くらい
	
	ISCVSTEP=a;		// モニタ用に現在の ISCVの開度を保存
	temp=MTU23.TGRB;
	temp=temp*a/100;
	MTU23.TGRA=temp;
}

// ISCVの開度を 0.1%づつ増減させる
void idl_control(signed char a)
{
	signed int temp;
	unsigned int min,max;
	
	min=MTU23.TGRB*ISCVMIN/100;
	max=MTU23.TGRB*ISCVMAX/100;

	temp=(signed int)a*MTU23.TGRB/1000;

	temp+=MTU23.TGRA;

	if(temp>max) temp=max;
	if(temp<min) temp=min;
	MTU23.TGRA=temp;
	ISCVSTEP=temp*100/MTU23.TGRB;	// モニタ用に現在の ISCVの開度を保存

	// TEN端子がLo(テストモード)だったらISCV基準値
	if(IN_TEN==0) set_iscv(ISCVDEF);
}

空燃比のフィードバック補正

O2センサーの出力を監視して、理論空燃比になるように燃料噴射量を補正します。触媒で排気ガスを浄化するためには理論空燃比付近で燃焼する必要があるので、この処理を行わないと車検に通らないんじゃないかと思います。

O2センサーの動作チェック

O2センサーの出力がリーンなら燃料を増量、リッチなら燃料を減量して理論空燃比付近で燃焼するように補正するのですが、O2センサーが故障する等してO2センサーの出力が変化しなくなると、燃料を増量し続けたり、減量し続けたりしてとんでもない空燃比になってしまうかもしれません。

そうならないように、O2センサーが正常に動作していることを確認してから補正処理を実施するようにしています。


intervaltimer.c
TIMER=0.1秒ごとにインクリメントするカウンタ


// 0.5秒ごと実行

// O2センサー異常判断
// 始動後は必ずO2センサー異常フラグが1になっている
// センサーの出力が変化するのを確認したらフラグをクリアする

// O2センサーの出力が前回と違っていたら、センサー異常フラグクリア
if(((O2>O2TH)&&((STATUS&SO2)==0)) || ((O2<O2TH)&&((STATUS&SO2)!=0)))
{
	STATUS&=~SO2ERR;	// O2センサー異常フラグクリア

	// センサー有効フラグの寿命 10[分]*60*10	(10分後にまたセンサーチェックする)
	// TIMERがオーバーフローする直前だと極端に寿命が短くなるけど、細かいことは気にしない
	// 0xFFFFFFFF*0.1[秒]=4294967295*0.1秒=7158278[分]=119304[時間]=4971[日]だからオーバーフローしないか
	O2TIMER=TIMER+600;	// 60秒変化しなかったらセンサー異常というか、出力が変化するまでフィードバック補正停止
}

if(O2TIMER<TIMER)
{
	STATUS|=SO2ERR;	// 寿命が切れたらO2センサー異常フラグセット
}

if(RPM==0) STATUS|=SO2ERR;	// エンジンが止まったらO2センサー異常フラグセット

if(O2>O2TH)
{
	STATUS|=SO2;	// リッチ
}
else
{
	STATUS&=~SO2;	// リーン
}

としています。

噴射マップが薄目にセットされていてエンジン始動後ずっと放置していると、O2センサーが正常でもずっとセンサーの出力がリーンを示して、いつまでたっても空燃比のフィードバック補正が始まらない場合があります。その場合でもセンサーの温度が上がってから軽く空吹かしするか少し走ればセンサーの出力が変化して、フィードバック補正が始まります。

アイドリング領域の噴射マップを少し濃い目にセットしておいて、空燃比を自動補正に任せておくと良いかもしれません。

空燃比補正処理

0.5秒ごとにO2センサーの出力を監視して

としているだけです。簡単。

噴射カットしている時と加速補正中はフィードバック補正を行いません。

フィードバック補正中はパージソレノイドバルブを開いて、燃料タンク内の蒸気を吸気に混ぜておきます。(パージソレノイドバルブが閉じている時には、ガソリンタンク内の蒸気は活性炭を通して大気に放出されます。)


intervaltimer.c


// 0.5秒ごと実行

// O2センサーフィードバック補正
if(((STATUS&
(SO2DIS				// O2センサーフィードバック補正禁止フラグが1なら補正処理しない
|SFUELPOWINC		// 加速増量補正中ならO2センサーフィードバック補正しない(温度補正中はフィードバック補正する)
|SO2ERR))==0)		// O2センサー異常なら補正しない
&&((STATUS&SINJF)!=0))	// 燃料噴射していないときは補正しない
{
	unsigned char a,r;
	if((INJAIR%10)<5) a=INJIDX_A1; else a=INJIDX_A2;		// 一番近いマスを選ぶ
	if((INJRPM%500)<250) r=INJIDX_R1; else r=INJIDX_R2;
	
	if((STATUS&SO2)==0)
	{
		// リーン
		// 薄かったら増量補正
		injcomp_add(r,a);
	}
	else
	{
		// リッチ
		// 濃かったら減量補正
		injcomp_sub(r,a);
	}
	// フィードバック補正中はパージソレノイドバルブを開いておく
	// (パージソレノイドバルブが開いて空燃比が濃くなっても補正される)
	PURGE=1;
}
else
{
	// フィードバック補正中でなければパージソレノイドバルブを閉じておく
	PURGE=0;
}


intervaltimer.c


// 空燃比補正マップ減量
void injcomp_sub(unsigned char r, unsigned char a)
{
	if(INJCOMP_MAP[r][a]!=0)	// 補正禁止領域なら補正しない
	{
		INJCOMP_MAP[r][a]-=1;		// 1%減量
		if(INJCOMP_MAP[r][a]<AFCMIN)
			INJCOMP_MAP[r][a]=AFCMIN;	// 下限を下回らないようにする
	}
}

// 空燃比補正マップ増量
void injcomp_add(unsigned char r, unsigned char a)
{
	if(INJCOMP_MAP[r][a]!=0)	// 補正禁止領域なら補正しない
	{
		INJCOMP_MAP[r][a]+=1;	// 1%増量
		if(INJCOMP_MAP[r][a]>AFCMAX)
			INJCOMP_MAP[r][a]=AFCMAX;	// 上限を上回らないようにする
	}
}

エアコンリレーの制御

エアコンスイッチONでサーモスイッチ動作していれば、エアコンコンプレッサーを動かすだけなのですが、うちの車にはエアコンが付いていないので本当に動くのかはわかりません。


intervaltimer.c


// エアコンリレーの制御
// エアコンスイッチONでサーモスイッチ動作していれば、エアコンコンプレッサーを動かす
if(IN_AC==0) AC_RELAY=1;
else AC_RELAY=0;


自作ECU動作説明に戻る


webmaster@kyoutan.jpn.org

('A`)