home1.gif

next1.gif
back1.gif
 

Serial Communication

 

基礎編
 PORT入出力
 シリアル通信
 タイマー機能
 A/Dコンバータ
 D/Aコンバータ
 外部割り込み
 液晶モニタ

応用編
 アウトプットコンペア
 インプットキャプチャ
 SRAM増設

Tips
 関数集
 シリアルEEPROM
 20桁×4行液晶モニタ
  (SC2004C)
 加速度センサを読む

シリアル通信

 イントロダクション
 シリアル通信と一口に言っても、日常会話からTCP/IPに至るまで世の中シリアル通信がほとんどです。そもそも「シリアル(Serial)」とは「連続した」という意味ですから、信号を連続的に1本の線から送り出せばどんなものでもシリアル通信です。
 ここでは一昔前にパソコンで当たり前のようによく使われ、マイコンとパソコンを繋ぐインターフェースとしてはもっとも扱いやすいRS-232C(厳密な規格としてはEIA-232-Eという)を説明していきます。

 RS-232C通信をするにあたり、H8の相手が居なければ通信できているかどうかも分かりませんので、今回はパソコンと繋いでみましょう。ターミナルソフトはWindows付属のハイパーターミナルでもOKですし、私のお薦めとしては窓の杜などからダウンロードできる「Tera Term」というソフトをお勧めします。

 接続方法
  RS-232C通信をするにはパソコン側にもいくつかの設定をしなければ行けません。ハイパーターミナルにせよ、TeraTermにせよ通信設定という物がありますので、そこで次のように設定してください。

ボーレート

9600bps

データ長

8ビット

パリティ

なし

ストップビット

1ビット

フロー制御

Xon/Xoff(またはソフトウェア制御)

 接続ケーブルはストレートケーブルでもクロスケーブルでもH8の配線さえ対応させればどちらでもいいですが、普通はストレートケーブルを使います。また、H8とケーブルの結線は次のようにします。

AKI-H8

RS-232Cケーブル(D-sub9ピン)

 

CN4-6(TXD1)

CN4-4(RXD1)

 

CN4-2(GND) 

 

 

7(8番ピンとショート)

 

8(7番ピンとショート)

 

 このTXDとRXDを逆にすると通信できないばかりか、双方の機器を壊しかねないので注意してください。

 H8から送信してみる
 H8の視点からは受信よりも送信の方が簡単ですので、こちらから説明します。
 まずは
このファイルをダウンロードしてください。

 今回はポートの時と違い、関数を呼び出す方法をとっていますので、最初にプロトタイプ宣言が入ります。

 

 #include "iodefine.h"

 void main(void);
 void InitSci1();             // 初期化ルーチンのプロトタイプ宣言
 void sendData(char key);     // 送信ルーチンのプロトタイプ宣言

 main()の中はおどろくほどシンプルです。

 void main(void)
 {
     InitSci1();                               // SCI1初期化

     while(1){

         sendData('A');

     }
 }

 まずはシリアルポートを初期化し、その次に「A」という文字を電源が切れるまで送り続けるという内容です。
ここで、InitSci1()とsendData()の2つは自分で作った関数です。

InitSci1()の中身

 void InitSci1()

 {
     unsigned int dmy;
     SCI1.SCR.BYTE = 0;       //シリアルコントロールレジスタ(SCIの送信/受信動作設定)
     SCI1.SMR.BYTE = 0;       //シリアルモードレジスタ(SCIの通信フォーマットの設定)
     SCI1.BRR = 51;           //9600bps
     for(dmy = 280;dmy > 0;dmy--); //Wait 1bit transfer time
     SCI1.SCR.BYTE = 0x30;    //Enable Tx & Rx ,Disable Rx interrupt
     SCI1.SSR.BYTE &= 0x80;   //Clear receive error flag
 }

 

sendData()の中身

 void sendData(char key)
 {
     while(!SCI1.SSR.BIT.TDRE); //トランスミットデータレジスタエンプティが真になるまで待機
     SCI1.TDR = key;            //トランスミットデータレジスタに送信データ(8bit)をセット
     SCI1.SSR.BIT.TDRE = 0;     //「送信中」フラグを立てる
 }

 大半は最初の内は分からなくていいと思います。このままプログラムに組み込み、mainの最初の方で初期化ルーチンを呼び出し、データを送りたくなったときにsendDataルーチンを使えばそれで動きますから。

 触る必要が出てくるのは2カ所で、SCI1.BRRとSCI1.SCRです。それぞれボーレートの設定と受信割り込みの設定です。BRRの方は動作クロックが変われば値が変わってきますが、ここでは16MHz駆動の場合で話を進めています。各種ボーレートと設定値は以下の表のように対応します。

