MCP23S17 "SPI"のピン配置と配線
No. | Name | Type | Function | Type | Name | No. | |
---|---|---|---|---|---|---|---|
1 | GPB0 | I/O | 双方向入出力ピンです。 "変更時割り込み"(IOC:Interrupt-on-change)または内臓の微弱なプルアップ抵抗、あるいはその両方を有効化出来ます。 |
I/O | GPA7 | 28 | |
2 | GPB1 | GPA6 | 27 | ||||
3 | GPB2 | GPA5 | 26 | ||||
4 | GPB3 | GPA4 | 25 | ||||
5 | GPB4 | GPA3 | 24 | ||||
6 | GPB5 | GPA2 | 23 | ||||
7 | GPB6 | GPA1 | 22 | ||||
8 | GPB7 | GPA0 | 21 | ||||
9 | VDD | P | 電源 1.8〜5.5V | PORTA B 割り込み出力 |
O | INTA | 20 |
10 | VSS | グランド | INTB | 19 | |||
11 | CS | I | チップセレクト | ハードウェアリセット | I | RESET | 18 |
12 | SCK | I | シリアルクロック入力 | ハードウェア アドレスピン |
I | A2 | 17 |
13 | SI | I | シリアルデータ入力 | A1 | 16 | ||
14 | SO | O | シリアルデータ出力 | A0 | 15 |
A、Bの各GPIOは8個なので1byte(8bit)単位で管理しやすいのと、8bitのみのMCP23008という製品もあるので、その辺りも兼ね合いがあるのかもですけどね。
実際の配線を見て行きましょう。
まずは、NodeMCU DevKitのハードウェアSPIのピン配置を確認。
SCKSISOは、NodeMCU DevKitのHSPI_CLKHSPI_MOSIHSPI_MISOと接続。 CSは、先の3つと異なり空いているGPIOならどれでも良いのですが、D8にHSPI_CSが割り当てられているので、こちらを使用。
A0A1A2の3つは、 CSでの選択が既定となるため必須ではありませんが、回路的に必ずプルアップもしくはプルダウン接続しておく必要があります。
RESETをプルアップしてリセットを無効化。
以上で検証に必要な配線は完了。
MivroPythonでMCP23S17へSPIで接続
ここから先は、NodeMCU DevKitでの作業。SPI通信では、データシートに従いスレーブアドレスに続けて以下のようにR/Wビットを設定し制御バイトとして送信します。
CS | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
制御バイト | |||||||||||
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | ||||
0 | 1 | 0 | 0 | A2 | A1 | A0 | R/W | ||||
スレーブアドレス | Read : 1 Write : 0 |
||||||||||
制御バイトアドレス一覧 | ||||||||
---|---|---|---|---|---|---|---|---|
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | |
0 | 1 | 0 | 0 | A2 | A1 | A0 | R | W |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 |
0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |
0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 |
NodeMCUを起動して、必要となるSPIとPinクラスををインポート。
Ctrl + eを押してからコピペしてCtrl + d。
MicroPython v1.10-8-g8b7039d7d on 2019-01-26; ESP module with ESP8266
Type "help()" for more information.
>>>
paste mode; Ctrl-C to cancel, Ctrl-D to finish
===
===
===
=== from machine import SPI, Pin
spi = SPI(1, baudrate=10000000, polarity=0, phase=0)
cs = Pin(15, Pin.OUT)
cs.value(1) # SPI、Pinクラスのインポート
# SPIオブジェクトの作成
# CSピンのアサイン
# CSピンをHIGHに
===
>>>
idは、0 : 使用不可 / 1 : ハードウェアSPI / -1 : ソフトウェアSPIType "help()" for more information.
>>>
paste mode; Ctrl-C to cancel, Ctrl-D to finish
===
===
===
=== from machine import SPI, Pin
spi = SPI(1, baudrate=10000000, polarity=0, phase=0)
cs = Pin(15, Pin.OUT)
cs.value(1) # SPI、Pinクラスのインポート
# SPIオブジェクトの作成
# CSピンのアサイン
# CSピンをHIGHに
===
>>>
baudrateは、ハードウェアSPIの最大値10MHz、極性と位相も設定。
CSは、GPIO15を指定。
SPIでの接続確認は、、、、残念ながらありません。
という事で、SPIで使用するメソッドを先に確認。
SPI. read(nbytes, write=0x00)
nbytes:受信するバイト数を指定。
write :連続して送信する1byteを数値型(16進数)で指定
nbytes:受信するバイト数を指定。
write :連続して送信する1byteを数値型(16進数)で指定
SPI.readinto(buf, write=0x00)
buf:受信するバッファをbytesオブジェクトで指定
write:連続して送信する1byteを数値型(16進数)で指定
buf:受信するバッファをbytesオブジェクトで指定
write:連続して送信する1byteを数値型(16進数)で指定
SPI.write(buf)
buf:送信するバッファをbytes型で指定
buf:送信するバッファをbytes型で指定
SPI.write_readinto(write_buf, read_buf)
write_buf:受信するバッファをbytearrayで指定
read_buf:送信するバッファをbytesオブジェクトで指定
write_bufと read_buf の両方のバッファは同じ長さに指定。write_buf:受信するバッファをbytearrayで指定
read_buf:送信するバッファをbytesオブジェクトで指定
SPI. read()以外は戻り値はナシ
pybの様に、"Read"ではなく"Receive"、"Write"ではなく"Send"が正しい気がする。
MivroPythonでMCP23S17の操作
MCP23S17の操作は、基本的に以下のレジスタアドレスを呼び出して読み書きするだけです。それ以上も以下も出来ません。MCP23S17のレジスタアドレス | |||
---|---|---|---|
Access to: | Address IOCON.BANK = 0 |
Address IOCON.BANK = 1 |
Function |
IODIRA | 0x00(0b00000) | 0x00(0b00000) | I/O方向レジスタ 0:OUTPUT/1:INPUT |
IODIRB | 0x01(0b00001) | 0x10(0b10000) | |
IPOLA | 0x02(0b00010) | 0x01(0b00001) | 入力極性ポートレジスタ 0:same logic state/1:opposite logic state |
IPOLB | 0x03(0b00011) | 0x11(0b10001) | |
GPINTENA | 0x04(0b00100) | 0x02(0b00010) | 状態変化割り込みピン設定レジスタ 0:Disable/1:Enable |
GPINTENB | 0x05(0b00101) | 0x12(0b10010) | |
DEFVALA | 0x06(0b00110) | 0x03(0b00011) | 状態変化割り込み用の既定値
コンペアレジスタ 7〜0 bitに比較値を設定 |
DEFVALB | 0x07(0b00111) | 0x13(0b10011) | |
INTCONA | 0x08(0b01000) | 0x04(0b00100) | 状態変化割り込み制御レジスタ 0:ピンの変化のみ参照/1:DEFVAL設定値との比較参照 |
INTCONB | 0x09(0b01001) | 0x14(0b10100) | |
IOCON | 0x0A(0b01010) | 0x05(0b00101) | I/Oエクスパンダコンフィグレーションレジスタ 7〜1 bitで設定 |
IOCON | 0x08(0b01011) | 0x15(0b10101) | |
GPPUA | 0x0C(0b01100) | 0x06(0b00110) | GPIOプルアップ抵抗レジスタ 0:無効化 / 1:有効化 |
GPPUB | 0x0D(0b01101) | 0x16(0b10110) | |
INTFA | 0x0E(0b01110) | 0x07(0b00111) | 割り込みフラグレジスタ ※読込みのみ 0:割り込み無し/1:割り込みを発生 |
INTFNB | 0x0F(0b01111) | 0x17(0b10111) | |
INTCAPA | 0x10(0b10000) | 0x08(0b01000) | 割り込み発生ポート値レジスタ ※読込みのみ 0:Low / 1:High |
INTCAPB | 0x11(0b10001) | 0x18(0b11000) | |
GPIOA | 0x12(0b10010) | 0x09(0b01001) | 汎用I/Oポ ートレジスタ 0:Low / 1:High |
GPIOB | 0x13(0b10011) | 0x19(0b11001) | |
OLATA | 0x14(0b10100) | 0x0A(0b01010) | 出力ラッチレジスタ 0:Low / 1:High |
OLATNB | 0x15(0b10101) | 0x0A(0b11010) |
これは、A、B二つのグループのレジスタアドレスを連続して(16bit)管理するか、分割して(8bit)管理するのかの違いです。BANK 1のアドレスは一見バラバラに見えますがBANK 0を右ビットローテーション(環状シフト)した値に統一されています。
ちなみに、ビットローテーションとは以下の様な働き。
この辺りの設定はコンフィグレーションレジスタ(IOCON)で行い、標準でBank0になるように設定されています。
話が外れはじめたので、実際にレジスタアドレスを読み込んで値を確認。
>>>
paste mode; Ctrl-C to cancel, Ctrl-D to finish
===
===
===
=== cs.value(0)
spi.write(b'\x41\x00')
spi.read(22)
cs.value(1) # CSピンをLOWに変更
# 制御バイト(0x41)、レジスタアドレス(0x00)を送信
# 上で指定したアドレス(0x00)から22byte(0x00〜0x15)を受信
# CSピンをHIGHに戻す
===
b'\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>>
SPI通信では、CSピンのHIGH、LOWが通信開始の終了のタイミングとなります。paste mode; Ctrl-C to cancel, Ctrl-D to finish
===
===
===
=== cs.value(0)
spi.write(b'\x41\x00')
spi.read(22)
cs.value(1) # CSピンをLOWに変更
# 制御バイト(0x41)、レジスタアドレス(0x00)を送信
# 上で指定したアドレス(0x00)から22byte(0x00〜0x15)を受信
# CSピンをHIGHに戻す
===
b'\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>>
受信した22byteの冒頭の戻り値b'\xff'('0b11111111')は、I/O方向レジスタレジスタ(IODIRA)で、0:OUTPUT/1:INPUTなので、全てのGPIOが入力に設定されている事を示しています。取得された値の上位ビットから下位ビットへの並びは、GPIOポートGPx7〜GPx0の並びに対応しています。
これらの詳細は、データシートの"TABLE 3-2"、"TABLE 3-3"を参照。
書込みも試してみます。
>>>
paste mode; Ctrl-C to cancel, Ctrl-D to finish
===
===
=== cs.value(0)
spi.write(b'\x40\x00\x55')
cs.value(1) # CSピンをLOWに変更
# 制御バイト(0x40)、レジスタアドレス(0x00)に"0x55"を送信
# CSピンをHIGHに戻す
>>>
制御バイトは、書き込み用に0x40(0b01000000)、IODIRA(0x00)へ0x55(0b01010101)の値を書き込み。paste mode; Ctrl-C to cancel, Ctrl-D to finish
===
===
=== cs.value(0)
spi.write(b'\x40\x00\x55')
cs.value(1) # CSピンをLOWに変更
# 制御バイト(0x40)、レジスタアドレス(0x00)に"0x55"を送信
# CSピンをHIGHに戻す
>>>
正常に書き込みが行われたのか確認。
>>>
paste mode; Ctrl-C to cancel, Ctrl-D to finish
===
===
===
=== cs.value(0)
spi.write(b'\x41\x00')
spi.read(22)
cs.value(1) # CSピンをLOWに変更
# 制御バイト(0x41)、レジスタアドレス(0x00)を送信
# 上で指定したアドレス(0x00)から22byte(0x00〜0x15)を受信
# CSピンをHIGHに戻す
===
b'U\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>>
先頭の値が"U"なのは、キャラクタに変換表示されているためなので、問題なく送信されているようです。ただ、ちょっと動作がわかりづらいのでLEDを繋いでみましょう。
paste mode; Ctrl-C to cancel, Ctrl-D to finish
===
===
===
=== cs.value(0)
spi.write(b'\x41\x00')
spi.read(22)
cs.value(1) # CSピンをLOWに変更
# 制御バイト(0x41)、レジスタアドレス(0x00)を送信
# 上で指定したアドレス(0x00)から22byte(0x00〜0x15)を受信
# CSピンをHIGHに戻す
===
b'U\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>>
※IODIR(0x00、0x01)などポートに関連するアドレスの変更直後は、GPIO(0x12〜0x15)の値に影響が出るようです。
MivroPythonとMCP23S17でLチカ
まずは、ブレッドボードを増設してLEDを配線。ちゃんと抵抗値を計算して接続しましょう!
って事で、Lチカコード。
from time import sleep from machine import SPI, Pin spi = SPI(1, baudrate=10000000, polarity=0, phase=0) cs = Pin(15, Pin.OUT) cs.value(1) cs.value(0) spi.write(b'\x40\x00\x00') cs.value(1) for i in range(10) : cs.value(0) spi.write(b'\x40\x12\xFF') cs.value(1) sleep(0.5) cs.value(0) spi.write(b'\x40\x12\x00') cs.value(1) sleep(0.5)8個のLEDがまとまって点滅するので、眩しいです。
続いて、もう少しヒネったLチカ。
from time import sleep from machine import SPI, Pin b = 0xFF spi = SPI(1, baudrate=10000000, polarity=0, phase=0) cs = Pin(15, Pin.OUT) cs.value(1) cs.value(0) spi.write(b'\x40\x00\x00') cs.value(1) for i in range(10) : cs.value(0) spi.write(b'\x40\x12\xFF') cs.value(1) sleep(0.5) cs.value(0) spi.write(b'\x40\x12\x00') cs.value(1) sleep(0.5) for i in range(8) : cs.value(0) spi.write(b'\x40\x12') spi.write(b.to_bytes(1, 'big')) cs.value(1) sleep(0.5) b = b >> 1 b = 0x01 for i in range(9) : cs.value(0) spi.write(b'\x40\x12') spi.write(b.to_bytes(1, 'big')) cs.value(1) sleep(0.5) b = b << 110回点滅したあと、一つづつ点灯しているLEDが現象し、残った1つが左方向に移動して消灯するだけのコード。
ベタに書くと長くなるので、メソッドでまとめないと使いづらいですね。
0 件のコメント:
コメントを投稿