AKI-SH2(HitachiSH2/7045)の使い方
マザーボードSWとLEDによるプログラミング
(cygwin+GCC)

Copyright(C) 1Dec2003


coskx

0. はじめに

0.1 この文書の構成
この文書はSH2/7045のCプログラムをコンパイルする方法,SH2/7045にフラッシュメモリ書き込み(転送)する方法を修得した学習者が,SH2/7045のマザーボードを用いてLEDをつけたりスイッチを取り込んだりするプログラムを学習するための文書です。

0.2 AKI-SH2(HitachiSH2/7045)
 SH2/7045(図1)は日立製作所の製品であるマイクロコンピュータSH製品群のSH2/7045を用いて,秋月電子通商がCPUボードを製作販売している製品の商品名です。写真では濃い緑色の基盤です。
このCPUボードでは動作の確認が何も出来ないため,スイッチとLEDを付加したマザーボードを作りました。


マザーボード上には次の要素が搭載されています
(1)   12V電源入力,5V出力3端子レギュレータ(12Vから安定した5Vを作るモジュール)
(2)   4ビットスイッチ
(3)   プッシュスイッチ
(4)   LED(発光ダイオード)

AKI-SH2/7045でのプログラミングはパソコン上で次のように行ないます。
(1)   C言語またはアッセンブリ言語でプログラムを開発後
(2)   コンパイル・リンクを行ない実行プログラムファイルを作ります
(3)   ロードモジュールに変換します
(4)   RS232C通信でロードモジュールをH8に送信(フラッシュメモリに書き込む)
(5)   実行

図1 AKI-SH2/7045 マザーボード

1. LEDの点滅のプログラム

図2のLEDが対象となるLEDです。



図2 2つのLED

1.1    LED点滅プログラム

マザーボード上の2つのLEDを点滅させるプログラムを作ります。
2つのLEDはAKI-SH2/7045CPUのポートEの第9ビット,第11ビットの端子につけられています。

参考 電流制限

CPUの出力端子はレベルHの時5V,レベルLの時0Vであり,LED単体の電圧降下が1.5V程度であるため,保護抵抗1.5kΩには3.5Vがかかっていることになります。この抵抗に流れる電流は3.5[V]/1500[Ω] = 0.0023[A] = 2.3[mA]となります。LEDは10mA駆動が標準ですが,1mA駆動でも光らないないわけではありません。一方CPUの端子出力電流は最大2.0mAとされている(SH2/7045の仕様)ので,わずかに仕様違反となっています。



1.2    LED点滅プログラムの考え方

(1)駆動の手順

(1) ポートEの第9ビット,第11ビットを出力に設定
(2) ポートEの第9ビット,第11ビットに0または1を出力してLED点滅を行ないます。
       0:OFF  1:ON
(3) 時間調整して1秒ごとに2つのLEDが交互に点滅するようにします。

(2)具体的なプログラミングの考え方

ポートEの第9ビット,第11ビットを出力に設定(PEのHの第1,第3ビット)
以下を無限ループ
  ポートEの第9ビットを1
  ポートEの第11ビットを0
  1秒間時間待ち
  ポートEの第9ビットを0
  ポートEの第11ビットを1
  1秒間時間待ち

1.3 LED点滅プログラム


    ポートEは入出力可能なポートで,各ビットごとに入力に使用したり出力に使用したりできる。
    入力・出力の使い分けを指定するレジスタPFC.PEIORに1を書き込んだビットが出力となる。
    出力はレジスタPE.DRに書き込めば出力される。

    実際に,レジスタPFC.PEIORの第9ビット,第11ビットを出力に設定する時には,
    他のビットに影響を与えないようにするため,OR演算で1を書き込む。

    実際に,レジスタPE.DRに書き込む時も他のビットに影響を与えないように0または1を書き込む。
    1を書き込む時はOR演算を使い,0を書き込む時はAND演算を使う。

1.4 実際のプログラム

led1st.c

#include "7040S.H"           //IOポートアドレス定義
#include "sh_7045.h"         //基本関数群 sh_7045.hの先頭に説明がある

void msecwait(int msec)
/*msec間なにもしない時間稼ぎ関数*/
{
    int i,j;
    for (i=0;i<msec;i++) {
        for (j=0;j<9450;j++);    /*9450は実測によって求めた値*/
    }
}

int main(void)
{
    PFC.PEIOR.BYTE.H |= 0x0a; /* PEのHのbit1,bit3を出力に設定 */
    while(1){
        PE.DR.BYTE.H |= 2; /*LED0の点灯*/
        PE.DR.BYTE.H &= 0xf7; /*LED1の消灯*/
        msecwait(500);
        PE.DR.BYTE.H &= 0xfd; /*LED0の消灯*/
        PE.DR.BYTE.H |= 8; /*LED1の点灯*/
        msecwait(500);
    }
}



1.5 実際のプログラム(「sh_7045.h」の利用)

実用プログラムでは,ポートの記述はできるだけ隠すようにしています。sh_7045.hはマザーボード上のハードウェア操作のプログラミングを関数呼び出しで行なえるようにしたものです。関数呼び出しを利用すると,どこのポートのどのビットをどうするということを考えずに済み,専用コマンドを使うような感覚でプログラミングでき,便利です。
関数の定義はsh_7045.h中にあります。本Webページの末尾に「sh_7045.h」がありますが,その先頭部分には関数の説明があるので読んでください。

led2nd.c

/**********************************************************
msecwait関数で1秒ごとのLEDのON-OFFを行う
**********************************************************/
#include "7040S.H"
#include "sh_7045.h"

void msecwait(int msec)
{
    int i,j;
    for (i=0;i<msec;i++) {
        for (j=0;j<9450;j++);    /*9450は実測によって求めた値*/
    }
}

main()
{
    initLed();  /*LED初期化*/
    while(1) {
        turnOnLed(0); /*LED0のON*/
        turnOffLed(1); /*LED1のOFF*/
        msecwait(500);
        turnOnLed(1); /*LED0のOFF*/
        turnOffLed(0); /*LED1のON*/
        msecwait(500);
    }
}

練習問題 led2nd.cを元にして次のプログラムを作りなさい。

