本チュートリアルでは前述のPWMを使用したモーター制御について解説する。まず、従来のトランジスタでのモーター制御方法について述べ、 デモ実践に先立ちPWMの利点について説明する。

モーターの制御方法

チュートリアル5ではLEDがPWMにより制御可能であると説明した。特徴は、LEDの輝度をアナログ調光器のように薄暗くすることが可能な点である。(つまり、二進法で1か0であるにもかかわらず電圧が多様に見えることである)。

この方法の最大の利点は非常に省電力なことである。LEDをアナログ方法で制御すると、完全にパワーアップされている時のみ効率的になる。その他の時、アナログ電圧調整は抵抗のように働くので放熱する弱点がある(不要な熱に対する冷却装置が必要で、エネルギー損失もある)。しかし、デジタル方法ではパワーON/OFFの高速繰り返しによりモーターが継続的に回転しているように見える。

つまり、C-05チュートリアルの方法はモーター制御にも同様に利用できる。

モーターの逆方向動作

モーターの回転方向は極性により変わる。極性を反転すると方向が変わる。

HBridgeこの図はモーターを制御する4つのトランジスタの1組である。概観図の形から、これはHブリッジと呼ばれている。2つのコマンド信号をそれぞれAとBに入力することにより、モーターの(+、ー)極を任意的に電圧もしくはグランドに接続することが可能である。

REOボードには2つのHブリッジを各々含む2つのチップが実装されている。したがって4つのDCモーター制御が可能である。

 以下の図はコマンド信号A、Bによる電流経路を示している。このトポロジーのメリットは、単一電源( つまり、シンメトリック電源が不要)でありながら、DCモーターがどちらの方向にも動作可能であることだ。

 

HBridgeNeg

HBridgePos

 

 

 

 

 

 

 

上の図が示すように、AとBをCPUのPWM出力に繋げば、モーターをどちらの方向にも制御可能となる。

プログラム概要

モーター 電極の1つがPWMで動く場合、別の電極は当然グランドに接続される。従って、モーター速度を設定する際にはコマンドサインによりPWMを常に有効化、無効化させる必要がある。

その方法は以下の通り

--------------------

void SetSpeed(int16 speed) {
    int8 sgn = 1;                            //    Sign of speed
    if(speed < 0) {
        sgn = -1;
        speed = -speed;
    }
    else if(speed > 0) sgn = 1;
    else sgn = 0;
    //    Truncate the speed
    if(speed > 999) speed = 999;
    //    Now process the different cases.
    if(sgn == 0) {
        P1SEL &= ~BIT4;
        P1SEL &= ~BIT5;
        P1OUT &= ~(BIT4+BIT5);
    }
    else if(sgn > 0) {
        P1SEL &= ~BIT4;
        P1SEL |= BIT5;
        P1OUT &= ~BIT4;
        TA0CCR4 = speed;
    }
    else {
        P1SEL &= ~BIT5;
        P1SEL |= BIT4;
        P1OUT &= ~BIT5;
        TA0CCR3 = speed;
    }
}

--------------------

解説:スピード設定機能はコマンドのサイン(回転方向)をチェックする。以下の3つの可能性がある

  • まずコマンドが0の場合、モーターはブレーキをかける。両ワイアのグランド接続により可能
  • コマンドが正の場合、正電極線(タイマーの4番目の出力により制御 されている)BIT5は有効化される必要があり、BIT4はグランド接続されている。
  • コマンドが負の場合、BIT4はPWM用に有効化される必要があり、BIT5はグランド接続されている。

スピードの絶対値は適切なタイマーCCR(TA0CCR3かTA0CCR4)により設定される。

プログラム全体

ADCプログラム(チュートリアル5)は、ほぼ再利用されている。2つの新機能はPWM設定とスピード設定である。ソースコードは本ページの最後に添付されている(ダウンロード可能なユーティリティー機能を除く)。

