位相カウンタ

Copyright (C) 19May2005 coskx

【1】H8/3048のITUを使用した位相カウンタのテスト

ITU3chを使用したPWMを用いてモータを駆動し,モータ軸に連動したフォトロータリエンコーダ(インクリメント)からの2相信号をITU2chを利用した位相カウンタの読み込みについてテストする。
なお,ITU2chを利用した位相カウンタはハードウェアとして,16ビットしか持っていないため,-32768から32767までの範囲でのみ,正しく値を読むことができる。
次のプログラムによるテストを行なった。実際動作させて,カウンタの値が±32768を超えた時にどのように表示されるかためしてみなさい。

H8/3048のITUを使用したPWM・カウンタテスト

/****************************************
インクリメント型2相ロータリエンコーダ信号カウント (ITU2)
ロータリエンコーダAB信号のカウント
タイマ割り込みを用いたカウント差分(速度に対応)
PCのターミナルソフトからのキーボード操作でモータ駆動を複数回実行可能
****************************************/

#include "h8-01.h"

/***************************************
PWM信号+回転方向信号出力 (ITU3,P4-0,P4-1)
void init_pwm3(int pwmMax)
 モータ駆動PWM信号発生機構の初期化
 ITU3はPWM信号を発生し,P4-0,1はモータの回転方向を指示する
 int pwmMAXはPWM周期を規定する。PWM周期 T は
 T = M/(16*10^6) [sec] となる。 M:pwmMAX

void driveMotor(int pwm)
 PWM駆動指令を与える
 -pwmMAX<pwm<pwmMAXを与えるとよい。
 ただし,この範囲を超える値を与えてもよい
 負のpwm値は逆転を意味する

void brakeMotor(void)
 モータドライバICの仕様に従って,ショートブレーキ状態を作る
****************************************/

void init_pwm3(int pwmMax)
{
    /*P4-0,P4-1初期化(P4-0,P4-1で回転方向を設定するため) */
    P4DDR|=0x03;  /*下位2ビットを出力に*/
    P4.DDR=P4DDR;
    P4.DR.BYTE=0x0; /*初期値 b0=cw,b1=ccw 正論理*/

    /*ITU3の初期化*/
    ITU3.TCR.BIT.TPSC=0;   /*内部クロックφでカウント*/
    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;   /*TIOCBにPWM信号を出力しないように設定*/
    ITU.TSTR.BIT.STR3=1;   /*カウントスタート(PWM信号が出力される)*/
}

void driveMotor(int pwm)
{
/*ITU3.GRA=pwmの時デューティ比100%で動作してほしい*/
/*しかし仕様上デューティ比100%を作る場合はGRA<GRBにすることになっている*/
    if (0<pwm) {
        P4.DR.BIT.B0=1;
        P4.DR.BIT.B1=0;
        pwm--;
        if (ITU3.GRA==pwm) pwm++; /*ITU3.GRA==pwm対策*/
        ITU3.GRB=pwm;
    } else if (pwm<0) {
        P4.DR.BIT.B0=0;
        P4.DR.BIT.B1=1;
        pwm=-pwm-1;
        if (ITU3.GRA==pwm) pwm++; /*ITU3.GRA==pwm対策*/
        ITU3.GRB=pwm;
    } else {
        P4.DR.BIT.B0=0;
        P4.DR.BIT.B1=0;
        ITU3.GRB=0;
    }
}

void brakeMotor(void)
{
/*ITU3.GRA=pwmの時デューティ比100%で動作してほしいが,GRA<GRBで100%になる*/
    P4.DR.BIT.B0=1;
    P4.DR.BIT.B1=1;
    ITU3.GRB=ITU3.GRA+1;
}


/***************************************
インクリメント型2相ロータリエンコーダ信号カウント (ITU2)
void initPhaseCounter(void)
 2相信号カウンタ初期化

int getPhaseCounter(void)
 2相信号カウンタ値取得

void clearPhaseCounter(void)
 2相信号カウンタ値クリア

****************************************/

