210.SH2先ずはPIOから(SH2-7045F)


2002/05/01 【ソフトウエア編TOPに戻る】

SH2のソフト開発環境が整って、SH2マイコンボード用のマザーボードも作ったので、いよいよ実際にSH2のプログラミングの話に移りましょう。プログラミングといっても、基本的にはH8でやってきたのと同じC言語です。ただちょっとだけレジスタの名称が違ったり、ハードウエアの設定の仕方が違ったり、プログラムのイニシャル処理にクセがあったりする程度で、H8に比べて特に難しいといったこともないと思います。

SH2にはいろいろな機能があり、これからロボット制御を行なっていく上で憶えなければならないことも沢山あるのですが、そんな長い道のりも、第一歩はやはりPIOによるパラレル信号の入出力から始まります。先ずは以下のページをご覧頂き、プログラミング環境と動作環境についての整備をお願いします。

電子回路編 SH2(7045F)マイコンボードSH2-7045F用マザーボード

ソフトウエア編 SH2(7045F)ソフトウエア開発環境


210−1.CPU端子の初期化の必要性

SH2に限らず、ほとんどのワンチップマイコンではひとつのピンに複数の機能を持たせてあり、用途によって使い分けが出来るようになっています。特にPIOのパラレル入出力機能の端子が、タイマーを初めとする、その他の機能の端子と共通になることが多いようです。

SH2では、このような共用端子(マルチプレクス端子)の用途を決めるためのレジスタが専用にあり、PFCピンファンクションコントローラレジスタ)と呼ばれています。

PFCでは、ポートA〜Eまでの、各パラレルポートと共用になっている端子の用途を決めるためのレジスタと、PIO側に設定された場合の入出力方向を決めるためのレジスタなど、16ビット幅のレジスタが全部で19個もあります。

シングルチップモード内蔵ROM有効モードの場合には、初期値のままにしておくと、基本的には全てPIOの入力端子として機能するような設定になりますが、それ以外の用途にしたい時には、プログラム立ち上げ時に、必ず該当するPFCのレジスタを設定する必要があります。個人的には、例えPIOの入力として利用する場合(書き換えが必要でない場合)でも、初期化の処理を明示的に行なうために、必ずレジスタの設定処理をするほうが良いと思います。

H8などでは、このようにわざわざピンファンクションを設定するための専用レジスタはなく、利用したい各種機能の初期設定だけで用途を決定することができます。その意味では、SH2を使う上で一番面倒な部分であり、敷居を高くしているひとつの要因ではないか、と思ったりもします。

ただし、一度ハードウエアマニュアルに目を通して見てみると、なにも大したことはしていません。レジスタの数が多くて面倒ですが、別に難しいことをしているわけではありませんので、要は慣れといったところでしょうか。

【先頭に戻る】


210−2.PIOテストプログラムpiotest1.cのご紹介

では、実際にPIOの機能を使ったプログラム例をご紹介します。このプログラムは、PD24〜31のポート8ビット分の状態を入力として取り込み、 出力に設定したPD16〜23PD8〜15PD0〜7PC8〜15およびPC0〜7の各8ビット分のポートから、その状態を素直に出力するだけのものです。

これ以上簡単なプログラムは無いだろうというくらい簡単ですが、PFCレジスタ設定の実際の様子を見て頂くにはちょうど良いかなと思います。

今回のプログラムでは割り込み処理を行なっていません。割り込みベクタテーブル用のファイルvector.sは、割込み用の外部関数の宣言をしていないものを用意しました。vector1.s  ベクタテーブルファイルの中に、使用しない外部関数の宣言がしてあるとコンパイル時にエラーが発生するようなので、その辺はきっちりしないといけないようです。

テストプログラムpiotest1.cは、ここをクリックするとダウンロードできます。また、割込みベクタテーブルファイルvector1.sここをクリックするとダウンロードできます。ただし、こちらはダウンロードできたらvector.sにファイル名称を変更して利用して下さい。名称変更しないと正常にコンパイルできません。

以下にプログラムのソースファイルを掲載します。

 

/*************************************
piotest1.c                                 2002.04.26
                                              RIKIYA
SH2_7045F
PD24〜31 DIP-SW INPUT
PD16〜23 LED OUTPUT
PD8〜15 OUTPUT
PD0〜7 OUTPUT
PC8〜15 OUTPUT
PC0〜7 OUTPUT
/*************************************/
#include "sh7040s.h"

void boot(void);
void __main(void);
void main(void);
void sectionInit(void);

/************************************/
/* __main(ダミー) */
/************************************/
void __main(void){
}

