ステッピングモータードライバーL6470の2台同時制御プログラム例

モータードライバー
ステッピングモータードライバー

STマイクロ製ステッピングモータードライバーL6470を組み入れたドライブキット(ここでは秋月電子通商製)を使い、2台のステッピングモーター(SM-42BYG011)を同時に制御するプログラムを紹介する。
L6470はSPI通信で制御するが、それぞれのモーターに順番に制御コマンドを送ると、タイムラグが生じ、2台同時に起動させることができない。CS信号の立上げでコマンドが実行されるため、同時起動の方法としては、別々に制御コマンドを送った後、CS信号を同時に送信する方法や、デージーチェーンの方法が紹介されているが、ここでは、CS信号をほぼ同時(5μsecのタイムラグあり)に送信する方法とした。この方法では、2台のモーターを別々に起動させたり、同時に起動させたりできる。CLK信号とCS信号のタイミングを合わせるために、spidevモジュールを使わず、RPi.GPIOモジュールを使って作成した。

●使用したハードウェア

モータードライバー
L6470ステッピングモーター
ドライブキット

L6470使用のステッピングモータードライブキット
\1,800(秋月電子通商)
<主な仕様>
・モータ電圧:8V~45V
・ロジック電圧:内部レギュレータの3Vの使用、または外部電源(最大5.5V)の使用
・最大駆動電流:3A(ピーク7A)

ステッピングモーター
バイポーラ ステッピングモーター
(SM-42BYG011)

バイポーラ ステッピングモーター(SM-42BYG011)
\1,380(秋月電子通商)
<主な仕様>
・相数:2
・ステップ角:1.8度±5%
・1回転ステップ数:200
・入力定格電圧:12V
・定格電流:0.33A/相
・静止トルク:0.23N・M

●ドライバーとモーターの結線

制御用電源は内部レギュレーター3Vを使う。
2台のモータードライバー(MD)の⑤SDO、⑥CK、⑦SDI、③GND端子は、Raspberry PiのSPIMISO⑨、SPICLK⑪、SPIMOSI⑩、GND端子に共通接続する。2台のMD(ch0/ch1)の⑧CS端子は、それぞれ、Raspberry PiのSPICS⑧、SPICS⑦端子に接続する。(ストロベリーリナックス社のL6470ステップモータードライブキットとは端子配列番号が違っている。)

配線図
結線図

●L6470モータードライバー(MD)とRaspberry PiのSPI通信手順

SPI通信手順チャート
(STマイクロ社の資料より)

MDの読み書き(Read/Write)は1バイト単位で行う。CSをLowにして、MSBから順に8ビット書込み/読込み後に、CSをHighにすることで1バイトの読み書きが終わる。
タイミングとして、SDI(MDへの書込み)はCKの立上りでサンプルされ、最低20nsec保持した後、次のビット書込みサイクルに移るが、最後のLSBビットの書込み後、CSをHighにした後に、MDの内部処理のため、最低800nsecのインターバルが必要となる。SDO(MDからの読込み)はCKの立下りでラッチされ、CK=Lowになって最大57nsec後に読取りデータが有効になる。
Raspberry PiのGPIO.outputのサイクルは約5μsecであるため、いずれの処理もタイミングを合わせるためにtime.sleep()を入れる必要はない。尚、以下に記述するMDへの1バイトのread/writeモジュールの実行時間は132μsec。

●L6470制御モジュール

以下のモジュールを使う際、引数chには、2台のMDを個別に設定する場合には、ch=0またはch=1とし、2台同時に書き込みをする場合には、ch=2を使う。

<MDのread/writeモジュール>

①open()
RPi.GPIOモジュールのGPIOの入出力を定義

②readDriver(ch, byteLen)
ch(0/1)のMDから、byteLenバイト数を読取り、結果をリストで返す。ch=2は使えない。
このモジュールの実行時間は132μsec(Raspberry Pi 3B使用時)

③writeDriver(ch, byteList)
ch(0/1/2)のMDに、バイトリストbyteListを書き込む。CSをHighにすることでコマンドが実行されるので、2台同時(ch=2)の場合には、2台のCSをLowにした後、コマンドとそれに必要なデータを同時に書き込んで、最後に、2台のCSをHighにしている。最後のCSをHighにするGPIO.output処理はch.0とch.1をシリーズで行っているので、実際には、モーターのスタートには5μsecのタイムラグがあるが、実用上は無視できる時間である。このモジュールの実行時間は132μsec(Raspberry Pi 3B使用時)。