ボーレート

BRRの値

4800

103

9600

51

19200

25

38400

12

 SCRはビット単位で細かく見ていくと次のようになっています。

ビット

機能

SCIのクロックソースを選択します。内部クロックで調歩同期モードで使うには00でOKです。

送信終了割り込みの許可(1)、禁止(0)

マルチプロセッサ割り込み許可(1)、禁止(0)

受信動作の許可(1)、禁止(0)

送信動作の許可(1)、禁止(0)

受信割り込みの許可(1)、禁止(0)

送信データエンプティ割り込みの許可(1)、禁止(0)

 上のサンプルプログラムではSCR = 0x30となっていますので、送受信動作のみ許可しているわけです。

 では、sendData()の使い方の例としてもう一つ紹介しましょう。さきほどポートの入力で作った回路を使って、スイッチ入力の状態を送信します。対応するビットがONの時には「1」、OFFの時には「0」と表現して8ビットデータとします。つまり、スイッチが

 ON OFF ON OFF ON OFF ON OFF

となっている場合には、0xaaとなります。
この場合のプログラムは次のようになります。

 void  main(void)
 {
     InitSci1();                  // SCI1初期化
     P2.DDR = 0x00;               // Port-2は入力設定
     P2.PCR.BYTE = 0xff;          // Port-2をプルアップ

     while(1){

         sendData(P2.DR.BYTE);

     }
 }

 

 H8で受信してみる
 H8での受信は送信ほど簡単ではありません。受信動作には2通りの方法があります。一つはプログラムの任意の場所に受信チェックを入れておき、その場所にプログラムカウンタが来たときのみ受信動作をする方法と、「受信割り込み」を使う方法です。

 前者の場合は受信するとSSRの6ビット目のRDRFが0から1に変わるので、それをチェックすれば受信したかどうかが分かり、次にRDRという8ビットレジスタに受信データが格納されているのでそれを受け取れば受信が完了します。プログラムで書くと次のようになります。受けただけでは受信したかどうか分からないので、送信元に同じ文字を送り返すようになっています。

 void  main(void)
 {
     InitSci1();                     // SCI1初期化

     char RecvDat;

     while(1){

         if(SCI1.SSR.BIT.RDRF){
             SCI1.SSR.BIT.RDRF = 0;  // 受信フラグを解除
             RecvDat = SCI1.RDR;     // 受信データをRecvDatに格納
             sendData(RecvDat);      // 受信データをそのまま送信
         }

     }
 }

 さて、本命は受信割り込みです。上の場合とどう違うかというと、割り込み機能を使うと、プログラムが他の作業をしている最中でもそれを一時中断して受信動作ができるということです。だから、たとえば「"Q"が送られてきたらその場でプログラム停止」というような事ができるわけです。

 割り込み処理をするためにはいくつか乗り越えなければいけない壁があります。まず、先ほどまではメインルーチンを含むファイルだけを触れば良かったのですが、今度は次の4つのファイルを変更する必要があります。

hwsetup.c

ハードウェアの使い方を定義

intprg.c

割り込み処理の中身を記述

main.c

メインルーチンのあるファイル

resetprg.c

リセット(電源投入)時に実行するファイル

 このファイルをダウンロードしてください。必要箇所は変更し、不必要な個所は削除してあります。
 resetprg.cの変更箇所は1文字だけです。

         set_imask_ccr(1); 

 56行目でこのようになっている部分を

         set_imask_ccr(0); 

 このようにします。「1」のままでは起動時に全割り込み処理(NMI割り込みだけは除く)が禁止されていますが、これをクリアすることで割り込みが有効になります。

 hwsetup.cは標準ではすべてコメント分になっていますが、次のようにコメント部分を解除します。

