読者です 読者をやめる 読者になる 読者になる

Raspberry piにATD7410をI2Cで接続する

Rubyから i2c_smbus_read_byte_data() のようなI2Cバス共通I/Fを呼べるようにC拡張ライブラリを作成し、秋月電子で購入したATD7410を接続して温度測定した。

1.前回と同様に C言語でクラスを作成します。

rasp_i2c.c

//  2016/02/19
//  rasp_i2c: Raspberry Pi i2c  Ruby module

#include <ruby.h>

#ifndef __RASP_I2C__
#define __RASP_I2C__

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

#include <linux/i2c-dev.h> // I2C用インクルード
#include <sys/ioctl.h>

//#define	LCD_CMD_WRIRE		0x00
//#define	LCD_DATA_WRITE	0x40

//#define	LCD_I2C_ADDR	0x3E
//#define	LCD_DDRAMADDR	0x40

#endif


// 関数の宣言
static	VALUE i2c_alloc(VALUE klass);
static	VALUE i2c_init(VALUE self, VALUE i_addr);
static	VALUE i2c_read_byte( VALUE self );
static	VALUE i2c_read_reg_byte( VALUE self,  VALUE i_reg);
static 	VALUE i2c_write_reg_byte( VALUE self, VALUE i_reg, VALUE i_data);

static	VALUE i2c_read_word( VALUE self, VALUE i_reg);
static	VALUE i2c_write_brock_byte( VALUE self, VALUE i_reg,  VALUE i_length, VALUE i_str);


//    require  'xxxxxx'  されたときに呼び出される関数    今回はライブラリ名を rasp_i2c とする
//    Init_xxxxxx という名前で、xxxxxxの部分にはライブラリの名前を指定する(ファイル名と同一である必要はない)。
//    makeすると、xxxxxx.soという名前のライブラリができる
//
//	extconf.rb
//		require "mkmf"
//		create_makefile("tasp_i2c")
//
//	create_makefileが、requireされたmkmf.rbで定義されている「Makefileを生成する関数」である。
//	引数の"rasp_i2c"には、Init_xxxxxxの、xxxxxxの部分を指定する。
//	この関数が呼び出されると、Init_xxxxxxによって定義されるライブラリを作成するためのMakefileが作られる。
//
void	Init_rasp_i2c(void)
{
	VALUE   RaspI2c;	

	// クラスを定義  クラス名:RaspI2c
	RaspI2c = rb_define_class( "RaspI2c", rb_cObject );		// class   RaspI2c < rb_cObject

	//フィールドのクラスへの関連付け
	rb_define_alloc_func(RaspI2c, i2c_alloc);
	
	// メソッドクラスへの関連付け
	//  rb_define_method(klass, methodname, func, args)
       //             klassに格納されたクラスに、methodnameで指定される名前のメソッド を定義する。 
	//             funcには、1で定義したメソッド本体への関数ポインタを渡す
	//             argsは、引数の数(selfは含まない)
	//
	rb_define_private_method( RaspI2c, "initialize", i2c_init, 1);
	rb_define_method( RaspI2c, "read_w",  i2c_read_word, 1);
	rb_define_method( RaspI2c, "read_b",  i2c_read_byte, 0);

	rb_define_method( RaspI2c, "read_reg_b",  i2c_read_reg_byte, 1);
	rb_define_method( RaspI2c, "write_reg_b",  i2c_write_reg_byte, 2);

	rb_define_method( RaspI2c, "write_brock_b",   i2c_write_brock_byte, 3);

}

//フィールドの定義
//  class RaspI2c
//	def initialize
//		@i2c_fd = 0
//	end
//	attr_reader :i2c_fd
//
struct	raspi2c {
	int i2c_fd;
};

//フィールドをクラスへセット
static	VALUE i2c_alloc(VALUE klass)
{
	struct raspi2co  *ptr = ALLOC(struct raspi2c);
	
	// klass へフィールドをセットする
	return Data_Wrap_Struct(klass, 0, -1, ptr );
}


//  I2c 初期化
//  initialize メソッドをクラスへセット
VALUE i2c_init(VALUE self, VALUE i_addr)	// 第一引数は、selfとなっている。メソッドの実行主体(つまり自分自身)が、ここに渡される。
{
    	struct raspi2c *ptr;

   	 // self("struct RData*"であるVALUE)から"struct counter*"を取り出す
    	Data_Get_Struct(self, struct raspi2c, ptr);

	char *i2cFileName = "/dev/i2c-1";
	const unsigned char i2cAddress =  NUM2INT(i_addr);
	
	if((ptr->i2c_fd = open(i2cFileName,O_RDWR)) < 0){
        	fprintf(stderr,"Faild to open i2c port¥n");
        	return T_FALSE;
	}
	// 通信相手のI2Cアドレス(7bit)を固定する
	if (ioctl(ptr->i2c_fd, I2C_SLAVE,i2cAddress) < 0) {
        	fprintf(stderr,"Unable to get bus access to talk to slave¥n");
        	return T_FALSE;
    	}
	return T_TRUE;
}