ADCはわずかに異なった方法で使用されている。ポテンショメーターがハーフスケールの場合、回転速度が0になるということである。0の時に回転速度は最速で逆方向になり、フルスケールの時に回転速度は最速で正方向になる。

以下のオシロスコープ画面はプロセッサの負荷率を示している。

MotCPULoad

MotCPULoad2

モーター 周波数更新は64Hzで、マニュアル周波数更新としては十分である。

スピード更新には約9.6μsかかり、CPU負荷率のわずか0.1パーセント以下である。

結論

モーター制御が容易に設定可能であり、実際の制御システムに処理パワーがさほど必要ないと説明してきた。つまり、REOボードの全モーター出力が使用されても CPU負荷率はわずか1パーセント程度で、他の処理に利用可能な余地が多く残されている。

ソースコード

--------------------

#include <msp430F5659.h>
#include "F5659Utils.h"

void EnableADCTimer(void);
void EnableADC();
void EnableMotor1();
void SetSpeed(int16 speed);

int main(void) {
    WDTCTL = WDTPW + WDTHOLD;                //    Stop WDT
    SetCoreVoltage(VCORE19);                 //    Set the core to 1.9V
    SetFLL(24);                              //    Set SMCLK to 24 MHz
    EnableMotor1();
    EnableADCTimer();
    EnableADC();
    __bis_SR_register(LPM0_bits + GIE);      //    Enter LPM0, enable interrupts
}

void EnableMotor1() {
    P1DIR |= BIT4 + BIT5;
    TA0CCR0 = 999;                           //    PWM Period
    TA0CCTL3 = OUTMOD_7;                     //    CCR3 reset/set
    TA0CCTL4 = OUTMOD_7;                     //    CCR4 reset/set
    TA0CTL = TASSEL_2 + MC_1 + TACLR;        //    SMCLK, up mode, clear TAR
}

void SetSpeed(int16 speed) {
    int8 sgn = 1;                            //    Sign of speed
    if(speed < 0) {
        sgn = -1;
        speed = -speed;
    }
    else if(speed > 0) sgn = 1;
    else sgn = 0;
    //    Truncate the speed
    if(speed > 999) speed = 999;
    //    Now process the different cases.
    if(sgn == 0) {
        P1SEL &= ~BIT4;
        P1SEL &= ~BIT5;
        P1OUT &= ~(BIT4+BIT5);
    }
    else if(sgn > 0) {
        P1SEL &= ~BIT4;
        P1SEL |= BIT5;
        P1OUT &= ~BIT4;
        TA0CCR4 = speed;
    }
    else {
        P1SEL &= ~BIT5;
        P1SEL |= BIT4;
        P1OUT &= ~BIT5;
        TA0CCR3 = speed;
    }
}

void EnableADCTimer(void) {
  TA1CCTL0 = CCIE;                           //    CCR0 interrupt enabled
  TA1CCR0 = 511;                             //    Check ADC every 15.6 ms (64 Hz)
  TA1CTL = TASSEL_1 + MC_1 + TACLR;          //    SMCLK, upmode, clear TAR
  P2DIR = 0x01;
}

void EnableADC() {
    ADC12CTL0 = ADC12ON+ADC12SHT0_2;         //    Turn on ADC12, set sampling time
    ADC12CTL1 = ADC12SHP;                    //    Use sampling timer
    ADC12CTL0 |= ADC12ENC;                   //    Enable conversions
}

uint16 ReadADCVal(void) {
    uint16 retval;                           //    Value for debugging purpose
    ADC12CTL0 |= ADC12SC;                    //    Start conversion-software trigger
    while (!(ADC12IFG & BIT0));              //    Wait for the ADC
    retval = ADC12MEM0;                      //    Set breakpoint here to verify value
    return retval;
}

// Timer0 A0 interrupt service routine
#pragma vector=TIMER1_A0_VECTOR
__interrupt void TIMER1_A0_ISR(void) {
    int16    adcval;
    adcval = ReadADCVal();
    adcval -= 2048;
    SetSpeed(adcval);
}

--------------------