RL78マイコンでI2C通信1

# RL78マイコンでI2C通信1

## 1. はじめに

RL78マイコンについてCS+の自動生成機能でI2C通信を行う手順(本稿)と、
少しイレギュラーな使い方を試してみた結果の記録(次回)を残します。

## 2. CS+のプロジェクトを立ち上げる

まずはCS+のプロジェクトを立ち上げましょう。
CS+を起動し[ファイル] > [新規作成] > [新しいプロジェクトを作成]を選択します。
![fig_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604076/136063db-b55b-3748-d99e-6968c197e945.png)

↓

マイコン、プロジェクト名、作成場所等を入力して作成をクリックします。
![fig_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604076/764caf81-e288-2cf9-3871-8474f068fcf9.png)

↓

プロジェクトが作成されたらまずはプロジェクトツリーの[コード生成(設計ツール)] > [クロック発生回路]をダブルクリックして[コード生成]のタブを開きましょう。
コードの自動生成機能を使う場合、端子割り当て設定を確定させる必要がありますので任意の設定を選択して[確定する]をクリックします。
![fig_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604076/689b4eba-6828-6381-9ba0-788b4d4e79ca.png)

※画面にも表示されている通り、端子割り当て設定は一度行うと変更できない為、この設計を変更したい場合はプロジェクトを作成しなおす必要があります。

↓

端子割り当て設定が確定したらプロジェクトツリーの[コード生成(設計ツール)] > [シリアル]をダブルクリックして[コード生成]のタブをシリアルのページに移します。
[IICA0]タブを開いてシングルマスタ(またはスレーブ)を選択します。
![fig_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604076/239515b9-64f3-8e2d-21e8-c16136239c2f.png)

↓

設定タブより以下の画像にあるような設定を選択できます。
![fig_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604076/40535a4c-2fec-4e45-9101-2d70399dbcac.png)


↓

設定し終えたら[コード生成]をクリックしてコードを自動生成します。
![fig_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604076/c7f3a0e5-e3b7-683d-5aef-6df3b055bc6e.png)

↓

下記のようなコードが自動生成されます。
I2CAに関連するファイルはr_cg_serial.c/hとr_cg_serial_user.cとなります。
![fig_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604076/2e0d1027-7e85-cb33-9271-6b54dd3caf44.png)

↓

以上で立上げ完了です。
その他の機能の生成やデバッグ設定等については割愛します。

## 3. ソースコードについて

ソースコードについて簡単に説明します。
基本的にはr_cg_serial.cで設定(送受信開始等)を行い、r_cg_serial_user.cで応答(送受信結果コールバック等)を受け取るような作りとなっているようです。

### 3-1. シングルマスタを選択した場合

| 関数名                                                | 説明  |
|-------------------------------------------------------|-------|
| void R_IICA0_Create(void)                             | I2CAを利用するのに必要なレジスタの設定を行います。起動時にコールされます。    |
| void R_IICA0_Stop(void)                               | I2CA機能を停止させます。  |
| void R_IICA0_StopCondition(void)                      | ストップコンディションを任意のタイミングで発生させます。  |
| MD_STATUS R_IICA0_Master_Send(uint8_t adr, uint8_t * const tx_buf, uint16_t tx_num, uint8_t wait)     | I2C送信処理を開始します。adrに送信先のスレーブアドレスを設定し、以降の送信データとそのサイズをtx_buf、tx_numに設定します。    |
| MD_STATUS R_IICA0_Master_Receive(uint8_t adr, uint8_t * const rx_buf, uint16_t rx_num, uint8_t wait)  | I2C受信処理を開始します。adrに送信先のスレーブアドレスを設定し、受信データの格納先とそのサイズをrx_buf、rx_numに設定します。  |
| static void r_iica0_callback_master_error(MD_STATUS flag)                                             | I2C通信に異常があった場合にコールされます。   |
| static void r_iica0_callback_master_receiveend(void)  | データの受信が完了したときにコールされます。 |
| static void r_iica0_callback_master_sendend(void)     | データの送信が完了したときにコールされます。 |

### 3-2. スレーブを選択した場合

| 関数名                                                | 説明  |
|-------------------------------------------------------|-------|
| void R_IICA0_Create(void)                             | I2CAを利用するのに必要なレジスタの設定を行います。起動時にコールされます。    |
| void R_IICA0_Stop(void)                               | I2CA機能を停止させます。  |
| void R_IICA0_Slave_Send(uint8_t * const tx_buf, uint16_t tx_num)      | マスタに送信するデータを設定します。 |
| void R_IICA0_Slave_Receive(uint8_t * const rx_buf, uint16_t rx_num)   | マスタから受信するデータの格納先及びサイズを設定します。  |
| static void r_iica0_callback_slave_error(MD_STATUS flag)              | I2C通信に異常があった場合にコールされます。   |
| static void r_iica0_callback_slave_receiveend(void)   | データの受信が完了したときにコールされます。 |
| static void r_iica0_callback_slave_sendend(void)      | データの送信が完了したときにコールされます。 |

