マイクロビット(e_11)MAG3110 地磁気センサー (1)
MAG3110 (I2C) 地磁気センサー(1)
micro:bitに実装されている地磁気センサーMAG3110(Tree-Axis Digital Magnetometer)を使ってみました。方位角の計算、レジスタデータの読出しを行ってデータ値の比較をしようと思います。MAG3110はI2Cデバイスです。
1.方位角の計算
compass.getとcompass.heading()
compass.get_x()、get_y()から得られる方位角とcompass.heading()で得られる角度を比較してみます。
x_com=compass.get_x()、y_com=compass.get_y()から得た値から方位を算出します。
方位角は下図で北(上)を0°として時計周りの角度なので90°-Θになります。
したがって、tanΘ = get_y()/get_x()となり、方位角は90-Θで示されます。
以下で算出しました。
from microbit import *
import math
def deg_atan(x,y): # 方位計算の関数
ath = math.atan(y/x) # 角度を算出
if x > 0 and y > 0:
deg = 90 - abs(math.degrees(ath)) # 方位角を計算
#print("1")
if x > 0 and y < 0:
deg= 90 + abs(math.degrees(ath))
#print("2")
if x < 0 and y < 0:
deg= 270 - abs(math.degrees(ath))
#print("3")
if x < 0 and y > 0:
deg= 270 + abs(math.degrees(ath))
#print("4")
if x == 0 or y == 0:
ath = 0
deg = 0
return ath,deg atanの値と計算した方位角を返します。
#get de-ta kara no deg wa headding to hobo onazi de keisan wa tadasii
x_com=compass.get_x() # x軸の磁場強度
y_com=compass.get_y() # y軸の磁場強度
z_com=compass.get_z() # z軸の磁場強度 計算には使ってません
print ('compass x, y, z =',x_com, y_com, z_com)
ath, deg = deg_atan(x_com, y_com) # 方位計算の関数に代入して計算
print('ath, deg =',ath, deg)
hdeg=compass.heading() # headingで方位角の取得
print('compass_head_deg=', hdeg)
※方位角の計算は象限で正負が変わるので場合分けし、角度を絶対値にして計算しました。もっといい方法があるのかも知れません。
実行結果です。
>>> %Run 1013_compass_deg-1.py
compass x, y, z = -19384 28076 20636 # x、y、Z軸の磁場強度
ath, deg = -0.966534 325.378 # 算出した角度、方位角
compass_head_deg= 326 # headdingでの方位角
磁場強度から算出した方位角325.378とheaddingでの方位角326とほぼ等しくなりました。
compass.get_x()、get_y()の磁場強度から方位角を算出できることが分かりました。
2.レジスタデータから計算
地磁気センサーMAG3110からデータを取り出します。データシートをみるとx、y、z軸の磁場強度のデータは8bitx2の符号付き16bit(2’s complement numbers)になっています。
X_MSB (0x01), X_LSB (0x02), Y_MSB (0x03), Y_LSB (0x04),Z_MSB (0x05), Z_LSB (0x06)のレジスタに各々入っています。
(0x0F)には温度データが8bitの符号付きデータが入っています。詳細はMAG3110のデータシートで確認してください。
全部で0x00から0x11の18個レジスタがあります。ちなみにI2Cのアドレスは0x0eです。
※MAG3110のデータシートから抜粋 印加筆
2-1.符号付きビット(2の補数字)
簡単にするため4ビットで確認します。前回の方がシンプルですが、少し違う方法で計算しています。
最上位ビットを符号ビットにして負の値を得ます。以下のようになります。
bin 符号付 (符号なし) 0b_0111 7 (7) 0b_1000 -8 (8) 0b_1001 -7 (9) 0b_1010 -6 (10) ・ 0b_1110 -2 (14) 0b_1111 -1 (15)
以下は’0b_xxxx’で与えた変数から符号付きの値を返します。
def s_comp(value):
# value '0b_1111'
a = value[2:] # 0bの接頭文字を外します
b_dat = '0'*(4-len(a)) + a # 埋まってない上位ビットを0で埋める
fugo = -int(b_dat[0]) # 最上位が1なら負が付く
conv = -int(b_dat[0]) << 3 | int(b_dat,2) # 符号付きになる
return b_dat, fugo, conv
例えば 0b1001 (9、符号付きなら-7)の場合
a = value[2:] は0bの接頭文字を除いた’1001’になります。b_datは’1001’です。
穴埋めが必要な場合は、上記の方法で穴埋めしています。
fugo = -int(b_dat[0])
b_dat[0]は最初の文字(最上位)の’1’なので’-1’になります。
int(b_dat,2)は元の’1001’と同じです。文字を整数にしています。
最終的に ’-1’を3つ左にシフトした’-1000’と’1001’の論理和(or)なので’-1001’になります。負の値になりました。
以下にまとめました。4bitの数字を入れると符号付の値が返ります。
def s_comp(value):
a = value[2:]
b_dat = '0'*(4-len(a)) + a
fugo = -int(b_dat[0])
conv = -int(b_dat[0]) << 3 | int(b_dat,2)
return b_dat, fugo, conv
x = input ('4bit x=')
dat= '0b' + str(x)
print('dat=',dat)
b_dat, fugo, conv = s_comp(dat)
conv_b1 = bin(conv)
conv_b2 = bin(conv & 0b1111)
# 変換する値、符号。変換値、bin 変換bin
print (b_dat, fugo, conv, conv_b1, conv_b2)
conv_b1は’-1’を2進数にした際の値です。Pythonが見やすいように変換しています。
実際の値を表示するにはconv_b2のように’0b1111’との積(and)になります。
実行結果です。
>>> %Run 1014_2s-complement_01.py
4bit x=1000 # 1000 を入力
dat= 0b1000
1000 -1 -8 -0b1000 0b1000 # 入力した値、符号、符号付き整数、bin値、補数
>>> %Run 1014_2s-complement_01.py
4bit x=1011 # input 1011 を入力
dat= 0b1011
1011 -1 -5 -0b101 0b1011
上の結果で注意が必要なところがあります。
#Pythonが下のように’-5’を見やすく表示するので注意が必要です。
>>> bin(-5)
'-0b101'
# 4bit符号付きの値を見るには
>>> bin(-5 & 0b1111)
'0b1011'
# 8bit符号付きの値を見るには
>>> bin(-5 & 0xff)
'0b11111011'
になります。
※符号付きの計算は前回の加速度センサーでの方法が簡単かもしれません。さらにいい方法がありそうです。
2-2.レジスタから読みだし
18個のレジスタがあるので全部読み出します。I2Cのアドレスは0x0eです。
# MAG3110 ID=0x0E(14)
addr=14
# read_start reg_addr
i2c.write(addr, b'\x00') # b'\x00'のレジスタから
sleep(100)
b_data=i2c.read(addr,18) # 18バイト(18個)読む
l_data=list(b_data) # リスト化
print(l_data)
実行結果です。
>>> %Run 1014_mag3110_read_b.py
[255, 251, 13, 5, 184, 248, 176, 196, 1, 0, 0, 0, 0, 0, 0, 16, 97, 32]
2個目の251から6個がx、y、z軸の磁気強度のデータです。(0x01~0x06)
使うのはこの6バイトのデータだけなので
i2c.write(addr, b'\x01') # b'\x01'のレジスタから
b_data=i2c.read(addr,6) # 6バイト(6個)読む
でも良いと思います。
まとめると以下のようになります。
各レジスタからデータを読んで、16bitのデータにした後に符号付きに変換します。
from microbit import *
#fugoutuki keisan no kannsuu
def s_comp(value):
# value '0b_1111_1111_1111_1111' no form
# '0b'wo nozoku
a = value[2:]
b_dat = '0'*(16-len(a)) + a # 16bitの穴埋
conv_value = -int(b_dat[0]) << 15 | int(b_dat,2) # 16bitなので15シフト
return conv_value
# MAG3110 ID=0x0E(14)
addr=14
i2c.write(addr, b'\x01') # b'\x01'のレジスタから
b_data=i2c.read(addr,6) # 6バイト(6個)x,y,zのHighとLow 3x2コを読む
l_data=list(b_data) # リスト化
print(l_data)
print( 'x_MSB,X_LSB=', hex(l_data[0]), hex(l_data[1]) )
#MSB 8bitshift 8bit_Hi+8bit_Low to onazi 16ビットデータにする
x_data= int(l_data[0]) << 8 | int(l_data[1])
#y_data= int(l_data[2]) << 8 | int(l_data[3])
#z_data= int(l_data[4]) << 8 | int(l_data[5])
print('x_data (int, hex, bin)',x_data,hex(x_data),bin(x_data))
x_mes=bin(x_data) # 2の補数関数に送るためbinデータにする
#y_mes=bin(y_data)
#z_mes=bin(z_data)
#16bit fugou tuki keisan he
x_hole = s_comp(x_mes) # 符号付きの関数に送る
#y_hole = s_comp(y_mes)
#z_hole = s_comp(z_mes)
print('out_X(hex,int)=', hex(x_hole), x_hole) # x軸の符号付きデータ
x_com=compass.get_x() # get_x関数で取得したデータ
print('get_x(hex,int)=' , hex(x_com), x_com)
表示(print)はx軸のデータだけにしています。
実行結果です。Thonnyのshellに表示されます。
>>> %Run 1014_mag_3110_register_b.py
[251, 24, 5, 183, 248, 190] # x,y,zのhigh、lowの各レジスタの読み出したデータ
x_MSB,X_LSB= 0xfb 0x18 # xのhigh、xのLow hexデータ
x_data (int, hex, bin) 64280 0xfb18 0b1111101100011000 # xの16bitデータ
out_X(hex,int)= -0x4e8 -1256 # x軸の符号付きデータ
get_x(hex,int)= -0x4ed8 -20184 # get_x関数で取得したデータ
とりあえずレジスタからデータは取り出せたようですが、レジスタから読み出したデータ(-1256)とget_x()で得た値(-20184)とは整合していません。
確か、comppassを使う時にはcompass.calibrate()で校正しました。レジスタからのデータにも何か校正が必要なのかも知れません。次回に記載します。
まとめ
micro:bitに実装されているI2C接続の地磁気センサーMAG3110(Tree-Axis Digital Magnetometer)の磁場強度をget_x()、get_y()で得た値から方位角の計算をしました。また、MAG3110のレジスタからデータを読み出しました。校正できていないためか、まだ数値が整合していません。校正については次回に記載します。