PICマイコン(PIC16F1827)でサーボモーターを操作する

組み込みエンジニア

こんにちは、ENGかぴです。

PIC16F1827のタイマ割り込みでサーボモーターが操作できます。サーボモーターはラジコンカーのステアリングに使用されており0~180度の範囲で動作するものが多くあります。MCCを使ってTMR0割り込みの設定を行いPWM波形を生成してサーボモーターを操作する方法をまとめています。

PIC16F1827のCCP機能でPWM波形を生成することができますがサーボモーターのキャリア周波数が50Hzと低いためTMR0割り込みを使用しています。CCP機能でPWM波形を生成する方法は下記記事にまとめています。

PICマイコン(PIC16F1827)でPWMを実装する

サーボモーターはSG90を使用しています。

サーボモーターを操作する

SG90の説明
SG90の説明

サーボモーターはパルス波形を与えてモータを操作します。SG90のデータシートによるとPWM周期(キャリア周波数)が50Hzの波形でデューティーサイクル(Duty Cycle)が0.5~2.4msになるように調整することで回転する角度が決まります。

データシートにはOperating speed(動作速度)が0.12s/60 degreeと記載されています。60度回転させる場合はキャリア周波数20msでデューティーサイクルが1.13msであるパルス波形を120ms経過するまで出力する必要があります。

PIC16F1827のタイマ0の割り込みでPWM波形を生成しSG90を動作させるまでの手順を説明します。

PWM波形を生成する

void tmr0Servo(void){

    if( period_cnt >= 20){ //デューティーサイクルが経過
         IO_RB3_SetLow(); //LOWを出力
    }

    if(++period_cnt >= 200){ //キャリア周波数経過
        period_cnt = 0;
        IO_RB3_SetHigh(); //HIGHを出力
    }
}

tmr0Servo()はTMR0がタイムアップするたびにコールバックされる関数です。MCCでTMR0の割り込み周期を100usにしています。SG90はキャリア周波数20ms(50Hz)で動作するため割り込みが200回発生すると1周期が経過したタイミングになります。

例ではデューティーサイクルが2msになるようにしています。period_cntが200を経過すると次のパルス波形を出力するためperiod_cntをクリアしてHIGHを出力します。次にperiod_cntが20になると2ms経過したことになるためLOWを出力します。

TMR0割り込みの中に処理を入れすぎると割り込みが渋滞してメイン関数に戻れなくなるため注意が必要です。

TMR0割り込みを使用することでパルス波形が優先して生成できるためデューティーサイクルが安定しやすくなります。メイン処理でパルス波形を生成する方法もありますが処理が遅れるとデューティーサイクルが長くなることがあり安定した動作が難しくなることがあります。

回転角度を指定する

void ServoWrite(uint8_t deg){

    if( deg > CYCLE_DEG){
        deg = CYCLE_DEG;
    }    

    duty_cycle = deg + CYCLE_MIN;
}
//使用例 90度を指定する場合
ServoWrite(9);

サーボモーターの回転角度を指定するServoWrite()関数を実装します。引数に回転させる角度を指定しますが、割り込みの分解能を100usにしているため細かなデューティーサイクルが指定できません。

0~18を10度毎の変化で指定して使用します。例えば90度回転させる場合は9を指定します。18(180度)を超える値を指定して動作が不安定にならないようにしています。

割り込みの分解能を10usにすると0~180度の指定ができますが割り込みの渋滞が発生するためマイコンの性能に合わせることが重要です。

10度毎の変化にしているため精度が荒くなっているため正確に90度を回転させたい場合はデューティーサイクルの調整が必要です。

すき間時間で資格をゲット【STUDYing(スタディング)】

TMR0を実装する

PIC16F1827のTMR0の設定はMCCを使って行っています。MCCの使い方については下記記事にまとめています。

MPLAB Code Configurator(MCC)の追加と使い方

System Moduleの設定

最初に共通事項であるSystem Moduleの設定を行います。MPLAB X IDEを起動しMCCのアイコンをクリックしてMCCを有効にします。

System Moduleの設定(MCC)
System Moduleの設定(MCC)

内部クロックを使用するためINTOSCを選択しています。今回は割り込みの頻度が増えることからクロック周波数を16MHzにしています。他にも設定項目はありますがクロックの設定のみとしています。

RUNTEQ-プログラミングで自由を手に入れる

TMR0を設定する

TMR0はDevice Resources内のPeripherals欄のTimerを選択しTMR0をクリックして追加します。

TMR0の設定(MCC)
TMR0の設定(MCC)

