AKI-H8(H8/3069)の使い方
マザーボード上のSWとLEDによるプログラミング

Copyright(C) 8Oct2009
Copyright(C) 15Dec2001


coskx

0. はじめに

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

テンプレートフォルダなどのダウンロード

DownLoad

0.2 AKI-H8(H8/3069)
 AKI-H8/3069(図1)はルネサスの製品であるマイクロコンピュータH8製品群のH8/3069Fを用いて,秋月電子通商がCPUボード,マザーボードを製作販売している製品の商品名です。


マザーボード上には次の要素が搭載されています
(1)   4ビットディップスイッチ
(2)   プッシュスイッチ
(3)   LED (発光ダイオード)
(4) LCD (液晶表示装置)

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


LEDは左から0,1
4bitDIPスイッチは左から0,1,2,3
プッシュスイッチは左から0,1,2,3,4

      左上:Dsub9 シリアルコネクタ(SCI1)
      右下:DC5V入力 センタ+

図0.1 AKI-H8/3069 AKI-マザーボード

図0.2 AKI-H8/3069 LAN+CPU Card


SCI1はCPUカード上にある。
DC5Vの供給口もCPUカード上にある
LCD右側はDsub9シリアルコネクタ(SCI0)

LEDは左から0,1
4bitDIPスイッチは左から0,1,2,3
プッシュスイッチは左から0,1,2,3,4

SCI1はCPUカード上にある。
DC5Vの供給口もCPUカード上にある
LCD右側はDsub9シリアルコネクタ(SCI0)

LEDは左から0,1
4bitDIPスイッチは左から0,1,2,3
プッシュスイッチは左から0,1,2,3,4

図0.3 AKI-H8/3069, TNCT試作マザーボード

図0.4 AKI-H8/3069, TNCTマザーボード

 

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

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

 

図1.1 2つのLED

図1.2 CN2 (コネクタ2)
下部にCN2の表示が見える。
2列の端子の左右に2,1,40,39の表示が見えるので,端子には1,2,...39,40の番号が付いていることがわかる

1.1    LED点滅プログラム

マザーボード上の2つのLEDを点滅させるプログラムを作ります。
2つのLEDはH8/3069CPUのポート4の第6ビット,第7ビットの端子につけられています。
H8/3069CPUでポートというのは8ビットの出入り口のことです。各ビットは第0ビットから第7ビットのように呼びます。(上位が第7ビット,下位が第0ビット)
ポートがCPUのどのピンに接続されているかはCPU設計者によって決められ,CPUのマニュアルに書いてあります。
またCPUのピンがCPUカードのどのピンに接続されているかは,CPUカードの設計者によって決められ,CPUカードのマニュアルに書いてあります。

LED

CPU コネクタ デバイス
P4-bit6 CN2-11 LED1
P4-bit7 CN2-12 LED2

通常ポートは入出力兼用なので,この例のようにポート4の第6ビット,第7ビットの端子の先にLEDがついている場合は,CPUに対し,この2つのビットは出力に使用することを教えてあげなければなりません。(CPU起動後1回だけ設定しなければなりません。この作業はスタートアップルーチンで行われているので,プログラム内では行う必要がありません。)

参考 電流制限

CPUの出力端子はレベルHの時4.8V,レベルLの時0Vであり,LED単体の電圧降下が1.8V程度であるため,保護抵抗1.5kΩには3.0Vがかかっていることになります。この抵抗に流れる電流は3.0[V]/1500[Ω] = 0.0020[A] = 2.0[mA]となります。LEDは10mA駆動が標準ですが,2mA駆動でも光らないないわけではありません。一方CPUの端子出力電流は最大2.0mAとされている(H8/3069の仕様)ので,この例ではAKI-H8/3069はぎりぎりの仕様となっています。



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

(1)マイコン上での駆動の手順

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

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

以下を無限ループ
  ポート4の第6ビットに1を出力
  ポート4の第7ビットに0を出力
  1秒間時間待ち
  ポート4の第6ビットに0を出力
  ポート4の第7ビットに1を出力
  1秒間時間待ち

1.3 LED点滅プログラム


    while(1) {/*これは無限ループ*/
        /*LED0をONにする*/
        P4DR.BIT.B6=1;
        /*LED1をOFFにする*/
        P4DR.BIT.B7=0;
        msecwait(1000);/*1000msecの間なにもしない*/
        /*LED0をOFFにする*/
        P4DR.BIT.B6=0;
        /*LED1をONにする*/
        P4DR.BIT.B7=1;
        msecwait(1000);
    }

勘どころ

ポート4のbit6とbit7にLEDをつけたのはマザーボードの設計者が決めたことなので,
回路がそのようにできている。
すでに取り付けられているLEDを別のポートを介して
点滅させることは,ソフトウェア
の変更だけではできない。
別のポートを介してLEDを点滅させるには,そのポートがどの端子とつながっている
のか調べて,マザーボードの設計変更が必要になる。

 

1.4 実際のプログラム

led1st.c

/*  msecwait関数で1秒ごとのLEDのON-OFFを行う  */
#include <3069f.h>

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

main()
{
    while(1) {/*これは無限ループ*/
        /*LED0をONにする  P4のDRの第6ビットを1にする*/
        /*DRとはDataRegister(データレジスタ)*/
        P4DR.BIT.B6=1;
        /*LED1をOFFにする  P4のDRの第7ビットを0にする*/
        P4DR.BIT.B7=0;
        msecwait(1000);/*1000msecの間なにもしない*/
        /*LED0をOFFにする  P4のDRの第6ビットを0にする*/
        P4DR.BIT.B6=0;
        /*LED1をONにする  P4のDRの第7ビットを1にする*/
        P4DR.BIT.B7=1;
        msecwait(1000);
    }
}


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

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

led2nd.c

#include <3069s.h>
#include "h8_3069.h"

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

main()
{
    while(1) {
        turnOnLed(0); /*LED0のON*/
        turnOffLed(1); /*LED1のOFF*/
        msecwait(1000);
        turnOffLed(0); /*LED0のOFF*/
        turnOnLed(1); /*LED1のON*/
        msecwait(1000);
    }
}

練習問題 led2nd.cを元にして次のプログラムを作りなさい。
(実行の様子を検察して,その結果,気付いた事を含め,別途指定された書式で括弧の中ファイル名で提出しなさい)

(1)1秒周期で両方のLEDが同時に点滅するプログラム(mp1ex01.txt)
(2)0.5秒周期で左右のLEDが交互に点滅するプログラム
  (0.5秒ごとにではないことに注意)  (mp1ex02.txt)
(3)0.5秒ごとに2つのLEDが次のような4つの点滅パターンを繰り返すプログラム
   (○は消灯,●は点灯を示す)
   ○○,○●,●○,●●
      (mp1ex03.txt)
