RX72N Envision Kit のLightセンサ ISL29034 を制御する

今回の目的
---
RX72N Envision KitにはLightセンサとして、旧Intersil社の ISL29034がオンボードで搭載されています。

下の写真のU20がそれです。

![2020-04-27 11-44-33.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624954/12f5534f-9dc4-1f91-8992-b14117e6def0.jpeg)

下記のデータシートに電気的特性やデータ制御の方式が記載されています。制御センサで取得したデータを16bitADCで変換し、I2Cで取り込むことができます。

[データシート(link)](https://www.renesas.com/jp/ja/doc/datasheet/isl29034.pdf)

RX72NでI2Cとしても使える端子は最大3系統ありますが、Envision KItではSDHI等ですべて使われています。
ISL29034はどこに接続されているかというとPB6,PB7というSCI用に割りあてられた端子で、SCIを簡易I2Cモードというモードに入れてI2C通信制御を行うことになります。

ISL29034は検索してもルネサス提供のサンプルコードが見当たらず、簡易I2Cと組み合わせた場合は更にArduinoライクな制御ができなくなると考えられるため、勉強も兼ねて制御方法を確認しました。

簡易I2C機能をプログラムで実装する
---
RX72Nの簡易I2C機能はユーザハードウェアマニュアルのSCIの章41.7に説明があり、フローチャートの通りに設定すれば通信できることが確認できました。

端子設定は下記としました。
```
int i2c_init(void) {

	int	i;

	//Port Settings
	MPC.PWPR.BYTE = 0x00;             //B0WI=0
	MPC.PWPR.BYTE = 0x40;             //PFS Write Enable

	PORTB.PMR.BIT.B6 = 1;				//PB6:peripheral
	PORTB.PMR.BIT.B7 = 1;				//PB7:peripheral

	PORTB.ODR1.BIT.B4 = 1;				//PB6:NchOpenDrain (ODR1.B4!)
	PORTB.ODR1.BIT.B6 = 1;				//PB7:NchOpenDrain (ODR1.B6!)

	MPC.PB6PFS.BYTE = 0x24;           //PB6:SSCL11
	MPC.PB7PFS.BYTE = 0x24;           //PB7:SSDA11

	//for I2C
	SYSTEM.MSTPCRC.BIT.MSTPC24 = 0;		//SCI11(I2C)
...
```
Kitの回路の通りPB6,7を簡易I2C向けに設定します。

初期設定と送受信は下記サイトが大変参考になりました。
[Fグループ電子工作講座](http://project12513.blog-fps.com/rxソフトウェア開発/rx220で簡易i2c%EF%BC%88送信編%EF%BC%89)

初期設定はSCI9の部分をKitに合わせてSCI11に変えるのみです。ボーレートの設定(BBR)もPCLKCを32MHz設定にすればこのまま使えます。

送受信はISL29034に合わせて手順を修正する必要がありますが、待たせ方やフラグクリアの手順はRX72Nのユーザマニュアルの記述と変わらないため基本は踏襲できます。

ISL29034を簡易I2Cで制御する
---
最初は検索で出てきた下記サイトのコードを参考にさせて頂こうとコードを組み込んでみたのですが、e2studioではうまく適用できませんでした。

http://docs.fabo.io/fabo/arduino/brick_i2c/217_brick_i2c_ambientlight.html

* class型での構造体宣言やbool型がヘッダファイルに含まれるとエラーになる

コンパイラにGCC(C++)を選んでも解決しませんでした。
e2studioは拡張子でCかC++か判別しているとのことで、実体(.cpp)の拡張子を.cppにしていればC++の構解析がされエラーにはならなかったのですが、ヘッダでエラーになると他から参照できなくなります。
結局、データシートを見ながらアドレスやコマンドを自分で入れていくことにしました。

**送信**

```send_i2c

    //Send Device Address
    SCI11.TDR = 0x88;					//Device Address[7:1]:44x,Read(1)/Write(0):0b
for(i=0;i<100;i++);					//wait
  while(SCI11.SSR.BIT.TEND==0);		//wait for Trans

    //Send Register Address
    SCI11.TDR = reg_addr;				//REGISTER ADDRESS
    for(i=0;i<100;i++);					//wait
    while(SCI11.SSR.BIT.TEND==0);		//wait for Trans

    //Send Data
    SCI11.TDR = data;					//Data for Register
    for(i=0;i<100;i++);					//wait
    while(SCI11.SSR.BIT.TEND==0);		//wait for Trans
...
```
ISL29034のデバイスアドレスは7bitで44xです。0bit目がReadかWrriteかの識別子となり、Writeの際は0bですので80xを最初に送信することになります。
2バイト目が書き込みたいレジスタのアドレス、3バイト目がデータとなります。

ISL29034のレジスタのアドレスとビットの説明は上記データシートのリンクのpage9～に記載されています。

**受信**
ISL29034のバイトアドレス受信の手順は下記になります。

```recv_i2c
...
    //Send Device Address
    SCI11.TDR = 0x88;				//Device Address[7:1]:44x,Read(1)/Write(0):0b
    for(i=0;i<100;i++);				//wait
    while(SCI11.SSR.BIT.TEND==0);	//wait for Trans

    //Send Register Address
    SCI11.TDR = reg_addr;				//REGISTER ADDRESS
    for(i=0;i<100;i++);    				//wait
    while(SCI11.SSR.BIT.TEND==0);		//wait for Trans

    //Restart Condition
    SCI11.SIMR3.BIT.IICSTIF=0;
    SCI11.SIMR3.BIT.IICRSTAREQ=1;
    SCI11.SIMR3.BIT.IICSDAS=0x1;
    SCI11.SIMR3.BIT.IICSCLS=0x1;
    while(SCI11.SIMR3.BIT.IICSTIF==0);

    SCI11.SIMR3.BIT.IICSTIF=0;
    SCI11.SIMR3.BIT.IICSDAS=0x0;
    SCI11.SIMR3.BIT.IICSCLS=0x0;

    //Send Device Address
    SCI11.TDR = 0x89;					//Device Address[7:1]:44x,Read(1)/Write(0):1b
    for(i=0;i<100;i++);					//wait
    while(SCI11.SSR.BIT.TEND==0);		//wait for Trans

    SCI11.SIMR2.BIT.IICACKT=1;			//Send NACK (for send/last data receive）

    //Receive Data
    SCI11.TDR = 0xFF;					//Dummy Data for Receive 
    for(i=0;i<100;i++);					//wait
    while(SCI11.SSR.BIT.TEND == 0);		//wait for Trans

    recv_data = SCI11.RDR;				//Read Receive Data
...
```
* 送信制御と同様にデバイスとリードしたいレジスタのアドレスを送信
* いったんリスタートして再度デバイスアドレスを送信。末尾0bit目はReadを示す1
* NACKを送信
* TDRにFFxを書くとデータ転送され、RDRに格納される

上記でISL29034の制御が行えるようになります。

データ取得
---
例として取得したADCのデータをLuxに変換するループ動作を載せます。

```メイン関数
int main(void) {
...
    //set Operation Mode
	send_i2c(ISL29034_REG_CMD1, ISL29034_OP_ALS_CONT);		//101b:Measures ALS continuously

	//set Full Scale Lux Range
	data[0] = recv_i2c(ISL29034_REG_CMD2, 1);
	data[0] &= 0xFC;											//mask: 11111100
	data[0] |= _range;
	send_i2c(ISL29034_REG_CMD2, data[0]);

	//set Integration Time ADC Resolution
	data[0] = recv_i2c(ISL29034_REG_CMD2, 1);
	data[0] &= 0xF3;											//mask: 11110011
	data[0] |= _resolution;
	send_i2c(ISL29034_REG_CMD2, data[0]);
...
    while(1) {
    	//temp
    	for (i=0;i<1000;i++) {}           //wait
    	//readADC
    	data[1] = recv_i2c(ISL29034_REG_DATA_H, 1);
    	data[0] = recv_i2c(ISL29034_REG_DATA_L, 1);
    	ee_printf("DATA_H: %x\n",data[1]);
    	ee_printf("DATA_L: %x\n",data[0]);
    	adc = (((uint16_t)data[1])<<8) | (uint16_t)data[0];
    	Lux = ((float)range / (float)count) * (float)adc;
    	ee_printf("Lux: %f\n",Lux);
    }
///
```
* COMMAND-1レジスタに動作モードを書き込む。上記では連続動作
* COMMAND-2レジスタにACのビット数設定とLuxのフルスケールレンジ設定を書き込む
* 8bitx2のDATAレジスタをリードしてLuxを計算

という流れです。結果をシリアル転送すると下記のようになりました。

![Lux.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624954/08bd75af-c150-0f31-0aad-2f08e359a841.png)

実際にセンサに手をかざすと数値が変化し、また蛍光灯のOn/Offで数値が大きく変わることも確認できました。

感想
---
C++形式のヘッダファイル周りのツールとの相性に苦労しましたが、簡易I2Cの制御は説明のしっかりされたサイトのおかげで早く理解でき、またISL29023の制御もデータシートが分かりやすく書かれていたためそこそこの時間で動作確認までできました。
ただ、今回のもですがセンサ系のサンプルプログラムやWebで見つかる情報はArduinoのライブラリありきで、何か一つ使おうとするたびに移植に苦労する製品はこの分野向きではないのかなという印象を持ちました。