<MDコマンドに関するモジュール>

④resetDevice(ch)
ch(0/1/2)のモータードライバーMDを初期化する。NOP_DEVICE(0x00)を3回送って、コマンドの引数をフラッシュした後、RESET_DEVICEコマンドを送っている。

⑤setKval(ch, kHold, kRun, kAcc, kDec)
モーターを保持、回転、加減速する際のトルク(電圧レベル=PWMデューティ比)はDefault値として、0x29(= Vs * 0x29 / 256 = 0.16Vs)が設定されているが、モーターによってはトルク不足となるので、変更する必要がある。

⑥setMoveVal(ch, sAcc, sDec, sMxSpd, sMnSpd, sFsSpd)
モータの加速度、減速度、最大速度、起動最低速度、フルステップ速度(マイクロステップ動作からフルステップ動作になる速度)で、それぞれDefault値は、0x08A, 0x08A, 0x041, 0x000, 0x027となっている。
注意すべきことは、それぞれ1単位の分解能が異なることである。

  • 加速度sAcc/減速度sDec : 12bits、分解能14.55 step/sec2 、最小/最大(14.55 ~59,590 step/sec2)
  • 最大速度sMxSpd : 10bits、分解能15.25 step/sec、最小/最大(15.25 ~15,610 step/sec)
  • 最低速度sMnSpd : 13bits、分解能0.238 step/sec、最小/最大(0 ~976.3 step/sec)
  • フルステップ速度sFsSpd : 10bits、分解能15.25 step/sec、最小/最大(14.55 ~59,590 step/sec)
    尚、回転時の速度レジスタ(SPEED)の分解能は、MAX_SPEEDの1/1024倍となる。
    速度SPEED(0x04) : 20 bits、分解能0.015 step/sec、最小/最大(0 ~15,625 step/sec)

⑦runDirSpeed(ch, sDir, sSpd)
ch(0/1/2)のモーターを、sDir(FWD=1 / REV=0)方向に、sSpd速度で回転する。
ここで、sSpdは上記のSPEED(0x04)レジスタの値と同じ分解能(0.015 step/sec)、最小/最大値となっている。ch=2の場合、2台が同じ指定速度、方向となる。

⑧moveDirStep(ch, sDir, sStep)
ch(0/1/2)のモーターを、sDir(FWD=1 / REV=0)方向に、sStepステップ数回転して停止する。
この時のsStepはマイクロステップ数(Defaultの1単位は1/128ステップ)。1回転200ステップのモーターの場合、1回転するためには、sStep = 200 × 128 = 25,600となる。

⑨turnDirStep(sDir, sStep)
このモジュールは、左右の2台のモーターで駆動するマウスを、その場でスピン回転することを想定したもの。
左右のモーターを、sDir(LEFT=0 / RIGHT=1)方向に、sStepステップ数スピン回転して停止する。90度スピン回転するには、φ65mmの車輪が、車輪中心間距離130mmで左右に配置されている場合、次のステップ数だけ左右の車輪を逆方向に回転すればよい。
   \(\displaystyle \frac{130\pi}{4} \div 65\pi \times 200 \times 128 = 12,800\)
左右のモーターの回転スタートが異なると、スピン中心が移動するので、その防止のため、このモジュールを作成した。

⑩goToAbsPos(ch, sPos)
ch(0/1/2)のモーターを、絶対位置sPosまで、最短方向に回転して停止する。
この時のsPosはマイクロステップ数(Defaultの1単位は1/128ステップ)で表す絶対位置。

⑪goToDirAbsPos(ch, sDir, sPos)
ch(0/1/2)のモーターを、sDir(FWD=1 / REV=0)方向に、絶対位置sPosまで回転して停止する。⑩goToAbsPosとの違いは、回転方向を指定するかしないかであるが、近くに目標位置sPosがあるにも関わらず、逆方向の指示をすると、目標位置sPosに到達するためには時間がかかることになる。

⑫getStatus(ch)
ch(0/1)のSTATUSレジスタ(0x19)を読取り、16bitsデータで返す。STATUSの値はSTマイクロ社の資料に書かれているが、要点を纏めると次の表のようになる。この中で、特にMOT_STATUS、BUSYステータスは役立つ。

