ATmega128のタイマ比較機能を使ってサーボPWM出力・構想編1

公開日:  最終更新日:2014/06/05

すいません m(_ _)m、本記事はブログ引越時に書式が崩れました。順次修正中です。

ℹ️本記事にはPRが含まれています。

さて、今度はサーボPWM出力です。今回は、外に分配基板を接続する前提ですので、まずは単純にタイマ1/3の「比較出力機能」を使い、3本のサーボPWMを作ってみようと思います。


サーボPWMを作るには?
20ms程度に一度(16msが本当かな?)、信号を短時間HIGHレベルにする事により、このHIGHレベルの時間によってサーボが指定角度まで動作するものです。サーボにもよりますが、GWSなら0.6msから2.4ms程度、JRサーボならば900us~2100usぐらいで180度動作します。


ということは、20msの間に、実際に信号を出しているのは2.5ms程度の時間のみで、後は待っているだけの、結構、暇なPWMになります。で、この2.5msの中で必要な分解度を持たせなければいけないので、そうなると最小単位は少なくとも10us、できれば1us以下にした方がいいことになります。また、カウンタにいしても、少なくとも2.5msのカウントができ、できれば20msのカウントができるときっとプログラムが簡単になると思います。


BTC068の場合、16MHzで動作していますので、クロックを1/8で使用すると1単位が0.5usとなり、ちょうどよさそうです。そうなると、8ビットでは0.128msしかカウントできず、16ビットだと32.768msカウントできるのでちょどいいな、ということになります。実はそういう背景もあって、捕獲入力の時もこのモードで動作させています。


また、16ビットカウンタである、タイマ1とタイマ3は、比較出力が3本ついています。それぞれA、B、Cと呼ばれています。この比較レジスタに値を設定する事で、同時に3つのPWMを作る事ができます。比較出力とは、「タイマが比較レジスタに設定された値になったら、出力信号をHIGHにしたりLOWにしたりすることができる」機能です。ハードウェア的に動作しますので、設定しておけば勝手にやってくれるため、その時のソフトの状態(例えばシリアル通信でデータ受信中とか)に影響されないPWM信号を作る事ができます。


まずは分配の事はおいといて、比較出力を使って「20msに一度、サーボ制御信号を作る」プログラムを作っています。


タイマモード
タイマ1、タイマ3は、機能豊富なタイマでして、PWMを作れそうな機能としては、「標準動作」と「高速PWM動作」があります。


高速PWM動作は、値を設定しておけばひたすらそのPWMを出力し続けてくれるモードです。これを使えば、すごく簡単に作れそうなのですが、あえてここでは使いません。というのも、高速PWM動作を使うと、周期を決めるのに、OCRnA(比較レジスタ)かICRn(捕獲レジスタ)のどちらかを上限値として使ってしまうからです。そうすると、比較出力が2つになる、もしくは捕獲機能入力が使えなくなってしまいます。


捕獲機能はぜひとも併用したいということで、「カウンタをクリアせずに使用する」と、「サーボ制御のために捕獲レジスタは使用しない」を目標としてサーボPWM生成プログラムを作る事にします。


というわけで、タイマモードは「標準動作」を使うことにします。


プログラム構想
割と素直に考えると、「まずは前回の最後の割り込み(サーボ信号LOWにするための割り込み)を使って、次のHIGHにするタイミングを比較レジスタに設定して、タイマ機能で信号をHIGHにし、HIGHになった時の割り込みで、次のタイミングでLOWになる設定をする」というが一番てっとり早い感じがしますが、実はこれだとうまくいきません。


先に書きましたが、割り込みは、比較レジスタAとBの一致でしかかけることができません。比較レジスタCもかけれれば、一番最後にLOWになる比較一致出力で割り込みをかけて、「一致時HIGH/LOW」の切り替えを行えばいいのですが、残念ながら、比較レジスタCはその機能がありません。もし、比較レジスタAのみで切り変え処理をやってしまうと、他の比較レジスタの値の方が大きい場合、他の信号がLOWになる前に、「次はHIGHにしておくれ」という指示を出してしまうからです。