(4)0.5秒ごとに点灯状態が変化し,次の点滅パターンを繰り返すプログラム
  LED0を2回点滅の後LED1を2回点滅
     (mp1ex04.txt)
(5)0.5秒ごとに点灯状態が変化し,次の点滅パターンを繰り返すプログラム
  左側2回点滅→右側2回点滅→左側1回点滅→右側1回点滅
  (mp1ex05.txt)
(6)10秒周期で両方のLEDが同時に点滅するプログラムをつくり動作させなさい。
  動作中にアナログテスタを使って,CN2の11,12の電圧を測定しなさい。
  測定に当たってはGNDを基準としなさい。
  (mp1ex06.txt)

 

練習問題 次の作業を行ないなさい。
(実行の様子を検察して,その結果,気付いた事を含め,別途指定された書式で括弧の中ファイル名で提出しなさい)

(1)h8_3069.h中にある関数turnOnLed(),turnOffLed()について説明しなさい。
  なおH8/3069ハードウェアマニュアルの関連する説明を抜き出しなさい。(9.6 ポート4,モード5参照)
  
http://tnct20.tokyo-ct.ac.jp/~kosaka/for_students/H8/h8_3069Hardwaremanual.pdf
  (mp1ex07.txt)
(2)ポート4のbit4,bit5,bit6,bit7に保護抵抗付きのLEDが接続されているとします。
  次のような設計仕様の場合のturnOnLed4(),turnOffLed4()を作りなさい。
  void turnOnLed4(int number) LEDを点灯させる関数。
    ただし,引数は0,1,2,3を取り,LED0,1,2,3をそれぞれ個別に点灯させます。
  void turnOffLed4(int number) LEDを消灯させる関数。
    ただし,引数は0,1,2,3を取り,LED0,1,2,3をそれぞれ個別に消灯させます。
  4つのLEDについて0.5秒ごとに0,1,2,3番LEDが順に点灯し消灯する(点灯時間は0.5秒)
  プログラムを作りなさい。最後の2つしか見えないけれど,動作させてチェックしなさい。
  (mp1ex08.txt)
(3)「(2)」で作成した関数を使って,ポート4のbit4,bit5,bit6,bit7に保護抵抗付きのLEDがついていると仮定して,
  10秒周期で4つのLEDが同時に点滅するプログラムをつくり動作させなさい。
  動作中にアナログテスタを使って,このプログラムが4つのビットを駆動していることを検証しなさい。
  (ヒントCN2のどこか)

CPU コネクタ
P4-bit4 CN2-9
P4-bit5 CN2-10
P4-bit6 CN2-11
P4-bit7 CN2-12

  (mp1ex09.txt)


 
 
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 <3069f.h>
#include "h8_3069.h"

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

main()
{
    int i;
    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の明るさが変化しているように見えます。

ledpwm1.c

/**********************************************************
LEDのPWM(PulseWidthModulation)駆動
**********************************************************/
#include <3069f.h>
#include "h8_3069.h"

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

練習問題 次の作業を行ないなさい。
(実行の様子を検察して,その結果,気付いた事を含め,別途指定された書式で括弧の中ファイル名で提出しなさい)

(1)「ledpwm.c」を参考にして,LED1をPWM駆動します。
  4秒ごとに,デューティ比を2%,4%,8%,16%,32%,64%と変化させるプログムを作りなさい。
    ヒント 次の関数の使用を検討する

void m4_secwait(int m4_sec)
/*10^-4secで指示する間なにもしない時間稼ぎ関数*/
/*たとえば m4_secwait(15);を呼ぶと1.5msec後にこの関数から戻る*/
{
    int i,j;
    for (i=0;i<msec;i++) {
        for (j=0;j<335;j++); /*335の根拠を考えてみよう*/
    }
}

  (mp1ex10.txt)

(2)「ledpwm.c」では高速点滅を眼で確認できなかったと思います。
  LEDをデューティ比50%で駆動し,1周期をどれくらいにすると点滅を眼で確認できてしまうのか,
  境界のを実験により求めなさい。そのためのプログラムを作って確認しなさい。
  (mp1ex11.txt) 
 




2. 4ビットスイッチ

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


図2.1 8ビットスイッチ

参考
通常用いられるのは,図2.1の回路である。スイッチがOFFの時CPUの入力ピンには5Vが与えられ,スイッチがONの時CPUの入力ピンには0Vが与えられる。この用途で用いられている抵抗のことをプルアップ抵抗と呼んでいる。通常この抵抗値は10kΩから100kΩが用いられる。図2.2左はスイッチがOFFであるため,電流は流れず,抵抗で電圧降下が起こらないため,5Vが出力されているところを示している。図2.2右はスイッチがONであるため,電流が流れ,抵抗で電圧降下が起こり,0Vが出力されているところを示している。
H8CPUのポート2,4,5では,スイッチのON-OFF状態の取得等に都合の良い仕掛けがある。図2.4に示すように,図2.2のプルアップ抵抗をCPUユニットが内蔵しており,プルアップ抵抗を有効にするかどうかをソフトウェアで決めることができるようになっている。このプルアップ抵抗の有効無効を設定するのが,プルアップコントロールレジスタ(PCR)である。
AKI-H8のマザーボードでは,ディップスイッチ(4ビットスイッチ)がポート5の4つのビットに,プッシュスイッチ(4つ)がポート2の上位4ビットにつながっており,P2.PCRとP4.PCRの対応するビットに1を書き込むことで,プルアップ抵抗を有効にして使用することができる。そのため,マザーボード上のこれらのスイッチにはプルアップ抵抗がついていない。(P2.PCRとP4.PCRの対応するビットに1を書き込む作業はスタートアップルーチン内で行われている。)
図2.2 スイッチ状態のCPUへの入力 図2.3 プルアップ抵抗の役割 図2.4 H8のプルアップコントロール

外部からのポートのビット入力が5Vの時,CPU内部では1としてとらえ,0Vの時は0としてとらえます。
その結果,ハードウェアの構成に依存して,スイッチの状態ONを0,状態OFFを1として,レジスタに取り込まれることになります。

スイッチの状態 ポートのビット入力端子の電圧 レジスタに取り込まれるビット状態

ON

0V

0

OFF

5V

1

 

2.1 4ビットSWでLED駆動

4ビットスイッチのON-OFFの状態によってLEDのON-OFFを制御するプログラムを作成します。
LED駆動部のみh8_3069.hを用いてプログラムを作ります。
4ビットスイッチの各端子はH8内部でプルアップされる設定なので,スイッチがONになるとポート2の対応するビットは0になります。OFFになると1になります。
「4bitSWの1(ポート5の第0ビット)がONの時では」というのは「if (P5DR.BIT.B0==0) {」のようになり,
「4bitSWの1(ポート5の第0ビット)がOFFの時では」というのは「if (P5DR.BIT.B0==1) {」のようになり,
通常の感覚と逆なので注意しなければなりません。

fourbitsw.c

/**********************************************************
マザーボードの4ビットスイッチによってLEDのON-OFFを行う
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"

main()
{
    while(1) {
        if (P5DR.BIT.B0==0) { /*4bitSWの1がONの時*/
            turnOnLed(0);
            turnOnLed(1);
        } else if (P5DR.BIT.B1==0) { /*4bitSWの2がONの時*/
            turnOnLed(0);
            turnOffLed(1);
        } else if (P5DR.BIT.B2==0) { /*4bitSWの3がONの時*/
            turnOffLed(0);
            turnOnLed(1);
        } else {
            turnOffLed(0);
            turnOffLed(1);
        }
    }
}

