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