ステータス表
ステータス表

⑬readAbsPos(ch)
ch(0/1)の絶対位置(ABS_POS:0x01)を、20bitsで返す。
この時のreturn値はマイクロステップ数(Defaultの1単位は1/128ステップ)。

●MD6470制御モジュールとプログラムサンプル

・プログラムサンプル(stepMotorSample.py)

# -*- coding: utf-8 -*-
import time
import MD6470 as MD

def stopStat(timeOut):
    sTime = time.time()
    while True:
        try:
            statMD = (MD.getStatus(0) | MD.getStatus(1))
            if not(statMD & 0x0060):		# if both motor STOP, exit
                sStat = True
                break
            elif time.time() - sTime > timeOut:
                sStat = False
                break
        except KeyboardInterrupt:
            sStat = False
            break
    return sStat

def main():
    MD.open()		# ドライバー設定
    MD.resetDevice(2)	# 初期化
    time.sleep(0.1)
    MD.setKval(2, 0x60, 0xB4, 0xB4, 0xB4)	# トルクコントロールパラメーター設定
    MD.setMoveVal(2, 0x1A, 0x1A, 0x14, 0x00, 0x027)	#モーションパラメータ設定

    sSpd = 13333	# sSpd = 1回転/sec = 1.0 * 200step  / 0.015step/s = 13,333
    sStp = 25600	# 1回転 = 200 * 128 = 25,600 steps

    MD.runDirSpeed(2, MD.FWD, sSpd)	#2台同時、速度sSpdで前進(継続移動)
    if not stopStat(3.0):
        MD.softStop(2)			# 加減速してソフト停止
        print("Soft-Stopped by Timeout")
        
    MD.moveDirStep(2, MD.FWD, sStp)	# 2台同時、ステップ数sStp、前進
    if stopStat(5.0):
        print("Stopped after moveDirStep")

    MD.turnDirStep(MD.LEFT, sStp)		# ステップ数sStp、左スピン回転
    if stopStat(5.0):
        print("Stopped after turnDirStop")
    
    MD.resetAbsPos(2)	# 2台同時、絶対位置リセット
    MD.goToAbsPos(2, 20000)	# 2台同時、絶対位置20,000に移動
    if stopStat(5.0):
        print("Stopped after goToAbsPos")
    print("Absolute Position = ", MD.readAbsPos(0), MD.readAbsPos(1))	# 絶対位置の読み出し
    
    MD.resetAbsPos(2)	# 2台同時、絶対位置リセット
    MD.goToDirAbsPos(2, MD.FWD, 20000)	# 2台同時、前進で絶対位置20,000まで移動
    if stopStat(5.0):
        print("Stopped after goToDirAbsPos")
    print("Absolute Position = ", MD.readAbsPos(0), MD.readAbsPos(1))	# 絶対位置の読み出し

    print("MD Status ({:d}) = {:016b}".format(0, MD.getStatus(0)))	# ステータスの読み出し
    print("MD Status ({:d}) = {:016b}".format(1, MD.getStatus(1)))
    
    MD.close()	# ドライバーの設定解除
    
if __name__ == '__main__':
    main()

 

・MD6470制御モジュール(MD6470.py)

# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO

# ピンの名前を変数として定義
SPICLK = 11
SPIMOSI = 10
SPIMISO = 9
SPICS = [8,7]	#MOTOR DRIVER CS(ch=0/1)

# 変数
FWD = 1
REV = 0
LEFT = 0
RIGHT = 1

# Motor Driver Register Name & Address
ACC_REG = 0x05			# Acceleration
DEC_REG = 0x06			# Deceleration
MAX_SPEED_REG = 0x07	# Maximum speed
MIN_SPEED_REG = 0x08	# Minimum speed
FS_SPD_REG = 0x15		# Full step speed
STEP_MODE_REG = 0x16	# Step mode
ALARM_EN_REG = 0x17		# Alarms enable
KVAL_HOLD = 0x09		# Holding Kval
KVAL_RUN = 0x0A			# Constant speed Kval
KVAL_ACC = 0x0B			# Acceleration starting Kval
KVAL_DEC = 0x0C			# Deceleration starting Kval

