AKI-H8のCクロスコンパイラ環境でのプログラミング
シリアルコミュニケーションインタフェイスCH1でリングバッファ+割り込みの実装
自分でスタートアップおよび割り込みベクタテーブルを管理する中級者向け
簡単コマンドh8ccBasic.cmd利用版

Copyright(C)26Jan2004
Copyright(C)18Sep2002
coskx TNCT
28May2003 updated


1.はじめに
この文書は,Windows2000パソコンにAKI-H8のC言語クロスコンパイル環境での,シリアルコミュニケーションインタフェイスCH1でリングバッファ+割り込みの実装サンプルを記述しています。C言語クロスコンパイラ環境で,C言語によるH8プログラミングを学ぼうとする方が,自分でスタートアップルーチンおよび割り込みベクタテーブルを管理し,プログラミングを行なう良い例題です。
なお割り込みベクタテーブルの管理はアセンブリ言語ファイルで行ない,割り込みルーチンはpragma命令でC言語で書くことにします。
秋月から購入したCコンパイラ環境では出来ませんが,VER2の環境では割り込みベクタテーブルの管理はCのソースファイルでできるようになっていますが,本解説では触れません。

ファイルのダウンロード

プログラムのダウンロード 
(5.プログラム(テキストデータ用))
DownLoad
バイナリデータ受信テストプログラムのダウンロード 
(6.バイナリデータ受信テストプログラム)
DownLoad

 

本文書で記述してある作業の特徴
Cプログラムソースファイルが出来上がったら,スタートアップルーチン(アセンブリ言語)と必要なら割り込みベクトルテーブルファイル(アセンブリ言語)をまとめてドラッグアンドドロップを1回行なうだけです。他に何もすることはありません。簡単コマンドを用いるので,作業が効率的です。

前提
(1)秋月から購入したCコンパイラ関係ファイルおよびインクルードファイル,ライブラリファイルはすべて「C:\Program Files\h8\C」に入っているものとします。
(2)転送ソフト「n-h8w.exe」を利用します。これも「C:\Program Files\h8\C」に入っているものとします。

h8w.exeはシェアウェアですが10kバイトまではライセンスキー不要で書き込める転送ソフトです。
h8w.exeは,ライセンス登録作業を行なうと,標準でn-h8w.exeに名前が変わり,10kバイトの制限が外れます。この文書では名前が変わった場合の記述をしておりますが,ライセンス取得前の「h8w.exe」でも作業することが出来ます。その場合はh8cc.cmd中の次の個所を変更してください。
変更前 rem フラッシュメモリ書き込みに使用するプログラムを指定します。
 set downloader=n-h8w.exe
変更後 rem フラッシュメモリ書き込みに使用するプログラムを指定します。
 set downloader=h8w.exe
なお,この書き込みソフトは(本校)情報工学科の前任助教授の前田先生が作成したものです。前田先生には大変お世話になっております。前田先生は別の名前をつけて使うように勧めていますが,作者に敬意をはらいまして,あえて標準のままにしておきした。
「h8w.exe」の取得は
http://www.vector.co.jp/soft/win95/prog/se232247.htmlになります。

注意
この文書で扱っているh8cc.cmdはWindows2000,WindowsXP,WindowsVista,Windows7で使用可能です。
残念ながらWindows95/98では動作しません。

2.プログラムの実行
プログラムのコンパイルリンク,H8へのフラッシュメモリ書き込み,実行は次の手順で行なってください。
(1)AKIH8:プログラム受信可にして,電源ONにする。
(2)「SCI1withringbuff_main.c」「IntTbl.src」の3つのファイルをまとめて「h8ccBasic.cmd」上にドラッグ&ドロップする。
(3)プログラムの送信まで無事終了したら,「to_H8.ht」をダブルクリックして「ハイパーターミナル」を立ち上げる。
(4)AKIH8を実行モードで起動する。
(5)最初の文字列が表示された後,ハイパーターミナルからのキーボード受け付け&エコーバックが始まる。
(6)キーボードからスペースが入力されると最初の文字列が表示される。

