稼働中

マイクロビット(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のレジスタからデータを読み出しました。校正できていないためか、まだ数値が整合していません。校正については次回に記載します。