I2Cデバイスの扱い方

CAT724に搭載されているLinuxOSにはI2Cデバイスの通信ドライバが含まれています。このためI2CターゲットICとの通信はとても簡単です。

本ページの内容はCAT724固有の部分を除けばそれ以外の組込みLinuxでもだいたい同じです。

まずはI2C通信をread()とwrite()を使って自作プログラムする方法を説明します。次にカーネルの既存のドライバを組み込む方法を説明します。

2013/2/21 y.ebihara

CAT724のカーネル 2013年2月21日以後のバージョンでユーザ空間からのI2C通信に対応しました。まずはカーネルのバージョンを上げてください。

参考 CAT724_ROM出荷状態への戻し方

カーネルイメージ(zImage)

リンク切れの場合はこちらから最新を取得してください。

を取得したのち、

CAT724kのLinux rootユーザコマンドラインにて
# cp zImage_3.0.4_cat724_20130221 /dev/mtdblock1

でもカーネルのバージョンアップができます。


目次

CAT724のI2C仕様

/dev/i2c-0    ... SH7724の I2C0 の通信バス。ユーザ回路で使用可能
/dev/i2c-1    ... SH7724の I2C1 の通信バス。ユーザ回路で使用可能
/dev/i2c-2    ... CAT24 CPU上のGPIOによるソフトウェアで実装したI2C。
                  CAT74 CPUボード上でEEPROMとRTCが接続さてている

注意:EB724AにはEEPROM用のパターンがあり、/dev/i2c-0 にはスレーブアドレス0xA0,0xA2が予約されています。U6に24LC04Bを実装していただくと512バイトのEEPROMとして使用できます。あるいは、スレーブアドレス0xA0を別に使用したい場合は 後述するように カーネルの一部をコメントアウトしてください。

EB724m.png
EB724回路図から抜粋。U6 未実装。ただしスレーブアドレス 0xA0,0xA2は予約済み。

サンプル回路

I2Cの温度センサであるLM75をI2C0に接続したサンプルを用いて説明します。

  • 接続回路

pic1.png
I2Cの信号線はCAT724 CPUボード上で3.3Kでプルアップされています。

  • 製作例

pic2s.jpg

プログラム解説

全体の流れ

I2CターゲットICと通信するには

  1. I2Cバスをopen()する
  2. ioctl()でターゲットのI2Cアドレス(7bit)を指定する
  3. 後はread()とwrite()をするだけです
  4. 使い終わったらclose()します

1,2の部分

/* LM75温度センサがつながっているI2Cバスの指定 */
#define I2CDEVICE "/dev/i2c-0"

/* LM75温度センサのI2Cアドレス指定(7bitアドレス値) */
#define LM75_ADDR        (0x90 >> 1)

int main(){
	int fd;		/* ファイルディスクリプタ */
	int addr = LM75_ADDR;
	int ret;

/* I2C 通信デバイスのオープン */
	fd = open(I2CDEVICE,O_RDWR);
	if(fd<0){
		perror(I2CDEVICE);
		exit(1);
	}

/* 通信相手のI2Cアドレス(7bit)を固定する */
	ret = ioctl(fd, I2C_SLAVE, addr);
	if(ret<0){
		perror(I2CDEVICE);
		exit(2);
	}

3の部分(詳細は後述)

/* -------------------------------------------------------------*/
/* それぞれの I2C 処理                                          */

/* config レジスタの書き込み */
	lm75_write_config(fd, 0x60);	/* 0b 0110 0000 */

/* config レジスタの読み出しと表示 */
	lm75_read_config(fd);

/* 温度の読み出しと表示 */
	lm75_read_temp(fd, NULL);	

/* 温度上限を27度、正常復帰温度を26度とする */
	lm75_write_Tos_Thyst(fd, 27<<8, 26<<8);	

4の部分

/* -------------------------------------------------------------*/
/* 使い終わったら close() する */
	close(fd);

} /* mainの終わり プログラム終了 */

通信部分詳細

*温度レジスタを読む

pic_read_temp.png

  • I2Cのスタートコンディション
  • 7bitアドレスとR/W転送方向指定
  • ストップコンディション

はkernelのI2Cドライバが自動的に送信します。アプリでは通信本体のみ read()、write() するだけとなります。

LM75の場合は電源投入直後であれば単純に2バイトread()することで温度(2byte)の読み出しが可能です。