# Motor Driver Command
NOP_DEVICE = 0x00			# Nothing
SET_PARAM = 0x00			# Write VALUE in PARAM register
GET_PARAM = 0x20			# Returns the stored value in PARAM register
RUN_DIR = 0x50				# Sets the target speed and the motor direction
MOVE_STEP = 0x40			# Makes N_STEP steps in DIR direction
GO_TO_ABS = 0x60			# Brings motor in ABS_POS position(min. path)
GO_DIR_ABS = 0x68			# Brings motor in ABS_POS position(DIR direction)
GO_HOME = 0x70				# Brings the motor in HOME position
RESET_POS = 0xD8			# Reset the ABS_POS register
RESET_DEVICE = 0xC0		# Device is reset to power-up conditions
SOFT_STOP = 0xB0			# Stops motor with a DEC
HARD_STOP = 0xB8			# Stops motor immediatelly
GET_STATUS = 0xD0			# Returns the status register value
ABS_POS = 0x01				# Absolute Position(22bits)

#=================================================================
def open():
    # SPI通信用の入出力を定義
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    GPIO.setup(SPICLK, GPIO.OUT)
    GPIO.setup(SPIMOSI, GPIO.OUT)
    GPIO.setup(SPIMISO, GPIO.IN)
    GPIO.setup(SPICS[0], GPIO.OUT, initial=GPIO.HIGH)	#CS Stand-By
    GPIO.setup(SPICS[1], GPIO.OUT, initial=GPIO.HIGH)	#CS Stand-By

#====== Motor Driver L6470 -> readDriver(ch, byteLen) ========
def readDriver(ch, byteLen):
    # ch=0/1
    byteData = []
    if byteLen <= 0 or byteLen >3:
        return byteData
    
    for n in range(byteLen):
        GPIO.output(SPICS[ch], GPIO.HIGH)
        GPIO.output(SPICLK, GPIO.HIGH)
        GPIO.output(SPICS[ch], GPIO.LOW)		# CS Active
        bitData = 0
        for bt in range(8):		# MSBから8ビット受信
            GPIO.output(SPICLK, GPIO.LOW)
            GPIO.output(SPICLK, GPIO.HIGH)
            bitData <<= 1
            if GPIO.input(SPIMISO) == GPIO.HIGH:
                bitData |= 0x1
        byteData.append(bitData)
    GPIO.output(SPICS[ch], GPIO.HIGH)		#CS Stand-By	
    return byteData

#====== Motor Driver L6470 -> writeDriver(ch, byteList) ========
def writeDriver(ch, byteList):
    # ch=0/1/2,  ch=2: both 0 and 1
    for n in range(len(byteList)):
        if ch == 2:
            GPIO.output(SPICS[0], GPIO.HIGH)
            GPIO.output(SPICS[1], GPIO.HIGH)
            GPIO.output(SPICLK, GPIO.HIGH)
            GPIO.output(SPICS[0], GPIO.LOW)		# CS Active
            GPIO.output(SPICS[1], GPIO.LOW)		# CS Active
        else:
            GPIO.output(SPICS[ch], GPIO.HIGH)
            GPIO.output(SPICLK, GPIO.HIGH)
            GPIO.output(SPICS[ch], GPIO.LOW)		# CS Active

        outBit = byteList[n]
        for bt in range(8):		# MSBから8ビット送信
            GPIO.output(SPICLK, GPIO.LOW)
            if outBit & 0x80:
                GPIO.output(SPIMOSI, GPIO.HIGH)
            else:
                GPIO.output(SPIMOSI, GPIO.LOW)
            GPIO.output(SPICLK, GPIO.HIGH)
            outBit <<= 1
    if ch == 2:
        GPIO.output(SPICS[0], GPIO.HIGH)		#CS Stand-By
        GPIO.output(SPICS[1], GPIO.HIGH)		#CS Stand-By
    else:	
        GPIO.output(SPICS[ch], GPIO.HIGH)		#CS Stand-By
    return
    
#=================================================================
def resetDevice(ch):
    #Reset motor driver
    writeDriver(ch, [NOP_DEVICE, NOP_DEVICE, NOP_DEVICE, RESET_DEVICE])
    #STEP_MODE:0x00(Full step).....0x07(1/128microstep)
    writeDriver(ch, [STEP_MODE_REG, 0x07])	# Default
    
