ATmega128のタイマ捕獲機能を使ってジャイロ値読み取り
すいません m(_ _)m、本記事はブログ引越時に書式が崩れました。順次修正中です。
さてっと、というわけで、ATmega128、早速、基礎実験です。
ATmega128には、タイマ0~3まで、4種類のタイマが搭載されていますが、このうち、サーボPWM出力やジャイロ取り込みに使えそうなタイマは、タイマ1とタイマ3です。この2つのタイマは16ビットで動作しますので、サーボPWMの分解度から考えると、一番素直な選択だと思います。というわけで、まずは一発もの、ということで、このタイマを使ってジャイロのインプットキャプチャをやってみようと思います(経験上、ジャイロ取り込みには苦労した事が無いのですが、サーボPWMはよく泣かされていますので…)。
一般的なラジコン用ジャイロは、サーボ信号の補正が目的であるため、サーボ制御信号を入れると、ジャイロの状態によって補正した信号が出力されます。ロボットに使用する場合も、ただ補正に使用するためだけに入れるのであれば、同じようにコントローラとサーボの間に接続すればいいのですが、実際には、状況によってジャイロの効きを変えたり、オフしたりしたくなります。そんなわけで、一旦、コントローラで取りこんで、コントローラ内でサーボ制御に反映するほうがスマートです。
最近は、ロボット専用という感じで、入力信号が無くてもパルスを出すものや、アナログ出力するものがありますが、ここでは手持ちのKRG-1をATmega128に接続してみます。
KRG-1
KRG-1は、近藤化学のロボット用ジャイロです。モードは2つあり、入力信号を補正するモードと、1500usを中心にして各速度に応じたパルスを出力するモードがあります。デフォルトでは、出力するだけのモード(ロボットモードだったかな)になっています。今回は、入力信号を与えなくても動作するということから、デフォルトで使用します。
他にも、パルスを出す周期を変える事ができるみたいですが、変更には、「ICS PCインターフェイス Red Version」というケーブルが必要になります(これが6,000円と、結構なお値段で…自作できないのかなー、ほしいなー、ほしいなー、誰か不要になった人いないかなー)。
パルス幅の計測
ATmega128のタイマ1/3には、捕獲機能があります。いわゆるインプットキャプチャという機能です。外部から信号変化を与えると、タイマカウンタの値をレジスタにコピーします。
信号変化は、LOW→HIGHとHIGH→LOWを切りかえることができます。そのため、この機能でジャイロのパルス幅を計測しようと思うと、LOW→HIGHの値を取得したら、今度は切り変えてHIGH→LOWの変化を取得できるように設定する必要があります。この切り替えタイミングは、計測完了(タイマカウンタの値をレジスタにコピー)した時に割り込みを発生することができますので、これを使います。
また、シンプルにはタイマカウンタを毎回クリアする方がわかりやすいのですが、次のステップで同じタイマを使ってサーボPWM信号も出すことも考慮し、タイマカウンタは走らせっぱなしにして使用します。
そうすると、パルス幅を計算するときにちょっと工夫が必要になります。LOW→HIGHの時に取得した値が、HIGH→LOWで取得した時の値より小さければ、そのまま「2つめの値 – 1つめの値」がそのままパルス幅になるのですが、最初に取った値より後に取った値の方が小さくなることがあります。
これは、タイマカウンタが16ビットなため、65535の次は0になります。例えば、最初に取った値が「65000」、そして後に取った値が「1000」になる、ということがあります。でも、これはちょっと計算を工夫してやれば解決できます。具体的にはこんな式になります。
( 65535 – 最初の値 ) + 後の値
これをif文で切り変えてやれば大丈夫です。
プログラム
動作確認はベストテクノロジのGDL1.7.0.4を使用しました。ATmega128でのプログラミングはまだ始めたばかりなので、いろいろ危ないところがあるかもしれません。今のところ、ベストテクノロジのサンプルプログラム、HERO’S Downloadで日本語化されたATmega128のマニュアル、Front Page – AVR Wiki、AVR Libc日本語翻訳、HIDEさんのHIDEロボ、あとはMAGOさん(RE001使いの友人です)のMAGO Labsを参考にさせていただいてやっています。ATmega128を使っている方は多そうなんですが、意外と情報が少ない感じです。
さて、プログラムのほうですが、まずはハードウェアの初期化です。今回、タイマ1を使用しますので、タイマ1を初期化します。BTC068は16MHzで動作していますので、8分周で使用します。これで、16ビットカウンタですと、最大、32ms程度まで計測できることになりますので、「20ms毎に入ってくるパルスを計測する」には十分だと思います。
TCCR1A = 0x00; // タイマ標準動作モード
TCCR1B = _BV( CS11 ); // クロック選択(8分周)カウント0.5us
捕獲入力を使うためには、まずは該当するI/Oポートを入力方向に設定します。実際には入力がデフォルトなので、初期化は不要と言えば不要です。
DDRD = 0x00; // PD入力
後は、タイマ1の捕獲入力の設定ですが、まずは立ち上がりエッジで最初の部分を検出する必要がありますので、立ち上がり検出設定をします。また、割り込みによって値の取得などを行いますので、捕獲入力による割り込みを許可します。
TCCR1B |= _BV( ICES1 ); // タイマ1捕獲立ち上がりエッジ
TIMSK |= _BV( TICIE1 ); // タイマ1捕獲割り込み許可
あとは適当にグルグル回りながら、ジャイロ値を表示でもしていればいいんですが…っと。肝心の割り込み処理の方を忘れました。割り込みを使用するには、「SIGNAL()」という関数を使用します。この関数に「SIG_INPUT_CAPTURE1」という値を設定すると、入力捕獲の割り込みルーチンになるようです。「SIG_INPUT_CAPTURE1」なんてどこから出てきたの?と疑問が出てくるかもしれません。コンパイラに同梱されているヘッダファイルから見つけました。iom128.hというヘッダファイルを、「SIG_」で検索するといろいろでてきます。定義名とマニュアルに書いてある用語を照らし合わせて、きっとこれだろうーって感じで使ってます。
というわけで、割り込み関数の宣言はこんな感じです。
SIGNAL( SIG_INPUT_CAPTURE1 )
※2006/09/15 0:26追記:その後の調査で、SIGNAL()はWinAVR 20050214以前のもので、WinAVR 20060125以降は、ISR()を使用することが推奨されていることがわかりました。GDL1.7.0.4で確認したところ、使用できました。
そして、立ち上がりエッジと立ち下がりエッジの切り替えは、値を読み出したときに信号の状態をチェックして、「読んだ時HIGHならば立ち下がりエッジにして、次の立ち下がり時にカウント」という感じにしました。また、立ち下がりエッジ検出時は、ジャイロ値の計算をして、値を設定するようにしています。
if(( PIND & _BV( PIND4 )) != 0 ){
TCCR1B &= ~(_BV( ICES1 )); // 捕獲入力エッジを立ち下がりに変更
GDusIcr1Up = ICR1; // ICR1値を読み出す。
}
else{
TCCR1B |= _BV( ICES1 ); // 捕獲入力エッジを立ち上がりに変更
GDusIcr1Dn = ICR1; // ICR1値を読み出す。
// ジャイロ信号幅の計算と設定
if( GDusIcr1Up < GDusIcr1Dn ) GDusGyro = GDusIcr1Dn – GDusIcr1Up;
else GDusGyro = ( 0xFFFF – GDusIcr1Up ) + GDusIcr1Dn;
}
これでジャイロのパルス幅を読み取ることができます。というわけで、全プログラムを見たい方は、以下のリンクよりソースコードをダウンロードして下さい。
プログラムをみると「uint8_t」といったデータ型が使われているのがわかりますが、この環境ではこれを使うことがお作法のようです(こういうのが、標準的なライブラリとかを開発するときの障害になったりするんだよねー)。
というわけで、「ICS PCインターフェイス Red Version」があまっている、不要になったという方、いらっしゃいましたら連絡ください(結構、真剣)。
こんにちは。いずみかわです。
昔私もKRG2買ったときに一緒に「ICS PC I/F Red Version」買ったのですが、正直なところ、SISOさんなら自作可能な代物です。
設定用ソフトはKOのサイトでダウンロードだし。
え~これが6000円!?って感じの。
公開したらまずいんだとは思いますが...