AKI-H8のCクロスコンパイラ環境でのプログラミング
シリアルコミュニケーションインタフェイスCH1でリングバッファ+割り込みの実装
(cygwin+GCC)

Copyright(C)26Jan2004
Copyright(C)18Sep2002
coskx TNCT


1.はじめに
この文書は,Windows2000パソコンにcygwin+GCC環境での,シリアルコミュニケーションインタフェイスCH1でリングバッファ+割り込みの実装サンプルを記述しています。

ファイルのダウンロード

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

 

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

2.プログラムの実行
プログラムのコンパイルリンク,H8へのフラッシュメモリ書き込み,実行は次の手順で行なってください。
(1)AKIH8:プログラム受信可にして,電源ONにする。
(2)「SCI1withringbuff_main.c」を「h8gcc.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ソースファイル+ヘッダファイルの形にしてください。

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
void int_rxi1() /*割り込みルーチン この名前はリンカスクリプトで決まっている*/
{
    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
void int_txi1() /*割り込みルーチン この名前はリンカスクリプトで決まっている*/
{
    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
void int_eri1() /*割り込みルーチン この名前はリンカスクリプトで決まっている*/
{
    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);
    }
}

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

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

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

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

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

void putByteHexSCI1_RB(const 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.