Timer Clock内でクロックに関する設定を行います。Clock Sourceは内部クロックを使用しているためFOSC(FOSC/4)を指定します。PrescalerはClock Sourceに対してTMR0のカウントアップする分周比を指定します。

Timer PeriodはTMR0がオーバーフローするタイミングを設定します。100us毎にオーバーフロー割り込みが発生しますがCallback Function Rateを0x01を指定しているため100ms毎にコールバック関数が呼び出されます。

void tmr0Servo(void); //タイマー0割り込みのコールバック関数
//コールバック関数の使用例
TMR0_SetInterruptHandler(tmr0Servo); //コールバック関数を指定

上記例ではtmr0Servo()が100us毎にコールされます。

動作確認

PIC16F1827によるサーボモーターの動作確認回路
PIC16F1827によるサーボモーターの動作確認回路

PIC16F1827とSG90を接続して動作確認を行います。PIC16F1827のRB3ピンをDOピンとして使用しTMR0割り込みによってPWM波形を出力します。

電源はDC5Vを準備していますがサーボモーターが動作する際に電圧降下してしまうことがあるのでC2の1000uFを追加しています。サーボモーター用の電源とPICマイコンの電源を分けている場合は不要です。

電源をONすると2秒間隔でSG90の回転角度を0度→90度→140度→0度→以下繰り返しの動作します。回転角度をギリギリにすると回転限界に達してギアに負担がかかることがあるのでギリギリの角度にならないように調整する必要があります。

割り込みを使用しているためDOによるPWM波形がほどよく正確に出力できており安定した動作になっていることが確認できました。回転角度を180度(ServoWrite()の引数を18)にすると動作点ギリギリの状態になりSG90からモーターの過回転によるじりじりとした音が鳴っていたため調整が必要だと感じました。PIC16F1827のピン配置は以下の通りです。

PIC16F1827のピン設定(MCC)
PIC16F1827のピン設定(MCC)

細かい角度の調整は割り込みの渋滞の問題が発生しやすいためスペックの高いマイコンが必要になりますが、今回の例のように簡易的な方法でも十分だと感じました。

スポンサーリンク

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。

#include "mcc_generated_files/mcc.h"

#define PERIOD_MAX    200  //PWM周期
#define CYCLE_MIN       5
#define CYCLE_MAX      24
#define CYCLE_DEG      18
#define TIME_UP 0
#define TIME_OFF -1
#define SERVO_WAIT    200

uint16_t duty_cycle;
uint16_t period_cnt;
uint8_t move;
int16_t timwait;

void tmr0Servo(void);
void ServoWrite(uint8_t deg);

/* Main application */
void main(void)
{
    SYSTEM_Initialize();
    TMR0_SetInterruptHandler(tmr0Servo);
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    while (1)
    {
        switch(move){
            case 0:
                if( timwait == TIME_UP ){
                    ServoWrite(0);
                    timwait = SERVO_WAIT;
                    move = 1;
                }
                break;
            case 1:
                if( timwait == TIME_UP ){
                    ServoWrite(9);
                    timwait = SERVO_WAIT;
                    move = 2;
                }
                break;
            case 2:
                if( timwait == TIME_UP ){
                    ServoWrite(14);
                    timwait = SERVO_WAIT;
                    move = 0;
                }      
                break;
        }
    }
}

/* PWM波形を生成(割り込みコールバック) */
void tmr0Servo(void){
    
    if( duty_cycle < CYCLE_MIN){
        duty_cycle = CYCLE_MIN;
    }
    
    if( duty_cycle > CYCLE_MAX){
        duty_cycle = CYCLE_MAX;
    }
    
    if( period_cnt >= duty_cycle){
         IO_RB3_SetLow();
    }
    
    if(++period_cnt >= PERIOD_MAX ){
        period_cnt = 0;
        IO_RB3_SetHigh();
    }
    
    if( period_cnt == 0 || period_cnt == PERIOD_MAX/2){
        if( timwait > TIME_UP ){
            timwait--;
        }
    }
}
/* サーボモーターの回転指令(0-18 1:10度 18:180度) */
void ServoWrite(uint8_t deg){

    if( deg > CYCLE_DEG){
        deg = CYCLE_DEG;
    }    

    duty_cycle = deg + CYCLE_MIN;
}
/* End of File */

本ソースコードはMPLAB X IDEにMCCのプラグインをインストールしていないと使用できません。

関連リンク

PICマイコンを使ってマイコンのレジスタの設定やMPLAB X IDEのプラグインであるMCCを使用して動作確認したことについてまとめています。

PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ

PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ

あなたのキャリアのお供に「生涯学習のユーキャン」

最後まで、読んでいただきありがとうございました。

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