μsecの戦い

そんなわけで、FOMAをパソコンから操作するハードが完成したので次はソフトを書いていくのだが、こいつはC#で書く。PIC16F877をいま電力消費を抑えるために4MHzで動作させる。4MHzってことは、1秒間に1M回(100万回)命令を実行できるってことだ。(PICは4サイクルで1命令を実行するRISCチップだと考えればヨロシイ)


ということで、1命令を実行するのに要する時間は 1/M 秒 つまり、1μ秒である。ということで、こいつとネゴシエートしようと思うと必然的にμ秒単位のwaitルーチンが必要になる。


まあ、そんなもんは、CPUのクロックタイマを使えばチョチョイのチョイである。Pentiumシリーズにはクロックレベルの分解能を持った64ビットカウンターがあるのだ!

RDTSC 命令:
http://www.wnishida.com/~wmemo/?date=20031001


とまあ、RDTSC命令を使ってもいいのだけど、C#にはインラインアセンブラがないので、この際だからQueryPerformanceCounterで我慢しておこう。こいつは、通例、3.5MHz(=1/3.5M秒)程度の分解能があるからまあ、5μsec程度のwaitルーチンならば十分だ。1μsecのwaitルーチンを書くにはちょっと精度が足りない気はするが...。


てなわけで、ネゴシエート用のスレッドひとつまわして、そいつのなかで、↓みたいな感じでwaitをかけながらパラレルポートに接続されたPICマイコンと通信するのがお手軽だ。

[DllImport("kernel32.dll")]
extern static short QueryPerformanceCounter(ref ulong x);
[DllImport("kernel32.dll")]
extern static short QueryPerformanceFrequency(ref ulong x);

ulong nowtime,freq;
private void Init()
{
unsafe
{
QueryPerformanceFrequency(ref freq);
QueryPerformanceCounter(ref nowtime);
}
}

// μ秒だけ待つ
private void Wait(ulong waittime)
{
ulong now = nowtime;
while (true)
{
unsafe
{
QueryPerformanceCounter(ref nowtime);
}
if (nowtime >= now+waittime*freq/1000000)
break;
Thread.Sleep(0);
}
}

この手のプログラムを書かせると、ときどきThread.Sleepをなくして超負荷状態*1にしてしまう馬鹿野郎様がいらっしゃるんだが、とりあえずWindowsのスレッドの仕組みを基礎から勉強してきやがれ!である。(`ω´)


とまあ、C#でμsecのwaitとか言ってる時点で、C#のプログラムらしからぬ気はする。id:akirameiさんからは、「やねうらおさんに手に掛かるとC#でもハードばりばりなプログラミングに(笑)」とお褒めの言葉を授かる始末だ。どうしてくれよう。

*1:「負荷」をCPU負荷の意味で使うなら、「超負荷状態」という表現はおかしい。この「負荷」はそういう意味で使っているのではなくて (適切な用語を知らないのだけど)あるプロセスがどれくらいの頻度で次のスレッドにSleep等でバトンタッチするかの尺度として「負荷」と書いている。