(1)1秒周期で両方のLEDが同時に点滅するプログラム
(2)0.5秒周期で左右のLEDが交互に点滅するプログラム
(3)0.5秒ごとに2つのLEDが次のような4つの点滅パターンを繰り返すプログラム
   (○は消灯,●は点灯を示す)
   ○○,○●,●○,●●
(4)0.5秒ごとに「LED0を2回点滅の後LED1を2回点滅」を繰り返すプログラム
(5)0.5秒ごとに点灯状態が変化し,次の点滅パターンを繰り返すプログラム
左側2回点滅→右側2回点滅→左側1回点滅→右側1回点滅


1.6  LEDのPWM駆動

LEDを高速にON-OFFを繰返し,ONになっている時間と周期との比(デューティ比)を変化させると,人間の目には点滅は見えず,デューティ比に応じて明るさが変化しているように見えます。
このような高速ON-OFFスイッチングで出力を制御する方法は「パルス幅変調(PWM)」「Pulse Width Modulation」と呼ばれます。
次のプログラムはLED0を点灯状態に保ち,LED1をPWM駆動します。3秒ごとに,デューティ比を90%,50%,10%と変化させています。

ledpwm.c

/**********************************************************
LEDのPWM(PulseWidthModulation)駆動
**********************************************************/
#include "7040S.H"           //IOポートアドレス定義
#include "sh_7045.h"         //基本関数群 sh_7045.hの先頭に説明がある

void msecwait(int msec)
/*msec間なにもしない時間稼ぎ関数*/
{
    int i,j;
    for (i=0;i<msec;i++) {
        for (j=0;j<1588;j++);    /*1588は実測によって求めた値*/
    }
}

main()
{
    int i;
    initLed();  /*LED初期化*/
    turnOnLed(0); /*LED0のON*/
    while(1) {
        for (i=0;i<300;i++) { /*ループ3秒間ループ デューティ比90%*/
            turnOnLed(1); /*LED1のON*/
            msecwait(9);
            turnOffLed(1); /*LED1のOFF*/
            msecwait(1);
        }
        for (i=0;i<300;i++) { /*ループ3秒間ループ デューティ比50%*/
            turnOnLed(1); /*LED1のON*/
            msecwait(5);
            turnOffLed(1); /*LED1のOFF*/
            msecwait(5);
        }
        for (i=0;i<300;i++) { /*ループ3秒間ループ デューティ比10%*/
            turnOnLed(1); /*LED1のON*/
            msecwait(1);
            turnOffLed(1); /*LED1のOFF*/
            msecwait(9);
        }
    }
}

 

約1秒間隔でLED2が徐々に明るくなる動作を繰り返すプログラム
pは0から999まで変化するが,各pの値に対してiの値が0から999まで変化します。
pが10の時は,iが0から9の時LED1はONで10から999まではOFFとなります。すなわちpが10の時LED1がONになっている時間割合は1%程度です。
pが100の時は,iが0から99の時LED1はONで100から999まではOFFとなります。すなわちpが100の時LED1がONになっている時間割合は10%程度です。
pが900の時は,iが0から899の時LED1はONで900から999まではOFFとなります。すなわちpが900の時LED1がONになっている時間割合は90%程度です。
このように時間経過を考えると,LED1がONになっている時間とOFFになっている時間比が変化しています。しかし大変高速にLED1が点滅しているため,人間の目にはLED1の明るさが変化しているように見えます。
このような高速ON-OFFスイッチングで出力を制御する方法は「パルス幅変調」「Pulse Width Modulation」と呼ばれます。

ledpwm1.c

/**********************************************************
LEDのPWM(PulseWidthModulation)駆動
**********************************************************/
#include "7040S.H"           //IOポートアドレス定義
#include "sh_7045.h"         //基本関数群 sh_7045.hの先頭に説明がある

main()
{
    int p,i;
    initLed();  /*LED初期化*/
    turnOnLed(0); /*LED0のON*/
    while(1) {
        for (p=0;p<2000;p++) {
            for (i=0;i<2000;i++) {
                if (i<p) turnOnLed(1); /*LED1のON*/
                else turnOffLed(1); /*LED1のOFF*/
            }
        }
    }
}



2. 4ビットスイッチ

図3の4ビットスイッチの読み取りを行い,LEDを制御します。
4ビットスイッチのSW1がONならLED0・LED1ともにON。SW2がONならLED0をON,SW3がONならLED1をON,それ以外は2つのLEDはOFFにします。
4ビットスイッチはPBのbit2,3,4,5につながっています。


図3 4ビットスイッチ

2.1 4ビットSWでLED駆動

4ビットスイッチのON-OFFの状態によってLEDのON-OFFを制御するプログラムを作成します。
LED駆動部のみsh_7045.hを用いてプログラムを作ります。
4ビットスイッチの各端子はプルダウンされる設定なので,スイッチがONになるとポートBの対応するビットは1になります。OFFになると0になります。

foursw0.c

/**********************************************************
4ビットスイッチによってLEDのON-OFFを行う
**********************************************************/
#include "7040S.H"           //IOポートアドレス定義
#include "sh_7045.h"         //基本関数群 sh_7045.hの先頭に説明がある

main()
{
    initLed();
    PFC.PBIOR.BYTE.L &= 0xc3; /*4bitSWのポートを入力に設定*/
    while(1) {
        if (PB.DR.BIT.BIT2==1) { /*4bitSWの1がONの時*/
            turnOnLed(0);
            turnOnLed(1);
        } else if (PB.DR.BIT.BIT3==1) { /*4bitSWの2がONの時*/
            turnOnLed(0);
            turnOffLed(1);
        } else if (PB.DR.BIT.BIT4==1) { /*4bitSWの3がONの時*/
            turnOffLed(0);
            turnOnLed(1);
        } else {
            turnOffLed(0);
            turnOffLed(1);
        }
    }
}


2.2  8ビットSWでLED駆動(「sh_7045.h」の利用)

8ビットSWの操作も「sh_7045.h」を利用します。2.1のようなプログラムは行なわず,ここにあるようなプログラムをするのがよいでしょう。
プログラム中でcheck4BitSWは
short int check4BitSW(short int number)
で定義されている4bitsw 0,1,2,3の状態を調べる関数で,引数numberは0,1,2,or 3をとり,
ONなら1、そうでなかったら0を関数の戻り値として返します。この関数のおかげで,「0:OFF,1:ON」という普通の感覚でプログラミングできます。