void initPhaseCounter(void)
/*ITU chanel2を使用*/
{
    ITU.TMDR.BYTE |= 0x40; /*MDF->1 ITU2:counter mode*/
    ITU.TSTR.BIT.STR2=1;
    ITU2.TCNT=0;
}

/*
int getPhaseCounter(void)
{
    return ITU2.TCNT;
}

void clearPhaseCounter(void)
{
    ITU2.TCNT=0;
}
*/
#define getPhaseCounter() (ITU2.TCNT)
#define clearPhaseCounter() (ITU2.TCNT=0)

/*何もしないで指定された時間[msec]たつと戻る関数*/
void msecwait(int msec)
{
    int i,j;
    for (i=0;i<msec;i++) {
        for (j=0;j<1588;j++);    /*1588は実測によって求めた値*/
    }
}

/* mainと割り込み関数のみで共有するグローバル変数,この変数名はINTで始まる*/
volatile int INTposition=0;      /*カウント*/
volatile int INToldposition=0;   /*前回のカウント*/
volatile int INTdifference=0;    /*カウント差分(10msec間)*/
volatile int INTrequest=0;       /*表示要求 1:要求あり 0:要求なし*/

#define PWM_MAX 1000

main()
{
    int pwm;
    initSCI1();
    initPhaseCounter();
    init_pwm3(PWM_MAX);
    SCI1_printf("       ** Motor Drive and Phase Counter for Rotary Encoder **\n");
    SCI1_printf("Hitting any key makes motor stop.\n");
    SCI1_printf("PWM value should be in the range of -%d .. %d.\n",PWM_MAX,PWM_MAX);
    initTimer1Int(10000); /*時間割り込み10000μsec ch1使用*/
    while (1) {
        pwm=getIntSCI1("PWM value=");
        msecwait(100); /*入力バッファのクリア*/
        while (0<chkgetCharSCI1()); /*入力バッファのクリア*/
        INTrequest=0;
        INToldposition=0;
        E_INT();             /*CPU割り込み許可*/
        startTimer1();      /*時間割り込みタイマスタート ch1*/
        clearPhaseCounter();
        driveMotor(pwm);
        while (chkgetCharSCI1()<=0) {
            if (INTrequest) {
                SCI1_printf("Position=%6d difference=%4d\n",INTposition,INTdifference);
                INTrequest=0;
            }
        }
        stopTimer1();      /*時間割り込みタイマストップ ch1*/
        D_INT();             /*CPU割り込み禁止*/
        brakeMotor();
    }
}

/*タイマ割り込み関数*/
/*この関数名は変更できない。プログラム中で1つしか使えない。*/
void interrupt_cfunc()
{
    INTposition=getPhaseCounter();
    INTdifference=INTposition-INToldposition;
    INToldposition=INTposition;
    INTrequest=1;
}

 

【2】仮想的な32ビットカウンタを作る

前述のプログラムでは,ITU2chの位相カウンタに対応した関数を3つ,次のように用意している。
void initPhaseCounter(void)
 2相信号カウンタ初期化
int getPhaseCounter(void)
 2相信号カウンタ値取得
void clearPhaseCounter(void)
 2相信号カウンタ値クリア

もし,32ビットのカウンタが存在しても同様の関数を作ると考えられる。異なる点は,getPhaseCounter()の返す値がlong intになるということである。
32ビットのハードウェアカウンタは存在しないが,存在するように見せかけることは出来る。そこで,この3つの関数は残しておき,次の3つの関数を新たに作成する。
void initVirtualPhaseCounter(void)
 ソフトウェア2相信号カウンタ初期化
 内部でinitPhaseCounter()を使ってもよい

long int getVirtualPhaseCounter(void)
 ソフトウェア2相信号カウンタ値取得
 内部でgetPhaseCounter()を使ってもよい

void clearVirtualPhaseCounter(void)
 ソフトウェア2相信号カウンタ値クリア
 内部でclearPhaseCounter()を使ってもよい

