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