foursw.c

/**********************************************************
4ビットスイッチによってLEDのON-OFFを行う
**********************************************************/
#include "7040S.H"           //IOポートアドレス定義
#include "sh_7045.h"         //基本関数群 sh_7045.hの先頭に説明がある

main()
{
    initLed();
    init4BitSW();/*4bitSWのポートを入力に設定*/
    while(1) {
        if (check4BitSW(0)) { /*4bitSWの1がONの時*/
            turnOnLed(0);
            turnOnLed(1);
        } else if (check4BitSW(1)) { /*4bitSWの2がONの時*/
            turnOnLed(0);
            turnOffLed(1);
        } else if (check4BitSW(2)) { /*4bitSWの3がONの時*/
            turnOffLed(0);
            turnOnLed(1);
        } else {
            turnOffLed(0);
            turnOffLed(1);
        }
    }
}

練習問題 foursw.cを元にして次のプログラムを作りなさい。

(1)4bitSWの1がOFFの時,0.5秒周期で2つのLEDを同時点滅,4bitSWの1がONの時,1秒周期で2つのLEDを同時点滅
(2)4bitSWのすべてのビットがOFFの時,0.5秒周期で2つのLEDを同時点滅,4bitSWの1がONの時,1秒周期で2つのLEDを同時点滅,4bitSWの2がONの時,2秒周期で2つのLEDを同時点滅

 

3. プッシュスイッチ

図4のプッシュスイッチの読み取りを行い,LEDを制御します。
プッシュスイッチ1を押すとLED1が点灯し,プッシュスイッチ2を押すとLED2が点灯するプログラムとします。
プッシュスイッチはポート4の第4ビットから第7ビットにつながっています。


図4 プッシュスイッチ

3.1  PushSWでLED駆動(「sh_7045.h」の利用)

pushsw.c

/**********************************************************
プッシュスイッチによってLEDのON-OFFを行う
**********************************************************/
#include "7040S.H"           //IOポートアドレス定義
#include "sh_7045.h"         //基本関数群 sh_7045.hの先頭に説明がある

main()
{
    initLed();
    initPushSW();/*PushSWの初期化*/
    while(1) {
        if (checkPushSW(0)==1) { /*PushSWの1がONの時*/
            turnOnLed(0);
            turnOffLed(1);
        } else if (checkPushSW(1)==1) { /*PushSWの2がONの時*/
            turnOffLed(0);
            turnOnLed(1);
        } else {
            turnOffLed(0);
            turnOffLed(1);
        }
    }
}

練習問題 pushsw.cを元にして次のプログラムを作りなさい。

すべてのプッシュスイッチがOFFの時,0.5秒周期で2つのLEDを同時点滅,プッシュスイッチの1がONの時,1秒周期で2つのLEDを交互点滅,プッシュスイッチの2がONの時,2秒周期で2つのLEDを交互点滅


4. 割り込み

通常の関数は,関数がプログラム中の他の関数から呼び出されたときに作業を行ないます。これに対して,割り込み関数は何らかの割り込み要因によって呼び出される関数です。
タイマ割り込み関数は,タイマ割り込み初期設定によって設定された時間間隔で起動する割り込み関数です。「4.1」のプログラムでは,500msのタイマ割り込み初期設定が行なわれ,CPUの割り込み許可がなされ,タイマがスタートした後,プログラムの流れは
while(1);
となり,何もしない無限ループに突入します。しかし,500ms(0.5秒)ごとにタイマ割り込み関数「int_cmi1()」が起動し,LEDのON−OFFが継続して行なわれます。変数tickはstatic修飾されているので,関数が呼び出されたときに,前回呼び出しの時の値が残っています。かつ0が代入されるのははじめの1回だけです。tick=1-tickの演算により,tickの値は0,1を繰り返します。
ロボットの制御には一定時間ごとに起動する定時間割り込み(タイマー割り込み)が良く用いられます。またPWMの生成にもこの定時間割り込みが用いられる場合があります。
タイマ割り込み関数「int_cmi1()」の名前はリンカスクリプトで決められています。

4.1  割り込みでLED駆動

プログラム中main()側で500msec(0.5秒)間隔でタイマ割り込みを設定します。設定では500000μsecとなっています。
sh7045は割り込みレベルは0から15まであり,タイマ割り込みの割り込みレベルをここでは15に設定しています。
CPUの割り込みマスクの値を14にしているので,割り込みレベル15のタイマ割り込みは有効に成っています。
main()関数内でなにもしないループ動作をしている最中に,割り込み関数int_cmi1()は0.5秒ごとに起動し,LEDのON-OFFを行ないます。
このタイマ割り込みはコンペアマッチタイマ(CMT)のch1(CMT1)が使われています。

割り込み関数の前には
#pragma interrupt
を書き,この関数が割り込み関数であることを示します。
また割り込み関数内で,割り込み原因となったフラッグをクリアします。

int1st.c

/**********************************************************
時間割り込みによってLEDのON-OFFを行う
**********************************************************/
#include "7040S.H"           //IOポートアドレス定義
#include "sh_7045.h"         //基本関数群 sh_7045.hの先頭に説明がある

main()
{
    initLed();
    setIntMask(14);                  //レベル15は割り込み許可
    initCMTInt(500000,1,15); /*500000μsec,CMT.ch1使用,割り込みレベル15*/
    startCMT1(); /* コンペアマッチタイマ1スタート */
    while(1);       /*なにもしないループ*/
}

/******************************************************
コンペアマッチタイマ割り込み関数
関数名はリンカスクリプト中で決められている
int_cmi0はコンペアマッチタイマch0の割り込み
int_cmi1はコンペアマッチタイマch1の割り込み
コンペアマッチフラッグのクリアを割り込み関数中で行なうこと
******************************************************/
#pragma interrupt
int_cmi1()
{
    static int tick=0;
    if (tick==1) {
        turnOnLed(0);
        turnOffLed(1);
    } else {
        turnOffLed(0);
        turnOnLed(1);
    }
    tick=1-tick;
    clearCMFlag1(); /* コンペアマッチフラッグのクリア */
}

練習問題 int1st.cを元にして次のプログラムを作りなさい。

(1)割り込み周期を1秒ごとにする。
(2)4秒周期のLED点滅を行なう。(引数がマイクロ秒であること,引数には最大値があることに注意)