def setKval(ch, kHold, kRun, kAcc, kDec):
    #KVAL_HOLD(8bits):Default=0x29(0.16Vs)
    writeDriver(ch, [SET_PARAM | KVAL_HOLD, kHold])
    #KVAL_RUN(8bits):Default=0x29(0.16Vs)
    writeDriver(ch, [SET_PARAM | KVAL_RUN, kRun])
    #KVAL_ACC(8bits):Default=0x29(0.16Vs)
    writeDriver(ch, [SET_PARAM | KVAL_ACC, kAcc])
    #KVAL_DEC(8bits):Default=0x29(0.16Vs)
    writeDriver(ch, [SET_PARAM | KVAL_DEC, kDec])

def getKval(ch):
    #KVAL_HOLD(8bits)
    writeDriver(ch, [GET_PARAM | KVAL_HOLD])
    kHold = readDriver(ch, 1)
    #KVAL_RUN(8bits)
    writeDriver(ch, [GET_PARAM | KVAL_RUN])
    kRun = readDriver(ch, 1)
    #KVAL_ACC(8bits)
    writeDriver(ch, [GET_PARAM | KVAL_ACC])
    kAcc = readDriver(ch, 1)
    #KVAL_DEC(8bits)
    writeDriver(ch, [GET_PARAM | KVAL_DEC])
    kDec = readDriver(ch, 1)
    return [kHold, kRun, kAcc, kDec]

def setMoveVal(ch, sAcc, sDec, sMxSpd, sMnSpd, sFsSpd):
    #ACC(12bits):Default=0x08A(resolution=14.55steps/s2)
    writeDriver(ch, [SET_PARAM | ACC_REG, (sAcc >> 8) & 0x0F, sAcc & 0xFF])
    #DEC(12bits):Default=0x08A(resolution=14.55steps/s2)
    writeDriver(ch, [SET_PARAM | DEC_REG, (sDec >> 8) & 0x0F, sDec & 0xFF])
    #MAX_SPEED(10bits):Default=0x041(resolution=15.25steps/s)
    writeDriver(ch, [SET_PARAM | MAX_SPEED_REG, (sMxSpd >> 8) & 0x03, sMxSpd & 0xFF])
    #MIN_SPEED(13bits):Default=0x000(resolution=0.238steps/s)
    writeDriver(ch, [SET_PARAM | MIN_SPEED_REG, (sMnSpd >> 8) & 0x1F, sMnSpd & 0xFF])
    #FS_SPD(10bits):Default=0x027(resolution=15.25steps/s2)
    writeDriver(ch, [SET_PARAM | FS_SPD_REG, (sFsSpd >> 8) & 0x03, sFsSpd & 0xFF])

def getMoveVal(ch):
    #ACC(12bits)
    writeDriver(ch, [GET_PARAM | ACC_REG])
    rDataList = readDriver(ch, 2)
    rAcc = (rDataList[0] & 0x0F) << 8 | rDataList[1]
    #DEC(12bits)
    writeDriver(ch, [GET_PARAM | DEC_REG])
    rDataList = readDriver(ch, 2)
    rDec = (rDataList[0] & 0x0F) << 8 | rDataList[1]
    #MAX_SPEED(10Bits)
    writeDriver(ch, [GET_PARAM | MAX_SPEED_REG])
    rDataList = readDriver(ch, 2)
    rMax = (rDataList[0] & 0x03) << 8 | rDataList[1]
    #MIN_SPEED(13Bits)
    writeDriver(ch, [GET_PARAM | MIN_SPEED_REG])
    rDataList = readDriver(ch, 2)
    rMin = (rDataList[0] & 0x1F) << 8 | rDataList[1]
    #FS_SPD(10Bits)
    writeDriver(ch, [GET_PARAM | FS_SPD_REG])
    rDataList = readDriver(ch, 2)
    rFsSpd = (rDataList[0] & 0x3F) << 8 | rDataList[1]
    return [rAcc, rDec, rMax, rMin, rFsSpd]
    
def runDirSpeed(ch, sDir, sSpd):
    # 方向(FWD/REV)を決めて、指定速度で移動
    # RUN_DIR | sDir(1:FWD) + 3byte(20bits)
    # ch=2: Ch0 & ch1 start simultaneusly
    setCmd = RUN_DIR | sDir
    spdH = (sSpd >> 16) & 0x0f		# Bit7-4 : XXXX
    spdM = (sSpd >> 8 ) & 0xff
    spdL = sSpd & 0xff
    writeDriver(ch, [setCmd, spdH, spdM, spdL])

