2021-12-30 PS I2Cを使ってADT7410の温度を読む

## 動作環境

- Windows 10 Pro (v21H2)
- Vivado v2019.1
- FPGA基板: CORA Z7 Zynq－7000評価ボード（Z7－07S）
- Analog Discovery 2

## 概要

PS I2Cの機能を使ってI2C通信をして、温度センサADT7410から温度を読み取る。

### 温度センサ

- 秋月電子
    - ＡＤＴ７４１０使用　高精度・高分解能　Ｉ２Ｃ・１６Ｂｉｔ　温度センサモジュール
    - データシート
        - https://akizukidenshi.com/download/ds/analog/ADT7410a.pdf

### 接続

ArduinoコネクタのSCL, SDAのピンを使う。
SCLとSDAはプルアップ抵抗があるため、外部プルアップ抵抗は使わなくてよいようだ。

https://digilent.com/reference/programmable-logic/cora-z7/reference-manual
> The pins on the shield connector typically used for I2C signals are labeled as SCL and SDA. On the Cora Z7, these signals are each attached to a pull-up resistor. While these pins can still be used as digital I/O, these pull-ups should be kept in mind. 

VCC, GND, SCL, SDAの4本線で接続した。

### Block Design

<img width="746" alt="image.png (27.0 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2021/12/30/21741/8984ef48-f0a8-480e-8e94-a776f0ddd387.png">

### MIO Configuration > I2C 

EMIOとしている。

<img width="1048" alt="image.png (47.0 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2021/12/30/21741/2d590b15-9b5f-4e67-9f2e-0474d07909fa.png">

### XDC

SCLとSDAのピンを設定しておく。
参考: https://github.com/Digilent/digilent-xdc/blob/master/Cora-Z7-07S-Master.xdc#L132

```XDC
set_property IOSTANDARD LVCMOS33 [get_ports IIC_0_0_scl_io]
set_property IOSTANDARD LVCMOS33 [get_ports IIC_0_0_sda_io]
set_property PACKAGE_PIN P16 [get_ports IIC_0_0_scl_io]
set_property PACKAGE_PIN P15 [get_ports IIC_0_0_sda_io]
```

## XSDK 

XSDKのアプリケーションとして以下を実装した。

```c
#include "xparameters.h"
#include "xiicps.h"
#include "xil_printf.h"
#include "sleep.h"

#define IIC_DEVICE_ID		(XPAR_XIICPS_0_DEVICE_ID)

#define IIC_SLAVE_ADDR		(0x48) // ADT7410
#define IIC_SCLK_RATE		(100000)

#define TEST_BUFFER_SIZE	(10) // 実際には1バイトでいいが...

int IicPsReadTemperature(u16 DeviceId);
XIicPs Iic; // IICインスタンス

u8 SendBuffer[TEST_BUFFER_SIZE]; // 送信バッファ
u8 RecvBuffer[TEST_BUFFER_SIZE]; // 受信バッファ

int main(void)
{
	int Status;

	while(1) {
		Status = IicPsReadTemperature(IIC_DEVICE_ID);
		if (Status != XST_SUCCESS) {
			xil_printf("Read ADT7410 Failed\r\n");
			return XST_FAILURE;
		}
		sleep(1);
	}
	return XST_SUCCESS;
}

#define ADT7410_SEND_SIZE (1) // 送信: 1 byte for 0x00 or 0x01 (Temperaure MSB and LSB)
#define ADT7410_RECV_SIZE (1) // 受信

int IicPsReadTemperature(u16 DeviceId)
{

	// 1. IICの初期化

	int Status;
	XIicPs_Config *Config;

	Config = XIicPs_LookupConfig(DeviceId);
	if (NULL == Config) {
		return XST_FAILURE;
	}

	Status = XIicPs_CfgInitialize(&Iic, Config, Config->BaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

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

	XIicPs_SetSClk(&Iic, IIC_SCLK_RATE);

	// 2. Temperature MSB, LSBを順番に読む

	char registers[2] = { 0x00, 0x01 }; // Register Address for Temperature MSB and LSB
	int result = 0;
	for(int idx=0; idx < 2; idx++) {
		XIicPs_SetOptions(&Iic, XIICPS_REP_START_OPTION); // enable repeated start condition
		SendBuffer[0] = registers[idx];
		Status = XIicPs_MasterSendPolled(&Iic, SendBuffer, ADT7410_SEND_SIZE, IIC_SLAVE_ADDR);
		if (Status != XST_SUCCESS) {
			return XST_FAILURE;
		}
		if ( !(Iic.IsRepeatedStart) ) {
			while (XIicPs_BusIsBusy(&Iic)) {
				/* NOP */
			}
		}
		Status = XIicPs_MasterRecvPolled(&Iic, RecvBuffer, ADT7410_RECV_SIZE, IIC_SLAVE_ADDR);
		if (Status != XST_SUCCESS) {
			return XST_FAILURE;
		}

		XIicPs_ClearOptions(&Iic, XIICPS_REP_START_OPTION); // disable repeated start condition

		result |= RecvBuffer[0];
		if (idx == 0) { // いったん16ビットにする
			result <<= 8;
		}
		usleep(100);
	}
	result >>= 3; // [15:3]を使うため

	// 3. 表示

	double atmp = (float)result / 16; // DataSheet p12 右側の式
	printf("Temp=%f\n", atmp);

	return XST_SUCCESS;
}
```

### 実行例

<img width="579" alt="image.png (19.4 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2021/12/30/21741/d8c735ba-fcc0-4473-8417-48eb724875e4.png">

指で温めたりすると温度が上がることを確認している。
エアコンを消した時に23.5度まで下がることは確認した。

### I2C通信のモニタ

Analog Discovery 2でI2C通信をモニタした。

<img width="1600" alt="image.png (43.3 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2021/12/30/21741/70cb223e-59f6-4158-a3b9-a5e8daf6ce1a.png">

0x00と0x01のRegisterの値を通信で得られていることが分かる。

WRとRDの間がRepeated Start Conditionになっていることもわかる。
<img width="1774" alt="image.png (15.6 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2021/12/30/21741/f4477a0e-5b80-4a52-8d85-279b7c72d540.png">


### 備考

sign bitの扱いをきちんとしていない。
マイナスの温度は当面扱わないので、今のままとしている（優先度は低い）。


### 参考

- ZYBOのPSでI2Cを動かしてみた (Todotaniのはやり物Log)
    - http://todotani.cocolog-nifty.com/blog/2016/09/zybopsi2c-615d.html
- xiicps_repeated_start_example.c
    - https://github.com/Xilinx/embeddedsw/blob/master/XilinxProcessorIPLib/drivers/iicps/examples/xiicps_repeated_start_example.c
    - XIICPS_REP_START_OPTIONの使用例


### 動作状況

<img width="819.2" alt="IMG_2965.JPG (938.9 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2021/12/30/21741/346587b8-f468-4d64-bf92-2028182926c9.JPG">