実行結果(ハイパーターミナル画面)

ABSCI
This is SCI Test with the ring buffer and interrupt.
This is SCI Test with the ring buffer and interrupt.
qqoowwiieemmxxnnzzllzz::112244

  This is SCI Test with the ring buffer and interrupt.

3.シリアル通信における割り込み
シリアル通信のようにCPUの処理速度に対して遅い作業をプログラム中から行なうと,ほとんどが待ち時間になってしまいます。
そこで,送信時には送信したいデータをバッファにためておき,1byte送信が終わるたびに,送信用割り込み関数を起動し,バッファ内にあるデータを1byte送信する作業を,バッファが空になるまで行なわせれば,プログラムの流れは別な作業ができるようになります。
逆に受信時には1byte受信するたびに受信用割り込み関数を起動させ,受け取ったデータをバッファに格納し,後から別な処理がバッファ内を読み出せば,受信中には,プログラムの流れは別な作業を行なうことが出来ます。
このような作業で用いられるバッファはリングバッファといって,バッファの最後と最初を連結したバッファが用いられます。書き込み場所を示す書き込みポインタと読み出し場所を示す読み出しポインタの2つのポインタがあり,読み出しポインタは書き込みポインタを追いかけながら作業を進めます。ただし,リング状の格納域ですから,お互いに追い越さないように作業します。書き込みポインタが読み出しポインタを追い越すようなら,バッファサイズが小さ過ぎることになります。
なおシリアル通信中のエラー発生時にもエラー発生割り込み関数が起動します。

リングバッファを用いたシリアル通信受信に関して

(1)リングバッファ
リングバッファといえども,バッファはただのメモリの領域(C言語からはunsigned char の配列に見える)です。このバッファに関連したポインタが2つあります。1つは現在バッファ中のどこに書き込んでいるかを示すポインタ[書き込みポインタ](本物のポインタ変数を用いるか,配列要素番号を格納したint型変数を用いる2つの方法があります。実用では本物のポインタ変数を使うのですが,学習のためならint型変数にポインタの役割を負わせ,配列要素番号で指すことにするのがよいでしょう)もう1つは現在バッファ中のどこを読み出しているのかを示すポインタ[読み出しポインタ]です。
正確に言うと[書き込みポインタ]は,次のデータを格納する場所を指しており,[読み出しポインタ]は次にデータを取り出す場所を指しています。
そして,初期状態では,2つのポインタは配列の先頭を指しています。

(2)受信割り込み関数
受信バッファフルを割り込み要因とした受信割り込み関数は次のような動作をします。(ただし,割り込み作業に関すること(レジスタの退避など)にはここでは触れません)
1)シリアル通信機能から受信データをもらい,[書き込みポインタ]の場所に格納します。
2)[書き込みポインタ]を1増加させ,次の受信に備えます。ただし,あらかじめ用意されたバッファ領域から外れた場合は先頭に戻し,次の受信に備えます。
3)シリアル通信機能に対し,受信データ貰い受け後に行なうべき処理をします。

(3)1文字受信関数
ユーザプログラムから呼び出される,1文字受信関数は次のようになっています。
ユーザプログラムにどのように受信データを渡し,受信データがない時にどうするかはいろいろ方法があるところですが,ANSIのgetchar()を真似すると次のような関数仕様になります。すなわち,受信データが存在するときは受信データを返す。このデータの値の範囲は0〜0xff(0〜255)です。受信データがないときは0xffff(-1)を返すようにします。
実際の作業は
1)[書き込みポインタ]と[読み出しポインタ]が同じ値の時は,受信データが存在しないと考えて-1を返すため,[返す値変数]に-1を格納します。。
2)そうでない時は[読み出しポインタ]の位置にあるデータを返すことにして,[返す値変数]にこのデータを保存します。そして[読み出しポインタ]を1増加させ,次の読み出しに備えます。ただし,あらかじめ用意されたバッファ領域から外れた場合は先頭に戻し,次の読み出しに備えます。
3)[返す値変数]を呼び出し側に返しながらこの関数は終ります。

