AKI-H8(HitachiH8/3664f)の使い方
LED,SCI,PWM,ADCによるプログラミング

Copyright(C) 21Nov2003
Copyright(C) 7Nov2002


coskx

1. はじめに

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

1.2 AKI-H8(HitachiH8/3364f)
 AKI-H8/3664f(図1.1参照)は日立製作所の製品であるマイクロコンピュータH8製品群のH8/3664fを用いて,秋月電子通商がCPUボード,ベースボードを製作販売している製品の商品名です。購入したキットの説明書通り製作しても何も出来ない上,9V電池駆動なので不便です。そこで,LEDを付加し,電源もACアダプタが使えるようにしました。ACアダプタが供給する電圧範囲は7Vから12V程度がよいでしょう。

図1.1 AKI-H8/3664f

電源部,LED部は独自仕様
P8-Bit6,P8-Bit7には330Ωで保護されたLEDが付いている


ベースボード上には次の要素が搭載されています(図1.2参照)
(1)   12V電源入力部,電源スイッチ(図1.3参照)
(2)   拡張したLED(発光ダイオード) (図1.4参照)
(3)   シリアル通信用DSUB9端子(図1.5参照)

図1.2 AKI-H8/3664f

電源部,LED部は独自仕様
P8-Bit6,P8-Bit7には330Ωで保護されたLEDが付いている

図1.3 ベースボード電源部

図1.4 LED拡張部

図1.5 ベースボードシリアル通信DSUB9コネクタ部

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

1.3 以下で扱うプログラムのダウンロード

ダウンロードはここです

2.LEDの点灯

H8_3664.hで定義された関数を用いてLEDの点滅を行なうプログラム。

ledtest.c

/*LED点灯プログラム*/
#include "H8_3664.h"

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

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

 

3.シリアル通信でホストPCと通信

H8_3664.hで定義された関数を用いてシリアル通信でホストと交信する。
ホストPC(Windowsマシン)では,ハイパーターミナルなどのターミナルソフトを起動しておく。

scitest.c

/*シリアル通信でホストと交信する*/
#include "H8_3664.h"

main()
{
    int ch;
    initSCI(); /*シリアル通信インタフェイスの初期化
                 38400baud, Async, 8bit , NoParity, stop1*/
    SCI_printf("****** SCI test ******\n");
    SCI_printf("Hit any character key\n");
    while (1) {
        ch=getCharSCI(); /*1文字取得*/
        putCharSCI(ch+1); /*1文字送信*/
        SCI_printf("  test %d %d %x\n",1234,9876,ch);
        SCI_printf("  %c %s %lx\n",'#',"hello",0x80804040L);
        SCI_printf("  %s %c %lx\n","hello",'#',0x80804040L);
    }
}

 

4.タイマー割り込みでLED点灯制御

通常の関数は,関数がプログラム中の他の関数から呼び出されたときに作業を行ないます。これに対して,割り込み関数は何らかの割り込み要因によって呼び出される関数です。
タイマ割り込み関数は,タイマ割り込み初期設定によって設定された時間間隔で起動する割り込み関数です。ここのプログラムでは,約33msのタイマ割り込み初期設定が行なわれ,CPUの割り込み許可がなされ,タイマがスタートした後,プログラムの流れは
while(1);
となり,無限ループに突入します。しかし,33ms(0.033秒)ごとにタイマ割り込み関数「interrupt_cfunc()」が起動し,cntの値が0から29まで増加しては0に戻るという動作が繰り返され,LEDのON−OFFが継続して行なわれます。変数cntはvolatile修飾されているので,変数が呼び出されたときに,必ずメモリから呼び出されます。(コンパイラにより最適化がなされると,一度メモリからレジスタに呼び出された変数は,メモリを参照されなくなる可能性があり,volatile修飾子をつけておくとこの現象を回避できます。)
ロボットの制御には一定時間ごとに起動する定時間割り込み(タイマー割り込み)が良く用いられます。またPWMの生成にもこの定時間割り込みが用いられる場合があります。
小坂のシステムを用いる限り,タイマ割り込み関数「interrupt_cfunc()」の名前を変更してはいけません。関数の内容は変更してもOKです。

