村田ジャイロセンサSCC2230をRaspberry Piに接続

　SCC2230は低ドリフトの1軸ジャイロ＋３軸加速度センサである。インターフェースがSPIなのでSPIのインターフェースを持つマイコンであれば簡単に利用できると安易に考えたが実に大変だった。
　データシートによると３２ビット（４バイト分）の同期型データ伝送が求められているので、raspberry pi の標準的なpython モジュールspidev.SpiDev()を用いて通信を行った。CSを下げてから４バイトを送信してCSを上げるという動作を行うxfer2を用いる。送信するデータはデータシートにあるStatus Summaryを読み出す命令である。
送信データ[0x7c,0x00,0x00,0xb3]

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290990/f1ae92db-b1c5-ffaf-1de5-66e25f571ca1.png)

　残念ながらこれはうまくいっていない。いくら送信してもレジスタ選択エラーの応答しか返ってこない。通信時間は２ｍ秒なので決して早くない。推奨インターバルの０．５ｍ秒に比べても遅いぐらいである。速度を早くしても、ディレイやCSまでの間隔を変えても応答は全く変化がなかった。

　波形をよく見ると、８ビットのCLKの間に少し隙間が空いている。このICのステータスマシンはクロックパルスが等間隔で現れることを想定して動いているためにうまくいかないのではないか。
　この隙間をなくする方法を模索したがRaspberry Piで可能なのかどうかもはっきりしなかった。
　ではSPIのポートの使用をやめてGPIOを4本使ってプログラムでポートを操作する方法に変えることにした。
　このICでは通信データのワードのチェックにCRC8を使用するのでカスタムなCRCを計算できるモジュールが必要である。pythonに標準のcrcでは対応していなかったので、<a href="https://github.com/Nicoretti/crc">こちら</a>を使用した。



\#scc-2230 module gpio

\# CS GPIO05   OUT
\# MISO GPIO06 IN
\# MOSI GPIO13 OUT
\# CLK  GPIO19 OUT

import sys
import os
import time
import datetime
import RPi.GPIO as GPIO
from crc.crc import CrcCalculator, Configuration
   