4.2  割り込みでLED駆動

割り込みを用いたPWM駆動でLEDの制御を行ないます。
このプログラムではITUのch1のみを用います。

PushSW1 → LED 0 明るく点灯(10/10) LED 1 暗く点灯(1/10)
PushSW2 → LED 0 暗く点灯(1/10) LED 1 明るく点灯(10/10)

volatile修飾子は,割り込み関数と通常の関数とで同じグローバル変数を用いるときに使います。
コンパイラは通常コードの最適化を行ないます。例えば,変数は通常はメモリ上にあります。ある変数を一度レジスタに読み込んで,その変数の値を変化させていない場合は,次にその変数の値が必要になったときには,メモリをもう一度読みに行くようなことはせず,レジスタの値を用います。しかし,その間に割り込み関数がその変数の値を変化させることもあります。そこでvolatile修飾子をつけた変数にしておくと必ずメモリにある変数を使うコードになります。この例の場合は,特に必要がありませんが,おまじないとしてつけておくようにしましょう。

このプログラムではLEDの暗い点灯時では1/10の時間割合で点灯している。LEDの暗い点灯時に5/10の時間割合で点灯するように変更しなさい。この点灯時間の割合は「デューティ比」と呼ばれる。

int2nd.c

/**********************************************************
プッシュスイッチと時間割り込みによってLEDのPWM制御を行う
**********************************************************/
#include "7040S.H"           //IOポートアドレス定義
#include "sh_7045.h"         //基本関数群 sh_7045.hの先頭に説明がある

volatile int led0,led1;
const int period=10; /*周期10msec*/
const int low=1;

main()
{
    led0=led1=0;
    initLed();
    initPushSW();
    setIntMask(14);                  //レベル15は割り込み許可
    initCMTInt(1000,1,15); /*1000μsec,CMT.ch1使用,割り込みレベル15*/
    startCMT1(); /* コンペアマッチタイマ1スタート */
    while(1){
        if (checkPushSW(0)==1) { /*PushSWの1がONの時*/
            led0=period;
            led1=low;
        } else if (checkPushSW(1)==1) { /*PushSWの2がONの時*/
            led0=low;
            led1=period;
        } else {
            led0=0;
            led1=0;
        }
    }
}

/******************************************************
コンペアマッチタイマ割り込み関数
関数名はリンカスクリプト中で決められている
int_cmi0はコンペアマッチタイマch0の割り込み
int_cmi1はコンペアマッチタイマch1の割り込み
コンペアマッチフラッグのクリアを割り込み関数中で行なうこと
******************************************************/
#pragma interrupt
int_cmi1()
{
    static int tick=0;
    if (tick<led0) {
        turnOnLed(0);
    } else {
        turnOffLed(0);
    }
    if (tick<led1) {
        turnOnLed(1);
    } else {
        turnOffLed(1);
    }
    tick++;
    if (tick==period) tick=0;
    clearCMFlag1(); /* コンペアマッチフラッグのクリア */
}

5. パソコンとのシリアル通信

プログラム書き込み用通信回線はそのままプログラム実行時にも使えます。
実行時にはハイパーターミナルを起動しておいて下さい。
ハイパーターミナルを利用した,ビットスイッチやプッシュスイッチのチェックおよびキーボードとの通信プログラムです。
ビットスイッチやプッシュスイッチのチェックの時はそれぞれのスイッチを動かしてみてください。


マイコンからパソコン画面への出力(WindowsのHyperTerminal使用)
パソコン(キーボード)からのマイコンへの入力(WindowsのHyperTerminal使用)

sciout.c

/**********************************************************
SCI1へ出力,WINDOWSのHyperTerminalなどで受信できる。
ただし,設定は
38400baud, Async, 8bit , NoParity, stop1
**********************************************************/
#include "7040S.H"           //IOポートアドレス定義
#include "sh_7045.h"         //基本関数群 sh_7045.hの先頭に説明がある

void func1(void)
{
    unsigned char sw,previous;
    SCI1_printf("Printing 4-bitSW status...\n");
    SCI1_printf("Change 4-bitSW and new status will appear.\n");
    SCI1_printf("If any key on the keyboard, this test will quit.\n");
    previous=sw=get4BitSW();
    SCI1_printf("4-bitSW status= %02x[%04b]\n",sw,sw);
    do {
        sw=get4BitSW();
        if (sw!=previous) {
            SCI1_printf("4bitSW status= %02x[%04b]\n",sw,sw);
            previous=sw;
        }
    } while (chkgetCharSCI1()<0);
}

void func2(void)
{
    unsigned char sw,previous;
    SCI1_printf("Printing PushSW status...\n");
    SCI1_printf("Change PushSW and new status will appear.\n");
    SCI1_printf("If any key on the keyboard, this test will quit.\n");
    previous=sw=getPushSW();
    SCI1_printf("PushSW status= %02x[%04b]\n",sw,sw);
    do {
        sw=getPushSW();
        if (sw!=previous) {
            SCI1_printf("PushSW status= %02x[%04b]\n",sw,sw);
            previous=sw;
        }
    } while (chkgetCharSCI1()<0);
}

void func3(void)
{
    int x;
    x=getIntSCI1("Key in a decimal number >>>");
    SCI1_printf("The number you keyed in is %d %x\n",x,x);
    x=getIntSCI1("Key in a hexdecimal number (ex. 0x23ff) >>>");
    SCI1_printf("The number you keyed in is %d %x\n",x,x);
}

main()
{
    int menu;
    initSCI1(); /*SCI-ch1の初期化*/
    initPushSW(); /*押しボタンスイッチの初期化*/
    init4BitSW(); /*4ビットスイッチの初期化*/
    SCI1_printf("Hello. How are you?\n");
    while (1) {
        SCI1_printf("***************menu**********\n");
        SCI1_printf("1:   get 4-bit SW and print \n");
        SCI1_printf("2:   get Push SW and print \n");
        SCI1_printf("3:   get integer from SCI1 and print \n");
        do {
            menu=getCharSCI1(); /*menuには'1','2','3'が入るはず*/
        } while (menu<'1'||'3'<menu);
        SCI1_printf("\n");
        switch (menu) {
        case '1':
            func1();
            break;
        case '2':
            func2();
            break;
        case '3':
            func3();
            break;
        default:
            break;
        }
    }
}


