今回はRL78マイコンでI2C通信をしてみました。
I2Cはマスターとスレーブがありますが、マスターはマイコン、スレーブはADコンバータや温度センサーなどのICであることが多いと思いますので今回はマスター側のみの説明になります。
初期設定はRL78マイコン初期設定を参照してください。
下記が開発環境になります。
開発環境 | CS+ for CC V8.02.00 |
デバッガ | E1 |
ボード | HSBRL78F15-64(北斗電子) |
マイコン | R5F113LLLFB |
コード生成
シリアルの設定
画面左のプロジェクトツリーの「コード生成(設計ツール)」>「シリアル」をダブルクリックすると画面右に「コード生成」タブが表示されるので、「IICA0」>「転送モード」タブでシングルマスタを選択する。
次に「設定」タブを開き、転送速度などを設定します。
I2Cの通信速度は100kbps、400kbps、1Mbpsがよく使われます。今回は400kbpsを設定します。
以上で設定完了。「コード生成」ボタンを押す。
コーディング
コード生成すると「r_cg_serial.c」「r_cg_serial_user.c」「r_cg_serial.h」の3個のファイルが自動生成されます。「r_cg_serial_user.c」にマスターエラー発生時、マスター受信完了時、マスター送信完了時のコールバック関数が生成されているので、必要な場合はコールバック関数内に処理をコーディングする。
I2Cマスター送信
マスター送信でICの各種設定、DAコンバーターの出力設定等を行います。
下記がマスター送信関数です。
MD_STATUS R_IICA0_Master_Send(uint8_t adr, uint8_t * const tx_buf, uint16_t tx_num, uint8_t wait)
第1引数の「adr」はスレーブアドレス
第2引数の「tx_buf」は送信データ格納先のアドレス(グローバル変数のアドレスを指定)
第3引数の「tx_num」は送信データバイト数
第4引数の「wait」はスタートコンディション~1ビット目送信までのウェイトです。
下記サンプルソースでは1秒間隔でアドレス0x48のスレーブへ2バイトのデータを送信します。
スレーブアドレスが0x90になっていますが、これは0x48を1ビット左にシフトしています。
なぜかというとI2cではスレーブアドレスは7ビットとなっており8ビット目はR/Wビットでマスターが送信するのか受信するのかをスレーブに伝えるビットになります。
送信の場合R/Wビットを0になります。
/*********************************************************************
Includes
**********************************************************************/
#include "r_cg_macrodriver.h"
#include "r_cg_cgc.h"
#include "r_cg_port.h"
#include "r_cg_serial.h"
#include "r_cg_timer.h"
#include "r_cg_wdt.h"
/* Start user code for include. Do not edit comment generated here */
/* End user code. Do not edit comment generated here */
#include "r_cg_userdefine.h"
/*********************************************************************
Pragma directive
**********************************************************************/
/* Start user code for pragma. Do not edit comment generated here */
/* End user code. Do not edit comment generated here */
/*********************************************************************
Global variables and functions
**********************************************************************/
/* Start user code for global. Do not edit comment generated here */
// 変数
uint8_t gTxBuf[2];
uint32_t gMsCounter = 0;
/* End user code. Do not edit comment generated here */
void R_MAIN_UserInit(void);
/*********************************************************************
* Function Name: main
* Description : This function implements main function.
* Arguments : None
* Return Value : None
**********************************************************************/
void main(void)
{
R_MAIN_UserInit();
/* Start user code. Do not edit comment generated here */
while (1U)
{
if(1000 <= gMsCounter){
gMsCounter = 0;
// I2C送信
gTxBuf[0] = 0x11;
gTxBuf[1] = 0x22;
R_IICA0_Master_Send(0x90, gTxBuf, 2, 16);
}
}
/* End user code. Do not edit comment generated here */
}
/*********************************************************************
* Function Name: R_MAIN_UserInit
* Description : This function adds user code before implementing main function.
* Arguments : None
* Return Value : None
**********************************************************************/
void R_MAIN_UserInit(void)
{
/* Start user code. Do not edit comment generated here */
R_TAU0_Channel0_Start(); // タイマー開始
EI(); // 割込み許可
/* End user code. Do not edit comment generated here */
}
I2Cマスター受信
マスター受信でADコンバーター、各種センサ等の値を読み込みます。
下記がマスター受信関数です。
MD_STATUS R_IICA0_Master_Receive(uint8_t adr, uint8_t * const rx_buf, uint16_t rx_num, uint8_t wait)
第1引数の「adr」はスレーブアドレス
第2引数の「rx_buf」は受信データ格納先のアドレス(グローバル変数のアドレスを指定)
第3引数の「rx_num」は受信データバイト数
第4引数の「wait」はスタートコンディション~1ビット目送信までのウェイトです。
下記サンプルソースでは1秒間隔でアドレス0x48のスレーブから2バイトのデータを受信します。
スレーブアドレスが0x90になっていますが、これは0x48を1ビット左にシフトしています。
なぜかというとI2cではスレーブアドレスは7ビットとなっており8ビット目はR/Wビットでマスターが送信するのか受信するのかをスレーブに伝えるビットになります。
受信の場合R/Wビットを1にする必要がありますが、R_IICA0_Master_Receive関数内で8ビット目を1にする処理がはいっているため、関数をコールする際は必要ない。
/********************************************************************
Includes
*********************************************************************/
#include "r_cg_macrodriver.h"
#include "r_cg_cgc.h"
#include "r_cg_port.h"
#include "r_cg_serial.h"
#include "r_cg_timer.h"
#include "r_cg_wdt.h"
/* Start user code for include. Do not edit comment generated here */
/* End user code. Do not edit comment generated here */
#include "r_cg_userdefine.h"
/********************************************************************
Pragma directive
*********************************************************************/
/* Start user code for pragma. Do not edit comment generated here */
/* End user code. Do not edit comment generated here */
/********************************************************************
Global variables and functions
*********************************************************************/
/* Start user code for global. Do not edit comment generated here */
// 変数
uint8_t gRxBuf[2];
uint32_t gMsCounter = 0;
/* End user code. Do not edit comment generated here */
void R_MAIN_UserInit(void);
/********************************************************************
* Function Name: main
* Description : This function implements main function.
* Arguments : None
* Return Value : None
*********************************************************************/
void main(void)
{
R_MAIN_UserInit();
/* Start user code. Do not edit comment generated here */
while (1U)
{
if(1000 <= gMsCounter){
gMsCounter = 0;
// I2C受信
R_IICA0_Master_Receive(0x90, gRxBuf, 2, 16);
}
}
/* End user code. Do not edit comment generated here */
}
/********************************************************************
* Function Name: R_MAIN_UserInit
* Description : This function adds user code before implementing main function.
* Arguments : None
* Return Value : None
*********************************************************************/
void R_MAIN_UserInit(void)
{
/* Start user code. Do not edit comment generated here */
R_TAU0_Channel0_Start(); // タイマー開始
EI(); // 割込み許可
/* End user code. Do not edit comment generated here */
}
まとめ
今回マスター送信関数とマスター受信関数を説明しました。
E2PROMなどの読込みは、まずメモリアドレスを送信して、その後メモリの内容を受信するので、送信→受信を連続で行います。過去に自動生成された関数では対応できず、それを改造して対応したことがあります。
I2Cに限らず関数が自動生成されることはかなり便利ですが、関数の内容をある程度解析して理解した上で使用した方が良いですね。
関数の内容を把握することで様々な要求仕様に対応可能なオリジナルの関数が作成できます。