参考 P5DR.BIT.B0は「P5のDRをビットごと指定した時の第0ビット」と読めばよい。「P2DR.BIT.B0」に入る値は0または1です。


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

 
4ビットSWの操作も「h8_3069.h」を利用します。
2.1のようなプログラムでは,スイッチのON-OFFの表現が直観とは異なるので,
関数check4BitSW()を使うのがよいでしょう。

プログラム中でcheck4BitSWは
short int check4BitSW(short int number)
で定義されている4bitsw 0,1,2,3の状態を調べる関数で,引数numberは0,1,2 or 3をとり,
ONなら1、そうでなかったら0を関数の戻り値として返します。(ポートへの入力を0-1反転して考えている)
この関数のおかげで,「0:OFF,1:ON」という普通の感覚でプログラミングできます。

fourbitswFunc.c

/**********************************************************
マザーボードの4ビットスイッチによってLEDのON-OFFを行う
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"

main()
{
    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);
        }
    }
}

練習問題 fourbitswFunc.cを元にして次のプログラムを作りなさい。
(実行の様子を検察して,その結果,気付いた事を含め,別途指定された書式で括弧の中ファイル名で提出しなさい)

(1)4bitSWの1がOFFの時,0.5秒周期で2つのLEDを同時点滅,
  4bitSWの1がONの時,1秒周期で2つのLEDを同時点滅
  (mp2ex01.txt)  ET
(2)4bitSWのすべてのビットがOFFの時,0.5秒周期で2つのLEDを同時点滅,
  4bitSWの1のみがONの時,1秒周期で2つのLEDを同時点滅,
  4bitSWの2のみがONの時,2秒周期で2つのLEDを同時点滅
  これ以外の組み合わせではすべて消灯
  (mp2ex02.txt)

 

練習問題

(1)4bitスイッチの回路図を見ると,CPUの端子からスイッチを経てGNDに接続されているだけである。
  通常はこの回路ではスイッチの状態を読み取ることができない。
  スイッチを読み取るプログラムの初期化部分の意味(4bitSWのプルアップ設定)とあわせてどうし
  
て可能なのか検討し,説明しなさい。
  なおH8/3069ハードウェアマニュアルの関連する説明を抜き出しなさい。(9.3 ポート2参照)
  また小坂のweb文書「h8CPU_Input.html」も参考にしなさい。
  (mp2ex03.txt)
(2)4bitスイッチで,4つのスイッチをON,残りの4つのスイッチをOFFにしなさい。
  4bitスイッチの初期化をともなうプログラム(例えば「fourbitsw.c」)を動作させ,アナログテ
  スタを使って,8ビットスイッチのON-OFFの状態が電圧として見えていることを検証しなさい。
  (ヒントCN3のどこか)
  本ページ下部の「参考1 H8ピン配置」を参照しなさい。
  どのように検証したか(テスタの赤黒を何処に触れたのかを含む),どのような結果が得られたか
  表にして報告しなさい。

4bitディップスイッチ

CPU コネクタ デバイス
P5-bit0 CN2-37 4bitSW-1
P5-bit1 CN2-38 4bitSW-2
P5-bit2 CN2-39 4bitSW-3
P5-bit3 CN2-40 4bitSW-4

  (mp2ex04.txt)

4bit
SW
ON/OFF 赤ピンで
触れたところ
黒ピンで
ふれたところ
電圧 port番号 bit番号 ソフトウェア
からみると1か0
   CN-          
2              
3              
4              


(3)次のポートのビットがCN1,CN2のどこのピンに見えるのかを表にして答えなさい。
  本文書の「参考1 H8ピン配置」を参照しなさい。
  1)ポート6のbit0からbit7
  2)ポート7のbit0からbit7
  3)ポートAのbit0からbit7
  4)ポートBのbit0からbit7
  (mp2ex05.txt)



 

3. プッシュスイッチ

H8-PUSHSW.JPG - 31,142BYTES H8-PUSHSWD.JPG - 14,425BYTES

図3.1 プッシュスイッチ

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

 

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

pushsw.c

/**********************************************************
プッシュスイッチによってLEDのON-OFFを行う
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"

main()
{
    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を元にして次のプログラムを作りなさい。
(実行の様子を検察して,その結果,気付いた事を含め,別途指定された書式で括弧の中ファイル名で提出しなさい)

(1)すべてのプッシュスイッチがOFFの時,0.5秒周期で2つのLEDを同時点滅,
  プッシュスイッチの1のみがONの時,1秒周期で2つのLEDを交互点滅,
  プッシュスイッチの2のみがONの時,2秒周期で2つのLEDを交互点滅
  それ以外の状態ではすべて消灯
  (mp3ex01.txt)

(2)プッシュスイッチの状態によって2つのLEDの同時PWM駆動を行なう。
  すべてのプッシュスイッチがOFFの時,なにもしない。
  プッシュスイッチの1のみがONの時,デューティー比100%で点灯,
  プッシュスイッチの2のみがONの時,デューティー比50%で点灯,
  プッシュスイッチの3のみがONの時,デューティー比25%で点灯
  それ以外の状態ではすべて消灯
  (ここではまだ割り込みを用いてはならない)
  (mp3ex02.txt)

(3)プッシュスイッチ1を1回押すごとに,LEDの点灯,消灯の切り替えが起こり,
  何回でも繰り返すことができるプログラム。
  正確に言うとプッシュスイッチを押したときには現在の点灯状態はまだ変化せず
  プッシュスイッチから指を離した瞬間に点灯状態が反転するように作りなさい。

  プッシュ  離す ━┓  ┏━━━━┓  ┏━┓  ┏━
  スイッチ  押す  ┗━━┛    ┗━━┛ ┗━━┛ 

  LED   点灯 ━━━━┓       ┏━━━━┓
        消灯     ┗━━━━━━━┛    ┗━

void pushreleaseSW(void)
/*プッシュスイッチが押されるまで待って,*/
/*その後離されるまで待つ関数      */
{
    while (checkPushSW(0)==0);
    while (checkPushSW(0)==1);
}


  注意深く100回ほどプッシュスイッチを押して動作を観察しなさい。
  不安定な動作が起こるかもしれない。「チャタリング」について調べること。
  (mp3ex03.txt)


 