H8_3664.hで定義された関数を用いてタイマー割り込みを起動し,LEDの点滅制御を行なうプログラム。

intledtest.c

/*タイマー割り込みを起動し,LEDの点滅制御を行なう*/
#include "H8_3664.h"

volatile int cnt=0;

main()
{
    initLed();/*LEDの初期化*/
    initTimerAInt(2); /*タイマAによる約30Hz割り込み*/
    E_INT(); /*CPU 割り込み動作許可*/
    startTimerAInt(); /*タイマA起動*/
    while (1) {
        if (cnt<15) {
            turnOnLed(0);
            turnOffLed(1);
        } else {
            turnOnLed(1);
            turnOffLed(0);
        }
    }
}

/*割り込み関数 関数名は変更してはならない*/
void interrupt_cfunc(void)
{
    cnt++;
    if (cnt==30) cnt=0;
}

H8_3664.hで定義された関数を用いてタイマー割り込みを起動し,LEDの点滅制御を行なうプログラム。
点滅動作はPWM駆動されている。

PWMintledtest.c

/*タイマー割り込みを起動し,LEDの点滅制御を行なう*/
/*点滅はPWM制御されている*/
#include "H8_3664.h"

volatile int cnt=0;
volatile int intency;
const int mask[9]={0,1,3,7,0xf,0x1f,0x3f,0x7f,0xff};

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

main()
{
    initLed();
    initTimerAInt(7); /*約7812Hz割り込み*/
    E_INT();
    startTimerAInt();
    while (1) {
        for (intency=0;intency<9;intency++) msecwait(500);
    }
}

/*割り込み関数 関数名は変更してはならない*/
void interrupt_cfunc(void)
{
    cnt++;
    if ((cnt&mask[intency])==mask[intency]) {
        turnOnLed(0);
    } else {
        turnOffLed(0);
    }
    if ((cnt&mask[8-intency])==mask[8-intency]) {
        turnOnLed(1);
    } else {
        turnOffLed(1);
    }
}

 

5.タイマーWユニットを用いて2系統PWM出力生成

タイマーWユニットを用いると3系統のPWM信号出力を行なうことができるが,このプログラムでは2系統のみ出力する。
initTimerWPWM(1000)では,システムクロック1000カウントを1周期とするPWM信号を出力するようにTimerWを初期化する。
setPWMValueB(700)では,FTIOB端子にデューティ比700/1000のPWM信号を出すように設定する。
setPWMValueC(400)では,FTIOC端子にデューティ比400/1000のPWM信号を出すように設定する。

pwmtest.c

/*「TimerW」を用いた2系統PWM出力*/
#include "H8_3664.h"

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

main()
{
    int ch;
    initSCI();
    initTimerWPWM(1000); /*TimerWの2系統PWM出力の初期化*/
    /*約16kHzの周期のPWM信号が発生される16MHz÷1000*/
    while (1) {
        setPWMValueB(1000); /*B系統に1000/1000*/
        setPWMValueC(1000); /*C系統に1000/1000*/
        putStringSCI("PWM 1000/1000\n");
        msecwait(3000);
        setPWMValueB(700); /*B系統に700/1000*/
        setPWMValueC(700); /*C系統に700/1000*/
        putStringSCI("PWM 700/1000\n");
        msecwait(3000);
        setPWMValueB(400); /*B系統に400/1000*/
        setPWMValueC(400); /*C系統に400/1000*/
        putStringSCI("PWM 400/1000\n");
        msecwait(3000);
    }
}

 

6.AD変換

6.1 シングルモードAD変換