hwsetup.c

    SYSCR.BYTE = 0;
    SYSCR.BIT.SSBY  = 1;
    SYSCR.BIT.STS   = 7;
    SYSCR.BIT.UE    = 1;
    SYSCR.BIT.NMIEG = 1;
    SYSCR.BIT.RAME  = 1;

    INTC.IPRB.BYTE = 0;
    INTC.IPRB.BIT.B2 = 1;  // Set SCI1      Priority Level

 intprg.cは割り込みが掛かったときの処理を記述するファイルです。今回はSCI1の受信割り込みだけを考えますので、次のように書きます。

intprg.c

  #include    <machine.h>
  #include    "vect.h"
  #pragma section IntPRG

  #include "iodefine.h"

  extern int  ComFlag;
  extern char RecvDat;

  void    INT_ERI1(void)           // 受信エラー時の処理
  {
      SCI1.SSR.BYTE &= 0xc7;       // Clear receive error flag
  }

  void    INT_RXI1(void)           // 正常に受信できた場合の処理
  {
          SCI1.SSR.BIT.RDRF = 0 ;  // 受信フラグを解除  
          RecvDat = SCI1.RDR;
          ComFlag = 1;
  }

 他の割り込み要因の部分は使わないので、気分的には消したくなるのですが、消すとコンパイルを通りませんので、そのままにしておいてください。
 この処理では void INT_RX1(void)という部分が受信時の処理です。受信エラーが起こったときのために上の
void INT_ERI1(void)も保険みたいな物ですから書いて置いてください。

 さて、
    extern int  ComFlag;
    extern char RecvDat;
 という文がありますね。この「extern」という単語は使ったことのない人も多いかと思いますが、これはこのファイル(intprg.c)以外のファイルでグローバル宣言されている変数をこのファイルでも使いたいときに宣言します。今回のサンプルではtest04.cに宣言があります。

main.c

  int   ComFlag;
  char RecvDat;

  void main(void)
  {
          InitSci1();

          ComFlag = 0;

          while(1){
              if(ComFlag){
                  sendData(RecvDat);
                  ComFlag = 0;
                }
          }
  }

 main.c(サンプルファイルではtest04.c)は上のような感じになります。結局やっていることは最初に説明した場合と同じように、受信したらその文字を送り返すだけなのですが、処理がだんだん複雑になってくると割り込み処理が有効になってきますので、できるだけこちらの方法に慣れるようにしてください。

 最後に、ドツボにはまりやすいので分けましたが、InitSCI(void)の中身も1文字だけ変更しなければいけません。

void InitSci1(void)
{
    unsigned int dmy;
    SCI1.SCR.BYTE = 0;             //シリアルコントロールレジスタ(SCIの送信/受信動作設定)
    SCI1.SMR.BYTE = 0;             //シリアルモードレジスタ(SCIの通信フォーマットの設定)
    SCI1.BRR = 51;                 //9600bps
    for(dmy = 280;dmy > 0;dmy--);  //Wait 1bit transfer time
    SCI1.SCR.BYTE = 0x70;          //Enable Tx & Rx ,Enable Rx interrupt
    SCI1.SSR.BYTE &= 0x80;         //Clear receive error flag
}

  どこが変わっているか分かりますか?
 今回からは割り込み処理が入るので、SCRの設定で受信割り込みを許可してやらなくてはいけません。だから、SCRの6ビット目の受信割り込みを0から1に変更して、その結果、0x30が0x70になっているのです。

 割り込みを使った場合でも使わなかった場合でも、パソコンから文字を1つ送ったらそれがそのまま返ってきますので、ソフトがちゃんと動いているかどうかは一目瞭然です。一度実感してみてください。

 もし、サンプルファイルでちゃんと動かない場合はハードウェアに問題があるとしか考えられません。一番ありがちなパターンはTXD線とRXD線が逆の場合です。配線をやり直すか、クロスケーブルで試してみてください。あと、D-sub9ピンコネクタの7・8ピンはショートさせましたか? これをしないとちゃんと動きません。

 また、開発環境はHEWですよね? サンプルがHEWで作っているんだから、それ以外のコンパイラが使えるわけありません。秋月のコンパイラなどを使いたいときにはそれ相応に書き換えてください。特に、割り込み関係は秋月のコンパイラでは不可能ではありませんが、かなり厳しいと思います。

 
home1.gif
 next1.gif back1.gif