(4)受信バッファオーバーフロー
[読み出しポインタ]は[書き込みポインタ]を追い越すことはないのですが,ユーザプログラムが他の仕事で忙しくてなかなか1文字受信関数を呼び出して受信データを読み出してくれない場合には,[書き込みポインタ]が[読み出しポインタ]を追い越してしまう現象が起こります。(あるいはプログラムを追い越し禁止にしておいて,オーバーフローエラーフラッグを立てることもあります。)
このような場合には受信バッファサイズを大きくしてあげることが必要になります。または,追い越しそうになったらシリアル通信のフロー制御で,相手に待ってもらうことも考えられます。

4.割り込みベクタテーブル
CPUマニュアルによれば,SCI1関係の割り込みベクタテーブルは次のようになっています。「IntTbl.src」の内容で確認してください。

割り込み
ベクタ番号
割り込み
ベクタアドレス
割り込み
要因の名前
割り込み要因

56

H'00e0

ERI1

SCI1でオーバーランエラー,パリティエラー,フレーミングエラ−発生

57

H'00e4

RXI1

受信バッファフル(受信バッファにデータを受信した)

58

H'00e8

TXI1

送信バッファエンプティ(送信バッファから送信を完了したので次のデータを要求)

59

H'00ec

TEI1

送信完了(このプログラムでは使用していない)

5.プログラム(テキストデータ用)
プログラムはヘッダファイルで提供されています。実用時にはCソースファイル+ヘッダファイルの形にしてください。
「StartupBasic.src」は他と共通です。

SCI1withringbuff.h

/*****************************************************************
    Serial Communication Interface Channel 1 functions
          with ring buffers and interrupt functions
            17Sep2002 TNCT CS KOSAKA
            28May2003 updated

<1>initializing
 void initSCI1_RB(void)
<2>get char from SCI1 ring buffer   -1:failure
 int getcharSCI1_RB(void)
<3>put char to SCI1 ring buffer
 void putcharSCI1_RB(char ch)

Interrupt Vectors
----------------------------------------------------
Vector   | Vector    | Interrupt   | Interrupt
Number   | Address   | Name        | Function Name
---------|-----------|-------------|----------------
56       | 0xe0      | ERI1        | SCI1_ERI_INT
57       | 0xe4      | RXI1        | SCI1_RXI_INT
58       | 0xe8      | TXI1        | SCI1_TXI_INT
59       | 0xec      | TEI1        | not used
----------------------------------------------------
*****************************************************************/
/*#include<3048f.h>*/

extern void E_INT(); /*defined in "StartupBasic.src"*/
extern void D_INT(); /*defined in "StartupBasic.src"*/

#define RECEIVERINGBUFFSIZE 256
#define SENDRINGBUFFSIZE 16

static volatile int receiveptr_in;  /*input pointer for receive*/
static volatile int receiveptr_out; /*output pointer for receive*/
static unsigned char receiveringbuff[RECEIVERINGBUFFSIZE];
static volatile int sendptr_in;  /*input pointer for receive*/
static volatile int sendptr_out; /*output pointer for receive*/
static unsigned char sendringbuff[SENDRINGBUFFSIZE];
static volatile int busyFlag;/*sending char busy*/

/* ---------------------------------- */
/* initialize Ring Buffer             */
/* ---------------------------------- */
static void initRingBuff(void)
{
    busyFlag=0;
    receiveptr_in=receiveptr_out=0;
    sendptr_in=sendptr_out=0;
}

