SCC2230は低ドリフトの1軸ジャイロ+3軸加速度センサである。インターフェースがSPIなのでSPIのインターフェースを持つマイコンであれば簡単に利用できると安易に考えたが実に大変だった。
データシートによると32ビット(4バイト分)の同期型データ伝送が求められているので、raspberry pi の標準的なpython モジュールspidev.SpiDev()を用いて通信を行った。CSを下げてから4バイトを送信してCSを上げるという動作を行うxfer2を用いる。送信するデータはデータシートにあるStatus Summaryを読み出す命令である。
送信データ[0x7c,0x00,0x00,0xb3]
残念ながらこれはうまくいっていない。いくら送信してもレジスタ選択エラーの応答しか返ってこない。通信時間は2m秒なので決して早くない。推奨インターバルの0.5m秒に比べても遅いぐらいである。速度を早くしても、ディレイやCSまでの間隔を変えても応答は全く変化がなかった。
波形をよく見ると、8ビットのCLKの間に少し隙間が空いている。このICのステータスマシンはクロックパルスが等間隔で現れることを想定して動いているためにうまくいかないのではないか。
この隙間をなくする方法を模索したがRaspberry Piで可能なのかどうかもはっきりしなかった。
ではSPIのポートの使用をやめてGPIOを4本使ってプログラムでポートを操作する方法に変えることにした。
このICでは通信データのワードのチェックにCRC8を使用するのでカスタムなCRCを計算できるモジュールが必要である。pythonに標準のcrcでは対応していなかったので、こちらを使用した。
#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()
この書き換えにより通信が安定しデータが取り出せるようになった。
ソフトウエア処理によりクロックに隙間がなくなったために解決したのだろうか。測定したところ、8ビットごとの隙間はなくなったが、OSのディスパッチもあるのでクロックパルスにあちこちに隙間がある。
それでも安定しているということは、8ビットごとの隙間の繰り返しこそ問題が発生する要因となっていると推測される。
GPIOのアクセス速度が遅いので通信は1.2ミリ秒くらいはかかる。このあたりはC言語を用いてレジスタを直接アクセスすれば早くなるだろう。通信のためのCPUの負荷はかなりのものなので使いにくいICではある。