この3つの関数の内部では共用するグローバル変数の存在を認めるが,それ以外のグローバル変数を使用してはならない。

このような仕様の3つの関数を作ると【1】のプログラムのmain()では,
initPhaseCounter() → initVirtualPhaseCounter()
getPhaseCounter()  → getVirtualPhaseCounter() 

clearPhaseCounter()→ clearVirtualPhaseCounter()
の3箇所を変更するだけで【2】のプログラムになる。【2】のプログラムは未完成である。3つの関数を完成させなさい。

H8/3048のITUを使用したPWM・カウンタテスト(32ビットの仮想ソフトウェアカウンタ利用)

/****************************************
インクリメント型2相ロータリエンコーダ信号カウント (ITU2)
ロータリエンコーダAB信号のカウント
タイマ割り込みを用いたカウント差分(速度に対応)
PCのターミナルソフトからのキーボード操作でモータ駆動を複数回実行可能
****************************************/

#include "h8-01.h"

/***************************************
PWM信号+回転方向信号出力 (ITU3,P4-0,P4-1)
void init_pwm3(int pwmMax)
 モータ駆動PWM信号発生機構の初期化
 ITU3はPWM信号を発生し,P4-0,1はモータの回転方向を指示する
 int pwmMAXはPWM周期を規定する。PWM周期 T は
 T = M/(16*10^6) [sec] となる。 M:pwmMAX

void driveMotor(int pwm)
 PWM駆動指令を与える
 -pwmMAX<pwm<pwmMAXを与えるとよい。
 ただし,この範囲を超える値を与えてもよい
 負のpwm値は逆転を意味する

void brakeMotor(void)
 モータドライバICの仕様に従って,ショートブレーキ状態を作る
****************************************/

void init_pwm3(int pwmMax)
{
    /*P4-0,P4-1初期化(P4-0,P4-1で回転方向を設定するため) */
    P4DDR|=0x03;  /*下位2ビットを出力に*/
    P4.DDR=P4DDR;
    P4.DR.BYTE=0x0; /*初期値 b0=cw,b1=ccw 正論理*/

    /*ITU3の初期化*/
    ITU3.TCR.BIT.TPSC=0;   /*内部クロックφでカウント*/
    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;   /*TIOCBにPWM信号を出力しないように設定*/
    ITU.TSTR.BIT.STR3=1;   /*カウントスタート(PWM信号が出力される)*/
}

void driveMotor(int pwm)
{
/*ITU3.GRA=pwmの時デューティ比100%で動作してほしい*/
/*しかし仕様上デューティ比100%を作る場合はGRA<GRBにすることになっている*/
    if (0<pwm) {
        P4.DR.BIT.B0=1;
        P4.DR.BIT.B1=0;
        pwm--;
        if (ITU3.GRA==pwm) pwm++; /*ITU3.GRA==pwm対策*/
        ITU3.GRB=pwm;
    } else if (pwm<0) {
        P4.DR.BIT.B0=0;
        P4.DR.BIT.B1=1;
        pwm=-pwm-1;
        if (ITU3.GRA==pwm) pwm++; /*ITU3.GRA==pwm対策*/
        ITU3.GRB=pwm;
    } else {
        P4.DR.BIT.B0=0;
        P4.DR.BIT.B1=0;
        ITU3.GRB=0;
    }
}

void brakeMotor(void)
{
/*ITU3.GRA=pwmの時デューティ比100%で動作してほしいが,GRA<GRBで100%になる*/
    P4.DR.BIT.B0=1;
    P4.DR.BIT.B1=1;
    ITU3.GRB=ITU3.GRA+1;
}


/***************************************
インクリメント型2相ロータリエンコーダ信号カウント (ITU2)
void initPhaseCounter(void)
 2相信号カウンタ初期化

int getPhaseCounter(void)
 2相信号カウンタ値取得

void clearPhaseCounter(void)
 2相信号カウンタ値クリア

****************************************/

