可変抵抗で水温を設定したら、おかしな値が出なくなった。スロットルセンサーは関係なかったみたい。
[ns]じゃなくて[us]だ。間違えた。
回転数と吸気量を自動的に変えて点火時期を計測してみた。テストに使ったのはAT用のECU。動作が怪しい気がするけど動いたみたい。
表計算ソフトでグラフにしてみた。OpenOfficeの使い方がよくわかっていない。
なんとなく、それっぽい形になっている気がする。
吸気量が最大付近で点火時期が大幅に遅くなっているのが謎。(追記)断線検出のためか、エアフロメーターの出力は0Vにならないようだ。0V付近まで下がった場合、フェイルセーフでアイドリングくらいの吸気量として制御しているんじゃないだろうか?
噴射時間も読み取ってみた。OpenOfficeで格子状のグラフってどうやるんだろう?このグラフだと見難い。
検索してみたら、OpenOffice 3.1には等高線グラフが無いみたいだった。
一ヶ月くらい前に直したストーブがまた壊れた。点火ヒーターを見てみたらニクロム線の途中でポッキリと折れていた。ヒーターがだいぶ脆くなっているみたい。
とりあえずまた繋いで使えるようにはなったんだけど、ニクロム線がだいぶ短くなってしまった。もうヒーター交換しなきゃ駄目だ。でも部品代が数千円するようなので、中古ストーブを買うことにする。予算1万円くらい。
ヤフオクでストーブに入札したら、3,500円スタートが3,500円で終了してしまった。1万円くらいいくかと思ってたのにびっくりだ。
ずいぶん前に買った中古のノックセンサーの動作テスト。
センサーの出力をピークホールド回路を通してマイコンでAD変換すればいいかな?と思って、ピークホールド回路のテストをしてみた。
これで良さそう。
USB-シリアル変換ケーブルを汎用パラレルポートとして使えるかどうか試してみた。
// Visual C# Express でおためし
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports; // SerialPortを使うために追加
using System.Threading; // sleep()を使うために追加
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
serialPort1.PortName = "COM4";
serialPort1.BaudRate = 38400;
serialPort1.DataBits = 8;
serialPort1.Parity = Parity.None;
serialPort1.StopBits = StopBits.One;
serialPort1.Handshake = Handshake.None;
serialPort1.Open();
timer1.Start();
}
private void button1_Click(object sender, EventArgs e)
{
//serialPort1.BreakState = true;
//Thread.Sleep(1000); // 1000ms待つ
//serialPort1.BreakState = false;
if (serialPort1.BreakState) serialPort1.BreakState = false;
else serialPort1.BreakState = true;
}
private void button2_Click(object sender, EventArgs e)
{
//if (serialPort1.DtrEnable) serialPort1.DtrEnable = false;
//else serialPort1.DtrEnable = true;
if (timer2.Enabled) timer2.Stop();
else
{
timer2.Interval = 10;
timer2.Start();
}
}
private void button3_Click(object sender, EventArgs e)
{
if (serialPort1.RtsEnable) serialPort1.RtsEnable = false;
else serialPort1.RtsEnable = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
if (serialPort1.CDHolding) label1.Text = "CD=1";
else label1.Text = "CD=0";
if (serialPort1.CtsHolding) label1.Text += "\r\nCTS=1";
else label1.Text += "\r\nCTS=0";
if (serialPort1.DsrHolding) label1.Text += "\r\nDSR=1";
else label1.Text += "\r\nDSR=0";
if (serialPort1.BreakState) label1.Text += "\r\r\nTxD=1";
else label1.Text += "\r\n\r\nTxD=0";
if (serialPort1.RtsEnable) label1.Text += "\r\nRTS=1";
else label1.Text += "\r\nRTS=0";
}
private void timer2_Tick(object sender, EventArgs e)
{
if (serialPort1.DtrEnable) serialPort1.DtrEnable = false;
else serialPort1.DtrEnable = true;
}
}
}
各プロパティを読むだけで、普通にリアルタイムで端子の状態を読むことができる。プロパティが true=Hi false=Lo
規格から外れるけど、0VでLo 5VでHiと認識してくれる。(本来は -3V〜-15VでLo(1) +3V〜+15VでHi(0))
RxDはうまいこと入力に使う方法が思いつかない。
各プロパティを書き換えるだけで、普通にリアルタイムで端子の状態を変化させることができる。プロパティが true=Hi false=Lo
TxDもBreakStateプロパティでボーレートに関係無く端子の状態を直接操作できた。
秋月のUSB-シリアル変換ケーブルで試した出力電圧は Hi=+6V Lo=-6Vだった。(リップルが多かった)
タイマーを使ってパルスを出力させてみると、USBの通信間隔の問題なのかタイマーの仕様なのか、20msくらい間隔がゆらいだ。あまり速い速度で端子の操作は出来ないみたい。
やってみると、ずいぶん簡単に使うことが出来た。あまり高速に操作できず、数10ms単位での操作になるけど入力3ビット、出力3ビット使えるのでけっこういろいろ出来そうな感じ。
while (true)
{
if (serialPort1.RtsEnable) serialPort1.RtsEnable = false;
else serialPort1.RtsEnable = true;
}
こんなふうに時間待ちを入れないで無限ループすると、周期約2msでパルスが出た。やっぱり周期は少し揺らぐ。
たしか、C#って中間言語を仮想マシンが実行する仕組みだった気がするんだけど、その仮想マシンが1ms毎に動いているとかそんな感じ?
違った。実行時、ネイティブコードにコンパイルされるみたい。
while (true)
{
if (serialPort1.RtsEnable) serialPort1.RtsEnable = false;
else serialPort1.RtsEnable = true;
Thread.Sleep(4);
}
sleep()で時間待ちすると、周期のゆらぎは2〜4msくらいだった。
シリアルポートの実験で動作がかなり遅いのは、USB-シリアル変換器のせいのようだ。マザーボードに直接付いているシリアルポートで実験してみると、USB-シリアルより10倍以上は高速に動作した。(マザーボードのヘッダピンから配線を出していないので、信号の観測は出来ていないけど)
USBだとある程度まとまったデータをパケットでやり取りする関係で、ポートを1ビットずつ操作するような事をすると遅くなるのかな?
USB-シリアル変換器で、PIC16F88のLV-ICSPモードでプログラムメモリ読み出しをやってみたんだけど動かなかった。コマンドの出力は出来ているようなんだけど、読みだしてもPGDピンがずっとLのまま。プログラムモードに入れていないのかな?
朝、水道が凍結して水が出なかった。台所を一時間くらいポータブルストーブで温めて、水が出るようになった。
ここ一ヶ月くらい天気のいい日ばかりで、そのぶん寒い。雪は10cmくらいしか積もっていないし。
ずいぶん前にジャンクで買ったDX7sのタクトスイッチ42個を全部交換した。ボタンの入りがすごく悪かったんだけど、これで調子良くなった。でもめんどくさかった。
ちょうどいい高さのタクトスイッチを持っていなかったので、秋月の100個700円のスイッチの頭を切って使った。
自作ECUのテスト。点火信号がちゃんと出るようになった。
CPUに秋月のSH Tiny(SH7125)を使っているんだけど、今回初めてSH2をを使うんでいろいろ手間取った。大まかなプログラムを書いて実行してみると全然動かない。調べてみるとすべての割り込みが動作していないようだった。ハードウェアマニュアルをよくよく読むと、割り込みマスクの初期値ですべての割り込みがマスクされていた。
それで、割り込みマスクを0に変更してみても、まだ割り込みが掛からない。さらにマニュアルを読むと、割り込みコントローラの設定ですべての割り込みの優先順位の初期値が最低の0になっていた。使用する割り込みの優先順位を1にして、やっと割り込みがかかった。
H8Sでは特に設定変更しなくても、使用する割り込みを有効にするだけで割り込みを使えたので、SH2も同じようなものかと思っていてなかなか気づかなかった。
エンジンシミュレータに繋いで、回転数に合わせてばっちり点火信号が出ている。点火時期とドエルタイムの変更も自由自在だ。
// テストでAT用純正ECUの点火マップを使用
const unsigned char IG_MAP[16][25]=
{ /* 空気量小 空気量大 */
/* 250 240 230 220 210 200 190 180 170 160 150 140 130 120 110 100 90 80 70 60 50 40 30 20 10 */
/* 500*/ 10, 10, 10, 10, 10, 10, 10, 10, 10, 12, 15, 16, 15, 13, 11, 6, 6, 5, 5, 5, 5, 5, 5, 5, 9,
/*1000*/ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 15, 16, 14, 11, 6, 5, 5, 5, 5, 5, 9,
/*1500*/ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 16, 21, 21, 19, 16, 10, 8, 8, 8, 8, 8,
/*2000*/ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 19, 25, 25, 22, 19, 15, 12, 12, 12, 8,
/*2500*/ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 23, 34, 32, 25, 22, 20, 20, 20, 8,
/*3000*/ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 34, 41, 34, 29, 24, 25, 25, 6,
/*3500*/ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 41, 42, 37, 25, 26, 26, 7,
/*4000*/ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 43, 38, 33, 27, 27, 7,
/*4500*/ 33, 34, 33, 34, 33, 34, 33, 33, 34, 34, 33, 33, 34, 34, 33, 34, 33, 33, 34, 40, 38, 34, 28, 28, 4,
/*5000*/ 32, 32, 32, 32, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 35, 37, 33, 28, 28, 6,
/*5500*/ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 37, 33, 29, 26, 6,
/*6000*/ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, 36, 31, 29, 5,
/*6500*/ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 37, 32, 26, 5,
/*7000*/ 36, 36, 36, 36, 36, 36, 36, 36, 37, 36, 36, 36, 37, 37, 36, 36, 36, 36, 36, 36, 38, 38, 33, 26, 4,
/*7500*/ 36, 36, 36, 36, 36, 36, 36, 36, 37, 36, 36, 36, 37, 37, 36, 36, 36, 36, 36, 36, 38, 38, 33, 26, 4,
/*8000*/ 36, 36, 36, 36, 36, 36, 36, 36, 37, 36, 36, 36, 37, 37, 36, 36, 36, 36, 36, 36, 38, 38, 33, 26, 4
};
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=IG_MAP[index_r1][index_a1];
b=IG_MAP[index_r1][index_a2];
a=a+((b-a)*(air%10)/10); // 2点間の補完
c=IG_MAP[index_r2][index_a1];
d=IG_MAP[index_r2][index_a2];
b=c+((d-c)*(air%10)/10); // 2点間の補完
a=a+((b-a)*(rpm%500)/500); // 2点間の補完
return a;
}
点火マップの読み出し処理を作った。うまく動いているように見える。
最初に動かしてみたときは、メチャクチャな値が帰ってきて少し悩んだんだけども、吸入空気量をADコンバータの出力のまま16ビットで渡していたせいだった。上位8ビットのみを渡したら考え通り動作した。
作ってから思ったんだけど、マップがこんなに細かったら補間処理無くてもいいんじゃないだろうか?まぁ、同じ処理を噴射でも使うからいいか。
マップは半分くらいに減らしても良さそうかな?
噴射処理が出来上がった。
調子よくプログラムを書いていたら、割り込み処理が大きくなってきたせいで PIntPRG と PResetPRG のセクションが重複しているぞ!とリンカに怒られた。マップファイルを見てみると確かに重複している。
なので、初期値から変更していなかったセクションの設定を、
こんな感じに変更した。
なぜ、初期状態ではわざわざ PIntPRG と PResetPRG と P がそれぞれ別になっているんだろう?
点火信号と噴射信号がちゃんと出てるようで、エンジンが回るような気がしてきたので配線をちまちまとやり始めた。こんな作業やったこと無くて、思った以上に大変だ。電源と出力とアナログ入力の配線がだいたい終わって、デジタル入力の作業中、これが数が多くて面倒だ。ちゃんと収まるのかな?
純正ECUよりも部品点数減って、回路はシンプルになっているのに狭苦しい。手作りだから、片面基板で配線も太いからだ。
エアコンとかパワステなんかの、無くてもいい配線ははぶけばよかった。けど、やり始めてしまったので純正ECUと同等の制御をする。
ストーブの点火ヒーターがまた切れたので、3,500円の中古ストーブに交換した。なぜ、こんなに安く買えたんだろ?
配線が終わったっぽい。一回チェックして間違いなさそうだ。
洗濯のりを塗ったOHPシートにレーザープリンターで印刷して、アイロンで基板に転写。
エッチングした。
基板の銅箔面に付いた傷で線のカスレが二箇所あったけど、それ以外はとても綺麗に出来た。0.25mmの幅、0.5mmピッチで引いた線もくっきりとエッチングできた。途中食事したりしながら、3時間くらいでできあがり。
ボール盤で穴あけ。安い奴だけど、ボール盤があると木工やら鉄工やら、いろいろ使って何かと便利。
2時間くらい掛かった。
部品を載せた。
表側
電源は無理にパターンを這わさずに、電線で飛ばした。
シルク印刷がないから、どこの穴にどういう向きで部品を入れたらいいのか、パターン図で良く確認しながら作業しなきゃならなくて、とても時間がかかった。スズメッキ線でジャンパーを飛ばすだけで8時間くらい掛かったような気がする。そのあと他の部品を載せるのに、さらに6時間くらい?
一番細かいCPU周り。0.5mmピッチのパッケージで配線幅0.25mm。フラットパッケージのはんだ付けなんかをするのは10年ぶりくらいだったけど、ちゃんとできた。
動作テスト。ちゃんと動いた。
これからやらなきゃならないこと
点火・噴射が時々おかしいのはプログラムのミスじゃなくて回路の間違いだった。
クランクアングルセンサー G信号の積分用のコンデンサがGNDじゃなくて、点火確認信号ラインにつながっていた。びっくりだ。
プログラムを見なおしても異常がなさそうなので、動作の大元になっているクランクアングルセンサーの信号をオシロスコープで見てみたら、G信号に激しくクロストークが乗っていた。波形をよく見てみると点火確認信号と互いに結合していた。
パターンの引き方が悪くてクロストークが出てるのかな?こんな低速な信号で?とパターンを見直してみたら、G信号と点火確認信号がコンデンサでつながっていた(笑)。回路を手直しして、ちゃんと動くようになった。
よくこんな状態で動いていたなぁ。
水温と吸気温での増量補正が動いた。次は始動時の増量補正処理を書く。
始動増量補正が動いた。
いままで仮の処理で、しきい値を超えたら燃料カット、下回ったらすぐ燃料噴射再開していたレブリミットの処理を、レブリミットとは別に決めた回転まで下がってから噴射再開するように、純正ECUと同じような感じに変更した。
スロットルポジションセンサーのアイドル接点での燃料カットをするようにした。
低温と始動時の増量補正を書いたから、これでエンジン始動しないかな?
ISCバルブの制御も書き終わった。ちゃんと動いてるっぽい。
あとは、加速補正とO2センサーかぁ。
回転が上がると、指示した値より点火時期が微妙に遅れる。計算の丸め誤差とかカウンタの丸め誤差以上に遅れていておかしいので、角度から時間に変換する計算式が間違っているのかと何度も見直したんだけども、おかしなところが見つからない。
ドエルタイムは指示どうりぴったりの時間で波形が出ているから、点火時期の計算が間違っているはずなんだけど…
思いついた!計算処理にかかる時間分だけ遅れて動くからじゃないだろうか?
割り込みが掛かってからタイマーを起動するまで117usも時間かかっていた。いまコアクロック24MHzで動かしているから、2808クロック分だ。うーんこんなもんか?
タイマーにセットする値から117us分引いておくことで対応した。クロックの分周比を変更してCPUのクロックを倍の48MHzで動かすこともできるけど、クロックを上げて無駄に電気を使うのももったいないし、これでいいよ。
クランクアングルNeと点火信号の遅れをエンジンシミュレータで測ると117us位だけど、クランクアングルNeでの割り込みから、点火タイマーにセットする値を計算し終わるまでをECU側のタイマーで時間を測ると16usだった。なぜこんなに差が出る?
エンジンシミュレータ側の問題かなぁ。クランク5度回転毎に割り込みかけてクランクアングルセンサーの波形を出力して、その合間に点火信号の波形計測をしているから、割り込みで波形計測が遅れるとかいかにもありがち。
点火時期をBTDC10に固定して(BTDC10だとNeと点火信号の立ち下がりがぴったり一致する)オシロスコープで波形を見てみると、やっぱり100usくらいズレている。エンジンシミュレータは正確だった。
117us分決め打ちでずらせば正しい信号が出るんだけど、なぜタイマーで測った値と大幅に違うのか?
まさか、割り込みのオーバーヘッドが100usもあるとか?そんなわけない。
プログラムを書くときに、変数名をどう決めたらいいかと困る。いつもGoogle翻訳で出てきた英単語をそのまま使ったり、縮めて使ったりしているけど、かなり間違っている英語のような気がして他人に見せたくない(笑)
加速増量補正を書いた。
// 加速増量補正
// 前回よりも吸気量が大きかったら、加速増量補正を行う
if((air-PreviousValue)>ACCELCOMP_TH) ACCELCOMP=ACCELCOMP_VAL;
PreviousValue=air;
a=a*ACCELCOMP/100; // aはインジェクタの開弁時間
ACCELCOMP=ACCELCOMP-ACCELCOMP_RDC; // 補正量を減らす
if(ACCELCOMP<100)ACCELCOMP=100; // 100以下にならないように
どのくらい増量したら良いかは、実際にエンジンを回してから調整するとして、とりあえず吸入空気量から加速と判断したら、ガソリンをピュッと200%増量して、その後毎噴射ごとに補正量を10%ずつ100%になるまで減らしていくようにした。吸入空気量が増加している間はずっと増量が続くところが、キャブレターの加速ポンプと違う。これでいいのかな?
きっと、エアフロメーターじゃなくてスロットルセンサーで加速補正すればレスポンスいいんだろうね。
ECU作るのは大変かな?と思っていたけど、こういう簡単なプログラムの積み重ねだから誰でもできる!
O2センサーで空燃比のフィードバック補正をする処理を一応書いたんだけど、O2センサーが正常なことを確認してから補正するようにしないと空燃比が大幅に狂ってしまうので、どこかでO2センサーの診断をしないといけない。
O2センサーは温度が上がってからじゃないと正常に動作しないようなので、水温が安定してから増量補正した時にリッチ && エンジンブレーキで燃料カットしたときにリーンを確認したときにO2センサー正常と判断したらいいんだろうか? それとも水温が安定してから、0.5秒くらい燃料を増減させてみてO2センサーの出力が変化するかどうかを確認するとか?うん、このほうがいいかな?
ふつうに制御している間にO2センサーの出力が変化するかを見ておいて、いつまでたっても変化しない場合意図的に燃料を増減させて、それでもO2センサーの出力が変わらなかったらセンサー異常とするとか?
あと、高回転とか高負荷の時なんかは補正しないように、補正禁止領域を決められるようにしたほうがいいな。
最初、補正は噴射マップを定数じゃなくて変数にして、噴射マップを直接修正したらいいかな?と思ったんだけど、これだと元のマップからどれくらい修正したのかがわからなくなるので、噴射マップは定数のテーブルにしておいて、補正量だけの変数テーブルを用意することにした。
ECUのプログラムがだいたい出来上がったので、PC側のモニターを作り始めて、とりあえずテキスト表示ができた。0.1秒毎に表示が更新される。
モニターを作っていて気がついたんだけど、レブリミットに当たると、なぜかO2センサー異常フラグがクリアされてしまう。プログラムを見直しても原因がわからない。こまった。
直った!
if(((O2>O2TH)&&(STATUS&SO2==0)) || ((O2<O2TH)&&(STATUS&SO2!=0)))
をif(((O2>O2TH)&&((STATUS&SO2)==0)) || ((O2<O2TH)&&((STATUS&SO2)!=0)))
こんなふうにカッコを増やしたら直った。&より==,!=のほうが優先順位高いんだ。
グラフを描こうとしたら失敗。
できた。
今読み出している位置を太線で示していて、これも0.1秒毎に更新される。
こんなふうになってきた。
上段の折れ線グラフも 0.1秒ごとに更新されて、右にスクロールしていく。
ECUのモニターでログをファイルに記録できるようになったので、自作ECUを実際にエンジンにつないで動かしてみたけど、エンジン掛からなかった。モニターで動作を確認してみると、アナログ入力がすべて0V。回転数もでたらめな値になっている。どうもI/O系の電源が入っていないような症状だ。I/O系の電源5Vをセンサーで分圧して読み取っているから、電源が0Vだと入力も0Vになってしまう。
回路をざっと見なおしてみてもおかしなところが見当たらないので、もう一回エンジンにつないでみたら
エンジンが始動した。(ビデオ) 動かなかったのは、コネクタを奥まで挿していなかったせいのようだ。
回してみてわかった問題点
エンジンシミュレータで動作テストをしていたから、エンジン始動しそうだなとは思っていたけど、プログラム修正無しに一発で始動するとは思わなかった。
ついでに、試しにAT用の純正ECUをMT車につないでみたら、ふつうにエンジンが掛かった。AT用とMT用では噴射マップや点火マップがだいぶ違うけど、マップを書き換えるなら流用できるっぽい、どうだろう?
時々燃料を噴射しないのは、点火確認信号の扱いが悪いせいだった。点火確認信号を無視すると症状が出なくなる。
イグナイタが実際に点火すると、イグナイタから幅1.5msくらいのパルスが出てくるんだけど、このパルスのエッジで割り込みを掛けて変数をインクリメントして、燃料を噴射した後この変数をクリア、次に燃料を噴射するときに変数がインクリメントされていなければ点火確認できなかったとして、燃料を噴射しないようにしている。
この割り込みが掛かるタイミングで他にクランクアングルセンサーや点火信号のタイマー割り込みなんかも発生しているから、多重割り込みで処理が遅れているのかなぁ。違った。
400〜430RPMで点火信号が出ていないのを見つけた。点火信号が出ていないから、点火確認信号も出なくて、噴射もしてないんだ。BTDC10より遅角しないのも見つけた。
点火信号は波形が重複しないからタイマー一本でいいかと思っていたんだけど、よく考えたらBTDC10より遅く点火しようとすると、おもいっきり重複するじゃないか。BTDC10より早く点火するなら今のままでも動くんだけど、どう直したらいいだろう。
とりあえず、原因がわかってよかった。
タイマーの割り当てメモ
CMTが二本余っているから、これで点火信号を出すようにすれば配線の修正無しでできるけど、CMTだとPWMモードが使えないから、タイミングが少しばらつきそう。
回転数と車速の計測をMTU5のインプットキャプチャで処理して、噴射をCMTで処理。MTU20とMTU21で点火信号を出すとかすればよかった。でも、回路の変更が必要だから別の方法にする。回路を作り直す機会があったらそうすることにしよう。
どうせ回路を作り直すなら、噴射もPWMで処理したほうがいいな。行き当たりばったりでタイマーの割り当てを決めたので問題が多い。
MTU20とMTU24で点火信号を出して、CMTでノックセンサーのタイミング生成をすれば、配線2本の修正でできるなぁ。
もし、今度作り直すことがあったら、こんな感じに割り当てよう。
CMTで点火信号を出すようにしたら、点火時期がめちゃくちゃになってしまった。かなり悩んだ後、ハードウェアマニュアルを読み直したら、CMTはコンペアマッチ発生で自動的にタイマカウンタがクリアされるって書いてあった。MTUではクリアするかしないかを選択できたので、CMTもカウンタクリアされないものと思ってプログラム書いてた。
修正して動作良好。時々点火信号が出なかったり、遅角出来ない症状も直った。
これまでMTUのPWMモードを使って、CPUを介さずにタイマーが勝手にIOポートを操作してくれていたので、CMTの割り込みでIOポートを操作すると、割り込みでのオーバーヘッドで信号のばらつきが出るかな?と思ったけど全然問題なかった。
タイマーの割り当てはこうなった。
パラメーターを変更するたびに、コンパイルしてマイコンに書き込んで、テストってやるのが面倒になってきたので、動作中にパラメーターを変更できるようにしたい。
秋月の新商品を眺めていたら、DIPのR8Cの取り扱いが始まっていた。ROM 2k、RAM 256とメモリが少ないけど、この容量で足りるならPIC16よりも使いやすそう。H8やSHと同じく、シリアルポートで書き込みができて、書き込み機がいらないので気楽に使えそう。100円と安いので、今度使ってみよう。ハードウェアマニュアルをちょっと見てみると、最小命令実行時間は1クロック1命令のようだ。PIC16より早い。
PIC16で制限のきつい無料版Cコンパイラを使うより、R8Cのほうがよさそうかも。
メモリ容量以外はPIC24に近い感じかな?
PC側の通信処理を書いていたら、ごちゃごちゃして、だんだんわからなくなってきた。
しょうがないので、通信処理を書きなおした。だいぶわかりやすくなった。
PCからデータを1バイト送信するときにデータが化けているような気がする。最上位ビットが無視されているような…
bit7が1になるデータを送信したときだけ、値がぐちゃぐちゃになる。
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
serialPort1.PortName = "COM4";
serialPort2.PortName = "COM6";
serialPort1.Open();
serialPort2.Open();
timer1.Enabled = true;
}
private void button1_Click(object sender, EventArgs e)
{
byte[] write = new byte[6];
write[0] = 0x7E;
write[1] = 0x7F;
write[2] = 0x80;
write[3] = 0x81;
write[4] = 0xAA;
write[5] = 0xFF;
serialPort1.Write(write, 0, 6);
}
private void timer1_Tick(object sender, EventArgs e)
{
while (serialPort2.BytesToRead > 0)
{
textBox1.Text+= serialPort2.ReadByte().ToString("X2");
}
}
}
}
ためしに、こんな簡単なプログラムを動かしてみたら、ちゃんと8ビット送信できている。マイコン側の問題か?
マイコン側で受信したデータをそのまま送信する(エコーを返す)ようにしてテストしてみたら、異常が出ない。
マイコン側で
unsigned short temp;
temp=getch()<<8;
temp|=getch();
これを
unsigned short temp;
temp=(getch()<<8)&0xFF00;
temp|=getch()&0xFF;
こうしたら直った。直ったけど、上ではなぜダメなんだろう?
なんとか、レブリミットとかのパラメーターと噴射マップを動作中に変更できるようになった。windowsのプログラムはめんどくさい。
またエンジンを回してみた。点火・噴射信号が途切れる症状は直って調子いいみたい。
アイドリングは調子悪い。回転数が一定の時には、まあ調子がいいんだけど、空吹かしして回転を上げてからアクセルを離して回転が下がってくると、ボッボッボッとエンジンが止まりそうになる。噴射マップを書き換えて少し燃料を薄くすると、少しだけ調子が良くなるけど、あんまり薄くすると回転数一定の時にエンジンが止まってしまう。
吸気ポートにけっこう燃料が付着しているんだろうなぁ。減速で燃料の減量補正が必要なのかも。
エンジンの掛かりが悪いから、純正ECUではどのくらい燃料を噴射しているんだろう?と調べてみたら、吸気量に関係なく一定時間40msとか、かなり長い時間噴射していた。噴射時間は水温に応じて変化するので、テーブルを作って純正ECUと同じような制御に書き直した。いままでは、噴射マップから読んだ値を適当に始動時補正で2倍して噴射していた。これだと燃料が足りないので掛かりが悪かったのでした。
あと、純正ECUでは加速でも減速でも噴射量の補正はしていないように見える。
テーブルの読み出しがうまくいかない。悩み中。
始動噴射マップが桁あふれしていた(笑)unsigned short(16ビット)の配列に71287とか入れてた。動かなくて、かなりしばらく悩んだ(笑)
今日もエンジン回してみた。
問題点
良かったところ
始動時の純正ECUの動きを調べたら、水温に応じてISCVの開き具合を固定していた。低温なほどたくさん開く。エンジンが始動したらISCVでアイドリング回転制御が始まる。
あと、水温によって燃料カット回転数が変化する。低温だとより高くなる。
クランクアングルセンサー入力のノイズのせいか、スターターを回しているときに、時々回転数の計測結果がおかしくなる。波形をコンパレーターで整形してやらなきゃだめなのかも。オシロスコープを外まで持っていくのが面倒なので、実際の波形はまだ見ていない。
スターターを回しているときに、ECUがフリーズしてしまうことがある。バッテリーが元気なときなら大丈夫みたいなんだけど、数日エンジンを掛けていないような時だと症状が出る。電源電圧が中途半端に変動するせいじゃないかと思う。マイコンの電源ラインのコンデンサを大きくしてみても変化がない。リセットICで確実にリセット掛かるようになきゃだめかも。
エンジンが冷えていると始動しない。いったん純正ECUで始動した後なら、自作ECUでも始動する。寒いし難しい。
低回転で回転数が安定しないのはISCVの制御が原因ではなく、たぶん空燃比が濃すぎるためだ。
交通の無い夜中に、数キロ走行してみた。時々失火することがあったけど、エンストすることもなく普通に走行できた。
空燃比がかなり濃いみたいで、排気が臭い。失火の原因はよくわからないけど、空燃比のせいかもしれないので、とりあえずまともな空燃比でエンジンが回るようになってから原因を調べることにする。
やっぱり空燃比フィードバック補正が動かない。プログラムを見なおしてやっと間違いを見つけて修正した。動くかな?→まだ動かなかった。
始動時にはISCVを全開にして、スターターモーターが止まったら、すぐに始動時噴射時間から通常の噴射時間へ向けて減少させていたのを5秒待ってから減少させるようにしたら、エンジンが冷えていても始動するようになった。始動時の水温は約0℃。5秒はちょっと長いかも?アクセルをちょっと踏めばすぐに通常噴射時間になるようになっているので、長めでいいかとも思う。
プログラムの条件指定を間違っていたのを直して、空燃比のフィードバック処理が動くようになった。
空吹かしした後アイドル制御しなくなるのも、条件指定を間違えていたせいだったので修正した。
また夜中に少し試走してみた。空燃比のフィードバック補正を有効にして走ってみると、昨日は加速時に失火する症状が収まっていた。
2000回転くらいでアクセルを軽く踏むか踏まないかくらいで一定の速度で走っているときにかなり大きくガクガクする時がある。走行中パソコンのログ画面を注視することが出来ないので、原因がよくわからないけど、ちょうどアイドルの時に燃料カットが働く回転数なのでその関係だろうか?純正ECUではじわりと噴射を再開しているけど、自作ECUではいきなり噴射を再開している。
それ以外は走行中とても普通にエンジンが回る。あと、排気ガスが臭くなくなった。
空燃比補正が動くようになったら、アイドリングが調子悪くなってしまって、交差点で3回くらいエンストしてしまった。昨日はアイドリング目標回転数以下に大きく下がることは無かったけど、今日は水温が上がってくると回転が大きく落ち込んでたまにエンストしてしまう。そういえば、昨日はアイドリングの制御に問題があって、一度回転数を上げるとアイドル回転数が高くなってしまっていたので、この症状が出なかったのかな?
ファイルに記録したログを見てみると、エンストしたときのO2センサーの出力はリッチだった。濃すぎてエンストしてるの?
スロットルアイドル時の燃料カットで、設定回転数を超えたら燃料カット、設定回転数を下回ったらすぐ噴射再開としていたのを、燃料カットと噴射再開回転数を別に設定できるようにした。ガクガクするのが直るといいけど。
エンジンを掛けて少しすると、アイドリングの制御が出来なくなる症状がどうしても消えなくて、純正ECUがISCVをどう制御しているのかを調べ直した。PWMの周期は前に調べた約6.2ms固定でいいみたいなんだけど、ディユーティー比が通電時間45〜78%の間で変化していた。自作ECUではこれまで0〜100%の間で変化させていたので、ISCVが引っかかっていたとかISCVの中の人に異常と判断されて固定されていたとかだろうか?
エンストしなくなったので、自作ECUで隣町のスーパーまで買い物に行ってきた。ガクガクすることもなく快調に走った。
たまーに失火するんだけど原因がわからない。シミュレーターでの動作でもたまに症状が出るので点火信号が出ないことがあるようなんだけど、規則性がないので調べにくい。アナログオシロで見てもさっぱりわからないし。
しばらく走ったので、空燃比のフィードバック補正で普通に走る領域の噴射マップがいい感じになってきた。
1000RPM以下くらいでアイドリングさせるとハンチングを起こすので、ISCVの制御をまだ直さなきゃいけない。
たまーに点火信号がおかしい問題を調査中。
回転が上がっても、クランクアングルNeの立ち上がりで点火信号も立ち上げ、Neの立ち下がりで点火信号も立ち下げとする始動モードだと症状が出ない。点火のタイマーが悪いのか、気筒判別が悪いのか。
気筒判別を停止して1番上死点から2番目に固定(1,4番の点火タイマーをセットするタイミング)すると症状が出ないので、気筒判別が悪いようだ。1番じゃないところで1番と判断しているような?
Neの立ち上がりでGがHだったら1番上死点(実際は1か4番上死点)とすると症状が出なくなった。でも、これだと1番上死点なのか4番上死点なのかが判断できないので、噴射がうまくいかない。これまではNeの立ち下がりでGがHだったら1番上死点としていたんだけど、これだと時々誤判別する。信号をオシロスコープで見てみても波形はノイズもなくきれいなのに、なぜ誤判別するんだろう?釈然としない。
NeのIRQ0は入力にシュミットトリガが付いているけど、GのPAには付いていないのでその関係?
オペアンプでシュミットトリガ回路を作って追加してみたけど、症状変わらず。ただ、ありあわせのLM358を使ったら出力が0.68-3.73Vくらいとずいぶん振幅が小さくなってしまった。オープンコレクタ出力のコンパレーターで作り直すことにする。
秋月にコンパレーターとか追加部品を注文したんだけど、リセットICを注文するのを忘れてしまった。
点火時期を固定して回転数を変化させてみると、症状が頻発する回転数を発見。点火確認信号の割り込みが掛からないタイミングがあるような。気筒判別の問題では無いのか?
症状が頻発するのは、Neの立ち下がりとIGfの立ち下がりが一致する時だった。どちらの信号も立ち下がりで割り込みを掛けている。どちらの割り込みが先に処理されても問題なく動くと思うんだけどどうして調子悪くなるんだろう?
NeとIGfの割り込みが重なると調子がわるいようなので、Neの割り込み優先順位をIGfより上げるとすこし症状が良くなった。IGfの立ち下がりで割り込みをかけていたのを立ち上がりに変更すると、めったに割り込みのタイミングが一致しないのでほぼ症状が出なくなった。
回転数が変化するときに一瞬症状が出るけど、これはエンジンシミュレーターの出力が急変するせいじゃないかな?まだ調べる。
ルネサスのFAQページを見ると、割り込み要求フラグが0の時にCPUが0を書き込んでフラグをクリアすると、フラグのセットとクリアが競合した場合に、それ以降フラグが変化しなくなってしまうようだ。
現在ほとんどの割り込みルーチン内でフラグが1であることを確認せずに0を書き込んで割り込みルーチンからリターンしているので、このせいだろうか?でも、割り込み処理中に同じ割り込みが再度発生することは無いように思うし、割り込みが発生したなら割り込み要求フラグは1の筈だし、IRQ割り込み以外はフラグが勝手にクリアされることは無い筈で、IRQ割り込みでは割り込みが受け付けられた時点で自動的にクリアされるので、CPUではフラグを操作していない。
IRQ割り込み処理中に、ノイズか何かでまた同じIRQ割り込みが発生すると良くないので、IRQ割り込みでも処理を終了するときに割り込みフラグが1だったらフラグをクリアしておいたほうがいいかも。
割込み処理の中で割込み要因をクリアしているにも関わらず、割込み処理から復帰後すぐに同一要因の割込み処理を実行してしまいます。
というFAQもあって
割り込み要因フラグをクリアしてから実際にCPU への割り込み要因が取り下げられるまでには「割り込み要求発生から、割り込みコントローラで優先順位判定およびSR のマスクビットとの比較後、CPU へ割り込み要求信号が送られるまでの時間」を必要とします。
そのためクリアしたはずの割り込み要因を誤って再度受け付けないようにするため、割り込み要因フラグをクリア後、割り込み要因フラグをダミーリードしてください。
とあった。これにはもろに該当しそうだ。フラグクリア後ダミーリードするように修正すると調子が良くなった。
FAQを初めて読んでみたけど、とても参考になる。IOポートへのビット操作中に発生した割り込みの中で同じポートにライトするとダメとか。読むまで気付かなかった。
昨日部品を注文したついでにR8C/M12Aも注文したので、到着前にシミュレーターで使ってみる。
【無償評価版】M16Cシリーズ, R8Cファミリ用Cコンパイラパッケージ M3T-NC30WA V.5.45 Release 01 nc30v545r01_ev にはR8C/M12Aのレジスタ定義ファイルが含まれていないので、ルネサスのR8C/M12Aグループ製品情報ページ内、ダウンロードコーナーからR8C/M12A用サンプルドライバ集をダウンロードして使用する。レジスタ定義ファイルのみでも用意されているけど、サンプルドライバが便利に使えそうなので、遠慮無くサンプルドライバをダウンロードしたら良い。この中のsfr_r8m12a.hがレジスタ定義ファイル。
HEWで新規プロジェクトを作る
nc_define.inc の __ROM_TOPADR__ .equ 0F000H を 0F800H に変更(ROM2K)
可変ベクタテーブルのアドレスは初期値のまま__VECTOR_ADR__ .equ 0fedcH
(固定ベクタテーブルの直前)でいいように思う。
RAMのサイズを指定していないけどいいのかな?
あとは、sfr_r8m12a.hをインクルードしてプログラムを書けば良い。
コンパイラマニュアルの第3章 プログラミング
くらいは読んでからプログラムを書く。コード効率の良いプログラミング方法
とか参考になる。
#include "sfr_r8m12a.h"
void main(void);
void main(void)
{
pd1_0=1; // ポート1のビット0を出力
while(1)
{
p1_0=0;
p1_0=1;
}
}
こんな簡単なプログラムを書いてみて、シミュレーターで動かしてみた。ポートレジスタの値が変化するので、動いているようだ。シミュレーターのMCU Settingでメモリマップが変更できなかったけど、動くからまあいいや。
#pragma section rom FLASH_DATA
const char buf1[ 6 ] = { 0 , 1 , 2 , 3 , 4 , 5 } ;
const char buf2[ 5 ] = { 10 , 11 , 12 , 13 , 14 } ;
#pragma section rom rom
main() {
}
"sect30.inc"
.section FLASH_DATA_NE , ROMDATA
.org 03000H
.section FLASH_DATA_NO , ROMDATA
のようにすると、データフラッシュに定数テーブルを配置することができる。アプリケーションノートデータフラッシュテーブル
に書いてあった。
試してみた。
sect30.incの70行あたり、ヒープとROMの領域を指定している間にデータフラッシュのセクションを追加。アドレス順がいいのかな?と思ってこの位置にした。
;---------------------------------------------------------------------
; heap section
;---------------------------------------------------------------------
.if __HEAPSIZE__ != 0
.section heap,DATA
heap_top:
.blkb __HEAPSIZE__
.endif
;---------------------------------------------------------------------
; データフラッシュ
;---------------------------------------------------------------------
.section FLASH_DATA_NE,ROMDATA
.org 03000H
.section FLASH_DATA_NO,ROMDATA
;---------------------------------------------------------------------
; Near ROM data area
;---------------------------------------------------------------------
.section rom_NE,ROMDATA
.org __ROM_TOPADR__
rom_NE_top:
.section rom_NO,ROMDATA
rom_NO_top:
;
さっきのプログラムにテーブルの宣言を追加して、シミュレーターで動かしてみてメモリをダンプすると、ちゃんとデータフラッシュのアドレスにテーブルが配置されていた。
#include "sfr_r8m12a.h"
void main(void);
#pragma section rom FLASH_DATA
const char table[13]={0,1,2,3,4,5,6,7,8,9,10,0x55,0xAA};
#pragma section rom rom
void main(void)
{
pd1_0=1; // 出力
while(1)
{
p1_0=0;
p1_0=1;
}
}
データフラッシュって名前だけど、データだけじゃなくてプログラムも配置して実行することもできる。データフラッシュもプログラムROMと同じく、ページ単位でしか消去できないんだから、データフラッシュを無くしてプログラムROMを倍の4Kにしちゃえばいいのにと思うけど、そう単純な話ではないのかな?
割り込みを使う場合は、コンパイラマニュアルの割り込みベクタテーブルの設定
を見る。
拡張機能リファレンス #pragma INTERRUPU
を見ると、#pragma INTERRUPU でベクタ番号も指定してベクタテーブルを定義できるみたい。
IOピンの駆動能力
地震で結構揺れたけど、まあ問題ない。
昨日、秋月から100円マイコンR8C/M12Aが到着したので動かしてみた。
まず、割り込みを使わずにLEDを点滅
"nc_define.inc"
__STANDARD_IO__ .equ 0 ; STANDARD I/O flag definition
__HEAPSIZE__ .equ 0H ; HEEP SIZE definition
__STACKSIZE__ .equ 080H ; STACK SIZE definition
__ISTACKSIZE__ .equ 080H ; INTERRUPT STACK SIZE definition
__VECTOR_ADR__ .equ 0fedcH ; INTERRUPT VECTOR ADDRESS definition
__ROM_TOPADR__ .equ 0F800H ; ROM TOP ADDRESS definition
#include "sfr_r8m12a.h"
void main(void);
void main(void)
{
pd1_0=1; // P1のBIT0を出力に設定
while(1)
{
unsigned int a;
p1_0=0;
for(a=0; a<4000; a++);
p1_0=1;
for(a=0; a<4000; a++);
}
}
簡単に使えた。プログラムの書き込みはH8とかSHと同じように、ルネサスの書き込みソフトFDTを使ってUSB-シリアル変換器で書き込む。USB-シリアル変換器がRS232じゃなくてTTLレベルの信号で使えるものだと、レベル変換が必要無いので便利に使える。完成品だと秋月で950円のAE-UM232Rとか。
RJ2タイマーのアンダーフロー割り込みでLEDを点滅
#include "sfr_r8m12a.h"
#include "stdint.h"
#include "r_tmr_rj2_timer.h"
void main(void);
void main(void)
{
// サンプルドライバでタイマーRJ2を使ってみる
// 1/125kHz*65535 = 約0.5秒ごとにタイマーのアンダーフローで割り込みが発生する
R_TMR_RJ2_Create_Timer(R_TIMER_RJ2_TIMER_OPTION_1,0,0xFFFF); // タイマーの設定
R_TMR_RJ2_Control_Timer(1,1); // タイマースタート
pd1_0=1; // P1のBIT0を出力に設定
asm("LDIPL #0"); // プロセッサ割り込み優先レベル0(この値よりも高いレベルの割り込みが受け付けられる)
asm("FSET I"); // 割り込み許可
//asm("FCLR I"); // ←割り込み禁止にする時
while(1);
}
#pragma INTERRUPT int_tmrrj2 // int_tmrrj2 関数は割り込みルーチンですよとコンパイラにお知らせ
void int_tmrrj2(void)
{
p1_0=~p1_0; // P1のビット0を反転
if(trjif_trjir==1) trjif_trjir=0; // 割り込み要求フラグクリア
}
"sect30.inc"
;
; 省略
;
;---------------------------------------------------------------------
; variable vector section
;---------------------------------------------------------------------
.section vector,ROMDATA
.org __VECTOR_ADR__
.if 1 ; ←割り込みを使うときには1にする
.lword dummy_int ; vector 0
;
; 省略
;
.lword dummy_int ; vector 21
.glb _int_tmrrj2 ; 割り込みルーチン登録
.lword _int_tmrrj2 ; vector 22 TIMER RJ2 割り込みルーチン登録
.lword dummy_int ; vector 23
;
; 省略
;
.lword dummy_int ; vector 62
.word dummy_int ; vector 63
.byte 0
.byte 02FH ; OFS2レジスタ
.endif
;
; 省略
;
;=====================================================================
; ID code & Option function select register
;---------------------------------------------------------------------
; ID code check function
.id "#FFFFFFFFFFFFFF"
; option function select register アセンブラの指示命令でOFSレジスタを設定
.ofsreg 0ABH
;
; 省略
;
OFSレジスタ(PICのコンフィグレーションワードみたいなもの)はアセンブラの指示命令で設定できるけど、OFS2レジスタはどうやって設定したらいいだろう?と考えたんだけど、割り込みベクタテーブルの設定にくっつけた(ベクタテーブルとは別にすると、アドレスが重複しているとリンカだかアセンブラだかに怒られた)。可変ベクタテーブルの開始アドレスを変更しないなら、これでいいんじゃないかと思う。
回路図。プログラム書き込み時にはMODEスイッチをショートしてからリセットすると書き込みモードになる。ブレッドボードで動かしたので、実際にはスイッチの代わりにジャンパー線を使った。あ、電源に0.1uFくらいのパスコン入れたほうがいいです。
R8C/M12AのUARTの非同期シリアル通信を使ってみた例。
#include "sfr_r8m12a.h"
void main(void);
void putch(unsigned char a)
{
while(ti_u0c1==0); // バッファが空になるまで待つ
u0tbh=0;
u0tbl=a;
}
void puts(unsigned char str[])
{
unsigned int a=0;
while(1)
{
if(str[a]==0) break;
putch(str[a]);
a++;
}
}
unsigned char getch(void)
{
while(ri_u0c1==0); // 受信するまで待つ
return (unsigned char)(u0rb&0xFF);
}
void main(void)
{
// 内蔵高速オシレーターに切り替え
prc0=1; // クロックレジスタアクセス許可
ococr=0b00000001; // 高速オンチップオシレーター発振 低速も発振
{
unsigned char a;
for(a=0; a<255; a++); // ここでオシレーターの発振が安定するのを待てとあるので適当に時間待ち
}
sckcr=0b01000000; // XIN/高速オシレーター選択で高速を選択 CPUクロック分周無し
ckstpr=0b10000000; // システムクロック低速/高速選択で高速を選択
phisel=0x00; // システムクロック分周無し
frv1=fr18s0; // 高速オンチップオシレーターを18.432MHzに調整
frv2=fr18s1;
// UART0の設定
p14sel0=1;
p14sel1=0;
p14sel2=0; // P1_4をTXD
p15sel0=1;
p15sel1=0;
p15sel2=0; // P1_5をRXD プログラム書込みに使うピンとは別なので注意
mstuart=0; // モジュールスタンバイ解除
u0mr=0b00000101; // 8ビット ストップビット1 パリティ無し
u0c0=0b00010000; // LSBファースト プッシュプル出力 フィルタON カウントソース分周無し
u0brg=119; // 9600bps
u0rrm_u0c1=0; // 連続受信モード禁止
u0tie=0; // 送信割り込み禁止
u0rie=0; // 受信割り込み禁止
te_u0c1=1; // 送信許可
re_u0c1=1; // 受信許可
puts("hello world!\r\n");
pd1_0=1;
p1_0=0; // LED点灯
while(1)
{
unsigned char a;
a=getch();
putch(a);
if(a=='\r')putch('\n');
}
}
RXDをプログラムの書込みに使うピンと同じに出来ないのが、ちょっと不便だなぁ。
R8C/M12Aのウエイト関数の例。
// クロックが 18.432MHz の時
void wait10us(unsigned char a)
{
unsigned char b;
for(;a>0;a--)
{
for(b=0; b<9; b++)asm("nop"); // だいたい10us 最適化なしの場合
//for(b=0; b<16; b++)asm("nop"); // だいたい10us ROMサイズを優先する最大限の最適化の場合
}
}
void waitms(unsigned char a)
{
for(;a>0;a--)
{
wait10us(100); // だいたい1000us
}
}
命令によってサイクル数が違って面倒なので、なんとなくいい感じの時間になるようにループするだけ。計算で時間ぴったりに出来ないかなと、しばらく考えたけどあきらめた。
食べ物を買いに行った帰りに、灯油も買おうかと思ったら1L88円とか93円とかだったので、買わずに帰ってきた。タンクにまだ50Lくらいあって、寒い日が続いた場合でも2週間くらいは持つから、値段が下がるのを待つことにする。
R8C/M12AでPIC16F88のLV-ICSPを試しているんだけども、1ブロック消去するBegin Eraseコマンドが動かない。コマンドを出しても消去されないでデータが残ったままになる。Bulk EraseコマンドとChip Eraseコマンドだとちゃんと消去されるのに、なんでだろ?
Load Data for Program Memoryコマンドで何か1ワード書いてからBegin Eraseコマンドを出すと動いた。データシートにそんなこと書いていないよう見えるんだけど、英語だから読み間違えなのかな?
リセット直後とかだとPCがプログラムメモリを指していなくてこうなるのかな?
100円マイコンのR8C/M12AでPICプログラマ(ライター)の作成中。PIC24のデバイスID読み出しが動くようになった。
とりあえず、PIC24FとPIC16F88の書込みができるようになる予定。
PIC24Fで書き込んだ値と読み出した値が一致しないぞーと、一日悩んで書込みルーチンを何度も見直していたんだけど、読み出しのほうのミスだった。
PIC24の書き込みとベリファイがまともに動くようになったので、LEDを点滅させるプログラムを実際にPIC24FJ64GA004に書きこんでみた。ちゃんとLED点滅した。
PIC16F88にも書き込みできるようになったので、LED点滅プログラムを書きこんでみたら、ちゃんと動いた。
PICプログラマの製作記事を書きました。あんな雑な回路図でも書くのにとても時間がかかっています。
余っていたPCを使えるようにしようと、これまた余っていたwindows2000をインストールしたらWindows2000ではGoogle Chromeをインストールできなかった。Google Chromeが使えないのは不便なのでwindowsはやめて、ubuntuをインストールした。
チップセットがAMD690Gなんだけどインストール時Xが起動して少ししたらフリーズしたりリブートしたりしてしまった。BIOSセットアップで AMD cool'n'quiet を Disabled にすると問題なくインストールできた。
ubuntu linux の自分用設定メモ
雪が融けたので、車の修理中。
初めてフェンダーを外したんだけど、泥が溜まっていた。泥で水の抜けが悪くなって、ますます錆びる状態。
バンパーを外すのがちょっと面倒だけど、バンパーを外せばフェンダーを外すのは簡単だった。
錆びて穴が開いている。
錆びて薄くなっているところを取り除いていくんだけど、
想像よりもひどい状態だった。面倒だ。
雨降りの日が多くて、作業があまり進んでいない。毎年4〜5月くらいは晴れの日が多いのに。
車検満了日が近づいてきたので、早く終わらせなきゃ。
溶接中。横向き、上向きの溶接は溶け落ちやすくて難しい。2秒くらい溶接したらいったん冷まさないと溶け落ちてしまう。同じ板厚でも下向きなら簡単。1.6mmと1.2mmの鉄板を使った。
やっと穴が塞がった。2.5mmの溶接棒5kg弱、200x300mm1.6mmの鉄板4枚、1.2mmの鉄板5枚使った。
車検を受けてきた。今回もモダ石油の30分車検で、合計60,000円だった。18年以上経った車の重量税減税が無くなったそうで、前回より数千円余分にお金がかかった。自作ECUのままで車検を受けたけど、何も問題無かった。
4点ロールバーにパットを巻いていないし、運転席側がバケットシートだけど、これも何も言われたことがない。助手席が付いていなかったり、シフトパターンの表示が無いときには怒られた。
畑を耕した後、2週間かけて雑草の根を取り除いて、やっと今日種蒔きをした。
去年手間なく収穫できた枝豆と人参のほか、テストで、大きく育たなくても食べられる大根とほうれん草の種を蒔いた。あと、なんとなく長ネギの種も蒔いてみたけど多分これは育たない。
去年は耕しただけで根が切れて、雑草はだいたい枯れるんじゃないかと思って根を取り除かなかったら、雑草生えまくりで、根が絡んでいるから草取りもやりにくかったので、今年は時間をかけて雑草の根を取り除くことにした。
昨日焼いたケーキが、とても美味しくふんわり出来たのでメモ。
これをまとめて適当に混ぜて、釜にバターがマーガリンを塗った炊飯器の早炊きモードで30分放置でできあがり。簡単。
重曹の苦味を中和するために初めて酢を入れてみたんだけど、酢と重曹が反応してたくさん細かい泡(二酸化炭素?)が出てよく膨らむ。酢のせいで味に影響が出るかな?と思ったけど、全然問題無かった。
砂糖50gだと結構甘めに出来上がるので、クリームを塗ったりするなら砂糖少なめがいいかも。
炊飯器で普通にスタートすると、米に水をしみこませる工程が実行されるんだけど、ケーキを焼く場合この工程は不要なので、早炊きモードがあればそれを使ったほうが良い。
焼き時間は炊飯器の機種や生地の水分量でかなり変わる。30分して爪楊枝でも刺してみて焼けているか確認して、まだ焼けていなければもう一度10分くらい炊飯すればいい。焼き過ぎたり、ケーキを焼いているのを忘れて保温のまま放置したりすると、水分が抜けてパサパサになってしまうので注意。
焼きあがったら、乾燥しないように熱いうちにラップを掛けて冷ます。ケーキは焼きたてよりも、一日くらい置いたほうが美味しい。
ちょうど2年使ったiphone3を解約して、ついでに献血してきた。
重曹と酢を混ぜて二酸化炭素が出るなら、水と砂糖を加えればサイダーになるんじゃない?と思ったので試してみた。
冷たい水200ccに重曹3gと砂糖スプーン1杯くらいを溶かした後、酢(普通の穀物酢)を20g加えると、シュワワワワーと泡が出て、サイダーになった。飲んでみるとたしかに炭酸水なんだけど、たぶん穀物酢のせいで、みりんのような味で爽やかじゃない。
こんど酢の代わりに、クエン酸でも買ってきて使ってみよう。レモン汁とかでもいいかも?
左フロントロアアームの偏心ボルトを交換しようと思ったら、錆びて二本のうち一本がどうしても抜けなかった。切断砥石でボルトを切ってロアアームを外してから、ハンマーで頑張って叩いてもやっぱり抜けない。
しょうがないからヤフオクで中古ロアアームに入札。
除雪機の走行クラッチの切れが悪くて、クラッチを切ってもすぐに止まらないので、ベルトを交換してみた。交換後、試運転では調子いいみたいだ。
付いていたベルトは結構ボロボロ。
走行のベルトがA35、除雪のベルトがB35を使用したんだけど、ベルトが少し長いような気がする。もう1インチ短いベルトのほうが良かったのかな?
いままで使っていた中古500円のホンダ純正オーディオの、右スピーカーから音が出なくなったので新品に買い換えた。
中国(香港?)の新宝宝デジタルというメーカーで、MilionってブランドのM1001Jという機種。eononと同じ会社だ。
本体のデザインはシンプルで無駄な装飾が少なく好感が持てるんだけど、表示がカッコ悪い。真ん中のビートルみたいな絵は邪魔だし、スペアナもどきも不要だ。バックライト付きの透過液晶表示なんだけど、直射日光が当たると表示が見えにくい。白黒の反射液晶のほうが見やすくていい。
CDとかカセットが無い代わりに、SDカードやUSBメモリのmp3ファイルを再生できる。前面AUX入力もある。mp3ファイルを再生できるのは便利なんだけど、自分の場合 itunes store で買った曲(AAC)も多いので、これまで通り AUX に ipod をつなぎっ放しで使うことにする。
光学ドライブが無い分値段も安くて、ヤフオクで2,400円だった。(即決価格が4,900円)
落札相場を調べてみたら、流通し始めたばかりの商品のせいか、たまたま特別安く落札できたみたい。
動作テストも終わって、取り付けようと思ったらネジ穴が無かった。
車種によっては、上の枠を使ってネジ穴無しで取り付けできるのかも?
分解してエビナットを付けてから取り付けた。
ホンダのデッキに合わせて加工してあるから、隙間がすごく大きい。気が向いたら隙間を埋めるかも?
音質はちゃんとしているし、AMラジオもよく入るしとてもいいんだけど、時計を表示させても数秒で曲名とか入力ソースの表示に戻ってしまうのが不便。常に時計を表示させることができたらいいのに。
エンジンをかけた後にミュートが解除されなくて、音量ボタンを一回押すと音が出る。ちょっと不自然な仕様だ。(2012年1月29日追記)この現象はバッテリーとアクセサリー電源を逆に接続していたせいだった。
(7月1日追記)ずっと時計表示のままに出来たり、ネジ穴があった方が便利だなというような事をメールで送ったら、けっこう丁寧な返事が来た。日本語があまり得意でないような文章で、ちょっと微笑ましい。
自作ECUが、やっと低い回転でもハンチングせずに安定してアイドリングするようになった。
水温が高くて気温も高いときにエンジン切ってからすぐに始動すると、空燃比が濃すぎてエンジンの掛かりが悪い。始動マップを修正したら治るかな?
ECUモニターにマップトレース表示を追加。
スピードメーターにリードスイッチを追加して、車速パルスをECUに入れるようにした。
まず、リードスイッチが反応するか確認。一回転で4パルス出てきた。リドードスイッチをかなり近づけないと反応しなかった。
基板の切れ端を使って、こんな感じに取り付けた。
配線は180km/hスイッチの端子を使って引き出し。元の180km/hスイッチの線(青い線)を切断して取り付けた。切断しなくても180km/hを超えると車速パルスが出なくなるだけで、実害は無いんだけど、せっかくだから切断。
しばらく走ってみると、距離は正しく計測できるのに、速度はずっと0km/hだった。計算間違いだ。直さなきゃ。
車速の計測を直した。
SPEED=((unsigned int)((60*60*IOCLK)/(637*1024*4)))/MTU22.TGRA;
// IOCLK=24000000
最初上のように計算していたんだけど、60*60*IOCLK のところでオーバーフローしているようだったので、
SPEED=(unsigned int)(60*60*(IOCLK/(637*1024*4)))/MTU22.TGRA;
順番を入れ替えたら、正常に速度を表示するようになった。
実際に走ってみると、一定速で走っても1割くらい速度のばらつきが出た。メーターの磁石の配置が均等じゃないんじゃないかと思う。
過去4回分の平均をとって、値を安定させるようにした。
自作ECUで、いままでエンジンを切ってもCPUが動きっぱなしだったのを、エンジンが停まったらソフトウェアスタンバイモードに移行するようにした。待機時の消費電流が 31mA から 6mA に低下した。もっと電流少なくなるかと思っていたけど、データシートを見ると電源回路の 7805 の無効電流が 4〜6mA くらいあるようなので、こんなもんか。純正ECUの待機時の消費電流は 1.6mA 程度と少ない。
3端子レギュレータって負荷が無い時でも結構電力を消費するんだな。いままで考えたこともなかった。
20kmくらい走った後のマップトレース表示。いい感じに動いているみたい。
ノックセンサー入力のハイパスフィルターを変更して低域をごっそり削ったら、信号がずいぶんと小さくなってしまった。
eeepc901 で自作ECUのファームウェアをビルドをすると(SSD のせいで)遅いので、中古のノートパソコンを買った。ソフマップの通販で 11,800円 送料無料だった。(ThinkPad R50e Celeron M 1.30 GHz Dothan-1M 1024 MB)
7年くらい前の機種だし値段も安いから、程度はかなり悪いだろうと思っていたんだけど、到着してびっくり。メーカーで再生した IBM Refreshed PC で少々傷はあるけどもすごく程度の良いものだった。光学ドライブは交換されているのか新品同様で読み取り快調だし、キーボードを外して CPU ファンを見てみてもほとんど埃がついていない。
OS は windowsXP PRO でオフラインフォルダを使えるのが便利。新品のリカバリCDも付属していた。
性能は Atom のネットブックと同じくらいで、動画のエンコードをしないならこの程度の性能で十分な感じ。
画面がずいぶん黄色っぽいなと思ったんだけど、デジカメでホワイトバランスを太陽光に固定して写してみると、ThinkPad が黄色いんじゃなくて、他のモニタが青かった。
とてもいい買い物だった。
パージソレノイドバルブ駆動回路の2Wの抵抗の発熱が大きいことに気がついた。PWM掛けて電流減らしたほうが良さそうだ。
うちの windows vista でどうしても 文化放送 超!A&G+ が聞けなくて windows XP の小さいパソコンを用意して聞いていたんだけど、マイクロソフトの Windows Media Player 11 を使用してデジタル著作権管理 (DRM) で保護されたメディア ファイルを再生しようとしても、メディアが再生されないことがある というサポート情報を見つけて、この通りにしたら聴けるようになった。一年以上悩んでいた問題がやっと解決した。
windows XP で、メディアプレーヤーを更新したら 超!A&G+ が聴けなくなったような時も同じ方法で解決できた。
引越ししてから一部屋だけまだ畳をはぐっていない部屋があったので、荷物を全部出して畳をはぐって掃除した。荷物を出すのが大変。
自作ECUで水温が高いとエンジンがかかりにくかったのを、始動処理を修正してだいぶ調子良くなった。
パージソレノイドバルブ駆動回路の 3.9Ω 2W の抵抗の発熱が大きいので電流を測ってみたら、0.42A とたいした値じゃなかった。抵抗で 0.68W の消費でまだまだ余裕があるから、電流を減らさずこのままでいいや。
電気代節約のためにFreeNASを廃止するので、最後に記念撮影。あれ?しばらくコンセント抜いていたら、日付ズレた?
NASの空きがなくなったので、2TBのHDDへコピー中。1TBのHDD3台をRAID5で使っていたんだけど、中身はアニメの録画だから消えてもいいやと、バックアップ無しで2TBのHDDに保管することにした。
自作ECUのセッティングがだいたい完了した。あとは冬でもエンジンが掛かるか、夏に吸気温度が高くてもノッキングしないかを確かめて、水温補正と吸気温補正を見直すくらいだ。
空燃比は、負荷の軽い領域ではO2センサーで理論空燃比にフィードバック制御して、負荷がかかると11.3くらい、アクセル全開で10.5くらいになるようにしてみた。冷却を期待して出力空燃比よりも濃い目にしてある。
空燃比計を持っていないので、まず純正のO2センサーを使って全域が理論空燃比(空気14.7g:ガソリン1g)になる噴射マップを作って、空燃比を11.3にしたければ(14.7/11.3で)噴射時間を1.3倍、空燃比を10.5にしたければ(14.7/10.5で)噴射時間を1.4倍にすればいいだろうといった処理にした。
点火時期は、負荷の軽い領域では純正ECUと同じくらい。負荷のかかる領域ではノッキングが出ない程度に進角してある。高回転では10度くらい点火時期が進んでいるんだけど、5000回転以上は怖いので、まだ甘めのセッテイングになっている。
1速5000回転くらいでクラッチをつないで急発進をしてみると、純正ECUよりかなりトルクが出ているような感じがする。気のせいかな?
普通に加速した場合は、純正ECUよりもスムーズに吹け上がる。これは、純正ECUの点火時期が5000回転を超えたくらいで遅くなっているけど、それを無くしたからだ。
ノッキングの判定はまだうまく動かない。ECUで読み取ったノックセンサーの値がなんだかおかしいので、入力回路が良くないみたい。車が走っている状態じゃなきゃテストできないので、オシロスコープで波形の観測が出来ず困り中。
あ、前に録音した波形を入力してみたらいいのかな?こんど試してみよう。
ノッキングがどういうものか判らなかったので、ノックセンサーを付けようと思ったんだけど、実際にノッキングを体験してみるとノックセンサーが無くても耳で聞けば十分と思って、ノックセンサーの処理をやる気があまり出ない。
たぶん初霜。いつもより遅いかな?
初雪。いつもより少し早い。
バイク冬眠、溶接機冬眠、除雪機試運転。
携行缶のガソリンが無いので買ってこなきゃ。
データロガーを作って、純正ECUと自作ECUで回転数の上昇具合をくらべてみた。(2速全開加速)
困ったことに純正ECUに負けている。3000〜4500回転くらいで純正ECUに負けて、その差が変わらないまま回転が上がってゆく。回転数が1000上昇するのに0.1秒程度の差だから体感では全然判らないので、リアルタイムで差を表示するようなデータロガーを作らないとセッティングに手間がかかって大変だ。動力計に載せた状態でセッティングできると楽なんだろうな。
点火時期はこんなもんだと思うので、空燃比だろうか?
横軸に回転数が来るようにロガーを作り直さないと、グラフの位置合わせが面倒で比較しにくい。
今は0.1秒ごとに回転数を記録するようにしているのを、10RPM上昇するごとに上昇にかかった時間を記録するとかでいいかな?
2速全開加速じゃなくて、もっと高いギアでデータ取ったほうが細かく計測できるんだけど、それだとスピードが出過ぎるので無理だ。