## 4. その他

自動生成で作成したコードに変更を加える場合は極力下記のコメント内で収まるようにしましょう。
下記のコメント内であれば再度自動生成を行ってもコードが引き継がれます。

```C
    /* Start user code. Do not edit comment generated here */
    //ここにコードを記述
    /* End user code. Do not edit comment generated here */
```

## 5. サンプルコード

実装のサンプルとして[FT232HでI2C通信](https://qiita.com/ykwmt/items/a6f88a98a0429387e3cd)で作成したコードを紹介します。
※異常発生時に関する処理は割愛します。

### 5-1. 要求動作

| スレーブアドレス | 挙動 |
|:--------------:|:----:|
| 0x10           | 0x12,0x34を受信すると、0x56,0x78を送信する。  |

### 5-2. コード生成の設定

| 項目                      | 設定      |
|---------------------------|-----------|
| 転送モード                | スレーブ  |
| カウント・クロック設定    | fCLK/2    |
| 自局アドレス設定          | 16        |
| 動作モード設定            | 標準      |
| ウェイクアップ機能設定    | オフ      |
| 割込み設定                | INTIICA優先度:低 |
| コールバック機能設定      | スレーブ送信完了:有 スレーブ受信完了:有 スレーブ・エラー:有 |

### 5-3. サンプルコード

```C:i2c.h
#ifndef I2C_H
#define I2C_H

void i2c_main(void);
void i2c_setI2cEnd(void);

#endif
```

```C:i2c.c
/***********************************************************************************************************************
Includes
***********************************************************************************************************************/
#include <string.h>
#include "r_cg_macrodriver.h"
#include "i2c.h"
#include "r_cg_serial.h"

/***********************************************************************************************************************
definitions
***********************************************************************************************************************/
typedef enum {
    E_I2C_STS_START = 0,
    E_I2C_STS_SET_RECV = E_I2C_STS_START,   // 受信開始
    E_I2C_STS_WAIT_RECV,                    // 受信待ち
    E_I2C_STS_SET_SEND,                     // 応答送信
    E_I2C_STS_WAIT_SEND,                    // 送信完了待ち
} E_I2C_STS;

#define D_COM_DATA_SIZE     (2)

/***********************************************************************************************************************
Global variables and functions
***********************************************************************************************************************/
static E_I2C_STS    g_i2cSts = E_I2C_STS_START;
static uint8_t      g_i2cEndFlg = 0;
static uint8_t      g_recvBuff[D_COM_DATA_SIZE] = {0};
static uint8_t      g_recvChk[D_COM_DATA_SIZE] = {0x12, 0x34};
static uint8_t      g_sendBuff[D_COM_DATA_SIZE] = {0x56, 0x78};

/***********************************************************************************************************************
* Function Name: i2c_main
* Description  : 本関数をmain関数から周期的にコールすること。
* Arguments    : None
* Return Value : None
***********************************************************************************************************************/
void i2c_main(void)
{
    switch (g_i2cSts) {
    case E_I2C_STS_SET_RECV:    // 受信開始
        R_IICA0_Slave_Receive(g_recvBuff, D_COM_DATA_SIZE);
        g_i2cSts = E_I2C_STS_WAIT_RECV;
        break;
    case E_I2C_STS_WAIT_RECV:   // 受信待ち
        if (g_i2cEndFlg == 1) {
            g_i2cEndFlg = 0;
            if (memcmp(g_recvBuff, g_recvChk, D_COM_DATA_SIZE) == 0) {  // 受信データが0x12,0x34ならば0x56,0x78を応答送信
                g_i2cSts = E_I2C_STS_SET_SEND;
            } else {
                g_i2cSts = E_I2C_STS_SET_RECV;
            }
        }
        break;
    case E_I2C_STS_SET_SEND:    // 応答送信
        R_IICA0_Slave_Send(g_sendBuff, D_COM_DATA_SIZE);
        g_i2cSts = E_I2C_STS_WAIT_SEND;
        break;
    case E_I2C_STS_WAIT_SEND:   // 送信完了待ち
        if (g_i2cEndFlg == 1) {
            g_i2cEndFlg = 0;
            g_i2cSts = E_I2C_STS_SET_RECV;
        }
        break;
    default:
        break;
    }
    return;
}
/***********************************************************************************************************************
* Function Name: i2c_setI2cEnd
* Description  : 本関数をr_iica0_callback_slave_receiveend関数、r_iica0_callback_slave_sendend関数からコールすること。
* Arguments    : None
* Return Value : None
***********************************************************************************************************************/
void i2c_setI2cEnd(void)
{
    g_i2cEndFlg = 1;
    return;
}
```

## 6. まとめ
コードの自動生成機能を利用すればレジスタの設定等の下回りをそこまで気にせずに実装可能で、
自前でその辺りを実装するより断然早く組み上げることができて便利です。
次回はマスタとスレーブ両方の挙動を行うI2Cの処理を試験的に実装したのでそれを紹介しようと思います。