/* ------------------------------------------------------------- */
/* SCI1 INITIALIZATION fixed baud at 38400 T_INT R_INT:enable    */
/* ------------------------------------------------------------- */
void initSCI1_RB(void)
{
    short int i;
    SCI1.SCR.BYTE = 0x00;       /* 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 = 12;          /* 38400baud (CPU=16MHz) */
    for(i=0;i<1000;i++);      /* wait more than 1bit time */
    initRingBuff();
    SCI1.SCR.BYTE |= 0xf0;    /* scr = 1111 0000 (TIE=1,RIE=1,TE=1,RE=1) */
    return;
}

/* ------------------------------------- */
/* getchar from SCI1 Ring Buffer         */
/* ------------------------------------- */
int getcharSCI1_RB(void)
{
    int ch;
    if (receiveptr_out!=receiveptr_in) {
        ch=receiveringbuff[receiveptr_out];
        receiveptr_out++;
        if (receiveptr_out==RECEIVERINGBUFFSIZE) receiveptr_out=0;
    } else {
        ch=-1;
    }
    return ch;
}

/* ------------------------------------- */
/* Receive data full interrupt function  */
/* ------------------------------------- */
#pragma interrupt(SCI1_RXI_INT)
void SCI1_RXI_INT(void)
{
    receiveringbuff[receiveptr_in]=SCI1.RDR;
    SCI1.SSR.BIT.RDRF = 0;
    receiveptr_in++;
    if (receiveptr_in==RECEIVERINGBUFFSIZE) receiveptr_in=0;
}

/* ---------------------------------------- */
/* putchar to SCI1 Ring Buffer              */
/* ---------------------------------------- */
void putcharSCI1_RB0(char ch)
{
    int status=-1;
    int send=(sendptr_in==sendptr_out);
    int check=sendptr_in-sendptr_out;
    if (0<check) check-=SENDRINGBUFFSIZE;
    while (check==-1) {
        check=sendptr_in-sendptr_out;
        if (0<check) check-=SENDRINGBUFFSIZE;
    }
    sendringbuff[sendptr_in]=ch;
    sendptr_in++;
    if (sendptr_in==SENDRINGBUFFSIZE) sendptr_in=0;
    if (busyFlag==0) {
        if (send) {
            busyFlag=1;
            if (SCI1.SSR.BIT.TDRE==1) {
                SCI1.TDR = ch;
                SCI1.SSR.BIT.TDRE = 0;
                SCI1.SCR.BIT.TIE=1;
            }
        }
    }
}
void putcharSCI1_RB(char ch)
{
    if (ch=='\n') putcharSCI1_RB0('\r');
    putcharSCI1_RB0(ch);
}

/* ------------------------------------- */
/* Send data empty interrupt function   */
/* ------------------------------------- */
#pragma interrupt(SCI1_TXI_INT)
void SCI1_TXI_INT(void)
{
    int tdre;
    if (sendptr_out!=sendptr_in) sendptr_out++;
    if (sendptr_out==SENDRINGBUFFSIZE) sendptr_out=0;
    if (sendptr_out!=sendptr_in) {
        while (SCI1.SSR.BIT.TDRE==0);
        SCI1.TDR = sendringbuff[sendptr_out];
        SCI1.SSR.BIT.TDRE = 0;
        SCI1.SCR.BIT.TIE=1;
    } else {
        SCI1.SCR.BIT.TIE=0;
        busyFlag=0;
    }
}

/* ------------------------------------- */
/* SCI error interrupt function          */
/* ------------------------------------- */
#pragma interrupt(SCI1_ERI_INT)
void SCI1_ERI_INT(void)
{
    SCI1.SSR.BIT.ORER = 0; /* Overrun Error Flag Clear */
    SCI1.SSR.BIT.FER = 0;  /* Framing Error Flag Clear */
    SCI1.SSR.BIT.PER = 0;  /* Parity  Error Flag Clear */
}

SCI1withringbuff_main.c

#include <3048f.h>
#include "SCI1withringbuff.h"

void putsSCI1_RB(char *str)
{
    while (*str) putcharSCI1_RB(*str++);
}