void initPhaseCounter(void)
/*ITU chanel2を使用*/
{
    ITU.TMDR.BYTE |= 0x40; /*MDF->1 ITU2:counter mode*/
    ITU.TSTR.BIT.STR2=1;
    ITU2.TCNT=0;
}

/*
int getPhaseCounter(void)
{
    return ITU2.TCNT;
}

void clearPhaseCounter(void)
{
    ITU2.TCNT=0;
}
*/
#define getPhaseCounter() (ITU2.TCNT)
#define clearPhaseCounter() (ITU2.TCNT=0)

/**************************************************************
この位置に3つの仮想関数で共通に使う変数を定義する。
その変数の名前は他のグローバル関数と異なることを強調するため
変数名はVPCで始まることとする。

この位置に次の3つの関数を記述する。
この3つの関数では共用するグローバル変数の存在を認めるが,
それ以外の関数からこのグローバル変数を使用してはならない

void initVirtualPhaseCounter(void)
 ソフトウェア2相信号カウンタ初期化
 内部でinitPhaseCounter()を使ってもよい

long int getVirtualPhaseCounter(void)
 ソフトウェア2相信号カウンタ値取得
 内部でgetPhaseCounter()を使ってもよい

void clearVirtualPhaseCounter(void)
 ソフトウェア2相信号カウンタ値クリア
 内部でclearPhaseCounter()を使ってもよい
**************************************************************/

void initVirtualPhaseCounter(void)
{
    ソフトウェア2相信号カウンタ初期化 ここを作る
}

long int getVirtualPhaseCounter(void)
{
    ソフトウェア2相信号カウンタ値取得 ここを作る
}

void clearVirtualPhaseCounter(void)
{
    ソフトウェア2相信号カウンタ値クリア ここを作る
}

/*何もしないで指定された時間[msec]たつと戻る関数*/
void msecwait(int msec)
{
    int i,j;
    for (i=0;i<msec;i++) {
        for (j=0;j<1588;j++);    /*1588は実測によって求めた値*/
    }
}

/* mainと割り込み関数のみで共有するグローバル変数,この変数名はINTで始まる*/
volatile int INTposition=0;      /*カウント*/
volatile int INToldposition=0;   /*前回のカウント*/
volatile int INTdifference=0;    /*カウント差分(10msec間)*/
volatile int INTrequest=0;       /*表示要求 1:要求あり 0:要求なし*/

#define PWM_MAX 1000

main()
{
    int pwm;
    initSCI1();
    initVirtualPhaseCounter();
    init_pwm3(PWM_MAX);
    SCI1_printf("       ** Motor Drive and Phase Counter for Rotary Encoder **\n");
    SCI1_printf("Hitting any key makes motor stop.\n");
    SCI1_printf("PWM value should be in the range of -%d .. %d.\n",PWM_MAX,PWM_MAX);
    initTimer1Int(10000); /*時間割り込み10000μsec ch1使用*/
    while (1) {
        pwm=getIntSCI1("PWM value=");
        msecwait(100); /*入力バッファのクリア*/
        while (0<chkgetCharSCI1()); /*入力バッファのクリア*/
        INTrequest=0;
        INToldposition=0;
        E_INT();             /*CPU割り込み許可*/
        startTimer1();      /*時間割り込みタイマスタート ch1*/
        clearVirtualPhaseCounter();
        driveMotor(pwm);
        while (chkgetCharSCI1()<=0) {
            if (INTrequest) {
                SCI1_printf("Position=%6d difference=%4d\n",INTposition,INTdifference);
                INTrequest=0;
            }
        }
        stopTimer1();      /*時間割り込みタイマストップ ch1*/
        D_INT();             /*CPU割り込み禁止*/
        brakeMotor();
    }
}

/*タイマ割り込み関数*/
/*この関数名は変更できない。プログラム中で1つしか使えない。*/
void interrupt_cfunc()
{
    INTposition=getVirtualPhaseCounter();
    INTdifference=INTposition-INToldposition;
    INToldposition=INTposition;
    INTrequest=1;
}