Pi:Co Classic製作記(移行中)

マイクロマウス製作五十五日目

こんにちは。
猛暑と言ったり冷夏と言ったり、本当に今年の夏の気候はあまのじゃくです。
夏バテしないよう、今夜の夕食はカレーを作りたいと思います!
(ですが、一人暮らしでカレーはなかなか食べきれないので大変です…。)

さて、今日のマイクロマウス研修も、「壁切れ補正」実装に向けて、のり先生からの授業です。
interrupt.cのプログラムを上から順に見ていきます。

R0011933

そんな本日のメカラウロコはこちら。
どうやってPi:Co Classicが時間の感覚を持っているのか?

「wait_ms(1000)」等々頻繁に使用しているジェニちゃんが持つ時間の感覚。
実はずっと「どうやってやってるんだろう」って、疑問に思っていたことです。

のり先生によると、それは、水晶発振子というヤツのおかげだそうなのです。
(まさか水晶が登場するとは思わなかった…)

R0011961

これが1ミリ秒というのをほぼ正確にずっとカウントしている。

timer++ ; //1mSごとにカウントアップ

↑これがプログラムでカウントしているところ!

でも、ずーっとカウントし続けていても意味がない。
例えば「wait_ms」で、何か処理を始めて終わる時間の目印になるにはどうしているのか?

答えはこちら;


void wait_ms(int wtime) //mS単位で待ち時間を生成する
{
//引数wtime[mS]待つ

unsigned int start_time; //関数が呼び出された時間を記録する変数

 start_time = timer; //関数が呼ばれた時間を記録

 while( (timer - start_time) < wtime) ; //指定された時間経過するまで待機

}

式だけ見てもピンと来ない。
よし、わからないことがあったら代入だ!

例えば、3000まで数えていたとして、そこから「wait_ms(1000)」がスタートしたとする。
そのスタートしたタイミングが「start_time」として記憶される。(この場合は3000。)
それからもずーっとタイマーは走り続ける。例えば3500になったときはこんな式になります。

3500(現在のタイマーのカウント) - 3000(スタートしたときの値) < 1000(wait_msで指定した値)

が成り立つので、処理を繰り返します。
そして、実際に1000ms待ったとき、

4000(現在のタイマーの値) - 3000(スタートしたときの値) < 1000(wait_msで指定した値)

となり、式が成り立たなくなるので、ついに処理を抜け出します。なるほど!

ふおおお、何気なく使っていたwait_msはこういう仕組みだったんですね。
先日の電圧監視用LEDのプログラムといい、
教わってみたらシンプルな仕組みだなと思うことが多いですが、それだけにメカラウロコ感がすさまじいです。

さてさて。
肝心の壁切れ補正を実装するために、さらにさらに割り込みのサンプルプログラムを見てみました。
Pi:Co Classicの割り込み用タイマー「CMT」の
CMT0では、
・電圧監視→LED制御
・速度制限(加減速)
・壁センサによる姿勢制御の計算(計算のみ)
・モータ用レジスタ代入
・時間生成
を行っており、
CMT1では、
・センサからのデータ読み込み
・左右の壁のあるなし判断(あるなしのみ)
を行っています。
のり先生と、CMT1のプログラムを見てみたところ、割り込みを使用した難しいプログラムを書かずとも、
すでにあるサンプルプログラムを元に壁切れ補正が実現できそうとのことです!

CMT1ですでに使われている「左右の壁のあるなし判断」を元に書けるそうなので、教わりながらやりたいと思います♪

ちなみに、ご参考までに
CMT1の件のプログラムをお見せします。
右壁のあるなしを判断している部分です。


case 0: //右センサ読み込み

SLED_R = 1; //LED点灯
 AD0.ADCSR.BIT.CH = 3; //AD変換のチャンネルを選択
 for(i = 0; i < WAITLOOP_SLED; i++) ; //待ちループ  AD0.ADCR.BIT.ADST = 1; //AD変換開始  while(AD0.ADCSR.BIT.ADF == 0); //AD変換終了まで待つ  AD0.ADCSR.BIT.ADF = 0; //フラグクリア  SLED_R = 0; //LED消灯 sen_r.p_value = sen_r.value; //過去の値を保存  sen_r.value = AD0.ADDR3 >> 6; //値を保存

if(sen_r.value > sen_r.th_wall) //壁の有無を判断
 {
 sen_r.is_wall = true; //右壁あり
 }
 else
 {
 sen_r.is_wall = false; //右壁なし
 }

 if(sen_r.value > sen_r.th_control) //制御をかけるか否かを判断
 {
 sen_r.error = sen_r.value - sen_r.ref; //制御をかける場合は偏差を計算
 sen_r.is_control = true; //右センサを制御に使う
 }
 else
 {
 sen_r.error = 0; //制御に使わない場合は偏差を0にしておく
 sen_r.is_control = false; //右センサを制御に使わない
 }

 break;

sen_r.is_wall = true; (右壁あり)が、sen_r.is_wall = false; (右壁無し)になる瞬間が大切な気がする。
ジェニちゃんを、その名の通り天才マウスにするべくがんばるぞ~!

タイトルとURLをコピーしました