5. 「パソコンとのシリアル通信」,「タイマー割り込み」を用いた時計

「パソコンとのシリアル通信」,「タイマー割り込み」を用いた時計を作ります。

timer.c

/**********************************************************
割り込みを用いた時計 起動時からの経過時間[秒]を
SCI1へ出力,WINDOWSのHyperTerminalなどで受信できる。
ただし,設定は
38400baud, Async, 8bit , NoParity, stop1
**********************************************************/
#include "7040S.H"           //IOポートアドレス定義
#include "sh_7045.h"         //基本関数群 sh_7045.hの先頭に説明がある

volatile unsigned int counter;

main()
{
    unsigned int counter1;
    unsigned int t1,t2;
    int mask;
    initSCI1();
    SCI1_printf("Hello world!\n");
    mask=getIntMask();
    SCI1_printf("mask=%02d\n",mask);
    setIntMask(14);                  //レベル15は割り込み許可
    mask=getIntMask();
    SCI1_printf("mask=%02d\n",mask);
    initCMTInt(100000,1,15); /*100000μsec,CMT.ch1使用,割り込みレベル15*/
    startCMT1(); /* コンペアマッチタイマ1スタート */
    counter=0;
    while(1) {
        counter1=counter;
        t1=counter1/10;
        t2=counter1%10;
        SCI1_printf("%10u.%1u\r",t1,t2);
    }
}

/******************************************************
コンペアマッチタイマ割り込み関数
関数名はリンカスクリプト中で決められている
int_cmi0はコンペアマッチタイマch0の割り込み
int_cmi1はコンペアマッチタイマch1の割り込み
コンペアマッチフラッグのクリアを割り込み関数中で行なうこと
******************************************************/
#pragma interrupt
int_cmi1()
{
    counter++;
    clearCMFlag1(); /* コンペアマッチフラッグのクリア */
}

 

練習問題 int2nd.c,sciout.c,timer.cを元にして次のプログラムを作りなさい。

(1)100msecごとの割り込みを用いて,ストップウォッチを作成せよ。スタート・ストップはキーボードのキーに割り当てても良いし,あるいはマザーボード上のプッシュスイッチを用いても良い。また割り込み関数内で表示を行なってはいけない。グローバル変数のカウンタを用いればよいだろう。
(2)キーボードから0から10までの整数をint型変数ratioに受け取り,「5」のプログラムで,点灯時間をratio/10のデューティ比になるようにしなさい。またデューティ比の変更は何回でもできるようにしなさい。


 参考1 AKI-SH2/7045ピン配置

コネクタ2 CN2

コネクタピン ピンの役割(/) コネクタピン ピンの役割(/) コネクタピン ピンの役割(/)
1 PD0/D0 21 PD20/D20 41 PC13/A13
2 PD1/D1 22 PD21/D21 42 PC12/A12
3 PD2/D2 23 PD22/D22 43 PC11/A11
4 PD3/D3 24 PD23/D23 44 PC10/A10
5 PD4/D4 25 PD24/D24 45 PC9/A9
6 PD5/D5 26 PD25/D25 46 PC8/A8
7 PD6/D6 27 PD26/D26 47 PC7/A7
8 PD7/D7 28 PD27/D27 48 PC6/A6
9 PD8/D8 29 PD28/D28 49 PC5/A5
10 PD9/D9 30 PD29/D29 50 PC4/A4
11 PD10/D10 31 PD30/D30 51 PC3/A3
12 PD11/D11 32 PD31/D31 52 PC2/A2
13 PD12/D12 33 PB9/A21 53 PC1/A1
14 PD13/D13 34 PB8/A20 54 PC0/A0
15 PD14/D14 35 PB7/A19 55 +5V
16 PD15/D15 36 PB6/A18 56 GND
17 PD16/D16 37 PB1/A17 57 +5V
18 PD17/D17 38 PB0/A16 58 GND
19 PD18/D18 39 PC15/A15 59 +5V
20 PD19/D19 40 PC14/A14 60 GND

コネクタ3 CN3

コネクタピン ピンの役割(/) コネクタピン ピンの役割(/) コネクタピン ピンの役割(/)
1 +5V 21 PA7/TCLKB 41 AVREF
2 GND 22 PA8/TCLKC 42 GND
3 +5V 23 PE15/TIOC4D 43 PF6/AD6
4 GND 24 PA6/TCLKA 44 PF7/AD7
5 -WDTOVF 25 PE13/TIOC4B 45 PF4/AD4
6 GND 26 PE14/TIOC4C 46 PF5/AD5
7 PA12/-WRL 27 PE12/TIOC4A 47 PF2/AD2
8 PA13/-WRH 28 PE11/TIOC3D 48 PF3/AD3
9 PA22/-WRHL 29 PE10/TIOC3C 49 PF0/AD0
10 PA23/-WRHH 30 PE9/TIOC3B 50 PF1/AD1
11 PA20 31 PE8/TIOC3A 51 PE4/TIOC1A
12 PA14/-RD 32 PE7/TIOC2B 52 PE3/TIOC0D
13 PA18 33 PE6/TIOC2A 53 PE2/TIOC0C
14 PA21 34 PE5/TIOC1B 54 PE1/TIOC0B
15 PB2 35 PA5/SCK1 55 PE0/TIOC0A
16 PA19 36 PA4/TXD1 56 PA15
17 PB4 37 PA3/RXD1 57 PA16
18 PB3 38 PA2/SCK0 58 PA17
19 PA9/TCLKD 39 PA1/TXD0 59 -NMI
20 PB5 40 PA0/RXD0 60 -RESET

AKI-SH2/7045付属のコネクタ番号表


参考2 sh_7045.h

sh_7045.h

