見出し画像

PIC18F タイマー1個でRCサーボ信号x4

先日の記事で、「PIC18F23K22で、一つのタイマーで4台分の信号を作って各々独立に制御します。」と書いたので、少し詳しく紹介しようと思います。

SG90 のRCサーボ信号

RCサーボは製品毎に細かな値は異なるものの、概ね下図のような信号を受けて動作します。
下図はSG90の場合の値、信号電圧は5.0Vです。

SG90 の制御信号

画像1

信号周期は20msecで、High期間は500~2400usec、1450usec で中点(0DEG)に制御します。500usec で -90 DEG(CW方向)です。

High 期間をこの範囲外にすると機械的な動作範囲を超えて制御しようとするのでRCサーボを損傷することがあります。

ただし、High期間を0usec、つまり連続してLowの状態を許容します。
この場合は、電源が供給されていても位置(角度)制御が行われず、手で回せるようになります。(電源無しの時よりは重く感じられます。)

(1450-500) = 950usec ですから、950usec/90DEGです。
タイマーを1usec/count に設定すれば、およそ 0.1 DEG 刻みの制御信号を作れることに成ります。

1つのタイマーで4台制御

20msec周期の内、最大でも2.4msec(2400usec)つまり12%しかHigh期間が無く、他の期間はLowを維持するだけですから、下図のように1つのタイマーで複数のRCサーボ信号を作ることが可能です。
1つのタイマーで8台までの信号を作れそうです。

下図は4台の例(以下タイミング図)

4台制御のタイミング図

画像2

上図では4つのRCサーボ信号を、PICのI/Oポート PA0 , PA1, PA2, PA3 から出力しています。各RCサーボ毎に5msecの間だけRCサーボ信号を制御し、残りの15msec間はLowを維持します。

具体例

高位割込みのプログラムです。(XC8 Ver 1.40)

タイミング図に示した8つの期間毎に信号を制御しています。
実際に作成したものはsvh, svl が各々2次元で、もう少し機能がありましたが簡単のため1次元の例で示します。

void interrupt high_isr (void)
{
   
   if( PIR2bits.TMR3IF == 1 )    //TMR3 割り込み時の処理 RC servo
   {
       PIR2bits.TMR3IF = 0 ;       //TMR3 割り込みフラグ クリア
       
       switch( svcnt )
       {
           case 0 :    // PA3 Low 期間先頭処理
               TMR3H = svh[7] ;
               TMR3L = svl[7] ;
               LATAbits.LA3 = 0 ;
               break ;
           case 1 :    // PA3 High 期間先頭処理
               TMR3H = svh[6] ;
               TMR3L = svl[6] ;
               LATAbits.LA3 = svenh[3] ;
               break ;
           case 2 :    // PA2 Low 期間先頭処理
               TMR3H = svh[5] ;
               TMR3L = svl[5] ;
               LATAbits.LA2 = 0 ;
               break ;
           case 3 :    // PA2 High 期間先頭処理
               TMR3H = svh[4] ;
               TMR3L = svl[4] ;
               LATAbits.LA2 = svenh[2] ;
               break ;
           case 4 :    // PA1 Low 期間先頭処理
               TMR3H = svh[3] ;
               TMR3L = svl[3] ;
               LATAbits.LA1 = 0 ;
               break ;
           case 5 :    // PA1 High 期間先頭処理
               TMR3H = svh[2] ;
               TMR3L = svl[2] ;
               LATAbits.LA1 = svenh[1] ;
               break ;
           case 6 :    // PA0 Low 期間先頭処理
               TMR3H = svh[1] ;
               TMR3L = svl[1] ;
               LATAbits.LA0 = 0 ;
               break ;
           case 7 :    // PA0 High 期間先頭処理
               TMR3H = svh[0] ;
               TMR3L = svl[0] ;
               LATAbits.LA0 = svenh[0] ;
               break ;
       }
       if( svcnt > 0 )
       {   svcnt -- ;  }
       else
       {   svcnt = svcnti ;    }
       
   }
}

TMR3 は前述の通り、内蔵クロックを分周して1usec/count 高位割込みに設定し、割込みを許可しています。

svcnt  8つの制御期間のカウンター
svcinti  svcnt の初期値(7)
svh[ ]  各期間のタイマー初期値の内上位 8bit
svl[ ]  各期間のタイマー初期値の内下位 8bit
svenh[ ] 各サーボモーター毎にHigh期間の信号 0 または 1

case 7 , 5 , 3 , 1 で読み出す設定値
svh , svl に入れる値 T1 は、High 期間(パルス幅)[usec]を T0 とすると
T1 = 0x10000 - T0
case 6 , 4 , 2 , 0 で読み出す設定値
svh , svl に入れる値 T2 は、High 期間(パルス幅)[usec]を T0 とすると
T2 = 0x10000 - ( 5000 - T0 )
であることに注意が必要です。

svh , svl の値はTMR3に書き込まれ、次回割り込みまでの時間(上のT0 , ( 5000-T0 ) ) を設定します。(次回割り込みまではキースキャンなど別の処理を行います。)

また、割込みのオーバーヘッドなどによる誤差(おそらく数usec)が気になる場合は、T1に補正を加える必要があります。
svenh[ ] の値は1にすると通常のRCサーボ信号を送出し、0にすると連続してLowを出力します。

 0 に設定したRCサーボは現在の位置(角度)で動作停止します。
長時間同じ位置に固定する用途で、「1 に設定して svh[ ] , svl[ ] の値を固定した時」に比べると現在位置(角度)を保持するトルクが小さくて構わない場合には積極的に 0 に設定します。

以上、何かの参考に成れば幸いです。




出来ればサポート頂けると、嬉しいです。 新しい基板や造形品を作る資金等に使いたいと思います。