4. 割り込み

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

4.1  割り込みでLED駆動(インターバルタイマによるタイマ割り込み)

プログラム中main()側で10msec(0.01秒)間隔でタイマ割り込みを設定します。
main()関数内でなにもしないループ動作をしている最中に,割り込み関数interrupt_cfunc()は0.01秒ごとに起動し,100回つきに1回のみLEDのON-OFFを行ないます。
このタイマ割り込みはITUのch1が使われています。

割り込みを実現するには次のことが必要になる。
(1)割り込みベクトルの設定(割り込みが発生した時に起動すべき関数のアドレスを所定の領域に書いておく)
(2)割り込み要因となる機能の初期化
(3)CPUの割り込み許可
(4)割り込み関数は割り込み関数として定義され,レジスタの退避復帰,割り込みリターンなどの特殊な作り方が必要

これらの必要事項は次のように記述される。
(1)アセンブラでしか書くことが出来ないのでインラインアセンブリで記述
  (インラインアセンブリ : C言語プログラム中にアセンブリ言語を記述すること)

  タイマ割り込みの関数のアドレスは0x70に書くことになっている。(割り込みベクタテーブル領域)
  アセンブラから見るとCの関数「TimerIntFunc」の名前は「_TimerIntFunc」に見える。
  関数の名前は関数の先頭アドレスを意味する。

#pragma asm
    .SECTION    MYVEC, DATA, LOCATE=H'000070
    .ORG        H'000070  ;IMIA1
    .DATA.L     _TimerIntFunc
    .SECTION    P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm

    ハードウェアマニュアル
    http://tnct20.tokyo-ct.ac.jp/~kosaka/for_students/H8/h8_3069Hardwaremanual.pdf
    において,割り込みベクタテーブルについての記述は4.1.3に記述されている。
    ここで用いているのは,ITUch1を使用したIMIA1という割り込み要因である。

(2)main関数内で記述

    initTimer1Int(10000); /*時間割り込み10000μsec(=10msec)  ch1使用*/
    startTimer1();  /*時間割り込みタイマスタートch1*/

(3)main関数内で記述

E_INT();        /*CPU割り込み許可*/

   この関数の本体はアセンブリ言語でないと書けないので,スタートアップルーチンのソースの後ろに
   ついている。   

(4)割り込み関数独特の宣言を行なう TimerIntFuncは割り込み関数名

#pragma interrupt (TimerIntFunc)

   この宣言が行われた関数は,プログラム内から関数呼び出ししてはいけない。

int1st.c

/**********************************************************
時間割り込みによってLEDのON-OFFを行う
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"
#define clearTimer1Flag() (ITU.TISRA.BIT.IMFA1=0)

main()
{
    initTimer1Int(10000); /*時間割り込み10000μsec=10msec ITUch1使用*/
    E_INT();        /*CPU割り込み許可*/
    startTimer1();  /*時間割り込みタイマスタートch1*/
    while(1);       /*なにもしないループ*/
}

#pragma asm
    .SECTION    MYVEC, DATA, LOCATE=H'000070
    .ORG        H'000070  ;IMIA1
    .DATA.L     _TimerIntFunc
    .SECTION    P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm

#pragma interrupt (TimerIntFunc)
void TimerIntFunc() /*タイマ割り込みルーチン*/
{
    static int tick=0;
    static int count=0;
    clearTimer1Flag();  /*タイマステータスフラグのクリア 忘れないこと*/
    count++;
    if (count==100) {
        count=0;
        if (tick==1) {
            turnOnLed(0);
            turnOffLed(1);
        } else {
            turnOffLed(0);
            turnOnLed(1);
        }
        tick=1-tick;
    }
}

ここではアセンブリ言語には踏み込まないが簡単に解説
(深入りはしない。後にアセンブリ言語を学んだ後に読み返せばよい)

#pragma asm
  ここの間はアセンブリ言語による記述ですという意味
#pragma endasm

.SECTION    MYVEC, DATA, LOCATE=H'000070
   MYVECという名前のセクションを0x70番地から始める

.ORG        H'000070  ;IMIA1
   0x70からこれ以降の内容を配置

.DATA.L     _TimerIntFunc
   関数TimerIntFuncの先頭アドレスをここに書く
   DATA.L というのは,32ビット幅のデータの意味

.SECTION    P,CODE,ALIGN=2 ;これを忘れてはいけない
   これ以降はCODEというセクションの続きになるの意味

アセンブリ言語の記述スタイルについて
プログラムの内容は行の先頭にいくつかのスペースまたはタブの後ろに書く。
「;」以降はコメントとみなされる。

勘どころ

ITU1の割り込みベクタ(割り込み関数の先頭番地)が0x70であることは,CPUの設計者が決めたことなので,
プログラム作成者が変更することはできない。
割り込み関数の名前は,プログラム作成者が決めることができる。

練習問題 int1st.cを元にして次のプログラムを作りなさい。
(実行の様子を検察して,その結果,気付いた事を含め,別途指定された書式で括弧の中ファイル名で提出しなさい)

(1)int1st.cを元にして点滅周期が0.5秒(0.25秒ON+0.25秒OFF
)になるプログラムに変更しなさい。
  (mp4ex01.txt)
(2)initTimer1Int()を用いて4秒周期のLED点滅を行なうプログラムを作りなさい。
  (引数がマイクロ秒であること,
引数にはshort int型が用いられていることに注意)
  (mp4ex02.txt)


4.2  割り込みでLED駆動

割り込みを用いたPWM駆動でLEDの制御を行ないます。
このプログラムではITUのch1のみを用います。
PushSW 1 → LED 0 明るく点灯(10/10)
PushSW 2 → LED 0 暗く点灯(1/10)
PushSW 3 → LED 1 明るく点灯(10/10)
PushSW 4 → LED 1 暗く点灯(1/10)

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

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

「h8_3069.h」にある関数initTimer1Int(),startTimer1()はITU1を使ったタイマ割り込みを可能にしている。
ハードウェアマニュアルのITUの基本動作(TCNT(カウンタ)がカウントアップ)を見なさい。
カウンタ(TCNT)がカウントアップしてあらかじめ設定した値(GRA)とコンペアマッチ(比較して一致)で割り込みが起こり,カウンタがクリアされ,この動作が継続される。カウンタは内部クロック(16MHz)の1/8の2MHz(周期は0.5μsec)で動作している。
このことにより,タイマ割り込みが行われている。

「1」で取り上げた関数msecwait()を使う方法と割り込みを使う方法とでは,時間精度が違う。高精度な時間管理には関数msecwait()を使うべきではない。タイマユニットが使えない場合に使用を検討するのがよい。

int2nd.c

