H8CPUのPWM信号でモータ駆動の
サンプルプログラム

Copyright(C) 13May2002
coskx

【1】 はじめに

 この文書はAKI-H8にて,PWM方式によるモータの利用のサンプルプログラムを提示するものである。
PWMはパルス幅変調(Pulse Width Modulation)のことである。
モータの駆動時において,高速にスイッチのON-OFFを繰り返し,ONになっている時間とOFFになっている時間の比を変更することによって,見かけ上,モータにかける電圧を変更する駆動方法である。
次の図1はPWM信号の例である。周期的なON-OFFの信号で,その周期はPWM周期と呼ばれる。また(ONになっている時間)÷(PWM周期)のことはデューティ比と呼ばれる。

図1 PWM信号の例

PWM信号の作り方は大きく分けて2つある。
(1)H8/3048のITUのPWM発生機能を用いる
  この方法では(入力クロック周期))×(PWM段階数)=(PWM周期)になるため,短い1msec以下のPWM周期を実現できる。
  1つのH8/3048のITUの個数により,制御できるモータ数が限られる。
(2)1つのITUによるタイマー割り込みを用い,ソフトウェアにより,PWM信号を作る
  この方法では(割り込み周期)×(PWM段階数)=(PWM周期)になるため,PWM周期が10msec程度以上になる
  2ビットの出力で,1つのモータが制御できるため,1つのH8/3048で多くのモータを制御できる

【2】 AKI-H8とのモータドライバ TA8429Hと接続
   (ITU Channel3のPWM機能を利用)
   外付け回路が必要だが,PWM周波数を高くできる

H8CPUはITUでPWM(Pulse Width Modulation)信号を発生することができる。このPWM信号と方向信号2ビットを用いてDCモータ駆動用のプログラムを作成することができる。ITUチャンネル3を用いることにすると,PWM信号はTIOCA3より出力される。また方向信号はポート4のビット0,1から出力するものとする。PWM信号と方向信号はアンド回路を経てモータドライバIC(東芝TA8429)に与えられ,DCモータを駆動するものとする。(図2.1,図2.2参照)
プログラムでは,RS232Cに接続されたパソコンのターミナルソフト(例えばハイパーターミナル)を用い,キーボードからPWM値を設定するものとし,設定値は-1000から1000までの値(負の値は逆転を意味する)を用いる。これ以外の値だったら,モータを停止するものとする。

プログラムでは,システムクロックφをそのまま用いているので,ITUカウンタへの入力クロックは16MHzである。PWM階段数はPWM_MAXで与えられ,1000である。PWM周波数は16kHzとなっている。(PWM周期は1/16msec)

H8_ITUCH3PWM.GIF - 5,218BYTES

図2.1 ITUチャンネル3を用いたモータのPWM駆動回路例

P_TA8429.JPG - 11,608BYTES

図2.2 モータドライバIC 東芝TA8429

モータへのPWM出力

/**********************************************************
モータへのPWM出力 
ITU Channel3 を用いたPWM信号発生
TIOCA3(PWM波形出力信号) はPB-0(PortB-bit0) Conecter CN1-16
P4-bit0 正転信号 Conecter CN1-32
P4-bit1 逆転信号 Conecter CN1-31
**********************************************************/
#include "h8-01.h"
#define PWM_MAX 1000

