AKI-SH2(HitachiSH2/7045)プログラミング例

Copyright(C) 29Jan2004

coskx

1. はじめに

この文書はSH2/4075を用いたプログラミング例を示します。

(1)PWM信号発生
(2)PWMを使ったPWM駆動
(3)phase counterを用いたロータリエンコーダパルス計測
(4)スキャンモードADCでのステレオ録音

サンプルプログラムのダウンロード

DownLoad

図1 AKI-SH2/7045 マザーボード
(卒業研究生山下晃弘君,永山明日香さんの設計製作)

2. MTU3を用いた単相PWM出力

シリアル通信でつながれたターミナル側(ハイパーターミナル)から数値(デューティ比)を与えてその値でのPWM波形を出力するプログラム。
PWM周波数は7.1kHz(7.1MHzクロック1000個分)でデューティ比は0〜1000の数値で与える。

pwmtest.c

#include "7040S.H"
#include "sh_7045.h"

/*
MTU3を利用したPWM生成機能の初期化関数
入力
countermax TGRAの設定、
clockindexは内部クロックの設定
 0:φ/1      28.6MHz
 1:φ/4       7.1MHz
 2:φ/16      1.78MHz
 3:φ/64      447kHz
 4:φ/256     112kHz
 5:φ/1024     27.9kHz
 
出力:TIOC3A端子(AKI-SH2/7045ではCN3-31)

TGRA<TGRBにするとデューティ比が100%になる
TGRA=TGRBにすると出力が変化しなくなる(0になるか1になるかは不定)
TGRB=0にしてもデューティ比が0にならないでひげがでる (これは無視する)
*/

void initPWM3AB(unsigned short int countermax, int clockindex)
{
    PFC.PECR1.BIT.PE8MD = 1; //PE8/TIOC3A端子をアウトプットコンペア出力に設定
    PFC.PEIOR.BIT.B8 = 1; //PE8/TIOC3A端子を出力に設定
    MTU3.TCR.BIT.TPSC = clockindex; //TCNTのカウンタクロックをclockに設定
    MTU3.TCR.BIT.CKEG = 0; //立ち上がりエッヂでカウント
    MTU3.TCR.BIT.CCLR = 1; //TGRAのコンペアマッチでTCNTをクリア
    MTU3.TGRA = countermax; //TGRAにcountermaxを設定
    MTU3.TGRB = 0; //初期のデューティー比を0に設定
    MTU3.TIOR.BIT.IOB = 1; /*TGRBをアウトプットコンペアレジスタに設定
                             初期出力0、コンペアマッチで0出力*/
    MTU3.TIOR.BIT.IOA = 2; /*TGRAをアウトプットコンペアレジスタに設定
                             初期出力0、コンペアマッチで1出力*/
    MTU3.TMDR.BIT.MD = 2; //PWMモード1に設定
}

void setPWMvalue3AB(unsigned short int value)
{
    if (value==MTU3.TGRA) value++;
    MTU3.TGRB = value; //TGRBの値をvalueに設定
}

/*
void startTimer3(void)
{
    MTU.TSTR.BIT.CST3 = 1;
}*/
#define startTimer3() (MTU.TSTR.BIT.CST3 = 1)

/*
void stopTimer3(void)
{
    MTU.TSTR.BIT.CST3 = 0;
}*/
#define stopTimer3() (MTU.TSTR.BIT.CST3 = 0)


int main(void)
{
    int PWMvalue;
    initSCI1();
    initPWM3AB(1000,1);
    startTimer3();
    SCI1_printf("PWM output with MTU ch3\n");
    while (1) {
        PWMvalue=getIntSCI1("PWMvalue for drivefunction(0..1000)=");
        setPWMvalue3AB(PWMvalue);
    }
}

この部分は卒業研究生山下晃弘君,永山明日香さんの貢献です。

 

3.    MTU0とPAbit16,17を用いたPWM出力によるモータドライバ用出力

シリアル通信でつながれたターミナル側(ハイパーターミナル)から数値(デューティ比)を与えてその値でのモータ駆動用PWM波形を出力するプログラム。
PWM周波数は7.1kHz(7.1MHzクロック1000個分)でデューティ比は-1000〜1000の数値で与える。負の値の時,モータは逆転する。