def moveDirStep(ch, sDir, sStep):
    # 指定(sDir:FWD/REV)方向に、指定sStep数移動(LEFT and RIGHT)
    # MOVE_STEP | sDir(1:FWD) + sStep(3byte:22bits)
    # ch=2: Ch0 & ch1 start simultaneusly
    setCmd = MOVE_STEP | sDir
    stepH = (sStep >> 16) & 0x3f		# Bit7-6 :XX
    stepM = (sStep >> 8 ) & 0xff
    stepL = sStep & 0xff
    writeDriver(ch, [setCmd, stepH, stepM, stepL])

def turnDirStep(sDir, sStep):
    # 左右(sDir)方向に、sStep数回転(左右モーター同時起動)
    # sDir = LEFT/RIGHT
    # MOVE_STEP | sDir(1:FWD) + sStep(3byte:22bits)
    # ch=2: Ch0 & ch1 start simultaneusly
    if sDir == LEFT:
        setCmd0 = MOVE_STEP | REV
        setCmd1 = MOVE_STEP | FWD
    else:
        setCmd0 = MOVE_STEP | FWD
        setCmd1 = MOVE_STEP | REV
    writeDriver(0, [setCmd0])
    writeDriver(1, [setCmd1])
    stepH = (sStep >> 16) & 0x3f		# Bit7-6 :XX
    stepM = (sStep >> 8 ) & 0xff
    stepL = sStep & 0xff
    writeDriver(2, [stepH, stepM, stepH])

def goToAbsPos(ch, sPos):
    # 絶対位置sPosまで最短パスで移動
    # GO_TO_ABS  + sPos(3bytes:22bits)
    # ch=2: Ch0 & ch1 start simultaneusly
    if sPos < 0:
        sPos = (~(-sPos-1)) & 0x3FFFFF
    setCmd = GO_TO_ABS
    sPosH = (sPos >> 16) & 0x3f		# Bit7-6 : XX
    sPosM = (sPos >> 8 ) & 0xff
    sPosL = sPos & 0xff
    writeDriver(ch, [setCmd, sPosH, sPosM, sPosL])

def goToDirAbsPos(ch, sDir, sPos):
    # 方向(FWD/REV)を決めて、絶対位置sPosまで移動
    # GO_TO_DIR | sDir(0:REV,1:FWD) + sPos(3bytes:22bits)
    # ch=2: Ch0 & ch1 start simultaneusly
    if sPos < 0:
        sPos = (~(-sPos-1)) & 0x3FFFFF
    setCmd = GO_DIR_ABS | sDir
    sPosH = (sPos >> 16) & 0x3f		# Bit7-6 : XX
    sPosM = (sPos >> 8 ) & 0xff
    sPosL = sPos & 0xff
    writeDriver(ch, [setCmd, sPosH, sPosM, sPosL])

def resetAbsPos(ch):
    # ABS_POS データを初期化
    writeDriver(ch, [RESET_POS])

def softStop(ch):
    # 加減速ありの停止(Soft Stop)		
    writeDriver(ch, [SOFT_STOP])

def hardStop(ch):
    # 加減速なし停止(Hard Stop)
    writeDriver(ch, [HARD_STOP])

def getStatus(ch):
    #STATUSレジスターの読み取り
    #GET STATUS command(0xD0)->2bytes return
    writeDriver(ch, [GET_STATUS])
    rDataList = readDriver(ch, 2)
    rData = (rDataList[0] << 8) | rDataList[1]	#MSB...16Bits
    return rData
        
def readAbsPos(ch):
    # ABS_POS の読み出し
    writeDriver(ch, [GET_PARAM | ABS_POS]) # Get ABS_POS data
    rDataList = readDriver(ch, 3)
    rSign = rDataList[0] & 0x3F 		#Bit22 to Bit17
    rData = rSign << 8 					#Bit22 to Bit17
    rData = (rData | rDataList[1]) << 8	#Bit16 to Bit9
    rData = rData | rDataList[2]		#Bit8 to Bit 0
    if rSign & 0x20:					# minus sign
        rData = -((~rData) & 0x3FFFFF) - 1
    return rData

def close():
    # MDをpower-up conditionに
    resetDevice(2)
    # SPI通信用のGPIOをcleanup
    GPIO.cleanup()