/************************************/
/* CPU初期化 */
/************************************/
void boot(void){

    /* IOポート設定 */
    PFC.PACRH.WORD = 0x0000;   /* PA23〜16 */
    PFC.PACRL1.WORD = 0x0000; /* PA15〜8   */
    PFC.PACRL2.WORD = 0x0145; /* PA7,6,5,2 TXD0,RXD0 */

    PFC.PBCR1.WORD = 0x0000;   /* PB9,PB8    */
    PFC.PBCR2.WORD = 0x0000;   /* PB7〜PB0 */

    PFC.PCCR.WORD = 0x0000;    /* PC15〜PC0 */

    PFC.PDCRH1.WORD = 0x0000; /* PD31〜PD24 */
    PFC.PDCRH2.WORD = 0x0000; /* PD23〜PD16 */
    PFC.PDCRL.WORD = 0x0000;   /* PD15〜PD0  */

    PFC.PECR1.WORD = 0x0000;   /* PE15〜PE8  */
    PFC.PECR2.WORD = 0x0000;   /* PE7 〜PE0  */

    PFC.PAIORH.WORD = 0x00ff;  /* [OUT]PA23〜16 [IN]-- */
    PFC.PAIORL.WORD = 0xffff;   /* [OUT]PA15〜0   [IN]-- */
    PFC.PBIOR.WORD = 0x03ff;    /* [OUT]PB7 〜0   [IN]-- */
    PFC.PCIOR.WORD = 0xffff;     /* [OUT]PC15〜0  [IN]-- */
    PFC.PDIORH.WORD = 0x00ff;  /* [OUT]PD23〜16 [IN]PD31〜24 */
    PFC.PDIORL.WORD = 0xffff;   /* [OUT]PD15〜0   [IN]-- */
    PFC.PEIOR.WORD = 0xffff;     /* [OUT]PE15〜0   [IN]-- */

    sectionInit();                        /* メモリ初期化 */

    main();                                /* メイン関数呼び出し */
}

/************************************/
/* MAIN関数 */
/************************************/
void main(void){

/* PD.DR.BYTE.HH は PD24〜PD31 */
    while(1){
        PD.DR.BYTE.HL = PD.DR.BYTE.HH; /* PD16 〜 PD23 */
        PD.DR.BYTE.LL = PD.DR.BYTE.HH; /* PD0 〜 PD7    */
        PD.DR.BYTE.LH = PD.DR.BYTE.HH; /* PD8 〜 PD15  */
        PC.DR.BYTE.L = PD.DR.BYTE.HH;   /* PC0 〜 PC7   */
        PC.DR.BYTE.H = PD.DR.BYTE.HH;   /* PC8 〜 PC15 */
    }
    return;
}

/************************************/
/* セクション初期化 */
/************************************/
extern char etext, sdata, edata, bss_start, end;
void sectionInit(void){

    char *src;
    char *dst;

    /* 初期化データ領域 */
    src = &etext;
    dst = &sdata;
    while (dst < &edata) {
        *dst++ = *src++;
    }
    /* 未初期化データ領域 */
    for ( dst = &bss_start; dst < &end; dst++ ) {
        *dst = 0;
    }
}

【先頭に戻る】


210−3.PIOテストプログラムpiotest1.cの動作説明

ここでは、SH2を起動させるために必要となるプログラム上でのある手順を、ひとつの決まりごととして捉えて下さい。その決まりごとの由来については力弥も勉強不足なため別の機会に譲ることにしますが、ここではひとつの決まったパターンに則ってプログラミングを行なうという程度で頭に入れて置いてください。

そのパターンとは、以下の通りになります。

 

 1.ダミー関数 _main( )を置く。

 2.boot関数 boot( )関数の中でPFCレジスタによるCPUのポート設定を行なう。

 3.続いてboot関数内でsectionInit()関数を実行してメモリーの初期化を行なう。

 4.boot関数内の最後でmain()関数の呼び出しを行なう。

 5.main()関数からプログラムの記述を開始する。

 

上に掲載しているプログラム例は、このパターンに則って記述しています。プログラムはboot関数から起動するようになっているため、boot関数の最後でmain関数を呼び出してあげることによって、メインとなる処理に移行させます。また、メモリー初期化のためのsectionInit関数の内容も変わることのない決まった処理です。

以上の流れを決まったパターンと考えて頂いたうえで、プログラムの動作についてご説明しましょう。

なお、秋月のH8用Cコンパイラなどと比べ、GCCの場合には少しだけ型宣言が厳しい感じを受けます。コンパイラのオプションスイッチで設定が可能かもしれませんが、宣言すべきところはきっちり宣言するクセを付けて置くのも悪くないかもしれません。

 