motor.c

#include "7040s.h"
#include "sh_7045.h"

/**********************************************************************
 MTU0とPA16,PA17を用いたモータ駆動
初期化関数でクロックの選択,PWM周期を与えるカウンタの最大値を設定する
駆動関数で正負両方向へのPWM値を与える。
出力
PE0/TIOC0A:PWM出力
PA-bit16: 正転の時1,そうでない時0出力
PA-bit17: 逆転の時1,そうでない時0出力
H型モータドライバへは,外部回路で[TIOC0A]AND[PA-bit16]と
[TIOC0A]AND[PA-bit17]をあたえるようにすればよい
**********************************************************************/

/**********************************************************************
 MTU0とPA16,PA17を用いたモータ駆動の初期化関数
countermax は TGRA にセットされる

clockindex = 0 then MTUclock = φ
clockindex = 1 then MTUclock = φ/4
clockindex = 2 then MTUclock = φ/16
clockindex = 3 then MTUclock = φ/64
**********************************************************************/
void initMotorMTU0PA1617(unsigned short int countermax,int clockindex)
{
    PFC.PAIORH.BIT.B16 = 1;     //PA16を出力に設定
    PFC.PAIORH.BIT.B17 = 1;     //PA17を出力に設定
    PA.DR.BIT.B16 = 0;          //正回転=0
    PA.DR.BIT.B17 = 0;          //逆回転=0

    PFC.PECR2.BIT.PE0MD = 1;    //PE0/TIOC0A端子をoutputコンペア出力に設定
    PFC.PEIOR.BIT.B0 = 1;       //PE0/TIOC0A端子を出力に設定
    MTU0.TCR.BIT.TPSC = clockindex;//TCNTのカウンタクロックをclockに設定
    MTU0.TCR.BIT.CKEG = 0;      //立ち上がりエッヂでカウント
    MTU0.TCR.BIT.CCLR = 1;      //TGRAのコンペアマッチでTCNTをクリア
    MTU0.TGRA = countermax;     //TGRAにcountermaxを設定
    MTU0.TGRB = 0;              //初期のデューティー比を0に設定
    MTU0.TIOR.BIT.IOB = 1;      //TGRBをアウトプットコンペアレジスタに設定
                                //初期出力0、コンペアマッチで0出力
    MTU0.TIOR.BIT.IOA = 2;      //TGRAをアウトプットコンペアレジスタに設定
                                //初期出力1、コンペアマッチで1出力
    MTU0.TMDR.BIT.MD = 2;       //PWMモード1に設定

    MTU.TSTR.BIT.CST0 = 1;      //PWM動作スタート
}

/**********************************************************************
PWM値を与えてモータを駆動する関数
pwmvalueはinitMotorMTU0PA1617()で与えたcountermaxで決まる。
通常は次の範囲
-countermax <= pwmvalue <= countermax
ただし,
(1)abs(pwmvalue)/countermaxのデューティ比で出力される
(2)負のpwmvalueを与えると逆回転する
(3)この範囲外の場合は±100%のデューティ比が出力される
**********************************************************************/
void driveMotorMTU0PA1617(short int pwmvalue)
{
    if (pwmvalue<0) {
        PA.DR.BIT.B16 = 0;          //正回転=0
        PA.DR.BIT.B17 = 1;          //逆回転=1
        pwmvalue=-pwmvalue;
    } else if (pwmvalue==0){
        PA.DR.BIT.B16 = 0;          //正回転=0
        PA.DR.BIT.B17 = 0;          //逆回転=0
    } else {
        PA.DR.BIT.B16 = 1;          //正回転=1
        PA.DR.BIT.B17 = 0;          //逆回転=0
    }
    if (pwmvalue == MTU0.TGRA) pwmvalue++;
    MTU0.TGRB=pwmvalue;
}

#define PWM_MAX 1000
main()
{
    short int pwmvalue;
    initSCI1();
    initMotorMTU0PA1617(PWM_MAX,1);
    while(1) {
        pwmvalue=getIntSCI1("PWMvalue for drivefunction(-1000..1000)=");
        SCI1_printf("%d\n",pwmvalue);
        driveMotorMTU0PA1617(pwmvalue);
    }
}