void initPWM3(int gra_value)
/*ITU channel3*/
{
                          /*初期化(方向を設定)*/
    P4DDR|=0x03;          /*(????????) | (00000011) 下位2ビットを出力に*/
    P4.DDR=P4DDR;
    P4.DR.BYTE=0x0;       /*初期値 b0=cw,b1=ccw 正論理*/

    ITU3.TCR.BIT.TPSC=0;  /*内部クロックφでカウント*/
    ITU3.TCR.BIT.CKEG=0;  /*立ち上がりエッジでカウント*/
    ITU3.TCR.BIT.CCLR=1;  /*GRAのコンペアマッチ/インプットキャプチャでTCNTをクリア*/
    ITU3.GRA=gra_value-1; /*GRAの値(GRB=GRAのときPWMが100%)をPWM_MAXに設定*/
    ITU3.GRB=0;           /*GRBの値を0に設定(=PWM0%)パルス幅を変えるときはこの値を変更する*/
    ITU.TMDR.BIT.PWM3=1;  /*ITUのPWMに使うchをPWMモードに設定*/
    ITU3.TIOR.BIT.IOB=0;  /*TIOCBにPWM信号を出力しないように設定*/
    ITU.TSTR.BIT.STR3=1;  /*カウントスタート(PWM信号が出力される)*/
}

void outPWM3(int value)
/* value : -gra_value...gra_value */
/*ITU channel3*/
{
    if (0<value) {
        P4.DR.BIT.B0=1;
        P4.DR.BIT.B1=0;
        value--;
        if (ITU3.GRA==value) value++;
        ITU3.GRB=value;
    } else if (value<0) {
        P4.DR.BIT.B0=0;
        P4.DR.BIT.B1=1;
        value=-value-1;
        if (ITU3.GRA==value) value++;
        ITU3.GRB=value;
    } else {
        P4.DR.BIT.B0=0;
        P4.DR.BIT.B1=0;
        ITU3.GRB=0;
    }
}

main()
{
    int x;
    initSCI1();         /*シリアルポート初期化*/
    initPWM3(PWM_MAX);  /*PWM出力初期化(ITU channel3使用)*/
    while(1) {
        x=getIntSCI1("PWM value =");
        SCI1_printf("\n%d\n",x);
        if (-1000<=x&&x<=1000) outPWM3(x);
        else outPWM3(0);
    }
}

 

【3】 AKI-H8とのモータドライバ TA8429Hと接続
   (割り込みを用いたソフトウェアPWM機能を利用)
   
駆動するモータの数を多くできるが,PWM周波数は低い

H8CPUのITUを用いてPWM(Pulse Width Modulation)信号を発生することができるが,最大で5チャンネルしか利用できない。割り込みを利用するとソフトウェアで方向信号を兼ねたPWM信号を発生することが出来る。この方向信号を兼ねたPWM信号2ビットを用いてDCモータ駆動用のプログラムを作成することができる。方向信号を兼ねたPWM信号2ビットはポート1のビット0,1から出力するものとする。方向信号を兼ねたPWM信号2ビットはモータドライバIC(東芝TA8429)に与えられ,DCモータを駆動するものとする。(図3.1参照)
プログラムでは,デューティ比は-10/10,-9/10,-8/10,....,-1/10,0/10,1/10,.....,9/10,10/10を使えることになっているがこのうち,プッシュスイッチに-10/10,-5/10,5/10,10/10を割り当てて駆動する。
RS232Cに接続されたパソコンのターミナルソフト(例えばハイパーターミナル)を用いると,プッシュスイッチのON-OFF状態,モータへの指令値が表示される。

 プログラムでは,割り込み周期が1msec。PWM階段数は10である。PWM周波数は100Hzとなっている。(PWM周期は10msec)

H8_INTPWM.GIF - 4,865BYTES

図3.1 割り込みとポート1第0,1ビットを用いたモータのPWM駆動回路例

モータドライバのテスト

/**********************************************************
プログラムの説明
モータドライバのテスト
タイマ割り込み利用のソフトウェアPWM
PushSW N0.0: ch1 slow CCW
PushSW N0.1: ch1 fast CCW
PushSW N0.2: ch1 slow CW
PushSW N0.3: ch1 fast CW
**********************************************************/
#include "h8-01.h"

volatile int motor1; /*0-10の値,速度に対応*/
const int period=10; /*周期10msec*/
const int fast=10;
const int slow=5;