シングルモードAD変換を行なう。シングルモードAD変換ではADCの初期化はその都度行なわれるので。プログラム中に初期化関数は存在しない。

ADCsingletest.c

/*シングルモードでADコンバータから信号を得る*/
/*シングルモードでは初期化は不要*/
#include "H8_3664.h"

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

main()
{
    unsigned int v0,v1,v2,v3;
    int i;
    initPCR();
    initSCI();
    putStringSCI("ADC single mode\n");
    while (1) {
        v0=getADCValue(0); /*ADCch0から入力*/
        v1=getADCValue(1); /*ADCch1から入力*/
        v2=getADCValue(2); /*ADCch2から入力*/
        v3=getADCValue(3); /*ADCch3から入力*/
        SCI_printf("ch0,1,2,3=");
        SCI_printf(" %4x",v0);
        SCI_printf(" %4x",v1);
        SCI_printf(" %4x",v2);
        SCI_printf(" %4x",v3);
        SCI_printf("\n");
        msecwait(1000);
    }
}

 

6.2 スキャンモードAD変換

スキャンモードAD変換では。一度どこまでのチャンネルまでを動作させるか初期化で指示すれば,その後は連続変換を続ける。
初期化の後は,変換結果を読み出せば,最新の変換結果が得られる。

ADCscantest.c

/*スキャンモードによるAD変換*/
#include "H8_3664.h"

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

main()
{
    unsigned int v0,v1,v2,v3;
    int i;
    initSCI();
    putStringSCI("ADC scan mode\n");
    initScanADC(3); /*ch0からch3までを連続変換する初期化*/
    while (1) {
        v0=getADCValue0(); /*ch0のAD変換結果の取得*/
        v1=getADCValue1(); /*ch1のAD変換結果の取得*/
        v2=getADCValue2(); /*ch2のAD変換結果の取得*/
        v3=getADCValue3(); /*ch3のAD変換結果の取得*/
        SCI_printf("ch0,1,2,3=%8u %8u %8u %8u\n",v0,v1,v2,v3);
        msecwait(1000);
    }
}

 

7.2 参考 AKI-H8/3664fピン配置

CN1

3664f ピン番号

名称・機能

 

CN2

3664f ピン番号

名称・機能

1

9

Vss(GND)

 

26

9

Vss(GND)

2

9

Vss(GND)

 

25

-

TXD(RS232Cレベル)

3

55

PB4/AN4

 

24

-

RXD(RS232Cレベル)

4

56

PB5/AN5

 

23

54

P17/IOQ3/TRGV

5

57

PB6/AN6

 

22

53

P16/IOQ2

6

58

PB7/AN7

 

21

52

P15/IOQ1

7

59

PB3/AN3

 

20

51

P14/IOQ0

8

60

PB2/AN2

 

19

46

P22/TXD

9

61

PB1/AN1

 

18

45

P21/RXD

10

62

PB0/AN0

 

17

44

P20/SCK3

11

6

Vc1

 

16

43

P87

12

7

RES

 

15

42

P86

13

8

TEST

 

14

41

P85(JP2)

14

13

P50/WKP0

 

13

40

P84/FTIOD

15

14

P51/WKP1

 

12

39

P83/FTIOC

16

19

P52/WKP2

 

11

38

P82/FTIOB

17

20

P53/WKP3

 

10

37

P81/FTIOA

18

21

P54/WKP4

 

9

36

P80/FTCI

19

22

P55/WKP5/ADTRG

 

8

35

NMI(JP3)

20

23

P10/TMOW

 

7

30

P76/TMOV

21

24

P11

 

6

29

P75/TMCIV

22

25

P12

 

5

28

P74/TMRIV

23

12

Vcc

 

4

27

P57/SCL

24

-

PS-IN(電源)

 

3

26

P56/SDA

25

9

Vss(GND)

 

2

9

Vss(GND)

26

9

Vss(GND)

 

1

9

Vss(GND)