int lm75_read_temp(int fd, int *temp)
{
	int ret;
	unsigned char data[2];
	unsigned short x;
	int value;

	/* I2Cターゲットから2バイト読む */
	ret = read(fd, data, 2);
	if(ret<0){
		perror(I2CDEVICE);
		return -EIO;	/* IOエラー */
	}

	/* 通信結果の生データをを表示する */
	printf("0x%02x 0x%02x\n",
		data[0], data[1]);

	x = (data[0]<<8) | data[1];	/* 一旦 unsigned のまま単純に16bitにして */
	value = x;	/* 符号付きintに拡張する*/

	/* value を 256で割ったのが温度(C) になる */
	printf("%lf \n",(double)((double)value/256));

	if(temp != NULL){
		*temp = value;	/* 温度(C)の 256倍値 */
	}
	return 0;	/* 成功 */
}

configレジスタへの書き込み

LM75では <レジスタポインタ番号> <データ> の順で書き込みを行うことでレジスタ値のセットができます。詳細は次の通りです。configレジスタはポインタ1です。

pic_write_config.png

int lm75_write_config(int fd, int config)
{
	int ret;
	unsigned char data[2];
	
	data[0] = 1;  /* 0b 0000 0001 */
	data[1] = config;
	
	/* I2Cターゲットへ2バイト書く */
	ret = write(fd, data, 2);
	if(ret<0){
		perror(I2CDEVICE);
		return -EIO;	/* IOエラー */
	}
	return 0;	/* 成功 */
}

Tos、Thystレジスタへの書き込み

LM75ではTos(温度上限)、Thyst(温度正常復帰ヒステリシス)を書き込むことでO.S.端子にコンパレータ出力ができます。
参考回路ではO.S.端子にLEDを付けていますので、温度がTosに達するとLEDが点灯し、Thystまで下がるとLEDが消灯します。

pic_write_tos.png

int lm75_write_Tos_Thyst(int fd, int Tos, int Thyst)
{
	int ret;
	unsigned char data[3];

/* Tos レジスタへの書き込み */
	data[0] = 0x03;				/* レジスタ・ポインタ 0b 00000011 */
	data[1] = (Tos >> 8);		/* 上位バイト */
	data[2] = (Tos & 0xFF);		/* 下位バイト */

	/* I2Cターゲットへ3バイト書く */
	ret = write(fd, data, 3);
	if(ret<0){
		perror(I2CDEVICE);
		return -EIO;	/* IOエラー */
	}

/* Thyst レジスタへの書き込み */
	data[0] = 0x02;				/* レジスタ・ポインタ 0b 00000010 */
	data[1] = (Thyst >> 8);		/* 上位バイト */
	data[2] = (Thyst & 0xFF);	/* 下位バイト */

	/* I2Cターゲットへ3バイト書く */
	ret = write(fd, data, 3);
	if(ret<0){
		perror(I2CDEVICE);
		return -EIO;	/* IOエラー */
	}

	return 0;	/* 成功 */
}

configレジスタの読み出し

LM75ではレジスタポインタを1バイトwrite()し、次いで読み出したいレジスタ内容を1バイトもしくは2バイトread()することでレジスタの読み出しが行えます。

pic_read_config.png

configレジスタを読みだすサンプル

int lm75_read_config(int fd)
{
	int ret;
	unsigned char data[1];
	unsigned char config;

	data[0] = 1;  /* 0b 0000 0001 */
	
	/* I2Cターゲットへ1バイト書く */
	ret = write(fd, data, 1);
	if(ret<0){
		perror(I2CDEVICE);
		return -EIO;	/* IOエラー */
	}

	/* I2Cターゲットから1バイト読む */
	ret = read(fd, data, 1);
	if(ret<0){
		perror(I2CDEVICE);
		return -EIO;	/* IOエラー */
	}

	/* 結果を表示 */
	config = data[0];
	printf("LM75 read config = 0x%02x\n",config);

	return 0;	/* 成功 */
}

ダウンロード

  • 回路図

fileCAT724_I2C_SAMPLE1.pdf

  • LM75レジスタ表

fileLM75register.pdf

  • ソースコード

filei2c_test.c

コンパイルして実行するには

開発PCにて
$ sh4-linux-gnu-gcc i2c_test.c
CAT724にて
NFSなどで該当ディレクトリに移動してから
# ./a.out

既存のI2Cドライバをカーネルに組み込む方法

LinuxカーネルにはいくつかのI2Cターゲットドライバが用意されています。LM75のドライバもあります。

カーネルをconfigし、ソースを一部編集するとそれらのドライバを組み込むことができます。目的に合うドライバがあれば、I2C通信部分を自作する必要はありません。

手順を示します。

ソースの展開

