RX621マイコン(秋月)でシリアル通信する方法(SCI)


RX621マイコンのシリアルコミュニケーションインターフェイス(SCI)を使ってみる

日の丸マイコンと言えばRenesas製マイコン,その中で入手が容易で比較的高性能な秋月のRX621マイコンボードを使用したシリアル通信モジュールの使い方について結構ハマったのでメモ代わりに.

RX621マイコンボードはこちら → http://akizukidenshi.com/catalog/g/gK-05763/
RX621マイコン単体はこちら → http://akizukidenshi.com/catalog/g/gI-04713/

前提条件

・USBシリアル変換モジュールを使ってPC等とシリアル通信する
・通信方式は調歩同期式
・SCI0モジュールを使用(RX621にはSCIモジュールが4基搭載されている)
MSB LSBファースト
・データ長8bit
・パリティなし
・受信データは割込みでリングバッファへ挿入(Arduinoっぽく)
・リングバッファのオーバーフローは考慮しない(この辺うまくやって下さい)
・ルネサスのユーザーズマニュアルハードウェア編を参照します.(r01uh0033jj0140_rx62n/Rev.1.40 2014.07)

いきなりソース載せます

ちょっと長いです.私が使用しているソースからの抜粋なので変数とか転記し忘れているかも.いい感じに補完してください.全ソースはそのうちGitHubにでも上げようかと.

initSCI0(){
    SYSTEM.MSTPCRB.BIT.MSTPB31 = 0;     //SCI0を起こす,デフォルトでは起きているので不要のはず.

    ICU.IPR[0x80].BIT.IPR = 0x05;       //割り込み優先度 ※15が最大なので比較的低め.取りこぼしが怖ければもっと優先度を上げるほうが良いかも.

    ICU.IER[0x1A].BIT.IEN7 = 1;         //受信割り込み
    //ICU.IER[0x1B].BIT.IEN0 = 1;       //送信割り込み要求許可.データ送信時にこれは許可するので今は許可しない.
    //ICU.IER[0x1B].BIT.IEN1 = 1;       //送信完了割り込み要求許可.私は使っていません.シビアなタイミングが欲しければ使えばいいかも.

    SCI0.SCR.BIT.TIE = 0x0;     //念のため一旦ゼロクリア → 本当に要るのか…?
    SCI0.SCR.BIT.RIE = 0x0;
    SCI0.SCR.BIT.TE = 0x0;
    SCI0.SCR.BIT.RE = 0x0;
    SCI0.SCR.BIT.TEIE = 0x0;

    PORT2.ICR.BIT.B1 = 1;       //RxDを入力にするために必要

    SCI0.SCR.BIT.CKE = 0x0;     //内部クロック使用  01->ボーレート出力, 00->ボーレート出力無し

    SCI0.SMR.BYTE = 0;      //いろんな設定,だけど全部ゼロ.
    SCI0.SCMR.BYTE = 0xF2;      //初期値

    SCI0.SEMR.BIT.ABCS = 1;     //8クロックで1ビット
    SCI0.BRR = 11;          //とりあえず250kbps.FT232RLとの組み合わせで問題なく動作

    for (volatile uint16_t i = 0; i < 1000; i++);//適当に待機

    SCI0.SCR.BIT.TIE = 1;       //送信割り込み許可ビット
    SCI0.SCR.BIT.RIE = 1;       //受信割り込み許可ビット
    SCI0.SCR.BIT.TEIE = 1;      //送信完了割り込み許可ビット
    SCI0.SCR.BYTE |= 0x30;      //シリアル送受信許可

    //SCR  |  7  |  6  |  5 |  4 |   3  |   2  |   1  |   0  |
    //SCR  | TIE | RIE | TE | RE | MPIE | TEIE |   CKE[1:0]  |
}


volatile char SendRingBuff[SendRingBuffSize];   //送信リングバッファ本体
volatile uint16_t SendCharNum = 0;      //送信リングバッファに溜まっている未送信文字数
volatile uint16_t SendInsertPosition = 0;   //送信リングバッファの挿入ポイント
volatile uint16_t SendSendPosition = 0;     //送信リングバッファの読み取りポイント
volatile uint8_t SendComp = 0;          //送信完了フラグ


//引数1:送信したい文字列,引数2:送信完了まで待機するかどうかの選択.デフォルト引数にするとよい
void Print(char SendData[], bool IsWaitUntilComplete = true){
    uint8_t count = 0; //文字数カウント(\0まで)
    SendComp = 0;       //送信完了フラグを下げる

    //SendDataの中身をすべてリングバッファへ送る.リングバッファの最大容量を考慮していないので注意
    for (uint16_t i = 0; SendData[i] != '\0' && i <= 65535; i++){
        count++;
        SendRingBuff[(SendInsertPosition++%SendRingBuffSize)] = SendData[i];
        SendCharNum ++;
    }

    //送信文字が何もなかったら何もせずにreturn
    if (count == 0) {return;}

    ICU.IER[0x1B].BIT.IEN0 = 1; //送信割り込み要求許可.リングバッファの中身を実際に送信するのは送信割込み中で行う

    //送信完了するまで待機
    while (IsWaitUntilComplete && !SendComp);
}

// SCI0 受信割込み(正確には受信データフル割込み)
#pragma interrupt(Excep_SCI0_RXI0(vect = 215))
void Excep_SCI0_RXI0(void){
    //受信リングバッファ挿入処理(これも書くと長くなるので宣言・定義含め省略)
}

// SCI0 送信割込み(正確には送信データエンプティ割込み)
#pragma interrupt(Excep_SCI0_TXI0(vect = 216))
void Excep_SCI0_TXI0(void){
    SCI0.TDR = SendRingBuff[(SendSendPosition%SendRingBuffSize)];
    SendSendPosition ++;
    SendCharNum --;

    if(SendCharNum == 0){
        //全データ送信し終わったら
        ICU.IER[0x1B].BIT.IEN0 = 0; //割込み禁止
        SendComp = 1;           //送信完了フラグをONに
    }
}

ハマりやすいポイント

私がはまったポイントをいくつか紹介します.

なんかうまく動かない.送信だけ,あるいは受信だけしか動かない

→ SCR.REビットとSCR.TEビットを同時に1にしないといけない.
よく見るとハードウェアマニュアルの1381ページの表の下(注2)にこのように書かれています.ちょっとわかりづらいですよね.「同時に1にしなければならない」と書いてほしかったです.これで数日潰しました.

送信できるのに受信できない

→ 入力バッファを有効にしなければいけない.
これはSCIに限らずすべての入力を使用する周辺機能使用時には必要な設定です.意外と盲点というか,忘れます.

PORT2.ICR.BIT.B1 = 1;