下の絵を見てください。A、B、Cが比較レジスタです。比較レジスタAで割り込みをかけると、①、②のポイントで割り込みが掛かる事になります。で、先の、「一致時HIGH/LOW」設定を切り変える処理も割り込み処理中に書く事になりますので、切り替わりのタイミングもここになります。そうすると、②の時点で、比較出力BはまだHIGH状態にも関わらず、「一致時HIGH」の設定をしてしまうことになります。


   ↓先頭      ~2.5ms↓
A |----________----________
B |----------__----------__
C |---_________---_________
   ①    ②           ①    ②


そこで、ちょっとトリッキーですが(「トリッキー」と書くと、ついついムイムイさんのTORICKYを思い出してしまいます)、割り込みを3回使います。この「割り込み3回案」は1粒で2度おいしいです。2度目のおいしさについては、後で説明します。


簡単に言うと、「サーボ制御信号の最大値を2.4msとして、0.1ms、LOWな時間をおく」ことです。0.1msが十分な時間かどうかはまだわかりませんが、これは使ってみて調整するしか無いかもしれません。割り込みはタイマだけじゃありませんので、他の割り込みハンドラの書き方によっても変わってくると思います。


   ↓先頭      ~2.5ms↓
A |_----________----_______
B |_----------__----------_
C |_---_________---________
   ①②    ③         ①②    ③


まずは①で、「0.1ms待ち、次の比較一致でHIGHにする」という設定をします。この時点では、前回の制御サイクルで信号がすべてLOWになっています。そして、②にて、「それぞれの比較レジスタに必要な値を設定し、次の比較一致でLOWになる」という設定をします。最後に③では、「比較一致レジスタAに次の周期の先頭になった時に割り込みが掛かる」という設定をします。


これにより、正確なPWMを得ることができるだけでなく、さらに、将来的に分配基板を外部に接続した場合でも、①という、明確な切り替えタイミングができますので、よりエレガントな感じです。これが2度目のおいしさです。 将来構想も含めて整理すると以下の通りです。



  • ①割り込み(アイドル)
    全比較一致レジスタに0.1msを設定。比較一致でHIGHになる設定をする。また、分配信号を出力。

  • ②割り込み(サーボ信号をHIGH)
    それぞれの比較レジスタにサーボPWMの幅を設定。比較一致でLOWになる設定をする。

  • ③割り込み(サーボ信号をLOW)
    比較レジスタAのみ、次の周期の先頭になる値を設定。

これでいけそうです。


気をつけないといけないのが、比較レジスタへ設定する値です。タイマカウンタ(TCNT)は今回、走らせっぱなしで使います。例えば、0.1msを設定する場合に「OCR1A = TCNT + 100」 とやってしまうと、このコードが走っている間にTCNTが進んでしまう恐れがあります。そのため、比較レジスタから前回設定値を取りこんで、それを使って再設定してやる必要があります。「OCR1A += 100」って感じです。


落とし穴…
こんな感じでできるんじゃないかなー、と思ったら落とし穴がありました。それは、「もし、PWM設定値が2.4msだったら何が起こるか?」ということです。そうすると、③と次の①の割り込みが同時に発生させないといけなくなってしまいますよね。でも、割り込みっていうのはオーバーヘッド(割り込むための時間)が必要ですので、うまくいきません。ということは、ここで何か小細工をするとか、サーボ制御の最大仕様を小さくするとか、あとは制御周期を伸ばす等の方法を取らないといけませんが、どうも納得のいくアイデアがでてきません。


しょうがないので、「お尻あわせ方式」で考え直そうと思います。まあ、ここまでで、だいぶレジスタの使い方がわかってきたので良しとします。

  • このエントリーをはてなブックマークに追加
  • Pocket

関連前後記事

Your Message

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

*

PAGE TOP ↑