/**********************************************************
プッシュスイッチと時間割り込みによってLEDのPWM制御を行う
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"
#define clearTimer1Flag() (ITU.TISRA.BIT.IMFA1=0)

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

main()
{
    led0=led1=0;
    initTimer1Int(1000); /*タイマ割り込み1msec */
                         /*単位はμsec ITUch1のみ使用*/
    E_INT();        /*CPU割り込み許可*/
    startTimer1();  /*時間割り込みタイマスタート*/
    while(1){
        if (checkPushSW(0)==1) { /*PushSWの1がONの時*/
            led0=period;
        } else if (checkPushSW(1)==1) { /*PushSWの2がONの時*/
            led0=low;
        } else {
            led0=0;
        }
        if (checkPushSW(2)==1) { /*PushSWの3がONの時*/
            led1=period;
        } else if (checkPushSW(3)==1) { /*PushSWの4がONの時*/
            led1=low;
        } else {
            led1=0;
        }
    }
}

#pragma asm
    .SECTION    MYVEC, DATA, LOCATE=H'000070
    .ORG        H'000070  ;IMIA1
    .DATA.L     _TimerIntFunc
    .SECTION    P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm

#pragma interrupt (TimerIntFunc)
void TimerIntFunc() /*タイマ割り込みルーチン*/
{
    static int tick=0;
    clearTimer1Flag();  /*タイマステータスフラグのクリア 忘れないこと*/
    if (tick<led0) {
        turnOnLed(0);
    } else {
        turnOffLed(0);
    }
    if (tick<led1) {
        turnOnLed(1);
    } else {
        turnOffLed(1);
    }
    tick++;
    if (tick==period) tick=0;
}

練習問題 int2nd.cを元にして次のプログラムを作りなさい。
(実行の様子を検察して,その結果,気付いた事を含め,別途指定された書式で括弧の中ファイル名で提出しなさい)

(1)int1st.cを元にして次の動作をするようにプログラムを変更しなさい。
  PushSWはどれも押されていない → 2つのLEDは暗く点灯(1/10)
  PushSW 1 → LED 0 中間点灯(2/10)  LED 1消灯
  PushSW 2 → LED 0 中間点灯(5/10)  LED 1消灯
  PushSW 1とPushSW 2 → LED 0 明るく点灯(10/10)  LED 1消灯
  PushSW 3 → LED 1 中間点灯(2/10)  LED 0消灯
  PushSW 4 → LED 1 中間点灯(5/10)  LED 0消灯
  PushSW 3とPushSW 4 → LED 1 明るく点灯(10/10)  LED 0消灯
  それ以外の状態ではすべて消灯
  (mp4ex03.txt)

(2)int2nd.cを元にして次の動作をするようにプログラムを変更しなさい。
  ITU2を使った関数initTimer2Int(),startTimer2()を自分のファイルに作り,int2nd.cの必要な
  所を書き換えて,int2nd.cと同じ動作をするようにしなさい。
  割り込み要因が,IMIA1からIMIA2に変わるので,割り込みベクタテーブルは0x70ではなくなる。
  (mp4ex04.txt)

 

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

プログラム書き込み用通信回線はそのままプログラム実行時にも使えます。
実行時にはハイパーターミナルを起動しておいて下さい。
サンプルフォルダ中の次に示すアイコンで起動すると,設定済みのハイパーターミナルが起動します。
                               
Async, 8bit, NoParity, stop1 ,38400baud, (Backspace:Ctrl+H, Space, Ctrl+H)で設定されています。

次のプログラムは,ハイパーターミナルを利用した,ビットスイッチやプッシュスイッチのチェックおよびキーボードとの通信プログラムです。
ビットスイッチやプッシュスイッチのチェックの時はそれぞれのスイッチを動かしてみてください。

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

                                
マイコンへのプログラムの転送が終わったら,ハイパーターミナルを先に起動してから,マイコンを起動してください。

sciout.c