VALUE i2c_read_byte( VALUE self )
{
	struct raspi2c  *ptr;
	// self("struct RData *" である VALUE)から "struct raspgpio *" を取り出す
	Data_Get_Struct( self, struct raspi2c, ptr );

	__s32 res = i2c_smbus_read_byte( ptr->i2c_fd );
    	if(res < 0){
       	 fprintf(stderr,"Error i2c_smbus_read_byte()¥n");
       	 return T_FALSE;
    	}	
	return  INT2FIX(res);
}


VALUE i2c_read_word( VALUE self, VALUE i_reg)
{
	struct raspi2c  *ptr;
	// self("struct RData *" である VALUE)から "struct raspgpio *" を取り出す
	Data_Get_Struct( self, struct raspi2c, ptr );

	int	t_reg = NUM2INT(i_reg);
	__s32 res = i2c_smbus_read_word_data( ptr->i2c_fd, t_reg);
    	if(res < 0){
       	 fprintf(stderr,"Error i2c_smbus_read_word_data()¥n");
       	 return T_FALSE;
    	}
	int word_data = (res & 0x00ff) << 8 | (res & 0xff00) >> 8;
	
	return  INT2FIX(word_data);
}


VALUE i2c_read_reg_byte( VALUE self, VALUE i_reg)
{
	struct raspi2c  *ptr;
	// self("struct RData *" である VALUE)から "struct raspgpio *" を取り出す
	Data_Get_Struct( self, struct raspi2c, ptr );

	int	t_reg = NUM2INT(i_reg);
	__s32 res = i2c_smbus_read_byte_data( ptr->i2c_fd, t_reg);
    	if(res < 0){
       	 fprintf(stderr,"Error i2c_smbus_read_word_data()¥n");
       	 return T_FALSE;
    	}	
	return  INT2FIX(res);
}



//
//
//
VALUE i2c_write_reg_byte( VALUE self, VALUE i_reg, VALUE i_data)
{
	struct raspi2c  *ptr;
	// self("struct RData *" である VALUE)から "struct raspgpio *" を取り出す
	Data_Get_Struct( self, struct raspi2c, ptr );

	unsigned char t_reg = NUM2INT(i_reg);
	unsigned char data = NUM2INT(i_data);

	i2c_smbus_write_byte_data(  ptr->i2c_fd, t_reg, data  );

	return  T_TRUE;
}


VALUE i2c_write_brock_byte( VALUE self, VALUE i_rs,  VALUE i_length, VALUE i_buff)
{
	struct raspi2c  *ptr;
	Data_Get_Struct( self, struct raspi2c, ptr );

	int	rs = NUM2INT(i_rs);
	int	length = NUM2INT(i_length);

	unsigned char *buff = StringValuePtr(i_buff);		//  STR2CSTR --> StringValuePtr

	i2c_smbus_write_block_data( ptr->i2c_fd, rs, length, buff);
	
	return  T_TRUE;
}


2.共有ライブラリの作成

 extconf.rb

require "mkmf"
create_makefile "rasp_i2c"

makefile を作成する

ruby extconf.rb

実行すると Makefile が作成されます。

make を実行すると rasp_i2c.so ができ上がります。

※rasp_i2c.o は test.rb と同じディレクトリに配置します。

3.テスト

require  './rasp_i2c'

class Atd7410  < RaspI2c
    CONF_REG=0x03

	attr_reader :temp
	
	def initialize(addr, bit)
		@bit = bit
		@temp = 0
		super(addr)		# class RaspI2c の initialize を実行
		if @bit == 16
			write_reg_b(CONF_REG, 0x80)		# 16bit mode
		end
	end

	def read
		@temp = read_w(0x00)
		if @bit == 16
			@temp -= 65536 if @temp >= 65536
			@temp /= 128.0
		else 
			@temp >>= 3 
			@temp -= 8192 if @temp >= 4096
			@temp /= 16.0 
		end
		puts "温度:%3.4f"%@temp		
		return @temp
	end

end


atd= Atd7410.new(0x48, 13) # 13ビット  
ondo = atd.read
温度:22.6875


atd= Atd7410.new(0x48, 16) # 16ビット 本当に切り替わったなかな・・・ 
ondo = atd.read
温度:22.7500