■1.ヘッダーファイルの取り込みと、使用する関数の型宣言

#include "sh7040s.h"

void boot(void);
void __main(void);
void main(void);
void sectionInit(void);

先ず、#include "sh7040s.h" で、使用するプロセッサのレジスタ類が宣言されているヘッダファイルを読み込みます。ここで、#include <sh7040s.h>とやるとエラーになりました。ヘッダファイルはダブルクォーテーションで囲みましょう。

次に、今回のプログラムで使用する全ての関数の型宣言を行ないます。個々の関数には戻り値があり、その戻り値の型が関数の型ということになります。また、( )の中は引数の型を宣言しておく必要があります。ちなみに、今回の関数は、全て戻り値も引数もないため、voidと入れてあります。なお、関数の型宣言をしないと、コンパイラでWarning(警告)が出ます。

 

■2.ダミー関数を置く

void __main(void){
}

すみません。力弥もこのダミー関数の意味が良くわかりません。ただのmain( )関数ではなく、頭にアンダーバーが着いている _main( ) です。とにかく必要みたいです。詳しい方、おられましたらお教えください。(^^;

 

■3.boot関数で、CPUの初期化設定を行なう。

SH2が起動すると、先ずこのboot関数が実行されます。ここでは、PFC(ピンファンクションコントローラ)レジスタによるCPUの初期化設定を行ないます。

    PFC.PACRH.WORD = 0x0000;   /* PA23〜16 */
    PFC.PACRL1.WORD = 0x0000; /* PA15〜8   */
    PFC.PACRL2.WORD = 0x0145; /* PA7,6,5,2 TXD0,RXD0 */

    PFC.PBCR1.WORD = 0x0000;   /* PB9,PB8    */
    PFC.PBCR2.WORD = 0x0000;   /* PB7〜PB0 */

    PFC.PCCR.WORD = 0x0000;    /* PC15〜PC0 */

    PFC.PDCRH1.WORD = 0x0000; /* PD31〜PD24 */
    PFC.PDCRH2.WORD = 0x0000; /* PD23〜PD16 */
    PFC.PDCRL.WORD = 0x0000;   /* PD15〜PD0  */

    PFC.PECR1.WORD = 0x0000;   /* PE15〜PE8  */
    PFC.PECR2.WORD = 0x0000;   /* PE7 〜PE0  */

以上の部分で、PA , PB , PC , PD , および PE の各PIOポートに関連する端子の用途を設定しています。ご覧の通り、PA0,PA1の端子を シリアル通信のTXD0 , RXD0 に設定している部分以外は、全てPIOポートとして動作させるように設定しています。で、ほとんどのレジスタには0x0000を書き込んでいます。

各レジスタ個別の設定方法をご紹介しようとも思ったのですが、今回は手抜きをさせて頂きます。是非、SH2-7045F用のハードウエアマニュアル(SH7040)をダウンロードし、ご自分でひも解いてみて下さい。(すみません...)

次に、以下の部分で、PIOポートとして利用する端子の入力/出力を決定しています。

    PFC.PAIORH.WORD = 0x00ff;  /* [OUT]PA23〜16 [IN]-- */
    PFC.PAIORL.WORD = 0xffff;   /* [OUT]PA15〜0   [IN]-- */
    PFC.PBIOR.WORD = 0x03ff;    /* [OUT]PB7 〜0   [IN]-- */
    PFC.PCIOR.WORD = 0xffff;     /* [OUT]PC15〜0  [IN]-- */
    PFC.PDIORH.WORD = 0x00ff;  /* [OUT]PD23〜16 [IN]PD31〜24 */
    PFC.PDIORL.WORD = 0xffff;   /* [OUT]PD15〜0   [IN]-- */
    PFC.PEIOR.WORD = 0xffff;     /* [OUT]PE15〜0   [IN]-- */

上記の各レジスタの該当するビットに1を書き込むと出力0を書き込むと入力になります。このレジスタはPIOに設定されている端子にのみ有効となるため、シリアル通信TXD0とRXD0に設定したPA0 , PA1の端子に対しては、設定が無効になります。

こちらのレジスタの使い方や各ビットの意味についても、ハードウエアマニュアルをご参照下さい。

ピンファンクションについての設定が終了したら、次にメモリーの初期化を行なうsectionInit()関数を実行しています。

    sectionInit();                        /* メモリ初期化 */

以上でSH2に関する初期設定が全て終了です。boot関数の最後にmain()関数を呼び出してあげて、いよいよメインとなるプログラム処理に移ります。


    main();                                /* メイン関数呼び出し */

main()関数によるメイン処理

void main(void){

/* PD.DR.BYTE.HH は PD24〜PD31 */
    while(1){
        PD.DR.BYTE.HL = PD.DR.BYTE.HH; /* PD16 〜 PD23 */
        PD.DR.BYTE.LL = PD.DR.BYTE.HH; /* PD0 〜 PD7    */
        PD.DR.BYTE.LH = PD.DR.BYTE.HH; /* PD8 〜 PD15  */
        PC.DR.BYTE.L = PD.DR.BYTE.HH;   /* PC0 〜 PC7   */
        PC.DR.BYTE.H = PD.DR.BYTE.HH;   /* PC8 〜 PC15 */
    }
    return;
}

以上の部分が、SH2マイコンボードにさせたいお仕事のプログラムです。

していることは、PD.DR.BYTE.HHというレジスタ変数の値を、各種PIOポート用のレジスタ変数に代入しているだけです。例えば、PD.DR.BYTE.HL = PD.DR.BYTE.HH; の部分は、PD.DR.BYTE.HL というレジスタ変数に、PD.DR.BYTE.HH の値を代入しています。

具体的には、PD.DR.BYTE.HH とは PD24〜31のPIOビット入力値を示し、PD.DR.BYTE.HL は、PD16〜23のPIOビット出力を示します。したがって、PD24〜31に接続されたスイッチのON/OFF情報で、PD16〜23に接続されたLEDランプを点灯させる、という動作になります。

以上の動作をwhile(1){  }文によって永遠に繰り返します。つまり、スイッチの変化に応じてLEDランプの点灯状態が即座に変化するという動作になります。

 

【ここで注意!】

例えば、ポートDの入出力用レジスタ変数として、以下の4種類が用意されています。

PD.DR.BYTE.LL  → PD7〜PD0用 8ビット幅

PD.DR.BYTE.LH  → PD8〜PD15用 8ビット幅

PD.DR.BYTE.HL  → PD16〜PD23用 8ビット幅

PD.DR.BYTE.HH  → PD24〜PD31用 8ビット幅

しかし、SH2には実際にこれらのレジスタがあるわけではありません。実際にはポートDの入出力用として存在するレジスタは、PDDRL(PD15〜PD0用)とPDDRH(PD31〜PD16用)の16ビット幅のものが2個あるだけです。つまり、上記の4個のレジスタ変数は、ヘッダファイルのsh7040s.h中で、8ビット幅で読み書きしやすい変数として宣言されているだけのものなのです。

ちなみに、16ビット幅で読み書きしやすいように、以下のレジスタ変数も宣言されています。

PD.DR.WORD.L → PD15〜PD0用 16ビット幅

PD.DR.WORD.H → PD31〜PD16用 16ビット幅

プログラム上でどちらを使うべきかは、場合によって様々でしょう。しかし、これらのレジスタ変数に関する専用の取り扱い説明書があるわけではありません。プログラミングを行なう上で、どうやってこれらの情報を得るのかというと、それは自分でsh7040s.hファイルの中身を見てみる以外にありません。

ファイルの中は構造体変数宣言だらけで、拒絶反応を起こす方も珍しくないかと思いますが、すぐになれます。逆に、ヘッダファイルの中身を自分で追いかけられないと、プログラミングがおぼつかないということになります。是非、ヘッダファイルの中身を覗いてみてください。

 

【先頭に戻る】


210−4.実験風景

以下の写真は、今回のプログラム動作の実験風景です。

 

PIO実験風景

ちょっと暗くて分かりづらいですが、マイコンボードの下段がマザーボードで、そこに8ビットのディップスイッチとLED表示があります。

ディップスイッチではひとつ置きにON/OFFに設定してあり、それにならってマザーボードのLEDもひとつ置きに点灯しています。

また、隣の実験用ボードにはPC8〜15を接続してありますが、スイッチ設定と同様にひとつ置きに点灯している様子が分かりますか?

【先頭に戻る】


今回は、SH2−7045FマイコンボードによるPIO動作のプログラミング導入部について、駆け足でご紹介してきましたが、SH2を動かす上で必要なPFCレジスタの設定を行なう様子を実際に見て頂くのが最大の目的のような感じでした。PFCと、プログラミング上の独特なクセ(ルール)さえ克服してしまえば、あとは普通に使い始めることができるのではないでしょうか。

でも、SH2は、やはり高機能なマイコンです。使いこなしていくためにはどうしても、自分でハードウエアマニュアルとにらめっこをしていく必要があります。ここでの解説をサボった言い訳でもありますが、是非、ハードウエアマニュアルや、sh7040s.hヘッダファイルに目を通してみて下さい。

 

【ソフトウエア編TOPに戻る】


【表紙に戻る】