/**********************************************************
SCI1へ出力,WINDOWSのHyperTerminalなどで受信できる。
ただし,設定は
38400baud, Async, 8bit , NoParity, stop1
**********************************************************/
#include <3069s.h>
#include "h8_3069.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= %2x[%08b]\n",sw,sw);
    do {
        sw=get4BitSW();
        if (sw!=previous) {
            SCI1_printf("4-bitSW status= %2x[%08b]\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= %2x[%08b]\n",sw,sw);
    do {
        sw=getPushSW();
        if (sw!=previous) {
            SCI1_printf("PushSW status= %2x[%08b]\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の初期化*/
    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;
        }
    }
}

特別な関数の説明
short int getCharSCI1()
 SCI-ch1から1byte入力コードを得る関数。通信エラーがあると-2が戻る。
 SIC-ch1入力バッファを検査し,データがあれば持ち帰るが,データがない場合はデータが来るまで待ち続け,
 データが来たら,それを持ち帰ってくる
 PCのキーボードが押され,SCI-ch1経由でデータが転送されてくるまで,永久に待つ関数である。
 ANSIの関数getchar()と同じ動作をする

short int chkgetCharSCI1()
 SCI-ch1入力バッファを検査し,受信データがあれば1byte入力コードを得る関数。
 受信データガなければ-1が,通信エラーがあると-2が戻る。
 SICバッファを検査し,データがあれば持ち帰るが,データがなくても-1をもってすぐに帰ってくる関数である。

int get4BitSW()
 4bitSWの状態をそのまま1バイトの値として読み込む関数。
 ただし,値は反転しており,スイッチの状態がONのビットは1,OFFのビットは0で読み込む。

 ●画面への表示をそのままファイル化する方法(テキストキャプチャ)

SCI通信の時PC側の「Enterキー」入力では,文字キーの右側にある大きなenter-keyを使うこと。

enter-keyについて
キーボードには2つのenter-keyがあるが,実は文字コードが異なる。
   文字キーの右側にある大きなenter-key   \r\n (0x0d,0x0a)
   数字キーの右側にある小さなenter-key   \r (0x0d)
である。
\r(0x0d) はコンソール上で文字ポインタを左端に戻すコード

\n(0x0a) はコンソール上で文字ポインタを次の行に進めるコード

練習問題 次のプログラムを作りなさい。
(実行の様子を検察して,その結果,気付いた事を含め,別途指定された書式で括弧の中ファイル名で提出しなさい)

(1)マイコンのプログラム実行開始後,4つのプッシュスイッチがそれぞれ何回押されたかを
  表示するプログラムを作成しなさい。
  このプログラムは電源がOFFされるまでカウントを続け,絶えず最新のカウントを表示し続
  けるものとします。
  (mp5ex01.txt)
(2)アドレス0x0000から0x01FFまでのメモリを十六進ダンプするプログラムを作りなさい。
  ヒント int型変数ptrに0x1200が入っている時,アドレス0x1200の内容を表示するには
  次のように書くと良い。
  SCI1_printf(" %02x",*(unsigned char *)ptr);
  (mp5ex02.txt)

実行例
memory dump 0x0000-0x01ff
     +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
0000 00 00 01 00 ff ff ff ff ff ff ff ff ff ff ff ff
0010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
0020 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
0030 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
0040 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
0050 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
0060 00 00 0a 24 ff ff ff ff ff ff ff ff ff ff ff ff
0070 00 00 0a 24 ff ff ff ff ff ff ff ff ff ff ff ff
0080 00 00 0a 24 ff ff ff ff ff ff ff ff ff ff ff ff
0090 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00a0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00b0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00c0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00e0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00f0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
0100 7a 07 00 0f ff 10 01 00 6b 20 00 00 0b 8c 01 00
0110 6b 21 00 00 0b 90 01 00 6b 22 00 00 0b 94 01 f0
0120 64 22 5a 00 01 2e 6c 03 68 93 0b 01 1b 72 46 f6
0130 01 00 6b 21 00 00 0b 98 01 00 6b 22 00 00 0b 9c
0140 f3 00 01 f0 64 22 5a 00 01 50 68 93 0b 01 1b 72
0150 46 f8 5e 00 09 90 40 fe ff ff ff ff ff ff ff ff
0160 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
0170 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
0180 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
0190 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
01a0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
01b0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
01c0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
01d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
01e0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
01f0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff


(3)指定したアドレスから0x100byteをメモリダンプするプログラムを作りなさい。
  ただし指定アドレスは十六進の1の位は0とする。
  何回でも続けるものとする。
  (mp5ex03.txt)

実行例
start address (0xnnnn) =0x400
memory dump 0400-04ff
     +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
0400 18 88 6e f8 00 32 7a 06 00 00 00 50 0a f6 0b 96
0410 73 0e 47 0e 7a 00 00 00 00 50 0a f0 0b 90 0b 70
0420 40 0a 7a 00 00 00 00 50 0a f0 0b 90 0f 86 7a 00
0430 00 00 00 25 0a f0 01 00 6f f0 00 2a 5a 00 08 64
0440 01 00 6f 70 00 50 68 0c 0c 44 46 44 ac 25 46 36
0450 f4 01 18 55 f8 20 6e f8 00 2f 18 88 6e f8 00 32
0460 6e f8 00 33 01 00 6f 70 00 2a 1b 70 0f 82 0b 70
0470 18 99 68 89 18 dd 18 88 6e f8 00 30 f8 2b 6e f8
0480 00 31 5a 00 08 56 0c c8 5c 00 ff 32 5a 00 08 56
0490 ac 6c 46 06 70 14 5a 00 06 54 ac 30 4d 2c ac 39
04a0 4e 28 0c 55 46 0e ac 30 46 0a f8 30 6e f8 00 2f
04b0 5a 00 06 54 f8 0a 50 50 08 c8 88 d0 0c 85 a8 20
04c0 58 30 01 90 f5 20 5a 00 06 54 ac 2d 46 0a f8 01
04d0 6e f8 00 32 5a 00 06 54 ac 64 46 0c 70 24 f8 0a
04e0 6e f8 00 33 5a 00 06 54 ac 75 46 0a f8 0a 6e f8
04f0 00 33 5a 00 06 54 ac 78 46 0a f8 10 6e f8 00 33
start address (0xnnnn) =0x500
memory dump 0500-05ff
     +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
0500 5a 00 06 54 ac 58 46 0e f8 10 6e f8 00 33 6e f8
0510 00 30 5a 00 06 54 ac 6f 46 0a f8 08 6e f8 00 33
0520 5a 00 06 54 ac 62 46 0a f8 02 6e f8 00 33 5a 00
0530 06 54 ac 70 46 18 f8 10 6e f8 00 33 6e f8 00 30
0540 f5 08 f8 30 6e f8 00 2f 70 14 5a 00 06 54 ac 63
0550 46 3c 0f e4 0b f4 73 0c 47 06 0f c0 0b 70 40 02
0560 0f c0 0f 86 1b f0 01 00 6f f0 00 26 73 08 47 0a
0570 01 00 6f 71 00 26 1b 71 40 06 01 00 6f 71 00 26
0580 6e 18 00 01 5c 00 fe 36 18 44 5a 00 06 54 ac 73
0590 58 60 00 b8 0c 55 47 7c 18 dd 0f e0 0b 90 01 00
05a0 6f f0 00 26 73 08 47 0a 01 00 6f 70 00 26 0b 70
05b0 40 06 01 00 6f 70 00 26 0f 86 1b 90 0f 84 73 08
05c0 47 06 0f c0 1b 70 40 02 0f c0 01 00 69 02 40 02
05d0 0a 0d 0c d8 17 50 17 70 0f a1 0a 81 68 18 46 f0
05e0 6e 78 00 32 46 10 0c dc 40 08 f8 20 5c 00 fd ce
05f0 0a 0c 1c 5c 45 f4 0f a0 5c 00 fd de 6e 78 00 32
start address (0xnnnn) =


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

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

timer.c

/**********************************************************
割り込みを用いた時計 起動時からの経過時間[秒]を
SCI1へ出力,WINDOWSのHyperTerminalなどで受信できる。
ただし,設定は
38400baud, Async, 8bit , NoParity, stop1
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"
#define clearTimer1Flag() (ITU.TISRA.BIT.IMFA1=0)

volatile unsigned int counter;

main()
{
    unsigned int counter1;
    unsigned int t1,t2;
    initSCI1(); /*SCI-ch1の初期化*/
    initTimer1Int(20000); /*時間割り込み20000μsec=20msec ch1使用*/
    E_INT();        /*CPU割り込み許可*/
    startTimer1();  /*時間割り込みタイマスタートch1*/
    counter=0;
    SCI1_printf("\n\n\ntimer\n");
    while(1) {
        counter1=counter;
        t1=counter1/10;
        t2=counter1%10;
        SCI1_printf("%10u.%1u\r",t1,t2);
    }
}

#pragma asm
    .SECTION    MYVEC, DATA, LOCATE=H'000070
    .ORG        H'000070  ;IMIA1
    .DATA.L     _TimerIntFunc
    .SECTION    P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm

#pragma interrupt (TimerIntFunc)
void TimerIntFunc() /*タイマ割り込みルーチン*/
{
    static int dutycount=0;
    dutycount++;
    if (dutycount==5) {
        dutycount=0;
        counter++;
    }
    clearTimer1Flag();  /*タイマステータスフラグのクリア 忘れないこと*/
}

練習問題 int2nd.c,sciout.c,timer.cを元にして次のプログラムを作りなさい。
(実行の様子を検察して,その結果,気付いた事を含め,別途指定された書式で括弧の
 中ファイル名で提出しなさい)

(1)1msec(1000μsec)ごとの割り込みを用いて,ストップウォッチを作成せよ。
  スタート・ストップボタンなどはキーボードのキーに割り当てる。
  また割り込み関数内で表示を行なってはいけない。グローバル変数のカウンタ変数を
  用いればよいだろう。
  キーの割り当て
  A:スタート(再開) B:ストップ C:クリア (ストップしても再開できる)
  getCharSCI1()ではなくchkgetCharSCI1()の使用を考えなさい。
  どうして割り込み関数内で表示していけないか考えなさい。
  H8CPUからPCへのデータ転送は38400bit/secである。1バイト送信するのに10ビット
  の転送が必要である。
  割り込み関数は,次の割り込み要求が生ずる前に作業を終了しなければならない。
  1msecごとの割り込みなら,割り込み関数内で文字をH8からPCへ転送するとしたら,
  何文字程度送信できるか考えなさい。
  (mp6ex01.txt) ET

(2)キーボードから0から10までの整数をint型変数ratioに受け取り,「4.2 int2nd.c」
  のプログラムで,点灯時間をratio/10のデューティ比になるようにしなさい。
  またデューティ比の変更は何回でもできるようにしなさい。
  (mp6ex02.txt)

(3)シリアル通信速度は"h8_3069.hの中の関数initSCI1で設定されている。
  この関数の中では通信速度が38400bit/secになるよう設定されている。
  (ハイパーターミナルも38400bit/secに設定されているので通信ができている。)
  この部分を自分のファイルに取り込み,19200bit/secの通信速度にした例を示す。
    
sciout38400_19200.c
  このプログラムを改造し通信速度を1200bit/secに設定し,通信を検証しなさい。
  ハードウェアマニュアルを読まないとできません。
  (mp6ex03.txt)
  「ハイパーターミナル」側の通信速度を設定するには
   「通信」メニューから「切断」を選ぶ
   「ファイル」メニューから「プロパティ」を選び,プロパティダイアログを開く
   「接続の設定」タブの「モデムの構成...」ボタンを選ぶ
   「ポートの設定」タブの「ビット/秒」を変更する
   2つの設定ダイアログを「OK」でもどる。 
   「通信」メニューから「電話」を選ぶ。
   ハイパーターミナルの下部に「19200 8-N-1」,「1200 8-N-1」
   など希望の設定が表示されたらOK

 

7.LCDへの表示プログラム

LCDに文字を表示してみよう。次の例は,プッシュスイッチの状態を16進数と2進数でLCDに表示します。
実際にはプッシュスイッチはポート4の上位4ビットに位置しているため,それを反映して,プッシュスイッチの1を押すと
16進数では「10」,2進数では「00010000」と表示される。

図7.1 LCDでプッシュスイッチの状態表示
この写真ではどこも押されていないので,すべて0表示

LCD.c

/**********************************************************
LCDへ出力
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"


main()
{
    unsigned char sw,previous;
    initLCD(); /*LCDの初期化*/
    LCDgotoxy(0,0);
    LCD_printf("pushSW status");
    previous=sw=getPushSW();
    LCDgotoxy(0,1);
    LCD_printf("%02x[%08b]",sw,sw);
    while(1) {
        sw=getPushSW();
        if (sw!=previous) {
            LCDgotoxy(0,1);
            LCD_printf("%02x[%08b]",sw,sw);
            previous=sw;
        }
    }
}