この部分のオリジナルは卒業研究生山下晃弘君の貢献です。

 

4.    MTU1と2を用いた二相カウンタ

インクリメンタルロータリエンコーダの二相パルスをカウントするカウンタが2チャンネルあるので,これを使い,2つのインクリメンタルロータリエンコーダによる角度検出を行なう。ターミナルのキーがどれか押されると,カウンタの値はクリアされる。

counter.c

#include "7040s.h"
#include "sh_7045.h"

/*入力の接続
チャネル1A相 PA6/TCLKA CN3-24
チャネル1B相 PA7/TCLKB CN3-21
チャネル2A相 PA8/TCLKC CN3-22
チャネル2B相 PA9/TCLKD CN3-19
*/

//位相計数カウンタ初期化
/*-----------------------------------------------------------------------
mode = 1  位相計数モード1
mode = 2  位相計数モード2
mode = 3  位相計数モード3
mode = 4  位相計数モード4
------------------------------------------------------------------------*/
void initCounter1(int mode)
{
    PFC.PACRL2.BIT.PA6MD = 1;       //PA6/TCLKA端子をMTUタイマクロック入力に設定
    PFC.PACRL2.BIT.PA7MD = 1;       //PA7/TCLKB端子をMTUタイマクロック入力に設定
    MTU1.TCR.BYTE = 0;
    MTU1.TMDR.BIT.MD = mode + 3;    //位相計数モード設定
}

void initCounter2(int mode)
{
    PFC.PACRL1.BIT.PA8MD = 1;       //PA8/TCLKC端子をMTUタイマクロック入力に設定
    PFC.PACRL1.BIT.PA9MD = 1;       //PA9/TCLKD端子をMTUタイマクロック入力に設定
    MTU2.TCR.BYTE = 0;
    MTU2.TMDR.BIT.MD = mode + 3;    //位相計数モード設定
}

/* 以下の関数は関数マクロとする。
//カウンタ動作スタート
void startCounter1(void){
    MTU.TSTR.BIT.CST1 = 1;
}
void startCounter2(void){
    MTU.TSTR.BIT.CST2 = 1;
}

//カウンタ値読み出し
unsigned int getCounter1(void){
    return (volatile)MTU1.TCNT;
}
unsigned int getCounter2(void){
    return (volatile)MTU2.TCNT;
}

//カウンタリセット
void clearCounter1(void){
    MTU1.TCNT = 0;
}
void clearCounter2(void){
    MTU2.TCNT = 0;
}
*/
#define startCounter1()   (MTU.TSTR.BIT.CST1 = 1)
#define startCounter2()   (MTU.TSTR.BIT.CST2 = 1)
#define getCounter1()     (MTU1.TCNT)
#define getCounter2()     (MTU2.TCNT)
#define clearCounter1()   (MTU1.TCNT = 0)
#define clearCounter2()   (MTU2.TCNT = 0)

main()
{
    short int counter1, counter2;
    initSCI1();
    initCounter1(4);
    initCounter2(4);

    SCI1_printf("Phase Counter 1 and Phase Counter 2\n");

    startCounter1();
    startCounter2();

    while(1){
        counter1 = getCounter1();
        counter2 = getCounter2();
        SCI1_printf("   %6d  %6d\r",counter1, counter2);
        if (0<chkgetCharSCI1()) {
            clearCounter1();
            clearCounter2();
        }
    }
}

この部分のオリジナルは卒業研究生山下晃弘君の貢献です。

 

5. ADコンバータ入力

ADC0,ADC1を初期化し,8bitサンプリングで1秒間ステレオ録音した後,録音データをシリアル通信でテキスト送信するプログラム。
サンプリング周波数は8kHz,であり,これはコンペアマッチタイマによる割り込みで実現している。
ADC0ではAN0のみ,ADC1ではAN4のみを変換する。ADCは10bitの変換を行ない,これがレジスタのbit15からbit6までに格納される。
右に8シフトすると0〜255の数値に変換される。