/****************************************************************
sh_7045.h
Copyright (c) Kosaka Lab CS TNCT

このインクルードファイルはH8/3048用ファイルを下敷きにして
2003年度研究生山下君,永山さんの協力でまとめたものである。
 4 Dec 2003 h8-01.h  小坂,山下,永山

【1】SCI ch1 関係
void initSCI1()
 SCI-ch1の初期化 38400baud, Async, 8bit , NoParity, stop1

short int getCharSCI1()
 SCI-ch1から1byte入力コード。エラーがあると-2が戻る。
short int chkgetCharSCI1()
 SCI-ch1を検査し,受信データがあれば1byte入力コードが戻る。
 なければ-1が,失敗すると-2が戻る。
short int getIntSCI1(char prompt[])
 SCI-ch1からプロンプト付で,short intの値を受け取る。
 正負の10進数または16進数を受け付ける。16進数は0xで始まる

void putCharSCI1(char c)
 SCI-ch1に1バイト出力する。
void putStringSCI1(char *str)
 SCI-ch1に文字列を出力する。
void SCI1_printf(char *format,...)
  関数printfのSCI版
  軽量化のためエラー処理はないので桁数指定の場合は注意
  対応書式
  %d   : [int] integer with sign. '%d','%4d','%-4d', and '%04d' are available
  %ld  : explicit [long int]  '%ld','%9ld','%-9ld', and '%09ld' are available
  %u   : [unsigbed int] unsigned integer.
            '%u','%4u','%-4u', and '%04u' are available
  %lu  : explicit [unsigned long int]
            '%lu','%9lu','%-9lu', and '%09lu' are available
  %x   : [unsigned int] in Hex  '%x','%4x','%-4x', and '%04x' are available
  %lx  : explicit [unsigned long int] in Hex 
            '%lx','%8lx','%-8lx', and '%08lx' are available
  %o   : [unsigned int] in Oct  '%o','%4o','%-4o', and '%04o' are available
  %lo  : explicit [unsigned long int] in Oct 
            '%lo','%8lo','%-8lo', and '%08lo' are available
  %b   : [unsigned int] in Bin  '%b','%8b','%-8b', and '%08b' are available
  %lb  : explicit [unsigned long int] in Bin 
            '%lb','%8lb','%-8lb', and '%08lb' are available
  %c   : char
  %s   : string %20s %-20s are available

【2】マザーボード関係
void initLed()
 LEDの初期化
void turnOnLed(short int number)
 LEDの点灯 numberはLED番号で0または1を指定する
void turnOffLed(short int number)
 LEDの消灯 numberはLED番号で0または1を指定する

void initPushSW(void)
 押しボタンスイッチの初期化
short int getPushSW(void)
 押しボタンスイッチの取得。
 押しボタンスイッチの状況は第0と第2ビットに現れる。
short int checkPushSW(short int number)
 push sw 0,1の状態を調べる number:0 or 1
 押されていたら1、そうでなかったら0を返す

void init4BitSW(void)
 4ビットスイッチの初期化
short int get4BitSW(void)
 4ビットスイッチの取得 ただしポートを読み込み,ビット反転のみ。
 4ビットスイッチの状況は第0-第3ビットに現れる。
short int check4BitSW(short int number)
 4bitsw 0,1,2,3の状態を調べる number:0,1,2,or 3
 ONなら1、そうでなかったら0を返す

【3】タイミング割り込み
void initCMTInt(unsigned long int period,int ch,int intLebel)
 コンペアマッチタイマによるタイマ割り込みの初期化
 割り込み間隔は引数peiodで単位はμsecである
 0<period<1170104 なので最長1.170104secまで可能
 チャンネルが2つあるのではchは0,1のどちらかを選ぶ
 割り込みレベルintLebelは1〜15にする
void startCMT0(void)
 コンペアマッチタイマ0スタート
void stopCMT0(void)
 コンペアマッチタイマ0ストップ
void clearCMFlag0(void)
 コンペアマッチフラッグ0クリア (割り込みルーチン内で呼び出すこと)
void startCMT1(void)
 コンペアマッチタイマ1スタート
void stopCMT1(void)
 コンペアマッチタイマ1ストップ
void clearCMFlag1(void)
 コンペアマッチフラッグ1クリア (割り込みルーチン内で呼び出すこと)

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

#include<stdarg.h>
void setIntMask(int mask);
int getIntMask(void);

/* ------------------------------------------------- */
/* SCI1 INITIALIZATION fixed baud at 38400           */
/* ------------------------------------------------- */
void initSCI1()
{
    int i;
    PFC.PACRL2.BIT.PA4MD = 1;/* 0:PA4, 1:TXD1 */
    PFC.PACRL2.BIT.PA3MD = 1;/* 0:PA3, 1:RXD1 */
    SCI1.SCR.BYTE = 0;       /* clear all flags */
    SCI1.SCR.BYTE = 0;       /* clear all flags */
                      /* 2400-38400baud are available at n=0(cks1=0,cks2=0) */
    SCI1.SMR.BYTE = 0;       /* Async, 8bit , NoParity, stop1, 1/1 */
    SCI1.BRR = 22;          /* 38400baud (CPU=28.6MHz) */
    for(i=0;i<1000000;i++);      /* wait more than 1bit time */
    SCI1.SCR.BYTE = 0x30;    /* scr = 0011 0000 (TE=1,RE=1) */
    return;
}

/* ------------------------------------------------- */
/* GET BYTE FROM SCI1 */
/* ------------------------------------------------- */
short int getCharSCI1()
/* return value 0x00-0xFF:received data */
/*              -2(0xFFFE):error */
{
    short int flags,recdata;
    do {
        flags = SCI1.SSR.BYTE;
        if (flags&0x38) {/* error */
            SCI1.SSR.BIT.RDRF = 0;
            SCI1.SSR.BIT.ORER = 0;
            SCI1.SSR.BIT.FER = 0;
            SCI1.SSR.BIT.PER = 0;
            return -2;
        }
        if (flags&0x40) {/* normally received one data */
            SCI1.SSR.BIT.RDRF = 0;
            recdata=SCI1.RDR;
            return recdata;
        }
    } while (1);
}

/* ------------------------------------------------- */
/* CHECK SCI BUFFER AND GET DATA */
/* ------------------------------------------------- */
short int chkgetCharSCI1()
/* return value -1(0xFFFF):no received data  */
/*              0x00-0xFF:received data */
/*              -2(0xFFFE):error */
{
    short int flags,recdata;
    flags = SCI1.SSR.BYTE;
    SCI1.SSR.BIT.RDRF = 0;
    if (flags&0x38) {/* error */
        SCI1.SSR.BIT.ORER = 0;
        SCI1.SSR.BIT.FER = 0;
        SCI1.SSR.BIT.PER = 0;
        return -2;
    }
    if (flags&0x40) {/* normally received one data */
        recdata=SCI1.RDR;
        return recdata;
    } else {
        return -1;
    }
}