LCDに関する関数
initLCD() LCDの初期化関数
LCDclrscr()

LCDの画面初期化関数,全消去

LCDgotoxy(x,y): LCDの表示ポインタ設定関数左上を(0,0)として,右側にx,
下側にy(0と1しかないが)の位置から次の表示を開始する。
LCD_printf() LCDに対する関数printf(),ただし,"\n"や"\r"には対応していない。

 

8.独立した複数の作業の書き方
リアルタイムOSを使うと,もっと別な書き方になるが,ここではリアルタイムOSを使わない方法でプログラミングする

例題

プッシュスイッチ1(内部表現では0)を押しした瞬間から2秒間LED0が点灯し、消灯する。
プッシュスイッチ2(内部表現では1)を押しした瞬間から2秒間LED1が点灯し、消灯する。
ただし、LED0が点灯中にプッシュスイッチ1が押されたら、残り点灯時間には無関係に、その時点から新たに2秒間点灯するものとする。
同様に、LED1が点灯中にプッシュスイッチ2が押されたら、残り点灯時間には無関係に、その時点から新たに2秒間点灯するものとする。
また、LED0が点灯中にプッシュスイッチ2が押されたら、LED0の動作には影響を与えず(残り0.5秒の点灯時間があるなら1.5秒後まで点灯を続ける)、LED1は、その時点から1秒間点灯するものとする。
同様に、LED1が点灯中にプッシュスイッチ1が押されたら、LED1の動作には影響を与えず(残り0.5秒の点灯時間があるなら1.5秒後まで点灯を続ける)、LED2は、その時点から1秒間点灯するものとする。
このような2つの動作を独立動作と呼ぶ。

独立した2つの作業を行う
失敗作(プログラムはわかりやすいが失敗する)

/**********************************************************
PushSW1でLED0をPushSW2でLED0を1秒間点灯させる
この2つの作業は独立に行われる
**********************************************************/
#include <3048f.h>
#include "h8_3048.h"
#define clearTimer1Flag() (ITU1.TSR.BIT.IMFA=0)

volatile int count1=-1;/*LED0用カウンタ -1の時は休止中*/
volatile int count2=-1;/*LED1用カウンタ -1の時は休止中*/
main()
{
    initLed();
    initPushSW();/*PushSWの初期化*/
    initTimer1Int(10000); /*時間割り込み10000μsec=10msec ch1使用*/
    E_INT();        /*CPU割り込み許可*/
    startTimer1();  /*時間割り込みタイマスタートch1*/
    while(1) {
        if (count1==-1 && checkPushSW(0)==1) {
            count1=0;
            turnOnLed(0);
            while (count1<200); /*200カウントで2秒経過*/
            count1=-1;
            turnOffLed(0);
        }
        if (count2==-1 && checkPushSW(1)==1) {
            count2=0;
            turnOnLed(1);
            while (count2<200); /*200カウントで2秒経過*/
            count2=-1;
            turnOffLed(1);
        }
    }
}

#pragma asm
    .SECTION    MYVEC, DATA, LOCATE=H'000070
    .ORG        H'000070  ;IMIA1
    .DATA.L     _TimerIntFunc
    .SECTION    P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm

#pragma interrupt (TimerIntFunc)
void TimerIntFunc() /*タイマ割り込みルーチン*/
{
    static int tick=0;
    clearTimer1Flag();  /*タイマステータスフラグのクリア 忘れないこと*/
    if (count1!=-1) count1++;
    if (count2!=-1) count2++;
}

独立した2つの作業を行う(成功例)

/**********************************************************
PushSW1でLED0をPushSW2でLED0を1秒間点灯させる
この2つの作業は独立に行われる
**********************************************************/
#include <3069f.h>
#include "h8_3069.h"
#define clearTimer1Flag() (ITU.TISRA.BIT.IMFA1=0)