起動後,ADC変換値の平均値(期待値0x8000)を表示するが,PCのキーを押すと「Ready」と表示され,さらにキーを押すと1秒間の録音が開始される。その直後データのシリアル出力が始まるので,テキストキャプチャしておくと良い。

ADCtest.c

#include "7040S.H"
#include "sh_7045.h"
//ad0...PF0...CN3-49
//ad4...PF4...CN3-45

/*スキャンモードでADC0を初期化 AN0のみ使用*/
void initAD0(void)
{
    AD0.ADCSR.BIT.ADF = 0;    /*エンドフラグをクリア*/
    AD0.ADCSR.BIT.ADIE = 0;   /*インタラプト禁止*/
    AD0.ADCSR.BIT.ADST = 1;   /*スタート/停止*/
    AD0.ADCSR.BIT.SCAN = 1;   /*スキャンモード*/
    AD0.ADCSR.BIT.CKS = 0;    /*変換時間=266ステート*/
    AD0.ADCSR.BIT.CH = 0;     /*AN0のみ */
    AD0.ADCR.BIT.TRGE = 0;    /*外部トリガによるA/D変換の開始を禁止*/
}

/*スキャンモードでADC1を初期化 AN4のみ使用*/
void initAD1(void)
{
    AD1.ADCSR.BIT.ADF = 0;    /*エンドフラグをクリア*/
    AD1.ADCSR.BIT.ADIE = 0;   /*インタラプト禁止*/
    AD1.ADCSR.BIT.ADST = 1;   /*スタート/停止*/
    AD1.ADCSR.BIT.SCAN = 1;   /*スキャンモード*/
    AD1.ADCSR.BIT.CKS = 0;    /*変換時間=266ステート*/
    AD1.ADCSR.BIT.CH = 0;     /*AN4のみ */
    AD1.ADCR.BIT.TRGE = 0;    /*外部トリガによるA/D変換の開始を禁止*/
}

/**********************************
以下の4関数はシングルモードでのみ使用する
マクロ関数に変更
void startAD0(void)
{
    AD0.ADCSR.BIT.ADST = 1;
}
void startAD1(void)
{
    AD1.ADCSR.BIT.ADST = 1;
}
void stopAD0(void)
{
    AD0.ADCSR.BIT.ADST = 0;
}
void stopAD1(void)
{
    AD1.ADCSR.BIT.ADST = 0;
}
***********************************/
#define startAD0() (AD0.ADCSR.BIT.ADST = 1)
#define stopAD0()  (AD0.ADCSR.BIT.ADST = 0)
#define startAD1() (AD1.ADCSR.BIT.ADST = 1)
#define stopAD1()  (AD1.ADCSR.BIT.ADST = 0)


unsigned char buffer1[16000];
unsigned char buffer2[16000];
volatile int counter;

int main(void)
{
    int i;
    unsigned int ad0,ad1;
   
    initSCI1();
    initAD0();
    initAD1();
    initCMTInt(125,0,15);//125μs(8000Hz),CMT0,InterruptLevel=15

    while(chkgetCharSCI1()<0){
        ad0=ad1=0;
        for (i=0;i<0x100;i++) {
            ad0+=AD0.ADDRA.WORD>>8;
            ad1+=AD1.ADDRA.WORD>>8;
        }
        SCI1_printf("(%4x %4x)     ",ad0,ad1);
    }
   
    SCI1_printf("Ready?\n");
    getCharSCI1();
   
    counter=0;
    setIntMask(14); /*割り込みマスクを14に下げたのでレベル15は割り込み許可 */
    startCMT0(); /* コンペアマッチタイマCMT0スタート */

    while (counter<8000);
   
    for (i=0;i<8000;i++) {
        SCI1_printf("%d\t%u\t%u\n",i,buffer1[i],buffer2[i]);
    }
    while(1);
}

#pragma interrupt
int_cmi0()
{
    if (counter<16000) {
        buffer1[counter]=((unsigned short int)AD0.ADDRA.WORD)>>8;
        buffer2[counter]=((unsigned short int)AD1.ADDRA.WORD)>>8;
        counter++;
    }
    clearCMFlag0(); /* CMT0のコンペアマッチフラッグのクリア */
}

この部分のオリジナルは卒業研究生永山明日香さんの貢献です。