void initMyMotor(void)
{
    P1DDR |= 0xff; /*第0ビットを出力設定だが,全ビットを出力設定*/
    P1.DDR = P1DDR;
    P1.DR.BYTE = 0; /*第0ビットに0を出力設定だが,全ビットを0*/
}

main()
{
    int PushSwStatus,PushSwStatusb=-1;
    motor1=0;
    initMyMotor();
    initPushSW();
    init8BitSW();
    initLed();
    initSCI1();
    initTimer1Int(1000); /*時間割り込み1msec ITUch1のみ使用*/
    E_INT();        /*CPU割り込み許可*/
    startTimer1();  /*時間割り込みタイマスタート*/
    while(1){
        if (checkPushSW(0)==1) { /* slow CCW */
            motor1=slow;
        } else if (checkPushSW(1)==1) { /* fast CCW  */
            motor1=fast;
        } else if (checkPushSW(2)==1) {  /* slow CW */
            motor1=-slow;
        } else if (checkPushSW(3)==1) {  /* fast CW  */
            motor1=-fast;
        } else {
            motor1=0;
        }
        PushSwStatus=getPushSW()&0xf0;
        if (PushSwStatus!=PushSwStatusb) {
            SCI1_printf("PushSW:%B motor=%d\n",PushSwStatus,motor1);
            PushSwStatusb=PushSwStatus;
        }
    }
}

void interrupt_cfunc() /*割り込み関数 (この名前は固定)*/
{
    static int tick=0;
    int motor;
    motor=motor1;
    if (motor<0) {
        motor=-motor;
        if (tick<motor) {
            P1.DR.BIT.B0=0;
            turnOffLed(0);
            P1.DR.BIT.B1=1;
            turnOnLed(1);
        } else {
            P1.DR.BIT.B0=0;
            turnOffLed(0);
            P1.DR.BIT.B1=0;
            turnOffLed(1);
        }
    } else {
        if (tick<motor) {
            P1.DR.BIT.B0=1;
            turnOnLed(0);
            P1.DR.BIT.B1=0;
            turnOffLed(1);
        } else {
            P1.DR.BIT.B0=0;
            turnOffLed(0);
            P1.DR.BIT.B1=0;
            turnOffLed(1);
        }
    }
    tick++;
    if (tick==period) tick=0;
}

/*実行時のターミナルソフトの表示
PushSW:00010000 motor= 0
PushSW:00000000 motor= 5
PushSW:00000000 motor= 10
PushSW:00100000 motor= 0
PushSW:00000000 motor=-5
PushSW:10000000 motor=-10
PushSW:00000000 motor= 0
*/

 

【4】 AKI-H8とのモータドライバ TA8429Hと接続
   (ITU3,ITU4を使用して,直接モータドライバを駆動する)
   ITUを2つ使ってしまうが,PWM周波数を高くできる

【2】の手法を使って,モータドライバへの出力を2つ作る。ここで,2つのITUを用いるため,ヂューティ比切り替えタイミングによっては,予期せぬ出力が非常に短いとはいえ,発生してしまうので,できるだけ避ける工夫が必要である。

ITUチャンネル3,4を用いることにすると,PWM信号はTIOCA3,TIOCA4より出力される。(図4.1参照)
プログラムでは,RS232Cに接続されたパソコンのターミナルソフト(例えばハイパーターミナル)を用い,キーボードからPWM値を設定するものとし,設定値は-1000から1000までの値(負の値は逆転を意味する)を用いる。これ以外の値だったら,モータを停止するものとする。

プログラムでは,システムクロックφをそのまま用いているので,ITUカウンタへの入力クロックは16MHzである。PWM階段数はPWM_MAXで与えられ,1000である。PWM周波数は16kHzとなっている。(PWM周期は1/16msec)

H8_ITUCH3PWM.GIF - 5,218BYTES

図2.1 ITUチャンネル3,4を用いたモータのPWM駆動回路例

モータドライバのテスト