volatile int count1=-1;/*LED0用カウンタ -1の時は休止中*/
volatile int count2=-1;/*LED1用カウンタ -1の時は休止中*/
main()
{
    initLed();
    initPushSW();/*PushSWの初期化*/
    initTimer1Int(10000); /*時間割り込み10000μsec=10msec ch1使用*/
    E_INT();        /*CPU割り込み許可*/
    startTimer1();  /*時間割り込みタイマスタートch1*/
    while(1) {
        if ((count1==-1 || 50<count1) && checkPushSW(0)==1) {
            count1=0;
            turnOnLed(0);
        } else if (200<count1) { /*200カウントで2秒経過*/
            count1=-1;
            turnOffLed(0);
        }
        if ((count2==-1 || 50<count2) && checkPushSW(1)==1) {
            count2=0;
            turnOnLed(1);
        } else if (200<count2) { /*200カウントで2秒経過*/
            count2=-1;
            turnOffLed(1);
        }
    }
}

#pragma asm
    .SECTION    MYVEC, DATA, LOCATE=H'000070
    .ORG        H'000070  ;IMIA1
    .DATA.L     _TimerIntFunc
    .SECTION    P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm

#pragma interrupt (TimerIntFunc)
void TimerIntFunc() /*タイマ割り込みルーチン*/
{
    static int tick=0;
    clearTimer1Flag();  /*タイマステータスフラグのクリア 忘れないこと*/
    if (count1!=-1) count1++;
    if (count2!=-1) count2++;
}

練習問題 (mp7ex01.txt)

プッシュスイッチ1(内部表現では0)を押した瞬間からLED0は1秒間点灯し、1秒間消灯し、
1秒間点灯して動作を終了する。このLED0の動作を定型動作とする。
プッシュスイッチ2(内部表現では1)を押した瞬間からLED1は1秒間点灯し、1秒間消灯し、
1秒間点灯して動作を終了するという定型動作をおこなう。
ただし、LED0が定型動作中にプッシュスイッチ1が押されたら、残り定型動作時間には無関係に、その時点か
ら新たに定型動作を開始するものとする。
同様に、LED1が定型動作中にプッシュスイッチ2が押されたら残り定型動作時間には無関係に、その時点から
新たに定型動作を開始するものとする。
また、LED0が定型動作中にプッシュスイッチ2が押されたら、LED0の定型動作には影響を与えず、LED1は、
定型動作を開始するものとする。
同様に、LED1が定型動作中にプッシュスイッチ1が押されたら、LED1の定型動作には影響を与えず、LED2は、
定型動作を開始するものとする。
この動作も前例題と同様に独立動作である。
なお,例題では失敗作と成功例があるが,どのように失敗していて,どのようにして成功させたかを考察で述べなさい。

練習問題 (mp7ex02.txt)

前課題の2つプッシュスイッチにより起動する定型動作に加え、プッシュスイッチ3によって起動するも
う1つの定型動作を行うようにする。
シリアル通信とハイパーターミナルを用いて次の動作をおこなう。
ハイパーターミナルあるいはLCDで電光掲示板のように「Hello, everyone!」を右から左に流れるように
表示する。表示中に再度プッシュスイッチが押されたら、表示動作は最初から再表示になる。
また、LED0、LED1とこの表示はそれぞれ独立動作である。
電光掲示板のように表示させるには次の文字列を0.1秒ごとに表示ればよい。
最後まで表示したら、この定型動作は終了である。
"               H\r"
"              He\r"
"             Hel\r"
"            Hell\r"
"           Hello\r"
"          Hello,\r"
       :
"Hello, everyone!\r"
"ello, everyone! \r"
"llo, everyone!  \r"
"lo, everyone!   \r"
       :
"e!              \r"
"!               \r"
"                \r"

 


 参考1 H8ピン配置

TNCTマザーボードの回路図(pdf)

コネクタ1(CN1)ピン割り当て

コネクタ2(CN2)ピン割り当て

CN1 pin H8/3069 pin name デバイス
1 58 P6-0

-

2 59 P6-1 -
3 60 P6-2 -
4 61 P6-7 -
5 64 NMI NMI input
6 69 P6-3 -
7 70 P6-4 RD
8 71 P6-5 WR(HWR)
9 72 P6-6 -
10 76 AVcc AVcc
11 77 Vref Vref
12 78 P7-0/AN0 -
13 79 P7-1/AN1 -
14 80 P7-2/AN2 -
15 81 P7-3/AN3 -
16 82 P7-4/AN4 -
17 83 P7-5/AN5 -
18 84 P7-6/AN6/DA0 -
19 85 P7-7/AN7/DA1 -
20 87 P8-0 -
21 88 P8-1 -
22 89 CS2 DRAM cs (DRAM RAS)
23 90 CS1 LAN cs
24 91 P8-4 -
25 93 PA-0 -
26 94 PA-1 -
27 95 PA-2 -
28 96 PA-3 -
29 97 PA-4 -
30 98 PA-5 -
31 99 PA-6 -
32 100 PA-7 -
33 2 PB-0 -
34 3 PB-1 -
35 4 PB-2 -
36 5 PB-3 -
37 6 PB-4 UCAS (DRAM CAS)
38 7 PB-5 -
39 8 PB-6 -
40 9 PB-7 -
CN2 pin H8/3069 pin name デバイス
1 - GND GND
2 - +5V(out) +5V(out)
3 16 P9-4/IRQ4 -
4 17 P9-5/IRQ5 LAN irq (INT4)
5 18 P4-0 LCD DB4
6 19 P4-1 LCD DB5
7 20 P4-2 LCD DB6
8 21 P4-3 LCD DB7
9 23 P4-4 LCD RS
10 24 P4-5 LCD ES
11 25 P4-6 LED1
12 26 P4-7 LED2
13 27 D8 (D8)
14 28 D9 (D9)
15 29 D10 (D10)
16 30 D11 (D11)
17 31 D12 (D12)
18 32 D13 (D13)
19 33 D14 (D14)
20 34 D15 (D15)
21 36 A0 (A0)
22 37 A1 (A1)
23 38 A2 (A2)
24 39 A3 (A3)
25 40 A4 (A4)
26 41 A5 (A5)
27 42 A6 (A6)
28 43 A7 (A7)
29 45 A8 (A8)
30 46 A9 (A9)
31 47 A10 (A10)
32 48 P2-3 PushSW-1
33 49 P2-4 PushSW-2
34 50 P2-5 PushSW-3
35 51 P2-6 PushSW-4
36 52 P2-7 PushSW-5
37 53 P5-0 4bitSW-1
38 54 P5-1 4bitSW-2
39 55 P5-2 4bitSW-3
40 56 P5-3 4bitSW-4