class scc2230():
    def __init__(self):
        BCM2708_PERI_BASE=0x20000000
        GPIO_BASE=(BCM2708_PERI_BASE + 0x00200000)
        BLOCK_SIZE=4096

        self.CS = 5
        self.MISO=6
        self.MOSI=13
        self.CLK=19

        self.comdata={
            "RATE":0x040000f7,
            "ACCX":0x100000e9,
            "ACCY":0x140000ef,
            "ACCZ":0x180000e5,
            "TEMP":0x1c0000e3,
            "RATE1":0x240000c7,
            "RATE2":0x280000cd,
            "ACC":0x3c0000d3,
            "HARDRESET":0xd8000431,
            "MONITOR":0xd80008ad,
            "ID0":0x600000a1,
            "ID1":0x640000a7,
            "STAT":0x6c0000ab,
            "SUMMARY":0x7c0000b3,
            "FLT60":0xfc200006,
            "FLT10":0xfc1000c7,
            "READID":0x740000bf
        }

        GPIO.setmode(GPIO.BCM)
        GPIO.setwarnings(False)
        GPIO.setup(self.CS, GPIO.OUT)
        GPIO.setup(self.MISO, GPIO.IN)
        GPIO.setup(self.MOSI, GPIO.OUT)
        GPIO.setup(self.CLK, GPIO.OUT)

        GPIO.output(self.CS, 1)
        GPIO.output(self.MOSI, 0)
        GPIO.output(self.CLK, 0)

        #CRC checker
        width = 8
        poly=0x1d
        init_value=0xff
        final_xor_value=0xff
        reverse_input=False
        reverse_output=False
        configuration = Configuration(width, poly, init_value, final_xor_value, reverse_input, reverse_output)
        use_table = True
        self.crc_calculator = CrcCalculator(configuration, use_table)

        #acc-2230 setup

        time.sleep(0.02)
        self.sendcom(self.comdata["FLT60"])
        time.sleep(0.73)

        self.sendcom(self.comdata["RATE1"])
        #self.sendcom(self.comdata["STAT"])
        self.sendcom(self.comdata["RATE2"])
        #self.sendcom(self.comdata["STAT"])
        self.sendcom(self.comdata["ACC"])
        #self.sendcom(self.comdata["STAT"])
        self.sendcom(self.comdata["SUMMARY"])
        #self.sendcom(self.comdata["STAT"])
        self.sendcom(self.comdata["READID"])
        #self.sendcom(self.comdata["STAT"])
        self.sendcom(self.comdata["SUMMARY"])

        #wait for ready

        while True:
            flag,readByteArray = self.sendcom(self.comdata["SUMMARY"])
            print(u"stat {0}".format(flag),hex(readByteArray))
            flag,readByteArray = self.sendcom(self.comdata["STAT"])
            print(u"summary {0}".format(flag),hex(readByteArray))

            if flag and readByteArray&0x4900 == 0x4900:
                break
            time.sleep(0.1)
            
    def BinToint(self,data):
        val=(data>>8)&0xffff
        if val>32767:
            val-=65536
        return val

    def rateconv(self,data):
        val=self.BinToint(data)
        rate=val/50.0 #deg/sec
        return rate

    def accconv(self,data):
        val=self.BinToint(data)
        acc=val/5886.0 #g
        return acc

    def tempconv(self,data):
        val=self.BinToint(data)
        temp=val/14.7+60.0 #°C
        return temp


    def sendcom(self,comdata):
        cnt=0
        indata=0b0
        #print(u"send",hex(comdata))
        GPIO.output(self.CS, 0) #CS assert
        while cnt < 32:
        #CS
            if comdata & 0x80000000:
                GPIO.output(self.MOSI, 1)
            else:
                GPIO.output(self.MOSI, 0)

            indata<<=1

            GPIO.output(self.CLK, 1) #CLK assert

            indata+=GPIO.input(self.MISO)

            GPIO.output(self.CLK, 0) #CS negate

            comdata<<=1
            cnt+=1

        GPIO.output(self.CS, 1) #CS assert

        #print(u"recv",hex(indata))

        # check crc8
        csum=indata&0xff
        dat2=(indata>>8)&0xff
        dat1=(indata>>16)&0xff
        dat0=(indata>>24)&0xff

        checksum = self.crc_calculator.calculate_checksum([dat0,dat1,dat2])
        if checksum != csum:
            print("crc error ",hex(dat0),hex(dat1),hex(dat2),hex(csum))
            return False,indata

        return True,indata

    def readdata(self):
    
        flag,stat = self.sendcom(self.comdata["RATE"])
        if flag==False:
            print("flag1 error",stat)
            return False
        flag,rate = self.sendcom(self.comdata["ACCX"])
        if flag==False:
            print("flag2 error",rate)
            return False
        flag,accx = self.sendcom(self.comdata["ACCY"])
        if flag==False:
            print("flag3 error",accx)
            return False
        flag,accy = self.sendcom(self.comdata["ACCZ"])
        if flag==False:
            print("flag4 error",accy)
            return False
        flag,accz = self.sendcom(self.comdata["TEMP"])
        if flag==False:
            print("flag5 error",accz)
            return False
        flag,temp = self.sendcom(self.comdata["STAT"])
        if flag==False:
            print("flag6 error",temp)
            return False
        return (rate,accx,accy,accz,temp)

    def __del__(self):
        GPIO.cleanup(self.CS)
        GPIO.cleanup(self.MOSI)
        GPIO.cleanup(self.MISO)
        GPIO.cleanup(self.CLK)

if __name__=='__main__':
    # main program

    scc=scc2230()

    while True:
        values=scc.readdata()
        if isinstance(values,bool):
            print(u"data error ")
        else:
            rate=scc.rateconv(values[0])
            accx=scc.accconv(values[1])
            accy=scc.accconv(values[2])
            accz=scc.accconv(values[3])
            temp=scc.tempconv(values[4])
            print(u"values {0} {1} {2} {3} {4}".format(rate,accx,accy,accz,temp))
        time.sleep(0.01)
        
        
    del scc
    sys.exit()


この書き換えにより通信が安定しデータが取り出せるようになった。

　ソフトウエア処理によりクロックに隙間がなくなったために解決したのだろうか。測定したところ、８ビットごとの隙間はなくなったが、OSのディスパッチもあるのでクロックパルスにあちこちに隙間がある。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290990/7add84a8-640c-2a65-2401-7570b68c7eae.png)

　それでも安定しているということは、８ビットごとの隙間の繰り返しこそ問題が発生する要因となっていると推測される。

　GPIOのアクセス速度が遅いので通信は1.2ミリ秒くらいはかかる。このあたりはC言語を用いてレジスタを直接アクセスすれば早くなるだろう。通信のためのCPUの負荷はかなりのものなので使いにくいICではある。