/**********************************************************
モータへのPWM出力 
ITU Channel3,4 を用いたPWM信号発生
  正逆転信号を2つのITUで作るぜいたくなシステム
TIOCA3(PWM波形出力信号) はPB-0(PortB-bit0) Conecter CN1-16
TIOCA4(PWM波形出力信号) はPB-3(PortB-bit2) Conecter CN1-18
**********************************************************/
#include "h8-01.h"

/*検査用PWM出力*/
/*出力端子 2 ITOCA3/PB0    CN1-16*/
/*出力端子 4 ITOCA4/PB0    CN1-18*/

/*ITU channel3,4 でPWM出力*/
/*clockindex 0:φ 1:φ/2 2:φ/4 3:φ/8 */
void initPWM3and4(unsigned short int pwmmax, int clockindex)
{
    ITU3.TCR.BIT.TPSC=clockindex;  /*内部クロックφでカウント*/
    ITU3.TCR.BIT.CKEG=0;  /*立ち上がりエッジでカウント*/
    ITU3.TCR.BIT.CCLR=1;  /*GRAのコンペアマッチでTCNTをクリア*/
    ITU3.GRA=pwmmax-1;    /*GRAの値(GRB=GRAのときPWMが100%)をPWM_MAXに設定*/
    ITU3.GRB=0;           /*GRBの値を0に設定(=PWM0%)パルス幅はこの値に依存*/
    /*ITU.TMDR.BIT.PWM3=1;  /*ITUのPWMに使うchをPWMモードに設定*/
    ITU3.TIOR.BIT.IOB=0;  /*TIOCB3にPWM信号を出力しないように設定*/
   
    ITU4.TCR.BIT.TPSC=clockindex;  /*内部クロックφでカウント*/
    ITU4.TCR.BIT.CKEG=0;  /*立ち上がりエッジでカウント*/
    ITU4.TCR.BIT.CCLR=1;  /*GRAのコンペアマッチでTCNTをクリア*/
    ITU4.GRA=pwmmax-1;    /*GRAの値(GRB=GRAのときPWMが100%)をPWM_MAXに設定*/
    ITU4.GRB=0;           /*GRBの値を0に設定(=PWM0%)パルス幅はこの値に依存*/
    /*ITU.TMDR.BIT.PWM4=1;  /*ITUのPWMに使うchをPWMモードに設定*/
    ITU4.TIOR.BIT.IOB=0;  /*TIOCB4にPWM信号を出力しないように設定*/
   
    ITU.TSTR.BIT.STR3=1;  /*カウントスタート*/
    ITU.TSTR.BIT.STR4=1;  /*カウントスタート*/
   
    PBDDR|=5;
    PB.DDR=PBDDR;
    PB.DR.BIT.B0=0;
    PB.DR.BIT.B2=0;
}

/*PWM出力*/
void outPWM(int value)
{
    if (0<value) {
        ITU.TMDR.BIT.PWM4=0;  /*ITU4のPWMモード解除*/
        value--;
        if (ITU3.GRA==value) value++;
        ITU.TMDR.BIT.PWM3=1;  /*ITU3をPWMモードに設定*/
        ITU3.GRB=value;
    } else if (value<0) {
        ITU.TMDR.BIT.PWM3=0;  /*ITU3のPWMモード解除*/
        value=-value-1;
        if (ITU4.GRA==value) value++;
        ITU.TMDR.BIT.PWM4=1;  /*ITU4をPWMモードに設定*/
        ITU4.GRB=value;
    } else {
        ITU.TMDR.BIT.PWM3=0;  /*ITU3のPWMモード解除*/
        ITU.TMDR.BIT.PWM4=0;  /*ITU4のPWMモード解除*/
    }
}

main()
{
    int pwmvalue;
    initSCI1();
    initPWM3and4(1000,0); /*clock:φ*/
    while(1) {
        pwmvalue=getIntSCI1("value=");
        outPWM(pwmvalue);
    }
}