void putStringSCI1(char *str);

/*SCI1より文字列入力[return]が終端だが,'\n'は取得されない*/
/*^Hでバックスペイス*/
int getStringSCI1(char *buff,int max)
{
    int i,ch;
    for (i=0;i<max-1;i++) {
        ch=getCharSCI1(); /*1文字取得*/
        *buff=(char)ch; /*1文字取得*/
        if (*buff=='\r'||ch<0) {
            *buff=0;
            return i+1;
        }
        if (*buff==0x8) {
            buff-=2;
            i-=2;
        }
        if (*buff!='\n') buff++;
        else i--;
    }
    *buff=0;
    return i+1;
}

/*SCI1へプロンプトを表示して,SCI1より整数値を入力*/
int getIntSCI1(char prompt[])
/*getting integer from serial port*/
/* format 123[ret] */
/*        -123[ret] */
/*        0x1a[ret] */
/*        -0x100[ret] */
{
    int x=0,y,m=0,n=0,v=0,i=0;
    char buff[16];
    putStringSCI1(prompt);
    getStringSCI1(buff,16);
    y=buff[i];
    while(y!=0){
        if(y=='-') m=1;
        if('a'<=y&&y<='z') y=y-'a'+'A';
        if(y=='0') n=1;

        if(v==1){
            if('0'<=y&&y<='9'){
                y=y-'0';
            }
            else if('A'<=y&&y<='F'){
                y=y-'A'+10;
            }
            x=16*x+y;
        }

        if(n==1&&y=='X'){
            v=1;
        }
               
        if(v==0&&'0'<=y&&y<='9'){
            y=y-'0';
            x=10*x+y;
        }

        y=buff[++i];
    }
    if(m==1) x=-x;
    return x;
}

/* ------------------------------------------------- */
/* PUT BYTE TO SCI1 */
/* ------------------------------------------------- */
void putCharSCI1(char c)
{
    unsigned char tmp;
    if (c=='\n') putCharSCI1('\r');
    do{
        tmp = SCI1.SSR.BYTE;
    } while((tmp & 0x80)==0);
    SCI1.TDR = c;
    SCI1.SSR.BIT.TDRE = 0;
    return;
}

void putStringSCI1(char *str)
{
    while(*str){
        putCharSCI1(*str);
        str++;
    }
}

const char hexstring[]="0123456789abcdef0123456789ABCDEF";
#define MAXDIGIT 34
void SCI1_printf(char *format,...)
{
    va_list arg_ptr;
    char buf[MAXDIGIT];
    unsigned char flag=0;  /*%d:bit2 l:bit1 %:bit0 */
    unsigned char digit=0; /* 桁数 */
    unsigned char minus=0;
    char fill,format1;
    unsigned char radix; /*N進基数*/
    char sign;
    char *ptr; /*出力文字ポインタ*/
    unsigned char cntr; /*出力文字数カウンタ*/
    unsigned char shift; /*16進シフト 0 or 6*/
    unsigned char i;
    unsigned long int value;
    va_start(arg_ptr,format);
    while (*format) {
        format1=*format;
        if (flag==0) {
            if (format1=='%') {
                flag=1;
                digit=0;
                fill=' ';
                minus=0;
                radix=0;
                ptr=&buf[MAXDIGIT-1];
                *ptr--='\0';
                cntr=0;
                shift=0;
                sign='+';
            } else {
                putCharSCI1(format1);
            }
        } else {
            if (format1=='l') {
                flag|=2;
            } else if ('0'<=(format1)&&(format1)<='9') {
                if (digit==0 && format1=='0') {
                    fill='0';
                } else {
                    digit=digit*10+((format1)-'0');
                    if (MAXDIGIT-2<digit) digit=MAXDIGIT-2;
                }
            } else if (format1=='-') {
                minus=1;
            } else if (format1=='d') {
                flag|=4;
                radix=10;
            } else if (format1=='u') {
                radix=10;
            } else if (format1=='x') {
                radix=16;
            } else if (format1=='X') {
                radix=16;shift=16;
            } else if (format1=='o') {
                radix=8;
            } else if (format1=='b') {
                radix=2;
            } else if (format1=='p') {
                radix=16;shift=16;digit=8;fill='0';flag|=2;
            } else if (format1=='c') {
                putCharSCI1((unsigned char)(va_arg(arg_ptr,int)));
                flag=0;
            } else if (format1=='s') {
                if (digit) {
                    cntr=0;ptr=va_arg(arg_ptr,char *);
                    while (ptr[cntr]) cntr++; /*cntrは文字数*/
                    if (!minus) for (i=cntr;i<digit;i++) putCharSCI1(' ');
                    putStringSCI1(ptr);
                    if (minus) for (i=cntr;i<digit;i++) putCharSCI1(' ');
                } else {
                    putStringSCI1(va_arg(arg_ptr,char *));
                }
                flag=0;
            } else {
                putCharSCI1(format1);
                flag=0;
            }
            if (radix) {
                switch (flag&6) {
                case 0: /* unsig int */
                    value=(unsigned int)va_arg(arg_ptr,int);
                    break;
                case 2: /* unsig long int */
                    value=va_arg(arg_ptr,long int);
                    break;
                case 4: /* sig int */
                    value=(long int)va_arg(arg_ptr,int);
                    if ((long int)value<0) {
                        value=-(long int)value;
                        sign='-';
                    }
                    break;
                case 6: /* sig long int */
                    value=va_arg(arg_ptr,long int);
                    if ((long int)value<0) {
                        value=-(long int)value;
                        sign='-';
                    }
                    break;
                default:
                    break;
                }
                while (value) {
                    *ptr--=hexstring[value%radix+shift];
                    cntr++;
                    value/=radix;
                }
                if (cntr==0) {
                    *ptr--='0';
                    cntr++;
                }
                if (fill==' ') {
                    if (sign=='-') {
                        *ptr--='-';
                        cntr++;
                    }
                    if (!minus) for (i=cntr;i<digit;i++) putCharSCI1(' ');
                    putStringSCI1(++ptr);
                    if (minus) for (i=cntr;i<digit;i++) putCharSCI1(' ');
                } else {
                    for (;cntr<digit-1;cntr++) *ptr--='0';
                    if (sign!='-'&&cntr<digit) *ptr--='0';
                    else if (sign=='-') *ptr--='-';
                    putStringSCI1(++ptr);
                }
                flag=0;
            }
        }
        format++;
    }
}

