PYNQ > XSDK > I2C通信 > MLX90632のEEPROM Versionを読む

```txt:動作環境
Windows 10 Pro (v1909) 
PYNQ-Z1 (Digilent) (以下、PYNQと表記)
PYNQ v2.5 Image
Vivado v2019.1 (64-bit)
Analog Discovery 2 (以下、AD2)
```


### 概要

- I2C(EMIO)を使用する
- XSDKにてI2Cの通信を実装する
    - EEPROM versionの読込み


### 関連

- [borgThermo > 放射温度計 > 第一回: STM32L476とMLX96032の通信(I2C)](https://qiita.com/7of9/items/58b1e1ceacbdb38c636e)
    - MLX96032の情報はこちらを参考にする


### 参考

- [ZYBOのPSでI2Cを動かしてみた](http://todotani.cocolog-nifty.com/blog/2016/09/zybopsi2c-615d.html)
    - Todotaniのはやり物Log様
    - 情報感謝です

こちらの記事を参考にPYNQ-Z1で試しました。

### Block Design

I2C 0のEMIOを選択しておく。
![コメント 2020-08-15 221535.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/357a1496-fc6f-e563-0727-be547d06a56b.png)

IIC_0が追加されるので、Make Externalする。名前はIIC_0とした。

以下のBlock Designになる。
![コメント 2020-08-15 221535.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/177e5e87-e713-c290-93ff-82e1637cebe0.png)


### 制約ファイル

ZYNQ-Z1用のプリセットファイル (`PYNQ-Z1_C.xdc`)に記載されている、I2CのP16とP15をI/O portsに設定しておく (Run Synthesize後)。
3.3Vで使用するためLVCMOS33を選択した。

```
set_property IOSTANDARD LVCMOS33 [get_ports IIC_0_scl_io]
set_property IOSTANDARD LVCMOS33 [get_ports IIC_0_sda_io]
set_property PACKAGE_PIN P16 [get_ports IIC_0_scl_io]
set_property PACKAGE_PIN P15 [get_ports IIC_0_sda_io]
```

### MLX90632

Zephyr RTOSの勉強時に購入した下記を使う。
EEPROM versionの読込み通信があり、それを用いてI2C動作を確認することとした。

MLX90632　放射温度計センサークリックボード [CLICK-IRThermo]
http://www.microtechnica-shop.jp/shopdetail/000000000108/disp_pc/

I2Cアドレスは`0x3A`。
プルアップ抵抗は基板上のものをそのまま使用した(1kΩのプルアップ抵抗)。


### 接続

Arduino Connectorの右上にあるSCL, SDAを使用した。
https://reference.digilentinc.com/reference/programmable-logic/pynq-z1/reference-manual
の「17 Arduino/chipKIT Shield Connector」

[Docs » PYNQ Libraries » Arduino](https://pynq.readthedocs.io/en/v2.5.1/pynq_libraries/arduino.html)にも記載がある (I2C: SCL, SDA)。


- PYNQ-Z1 <--> MLX90632 [CLICK-IRThermo] 
    - 3V3 <--> 3V3
    - GND <--> GND
    - SCL <--> SCL
    - SDA <--> SDA


### XSDK

今回はPythonではなく、XSDKで実行する。
Python(PYNQ Library)でのI2C実行については不明である。

実装は[こちらに紹介されているもの](http://todotani.cocolog-nifty.com/blog/2016/09/zybopsi2c-615d.html)に対して、書き込みを複数文字列で実行できるように`i2c_buffwrite()`を追加した。


```helloWorld.c
#include "platform.h"
#include "xparameters.h"
#include "sleep.h"
#include "xiicps.h"
#include "stdio.h"


// I2C parameters
#define IIC_SCLK_RATE       100000  // clock 100KHz
#define MLX90632_ADDRESS    0x3A    // 7bit address
#define IIC_DEVICE_ID       XPAR_XIICPS_0_DEVICE_ID

XIicPs Iic;

int Init()
{
    int Status;
    XIicPs_Config *Config;  /**< configuration information for the device */

    Config = XIicPs_LookupConfig(IIC_DEVICE_ID);
    if(Config == NULL){
        printf("Error: XIicPs_LookupConfig()\n");
        return XST_FAILURE;
    }

    Status = XIicPs_CfgInitialize(&Iic, Config, Config->BaseAddress);
    if(Status != XST_SUCCESS){
        printf("Error: XIicPs_CfgInitialize()\n");
        return XST_FAILURE;
    }

    Status = XIicPs_SelfTest(&Iic);
    if(Status != XST_SUCCESS){
        printf("Error: XIicPs_SelfTest()\n");
        return XST_FAILURE;
    }

    XIicPs_SetSClk(&Iic, IIC_SCLK_RATE);
    printf("I2C configuration done.\n");

    return XST_SUCCESS;
}

int i2c_buffwrite(XIicPs *Iic, u8* buff, u32 len, u16 i2c_adder)
{
    int Status;

    Status = XIicPs_MasterSendPolled(Iic, buff, len, i2c_adder);

    if(Status != XST_SUCCESS){
        return XST_FAILURE;
    }

    // Wait until bus is idle to start another transfer.
    while(XIicPs_BusIsBusy(Iic)){
        /* NOP */
    }

    return XST_SUCCESS;
}


int i2c_read(XIicPs *Iic, u8* buff, u32 len, u16 i2c_adder)
{
    int Status;

    Status = XIicPs_MasterRecvPolled(Iic, buff, len, i2c_adder);

    if (Status == XST_SUCCESS)
        return XST_SUCCESS;
    else
        return -1;
}


int main()
{
    init_platform();
    Init();

    u8	  wrbuff[] = { 0x24, 0x0B }; // Raed EEPROM version
    u8    rdbuff[4];
    u16   rawdata;
    float temp;

    while(1) {
    	i2c_buffwrite(&Iic, wrbuff, 2, MLX90632_ADDRESS);
        i2c_read(&Iic, rdbuff, 2, MLX90632_ADDRESS);

        usleep(1000*1000);      // sleep 1sec (1000 x 1000us)
    }

    cleanup_platform();
    return 0;
}
```

### Analog Discovery 2(AD2)でのモニタ

上記の実装をデバッグ実行した。

下記のようにAD2で通信を確認できた。

![コメント 2020-08-15 221535.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/01df6993-61a4-552f-d9f9-6ea2c83f2748.png)

少しひげがでているが、0x04, 0x87というEEPROM Versionが読取りできているようだ。
実使用時にはこのひげの対処が必要になるが、とりあえずPYNQからI2C通信はできるようにはなった。



![IMG_1978.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/eb59c91c-4ca0-9d02-d6f9-0b845e903590.jpeg)



### PYNQ Library

https://discuss.pynq.io/t/how-to-use-i2c-with-pynq-z1/1039
上記の実装を試そうとしたが、PL.ip_dictが空だったので実施できなかった。