はじめに
シリアルバス規格の一つであるSPI(Serial Peripheral Interface)を利用したデバイスの接続をPicoMite(MMBasic)で試してみた。
PicoMiteのSPI
PicoMite(MMBasic)でSPIを利用する手順は次の通り。
- SPIデバイスとPicoのピンの対応付け
- SPIチャンネルを開く
- SPIデバイスの設定
- SPIデバイスからのデータ受信
- SPIチャンネルを閉じる
ピン割り当て
SETPINコマンドでSPIデバイスのピンとRaspberry Pi Picoのピンを対応付ける。構文は次の通り。
SETPIN RX, TX, CLK, SPI
- SPIはチャンネル1でチャンネル2に割り当てる場合はSPI2にする
- TXはRaspberry Pi PicoからSPIデバイスへのデータ送信
- RXはRaspberry Pi PicoがSPIデバイスからデータ受信
- RX、TX、CLKに割り当てできるPicoのピンは下表の通り
パラメータ | チャンネル1のピン | チャンネル2のピン |
---|---|---|
RX | GP0、GP4、GP16、GP20 | GP8、GP12、GP28 |
TX | GP3、GP7、GP19 | GP11、GP15、GP27 |
CLK | GP2、GP6、GP18 | GP10、GP14、GP26 |
SPIチェンネルを開く
SPIを利用するにはSPI OPEN
コマンドでSPIチャンネルを開く必要がある。
構文は次の通り。
SPI OPEN speed, mode, bit
それぞれの引数は次の通り
- speed:Hz単位のクロック周波数
- mode:下表に示す送信モード0〜3までの数値で指定する
- bit:送受信するデータ幅の指定、4〜16ビットを指定する
モード | 説明 | CPOL | CPHA |
---|---|---|---|
0 | クロックはアクティブハイで、データは立ち上がりエッジでキャプチャ、立ち下がりエッジで出力 | 0 | 0 |
1 | クロックはアクティブハイで、データは立ち下がりエッジでキャプチャ、立ち上がりエッジで出力 | 0 | 1 |
2 | クロックはアクティブローで、データは立ち下がりエッジでキャプチャ、立ち上がりエッジで出力 | 1 | 0 |
3 | クロックはアクティブローで、データは立ち上がりエッジでキャプチャ、立ち下がりエッジで出力 | 1 | 1 |
データの送受信
SPIデバイスとのデータの送受信にはSPI()
関数を使います。構文は次の通り。
receive_data = SPI(data_to_send)
data_to_sendは送信データで同時に受信も行なう。受信したデータはreceive_dataに格納される。
受信のみを実行する場合は送信データは任意の値、たとえば0でよい。
バルク送受信
データの一括送受信が可能です。
データの一括送信は送信するデータの列挙、文字列、配列を引数に指定できます。構文を以下に示します。
SPI WRITE nbr, data1, data2, data3,..
SPI WRITE nbr, string$
SPI WRITE nbr, array()
- nbrは送信データ数
- 文字列や、配列はnbr以上の文字数、要素数が必要です
一括受信では受信したデータを配列に格納します。構文を以下に示します。
SPI READ nbr, array()
使用部品
SPIデバイスとして秋月電子通商で購入したADT7310使用 高精度・高分解能 SPI・16Bit 温度センサーモジュールを使用し、取得した温度の表示にI2C接続のOLED、YUWENW OLEDディスプレイ 0.96インチ 2個入り OLEDモジュール I2C 128X64 4ピン ホワイト Arduinoに対応に使用した。
ADT7310
ADT7310はADT7410と同様に13/16 ビットADC を内蔵しており、13ビット設定の場合で0.0625℃の、16ビット設定で0.0078℃の分解能がある。
バイト単位で受信した2バイト分のデータをシフト演算などで13ビットあるいは16ビットの値に変換して温度を算出する必要がある。
13ビットデータからの温度計算
13ビットデータの場合の計算式を以下に示す。
- $正の温度=読み取ったデータ/16$
- $負の温度=(読み取ったデータ - 8192)/16$
温度の正負判定はADT7410からのデータは2の補数なのでデータの最上位ビット(MSB)が1か否かで判断できる。13ビット出力の場合、4096以上の値が負数である。
16ビットデータからの温度計算
16ビットデータの場合の計算式を以下に示す。
- $正の温度=読み取ったデータ/128$
- $負の温度=(読み取ったデータ - 65536)/128$
16ビット出力の場合、読み取ったデータが32768以上の値が負数である。
配線
ADT7310とRaspberry Pi Picoの接続は下記のとおり。
ADT7310のピン | Picoのピン(物理ピン) |
---|---|
VDD | 3V3(36) |
SCL | GP18(24) |
SDO | GP20(26) |
SDI | GP19(25) |
CS | GP21(27) |
GND | GND |
OLEDとRaspberry Pi Picoの接続は下記のとおり。
OLEDのピン | Picoのピン(物理ピン) |
---|---|
GND | GND(3) |
VCC | 3V3(36) |
SCL | GP15(20) |
SDA | GP14(19) |
プログラム
ADT7310の出力をデフォルトの13ビット受信するプログラムを以下に示します。
Dim data%(2), raw%, temp!, stemp$ ' Define variables
SetPin GP20, GP19, GP18, SPI ' Set the pins to be used as SPI
SetPin GP21, DOUT ' Set the pin to SPI chipselect
SPI OPEN 100000, 3, 8 ' Open SPI,100kHz, mode 3, 8bit
Pin(GP21) = 0 ' Select ADT7310
SPI WRITE 4, &HFF, &HFF, &HFF, &HFF ' Rest SPI
Pin(GP21) = 1 ' Deselection ADT7310
Pause 300 ' Wait 300msec
Pin(GP21) = 0 ' Select ADT7310
Dummy = SPI(&H54) ' Set continuous read mode
CLS ' Clear OLED screen
Do
SPI READ 2, data%() ' Read temperature data, store to array
raw% = data%(0) << 8 Or data%(1) ' convert read data
raw% = raw% >> 3
If raw% >= 4096 Then raw% = raw% - 8192 ' Conversion when negative
temp! = raw% / 16 ' Calculate temperature
stemp$ = Str$(temp!, 2, 1) + Chr$(&H60) + "C" ' Create temperature string
Text 0, 0, stemp$,,3 ' Draw temperature on OLED
Pause 1000 ' Wait 1 sec
Loop
Pin(GP21) = 1 ' Deselection
SPI CLOSE ' Close SPI
End
このプログラムは次の処理を実行している。
ADT7310とRaspberry Pi Picoの接続に合わせてピンをSPIチャンネル1に設定
SetPin GP20, GP19, GP18, SPI
SetPin GP21, DOUT
SPIチャンネル1を100kHz、モード3、8ビットデータ送受信で開く
SPI OPEN 100000, 3, 8
SPIインターフェイスのリセット。
SPIデバイスの入力に32シリアルクロック周期以上の1を加えるとインターフェースがリセットされる。リセットしないとうまく動作してくれなかった。
CSをローレベル(PIN(GP21) = 0
)にしてSPIデバイスにデータを送信する。送信後CSをハイレベル(PIN(GP21) = 1
)にしている。
Pin(GP21) = 0
SPI WRITE 4, &HFF, &HFF, &HFF, &HFF
Pin(GP21) = 1
連続読み出しモードに設定する。
ADT7310のデータシートではSPIデバイスへのコマンド01010100(&H54)
を送信するとADT7310は温度データを連続して読み出せるようになります。
Pin(GP21) = 0
Dummy = SPI(&H54)
送信するコマンド01010100(&H54)
の各ビットは次の意味がある。
- C7、C1、C0は常に0
- C6のR/Wは読み書きの指定で、1にすると読み出しに、0にすると書き込みになる
- C5〜C3はレジスタの指定で010(0x02)は温度データのレジスタを示す。ちなみに0x01はコンフィギュレーションレジスタ
- C2は連続読み出しの指定で、1にすると連続読み出しになる
C7 | C6 | C5 | C4 | C3 | C2 | C1 | C0 |
---|---|---|---|---|---|---|---|
0 | R/W | レジスタアドレス | 連続読み出し | 0 | 0 | ||
0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
戻り値は利用しない
ADT7310からのデータを2バイト分読み取り、配列data%に保存する。
先頭8ビット分がdata%(0)に、後続の8ビットがdata%(1)に格納される。
SPI READ 2, data%()
受信した温度データを13ビットに変換
温度データは8ビットずつ受信し、2つの配列要素に格納されているため13ビットデータへの変換を行なう。先頭8ビット分を左へ8ビットシフトした値(data%(0) << 8)と後半の8ビット(data%(1))との論理和を求め16ビットの値にすし、変数raw%に格納する。
次に不要な下位3ビット分を取り除くために右へ3ビットシフトする。
raw% = data%(0) << 8 Or data%(1)
raw% = raw% >> 3
温度データから温度の算出
ADT7310から取得した温度データは2の補数なので符号ビットのみが1の4096(1 0000 0000 0000)以上は負数となり、値の変換が必要になる。負数のときは2の補数の温度データから8192(=$2^{13}$)を引くと負数が得られる。
13ビットの分解能が0.0625℃なので温度データを16(=1/0.0625)で割れば温度が得られる。
If raw% >= 4096 Then raw% = raw% - 8192
temp! = raw% / 16
表示用文字列の作成
算出された温度の値を整数部2桁、小数部1桁の文字列に変換し、OLEDへ出力する。
stemp$ = Str$(temp!, 2, 1) + Chr$(&H60) + "C"
Text 0, 0, stemp$,,3