/* ------------------------------------------------- */
/*                 LED INITIALIZATION                */
/* ------------------------------------------------- */
/*LEDのbitすなわちポートEのbit9,bit11(PEのHのbit1,bit3)を出力に設定*/
/*void initLed(void)
{
    PFC.PEIOR.BYTE.H |= 0x0a;
}*/
#define initLed() (PFC.PEIOR.BYTE.H |= 0x0a)

/* ------------------------------------------------- */
/* LET LED ON */
/* ------------------------------------------------- */
/*numberは0または1*/
void turnOnLed(short int number)

    static unsigned char mask[]={0x2,0x8};
    PE.DR.BYTE.H |= mask[number];
}

/* ------------------------------------------------- */
/* LET LED OFF */
/* ------------------------------------------------- */
/*numberは0または1*/
void turnOffLed(short int number)
{
    static unsigned char mask[]={0xfd,0xf7};
    PE.DR.BYTE.H &= mask[number];
}

/* ------------------------------------------------- */
/*              PUSH SW INITIALIZATION               */
/* ------------------------------------------------- */
/*PushSWはPEのbit13,15に割り当てられている*/
/*PushSWのポートを入力に設定*/
/*void initPushSW(void)
{
    PFC.PEIOR.BYTE.H &= 0x5f;
}*/
#define initPushSW() (PFC.PEIOR.BYTE.H &= 0x5f)

/* Pushswの状態をそのまま返す */
short int getPushSW(void)
{
    return ((PE.DR.BYTE.H>>5)&0x5);
}

short int checkPushSW(short int number)
/*Pushsw 0,1の状態を調べる number:0,or 1*/
/*ONなら1、そうでなかったら0を返す*/
{
    short int ret;
    static const unsigned char mask[]={0x20,0x80};
    if (PE.DR.BYTE.H&mask[number&1]) ret=1;
    else ret=0;
    return ret;
}

/* ------------------------------------------------- */
/*              4 BIT SW INITIALIZATION              */
/* ------------------------------------------------- */
/*4BitSWはPBのbit2,3,4,5に割り当てられている*/
/*4bitSWのポートを入力に設定*/
/*void init4BitSW(void)
{
    PFC.PBIOR.BYTE.L &= 0xc3;
}*/
#define init4BitSW() (PFC.PBIOR.BYTE.L &= 0xc3)

/* 4bitswの状態をそのまま返す */
short int get4BitSW(void)
{
    return ((PB.DR.BYTE.L>>2)&0xf);
}

short int check4BitSW(short int number)
/*4bitsw 0,1,2,3の状態を調べる number:0,1,2,or 4*/
/*ONなら1、そうでなかったら0を返す*/
{
    short int ret;
    static const unsigned char mask[]={4,8,0x10,0x20};
    if (PB.DR.BYTE.L&mask[number&3]) ret=1;
    else ret=0;
    return ret;
}

/* ------------------------------------------------- */
/*         COMPARE MATCH TIMER INITIALIZATION        */
/* ------------------------------------------------- */
/* コンペアマッチタイマによるタイマ割り込みの初期化 */
/* 割り込み間隔は引数peiodで単位はμsecである */
/* 0<period<1170104 なので最長1.170104secまで可能 */
/* チャンネルが2つあるのではchは0,1のどちらかを選ぶ */
/* 割り込みレベルintLebelは1〜15にする */
void initCMTInt(unsigned long int period,int ch,int intLebel)
{
    /* φ:28.636MHz */
// double dblcmcor;
    unsigned long int cmcor;
    unsigned char cks;
    if (period<16000) {
//  dblcmcor=(double)period*(28.636/8.);
        cmcor=period*28636/8000;
        cks=0; /* φ/8 */
    } else if (period<64000) {
//  dblcmcor=(double)period*(28.636/32.);
        cmcor=period*7159/8000;
        cks=1; /* φ/32 */
    } else if (period<256000) {
//  dblcmcor=(double)period*(28.636/128.);
        cmcor=period/4*7159/8000;
        cks=2; /* φ/128 */
    } else {
//  dblcmcor=(double)period*(28.636/512.);
        cmcor=period/16*7159/8000;
        cks=3; /* φ/512 */
    }
    if (ch==0) {
//  CMT0.CMCOR=(unsigned short int)(dblcmcor+0.5);
        CMT.CMSTR.BIT.STR0=0; /* コンペアマッチタイマ0ストップ */
        CMT0.CMCOR=cmcor-1;
        CMT0.CMCSR.BIT.CKS=cks; /* φの設定 */
        CMT0.CMCSR.BIT.CMIE=1; /* 割り込み可設定 */
        INTC.IPRG.BIT._CMT0=intLebel; /* 割り込みコントローラの割り込みレベル */
    } else {
//  CMT1.CMCOR=(unsigned short int)(dblcmcor+0.5);
        CMT.CMSTR.BIT.STR1=0; /* コンペアマッチタイマ1ストップ*/
        CMT1.CMCOR=cmcor-1;
        CMT1.CMCSR.BIT.CKS=cks; /* φの設定 */
        CMT1.CMCSR.BIT.CMIE=1; /* 割り込み可設定 */
        INTC.IPRG.BIT._CMT1=intLebel; /* 割り込みコントローラの割り込みレベル */
    }
}

#define startCMT0()      (CMT.CMSTR.BIT.STR0=1) /* コンペアマッチタイマ0スタート */
#define stopCMT0()       (CMT.CMSTR.BIT.STR0=0) /* コンペアマッチタイマ0ストップ */
#define clearCMFlag0()  (CMT0.CMCSR.BIT.CMF=0) /* コンペアマッチフラッグ0クリア */
#define startCMT1()      (CMT.CMSTR.BIT.STR1=1) /* コンペアマッチタイマ1スタート */
#define stopCMT1()       (CMT.CMSTR.BIT.STR1=0) /* コンペアマッチタイマ1ストップ*/
#define clearCMFlag1()  (CMT1.CMCSR.BIT.CMF=0) /* コンペアマッチフラッグ1クリア */