開発PCにて作業ディレクトリ work を作り、最新カーネルを展開してください。

$ mkdir work
$ cd work
$ tar xzf linux-3.0.4_cat724_20130221.tgz
$ cd inux-3.0.4_cat724

コンフィグおよびソースの編集

コンフィグレーションを一旦デフォルトに戻してから、menuconfigに入ります。

$ make cat724_defconfig
$ make menuconfig

LM75ドライバを組み込みます

Device Drivers  --->
  <*> Hardware Monitoring support  --->
    <*>   National Semiconductor LM75 and compatibles

カーネルビルド

$ make

CAT724への組み込み

出来あがった arch/sh/boot/zImage がカーネルのですので、これをCAT724にて

# cp zImage /dev/mtdblock1

として上書きし、再起動します。

CAT724上で
# echo lm75 0x48 > /sys/class/i2c-adapter/i2c-0/new_device

とするとLM75ドライバが組み込まれます。

使用方法

rootユーザコマンドラインにて

# cat /sys/class/i2c-adapter/i2c-0/0-0048/temp1_input
25500

上記は 25.500 度である意味

# echo 27000 > /sys/class/i2c-adapter/i2c-0/0-0048/temp1_max
# echo 26000 > /sys/class/i2c-adapter/i2c-0/0-0048/temp1_max_hyst

温度上限を 27.00度、正常復帰ヒステリシスを26.00度にする例

注意

カーネルにはたくさんのドライバが用意されていますが個々のドライバの組み込み方法や使い方は個々のドライバによって異なりますので、上記を例に研究してください。

sysfsを使ったデバイスの確認方法

# ls -l /sys/bus/i2c/devices/
lrwxrwxrwx 1 root root 0 Aug  1  2000 0-0050 -> ../../../devices/platform/i2c-sh_mobile.0/i2c-0/0-0050
lrwxrwxrwx 1 root root 0 Aug  1  2000 0-0051 -> ../../../devices/platform/i2c-sh_mobile.0/i2c-0/0-0051
lrwxrwxrwx 1 root root 0 Aug  1  2000 2-0032 -> ../../../devices/platform/i2c-gpio.2/i2c-2/2-0032
lrwxrwxrwx 1 root root 0 Aug  1  2000 2-0050 -> ../../../devices/platform/i2c-gpio.2/i2c-2/2-0050
lrwxrwxrwx 1 root root 0 Aug  1  2000 2-0051 -> ../../../devices/platform/i2c-gpio.2/i2c-2/2-0051
lrwxrwxrwx 1 root root 0 Aug  1  2000 i2c-0 -> ../../../devices/platform/i2c-sh_mobile.0/i2c-0
lrwxrwxrwx 1 root root 0 Aug  1  2000 i2c-1 -> ../../../devices/platform/i2c-sh_mobile.1/i2c-1
lrwxrwxrwx 1 root root 0 Aug  1  2000 i2c-2 -> ../../../devices/platform/i2c-gpio.2/i2c-2

これらのデバイスが登録されていることが確認できます。スレーブ番号は右に1bitシフトしています。

時計IC (/dev/i2c-2, 0032)

# cat /sys/bus/i2c/devices/2-0032/name
rx8581
# cat /sys/bus/i2c/devices/2-0032/rtc/rtc0/date
2014-06-30
# cat /sys/bus/i2c/devices/2-0032/rtc/rtc0/time
10:27:26

I2Cドライバ補足

debianのi2c-tools

# apt-get install i2c-tools
# i2cdetect -y -r 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- --
# i2cdetect -y -r 2
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- UU -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: UU UU 52 53 54 55 56 57 -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

カーネルの編集

古いメモです。行う必要はありません。

カーネルソース

arch/sh/boards/mach-cat724/setup.c

の200行目付近を編集します。行頭に + 記号が付いている行を追加します。

/dev/i2c-0 で予約しているEEPROM(U6)を無効にする場合もこのソースを編集します。

 /* I2C0 につながっているデバイス(EEPROM) */
 static struct i2c_board_info __initdata cat724_i2c0_board_info [] = {
+#if 0          /* EB724A上の未実装 EEPROM (U6) を無効にする */
        {
                .type = "at24", /* EB724AのU6 ただし未実装 */
                .addr = 0xa0 >> 1,
                .platform_data = &eb724_board_eeprom,
        },
+#endif
+/* ------ ここから ------ */
+       {
+               .type = "lm75",
+               .addr = 0x90 >> 1,
+       },
+/* ------ ここまで ------ */
 };

関連

Last-modified: 2014-08-19 (火) 20:59:24 (1155d)