main()
{
    int ch;
    char str[]="This is SCI Test with the ring buffer and interrupt.\n";
    initSCI1_RB();
    E_INT();
    putcharSCI1_RB('A');
    putcharSCI1_RB('B');
    putsSCI1_RB("SCI\n");
    putsSCI1_RB(str);
    putsSCI1_RB(str);
    while (1) {
        while ((ch=getcharSCI1_RB())<0);
        putcharSCI1_RB(ch);
        if (ch==' ') putsSCI1_RB(str);
    }
}

IntTbl.src

;************************************************************************
;interrupt vector table    IntTbl.src    Copyright (C) 2002 coskx
; 28 Aug 2002
; 割り込みを使用する場合はここに割り込みベクトルを記述すること
;************************************************************************
    .CPU 300HA
    .IMPORT _SCI1_ERI_INT,_SCI1_RXI_INT,_SCI1_TXI_INT
   
    .SECTION A,DATA,LOCATE=H'000000

    .ORG H'0000e0  ;ERI1
    .DATA.L _SCI1_ERI_INT
    .ORG H'0000e4  ;RXI1
    .DATA.L _SCI1_RXI_INT
    .ORG H'0000e8  ;TXI1
    .DATA.L _SCI1_TXI_INT
    .END

 

6.バイナリデータ受信テストプログラム
「5.」のプログラム中,関数main()のファイルのみ書き換えてテストします。

SCI1withringbuff_main.c バイナリデータ受信テスト

#include <3048f.h>
#include "SCI1withringbuff.h"

void putsSCI1_RB(char *str)
{
    while (*str) putcharSCI1_RB(*str++);
}

static const char HEXChar[]="0123456789ABCDEF";

void putByteHexSCI1_RB(unsigned char ch)
{
    char hex[4]=" 00";
    hex[1]=HEXChar[0xf&(ch>>4)];
    hex[2]=HEXChar[ch&0xf];
    putsSCI1_RB(hex);
}

void bintest(void)
{
    int ch;
    static const char str1[]="\n\n\nThis is SCI BIN receiving Test.\n"
    "Send a bin file.\nIf five '@'s are received, the test will quit.\n\n\n";
    static const char str2[]="\n\nSCI BIN receiving Test terminated.\n";
    int counter=0;
    int atcounter=0;
    putsSCI1_RB(str1);
    while (atcounter!=5) {
        while ((ch=getcharSCI1_RB())<0);
        putByteHexSCI1_RB(ch);
        counter++;
        if (counter%16==0) putsSCI1_RB("\n");
        if (ch=='@') atcounter++;
        else atcounter=0;
    }
    putsSCI1_RB(str2);
}

main()
{
    int ch;
    char str[]="This is SCI Test with the ring buffer and interrupt.\n";
    initSCI1_RB();
    E_INT();
    putcharSCI1_RB('A');
    putcharSCI1_RB('B');
    putsSCI1_RB("SCI\n");
    putsSCI1_RB(str);
    putsSCI1_RB(str);
    while (1) {
        while ((ch=getcharSCI1_RB())<0);
        putcharSCI1_RB(ch);
        if (ch==' ') putsSCI1_RB(str);
        if (ch=='@') bintest();
    }
}

テストの結果
テキストデータ送信テストの後,「@」を送信することでバイナリ受信テストモードになるため,ホストPC側が00-FFのデータを送信すると,H8側がhex形式で返信してきたところ。最後に「@」を5回送信してテストから抜け出している。

このテストはteratermにて行なわれ,ホストPCの1バイト送信に対して,H8は3バイト返信するため,H8の受信バッファがあふれてしまう。
そこでteraterm側で1バイト送信するごとにwaitを入れてテストしている。

バイナリデータ受信テストの結果

@

This is SCI BIN receiving Test.
Send a bin file.
If five '@'s are received, the test will quit.

 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
 C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
 D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
 40 40 40 40 40

SCI BIN receiving Test terminated.