PWMモジュールでサーボ制御
そろそろPSoCも、もうちょっとロボットなコンテンツということで、今日はサーボパルスを出力してみようと思います。
サーボパルスに最適なのは…PastelMagicさん
のチュートリアルにもあります、PWMによるLED点滅を応用すればできそうです。
このPWMモジュール、周期とパルス幅を設定することができます。一般的にサーボ制御信号は、20ms周期で、
その中で、ON時間を600〜2400usぐらいにしてやると、それに応じてサーボの動作角度が決定されるようになっています。
で、1500usぐらいにするとニュートラルって感じです。PWMモジュールにはいくつかの種類があるのですが、
その中のPWM16(16ビットのPWM)を使用し、1usのクロックを入力してやれば、20000カウントで20msecになり、
1500カウント分ONしてやればサーボ制御信号のできあがり…となるはずです。それではさっそくいってみようと思います。
今回のプロジェクト名は…「ServoPWM」とでもしておきます。
クリックで拡大します。
|
では、さくさくっとプロジェクトを作ります。プロジェクト名は「ServoPWM」とでもしておきます。
今回使用する主役モジュールは、PWM16です。これは「PWMs」の中から選択することができます。
まずはこれをちょいちょいっと選択しまいましょう。そうすると、デフォルトで「PWM16_1」というモジュールが追加されます。
今回は、これしか使いませんから、「Interconnect」画面へ移動しましょう。
|
クリックで拡大します。
|
まずはいつものように「Global Resources」を設定します。
デフォルトがなんで「8_MHz」なのかよくわからない「CPU_Clock」ですが、今回もこのまま行きます。
で、PWM16_1に与えるクロックをここで設定するわけですが、「1us」単位を作ってみます。
1usという単位を作るためには、周波数は1MHzを使えばよいいので、
SysClkである24MHzを24分周してやればいいことになります。VC1とVC2を組み合わせて24分周してもいいのですが、
VC3が256まで分周できますので、VC3で一気に行きます。というわけで、今回のポイントは、
「VC3 Divider」を「24」にセットすることです。それにしても、なんで「VC3」だけ「Divider」なんでしょうね〜
…と疑問に思ったのですが、考えてみると「VC3」だけは、いろいろなクロック源が使えるんです。
それで、「VC1」、「VC2」と同じ表記が使えないからみたいです。
|
クリックで拡大します。
|
次は、今回の主役モジュール、「PWM16_1」の設定に移ります。まずは「Place」します。
|
クリックで拡大します。
|
「Place」したら、今度はモジュールの設定です。今回は、「20msecの全体周期を作っておいて、
1.5msだけオンしておく」というのが目的ですので、以下のようになります。
- 「Clock」…このために1MHzを設定した「VC3」とします。
- 「Enable」…きっと「有効にする」という意味だろうということで「High」。
- 「CompareOut」…「Row_0_Output_0」にします。今回、サーボパルスをPORT0[0]に出そうと思っています(MiniEval1のTP6…LEDのすぐ隣)。
さすがのPSoCも「どれでも設定でOK」と言うわけではありません。そのため、PORT0[0]に接続できる「Row Output」を選択する必要があります。
- 「TerminalCountOut」…よくわからないけど、使ってない気がするので「None」
- 「Period」…これで全体の周期を設定します。今回、「1us」が入力ですので、「20000」で「20ms」になります。そんなわけで、「-1」して「19999」をセット。
- 「PulseWidth」…信号がオンになっている期間です。デフォルトで「1.5ms」を出そうと思いますので、例によって「-1」して「1499」とします。
- 「CompareType」…例によって「Less Than Or Equal」
- 「InterruptType」…割り込みは使用しないので、今回は適当。
- 「ClockSync」…相変わらず、意味はよくわかってませんが、いつもの「Sync to SysClk」
- 「InvertEnable」…とりあえず「Normal」
|
クリックで拡大します。
|
そして、「Row_0_Output_0」を「PORT0[0]」まで接続します。直接接続するのではなく、まず、
「Row_0_Output_0」を「GOE0」(グローバルアウトイーブン0)へ接続します。
そのためには、まずは「Row_0_Output_0」の先っちょについている四角い箱をクリックします。
そうすると、「Digital Interconnect Row_0_Output_0」というダイアログボックスが表示されます。
|
クリックで拡大します。
|
この「Digital Interconnect Row_0_Output_0」中の、一番上の三角をクリックして「GlobalOutEven_0」を
選択すると、「GOE0」に接続されます。デバイスエディタのブロック図でも、線が実際に接続されます。
|
クリックで拡大します。
|
今度は、「GOE0」を「PORT0[0]」に接続します。まず「GlobalOutEven_0」をクリックします。
そうするとメニューが表示されますので、「Pin」を選択します。
|
クリックで拡大します。
|
すると接続先を選択することができますので、「Port_0_0」を選択します。
|
クリックで拡大します。
|
はい、こんな風に結線されていることが表示されます。
|
クリックで拡大します。
|
また、ポートの設定を見ると、PORT0[0]が"Strong"へ自動的に変更されています。
きっと、このポートをPWM出力に割り当てたので、出力に自動設定されたんでしょうね。
これで「Genrate Application」します。
|
クリックで拡大します。
|
次はプログラムの方を書きます。1500usを出力するだけならば、このまま処理開始プログラムだけ書いてビルドすればよさそうですね。
というわけで、オンラインマニュアルを見ながらちょいちょいと書きます。まずは「Application Editor」画面へ移動します。
んで、オンラインマニュアルを開いてコピーしたいところを選択して「右クリックメニューからCopy」を選びます。
これ、ず〜っと疑問だったんです。普通に「Ctrl+C」してもコピーできないもんですから、ずっと、コピーできないと思っていたんですが、実は、メニューからやればできるということを最近知りました。
|
クリックで拡大します。
|
というわけで、ちょちょっとmain.cのコードを追加します。
PWM16_1_Start(); // Start the PWM16
while( 1 );
んでは後はビルドして、できあがり…と。例によって警告がでていますが、よくわからないので無視。
|
クリックで拡大します。
|
さて、テストですが、一応、サーボから変な電気信号が入ってMiniProg1を壊したりしたら嫌なので、
一応、MiniProg1をはずし、MiniEval1に5Vを電源で供給するようにしました。また、サーボのグランドと、
MiniEval1のグランドは必ず接続してください。MiniEval1の大きな穴ですが、
みの虫クリップで挟むのにちょうどいいです。今後の実験用に、ケーブルをいくつか作っておくと便利そうですね。
こうやって見ると、「実験装置」という感じです。でかいアルインコの電源が雰囲気を出してます。でも、S03Tを一個、動かすだけ…。
実行すると、サーボが「ビシ」っとセンター位置で停止します(これをセンター位置と呼ぶかどうかは定かじゃないけど)。
サーボホーンを触って見ると、しっかりとトルク感がありますのでサーボ制御信号もバッチリでているようです。
よしよし、基礎実験成功!
動画を載せようか悩んだんですが、止まっている動画を載せるのもどうかと思い、自主規制…。
|
…と、ここで終わったら、いくらなんでも寂しいですよね〜。だって、何にも動かないもの。というわけで、
スイッチを押すと一時的に「1800us」に切り替わるようにしてみます。
まずは「Device Editor」の「Interconnect」画面に戻って、
スイッチ入力ができるように設定します。「2005/10/07 PSoCのMiniEval1でLED点灯」を参考に設定してみてください。
んでは「Application Editor」でプログラミングします。ループの中に、「スイッチが押されたら、
パルス幅を1800usにする。んで、離されたら1500usに戻す」という処理をいれればよさそうです。
じゃあ、どうやったら「パルス幅」を変更することができるか?がポイントになりますが、これは、
オンラインマニュアルのAPIのところを見ると、それらしいのが載ってまして、
PWM16_WritePulseWidth()を使えばよさそうです。
と、その前に、スイッチから入力できるようにする必要がありますので、以下のようにして、
ポートの設定を変更します。
- Device EditorのInterconnect画面に戻る。
- PORT1[4]を「Pull Down」にする。
- 「Generate Application」する。
で、main.cをちょこちょこっと修正します。「もし、SWが押されていたら、1800us信号に切り替える。
そしてSWが離されるのを待ち、離されたら1500us信号に戻す。」という処理を書けばいいので、
以下のようになります。
PWM16_1_Start(); // Start the PWM16
while( 1 ){
if(( PRT1DR&0x10 ) != 0 ){ // P1[4](SW) ON Check
PWM16_1_WritePulseWidth( 1799 );// Set 1800us
while(( PRT1DR&0x10 ) != 0 ); // P1[4](SW) ON Check
PWM16_1_WritePulseWidth( 1499 );// Set 1500us
}
}
動作中の楽しい動画をご覧あれ!
クリックで再生します。
さて、次は、いよいよ「たまぞうプロジェクト」いきます(TAHMAZOじゃないよ、たまぞうだよ)。
|