USB-SPI変換ボードMCP2210とFT232Hの比較


USB-SPI変換ボードが欲しい

 仕事でSPI通信で制御する機器があるのでRaspberry Piとspidevを使って制御しています。Raspberry PiはSPI通信の実装が簡単で便利ですが、SPIのコントローラとして使うとなると毎回電源を入れてからOS立ち上がりまで時間がかかる、コンセントを1つつぶさないといけない、GUIアプリケーションを立ち上げたとき動作が重いといった不便点があり、USB-UART変換ボードのようにパソコンのUSBポートに繋げてSPI通信ができる製品はないかと思って調べてみました。
 そしてMCP2210とFT232Hという2種類の製品が見つかったので、実際SPI通信ができるところまで動かしてみて使い勝手などを比較した記事になります。一応CH341Aというボード見つかりましたが、マイコンのプログラム書き込みが主な利用目的となっており、任意のデータのSPI通信を行うのは私の知識では困難に思えたので除外しています。
 MCP2210とFT232Hの特徴を比較うると以下の表のようになります。

$$
\begin{array}{|c|c|c|} \hline
項目 & MCP2210 & FT232H \\ \hline
価格 & 2千円弱~ & 2千円前後 \\ \hline
環境構築 & 簡単 & 割と簡単 \\ \hline
コード作成 & 面倒 & 簡単 \\
(Python) & & ※1チップだけなら\\ \hline
最大速度(仕様) & 3Mbps & 12Mbps? \\ \hline
最大速度(実質) & 25kbps? & 不明 \\ \hline
最大子機数 & 8 & 12 \\
& & ※低速 \\ \hline
\end{array}
$$

それぞれの詳細についてはここから述べていきます。

MCP2210

 MCP2210はUSBHIDで通信してGPIOやSPI通信が行えるチップです。USBHIDはマウスやキーボードなどのデバイスを制御するための規格で、別途ドライバをインストールしなくてもいい手軽さがメリットです。

https://www.microchip.com/en-us/product/mcp2210

IOピン

 MCP2210のピンはSPI通信に必要なSCK、MOSI、MISOと、CSとして利用できるGPIOが8本(0~7)ついています(電源やUSBなどチップ動作に必要なピンは割愛します)。9本目のGP8は入力オンリーであるためCSとしては使えません。GP0~7はユーザが任意にGPIOかCSに割り当てることができ、最大8個の子機を制御することができます。

通信速度

 データシートの1ページに「Bit Rates from 1500 bps Up to 3 Mbps」となっていますが、実測では少し様子が異なっています。

1Mbpsに設定したときの波形

 上の図はMCP2210のSPI通信速度を1Mbpsに設定して通信を行ったときの出力波形です。黄色はクロック、紫はMOSI、青はCSの波形を表しています。黄色のクロックは1MHzが出力されていますが、データを1バイト送信する毎に4usecの遅延が発生しており、大量のデータを送信する場合実質ビットレートは25kbpsになります。あいにくこの不具合がチップの仕様なのか、制御プログラムの問題なのか、それとも購入したボードの問題なのかは特定できていません。
 ちなみに、SPIが十分低速であればバイト間の遅延は生じません(下図)。

10kbpsに設定したときの波形

環境構築

 上でも書いたようにMCP2210はUSBHIDで通信するため別途ドライバのインストールなどは必要ありません。シンプルにデータを送りたいだけならチップの製品ページから制御用GUIユーティリティをダウンロードして使えますし、Pythonで制御する場合はhidapiというパッケージをインストールするだけで動かせます。

コード作成

 PythonのHIDAPIでMCP2210を制御するのはかなり面倒です。
 HIDでは「レポート」と呼ばれる64バイトのデータ列をやりとりしますが、MCP2210を制御するためにはデータシートの3章を参考にしながらやりたい制御に対応したデータの組み合わせを生成しなければなりません。例えばGP7~0のピンを出力に設定する場合は以下のようなコードになります。

import hid
mcp2210 = hid.device()
mcp2210.open(1240, 222)  # vendor ID, product ID

data = [0]  # Windowsではレポートの先頭に[0]を付けて65バイトにする必要あり
# データシートp.47 3.2.6
data +=[0x32, 0, 0, 0]  # 0x32はGPIOピン向き設定コード
data += [0x00, 0x01]  # 1バイト目がGP7~0、2バイト目LSBがGP8(入力only)
data += 58*[0]  # 残りは0埋め
mcp2210.write(data)

 データーシートの「3.2.6 SET(VM) GPIO CURRENT PIN DIRECTION」のTABLE 3-44と合わせて読めば、送信するバイト列がどういう構造になっているか理解できると思います。また、コード中にも書いてますが、Windowsでは64バイトのレポートの先頭に[0]を加えて送信しないと正常に通信できない既知のバグがあるようです。
 現在のピンの向きを読み込む場合は下のようなコードになります。

import hid
mcp2210 = hid.device()
mcp2210.open(1240, 222)  # vendor ID, product ID

data = [0]  # Windowsではレポートの先頭に[0]を付けて65バイトにする必要あり
# データシートp.46 3.2.5
data +=[0x33, 0, 0, 0]  # 0x32はGPIOピン向き設定コード
data += 60*[0]  # 残りは0埋め
mcp2210.write(data)

recv = mcp2210.read(64)
direction = recv[4:6]

 本来であればここはSPI通信のサンプルを載せるべきですが、初期設定込みだと説明が長くなりますし、どちらかというと「コード作成がいかに大変か」という点を伝えたかったため比較的分かりやすいGPIO制御のコードにしまし。これで伝わったと思いますが、全ての制御をデータシートのコマンド表から調べながら行うのはかなり大変で、まずは使いやすい制御用自作モジュールを作らないことにはまともに使うことはできないと思います。

 一応、mcp2210-pythonという、mcp2210で簡単にSPI通信が行えるパッケージもあるのでこちらを使うという手もありますが

  • ドキュメントの情報が少なく、使うならコードの中身を読み解かなければならない

  • 同時に複数のCSをアクティブにできない

  • ビットレートの変更メソッドがない

といった不便点があるので、GPIO制御だけなら有用ですが、SPI通信ではかゆいところに手が届かないといったところなので、私はhidapiで自作モジュールを作成しました。

価格

 MCP2210の乗ったボードは3種類見つけることができました。

  • みんなのラボ「USB2SPI(ブレッドボード版)」¥1,650@マルツ

  • MikroElektronika「MIKROE-1204」¥2,411@RS

  • MicroChip「BREAKOUT MODULE MCP2210」¥8,171@マルツ

 私は一番安いみんなのラボ「USB2SPI(ブレッドボード版)」を買っており、現状特段問題は感じていません。ただ、「みんなのラボ」の方はホームページを見てみるとかなり小規模の生産体制のように見受けられ、恥ずかしながら私は「みんなのラボ」のこれまでの実績なども知らないので、今後安定して供給されるかの懸念点が残ります。

 2つめのMikroElektronika「MIKROE-1204」は回路図を確認するとGPIOピンが5本しか使えない仕様になっています。

https://docs.rs-online.com/5cbb/0900766b8140743f.pdf

 MicroChip「BREAKOUT MODULE MCP2210」はGPIOも全て使えて供給の不安点もありませんが、いささか高価です。データシートにschematicとボードのレイアウト図のようなものが乗っていて、そこまで複雑な回路には見えないので、自分で基板設計ができるのであれば安価で大量に生産できるかもしれません。

https://ww1.microchip.com/downloads/en/DeviceDoc/52056A.pdf

FT232H

IOピン

 FT232HにはSPI通信を行うためのSCK、MOSI、MISO、CSと12本のGPIOがついています。12本のGPIOはCSとして割り当てることはできず、もし複数のチップを制御するのであればGPIOとして制御してからSPIのデータ通信を実行するという形で実装する必要があります。

通信速度

 データシートでは12MbpsのUART通信ができるという記述がありましたが、SPI通信の最大速度は確認することができませんでした。試しにSPIの速度を50MHzに設定して出力波形を確認すると20~25MHz程度のクロック(下図黄色波形)が出力されています。オシロスコープの帯域が100MHzしかないせいで波形がなまっているため正確な最大速度は測定できませんでしたが、12Mbps程度であれば矩形波として出力できていることが確認できました。

50MHz設定の出力

環境構築

 FT232Hを制御するためにはWindowsで自動でインストールされるものとは違うドライバが必要になるためMCP2210よりは環境構築には少し手間がかかります。とはいえあくまでも「MCP2210に比べて」のことで、Adafruit社のホームページのガイダンスに従って進めれば難しいところは特にありません。

コード作成

 FT232Hは以下のコードでSPI通信ができます。

import busio
import board

baudrate = 1e6
polarity = 0
phase = 0
bits = 8

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
spi.try_lock()
spi.configure(baudrate, polarity, phase)

buf_out = [0xa5, 0x5a]
buf_in = [0, 0]
spi.write_readinto(buf_out, buf_in)  # buf_outを送信して受信データをbuf_inに格納
print(buf_in)

spi.unlock()

 MCP2210と比べると各行で何をしているかが分かりやすくすっきりとしたコードになっています。
 子機が1つだけであればこれで十分ですが、複数を個別制御するためにGPIOを併用するとなると少し難易度があがります。上にリンクを貼ったAdafruitのホームページのGPIO制御のコードは以下のようになっていますが、以下のコードのdigitalio.DigitalInOutをbusio.SPIと併用しようとするとGPIOの制御ができなくなります。

import time
import board
import digitalio

led = digitalio.DigitalInOut(board.C0)
led.direction = digitalio.Direction.OUTPUT

while True:
    led.value = True
    time.sleep(0.5)
    led.value = False
    time.sleep(0.5)

 原因を調べてみたところ、boardのPinクラスの定義のところにありました。boardとbusioはFT232Hを制御するときpyftdiのI2cControllerというクラスを使ってGPIO制御用のインスタンスを、SpiControllerでSPI制御用のインスタンスを生成していますが、どうやらI2cControllerとSpiControllerが競合しているようで、SpiControllerでSPI通信を行おうとするとI2cContollerから生成したGPIOインスタンスからの制御が効かなくなるようです。Adafruitのblinka(boardやbusioが含まれるパッケージ)ではFT232HのGPIOをI2cContollerで生成すると固定されているので、GPIOとSPIを併用するためにはpyftdiでコードを書かなければなりません。

 pyftdiでSPI通信とGPIO制御を並行するコードは以下のようになります。ちなみに、ボードについている「I2C Mode」のスイッチがオンになっているとMOSIピンとMISOピンがショートになるので、SPI通信をするときはオフにしておきます。

from pyftdi.spi import SpiPort, SpiController
import time

ctrl = SpiController()
ctrl.configure('ftdi://ftdi:232h/1')

gpio = ctrl.get_gpio()
gpio.set_direction(0xfff0, 0xfff0)  # GPIOの入出力を指定

spi = ctrl.get_port(0, freq=1000, mode=0)

gpio.write(0xfff0)  # 12本のGPIOをHIGHに
time.sleep(2)
gpio.write(0x00)  # 12本のGPIOをLOWに

recv = spi.exchange([0xa5, 0x5a], duplex=True)
gpio.write(0xfff0)

 コードの中のgpioインスタンスはGPIOの制御、spiインスタンスはSPIを制御しており、どちらもSpiControllerから生成しているので同時に有効化されています。
 GPIO制御について少し補足しますと、ピンを制御している4ケタの16進数はMSBからLSBまでC7~C0、D7~D0の順番で割り当てられています。そしてD3~D0の4本はSPIのSCK、MOSI、MISO、CSに割り当てられているためGPIOとして制御することができません。コードの中の

gpio.set_direction(0xfff0, 0xfff0)

は、1つ目の引数が対象のGPIOピン、2つ目の引数がピンの向きの指定で、ここではC7~C0とD7~D4を(1つ目の0xff)全て出力(2つ目の0xff)として使うという意味になります。MCP2210では入力が1で出力が0でしたが、FT232Hでは入力が0、出力が1になります。続いて

gpio.write(0xfff0)

は、C7~C0とD7~D4を全てHIGH状態にするという意味です。制御することができないD3~D0のところを1にすると実行エラーになります。GPIOをCSの代わりにする場合使うピンと使わないピンを把握して16進数のビットマスクでの指定になるのが、冒頭の表で「1チップだけなら」簡単と書いた理由です。

 GPIOをCSとして使う場合もう一つ問題点があります。上のコードを実行し、SCK、CS、そしてGPIOのC6の波形を確認したものが下図になります。

GPIOとSPIの併用

黄色がクロック、水色がSPIのCS、紫がGPIO制御しているピンの出力になります。SPIのCSとクロックの間は1クロック周期程度の時間ですが、GPIOの立下りとクロックの間は17msecほど差があります。コードではGPIOの立下りとSPIの通信の間に一切遅延を入れてないにもかかわらずです。この17msecの間隔はSPIのビットレートをあげてもほぼ同じなので、GPIOをCSとして使う場合はプログラムの実行時間がこの遅延時間に影響されることになります。
 ちなみに、GPIOの出力を反対にしてもSPI通信の前の方に遅延が発生する点、そして通信の後の立ち上がりは周波数にある程度追従していることが分かりました。

価格

 FT232Hのボードは純正の物なら秋月電子で2,640円ですが、互換品であればAliexpressで送料込み9ドル程度(このnote作成時点で9ドル=1,419円)で手に入ります。

まとめ

 内容をまとめると以下のようになります。

MCP2210
- 環境構築が簡単
- ネイティブで複数CSに対応
- コード作成はドキュメントを読み漁る必要があって面倒

FT232H
- 比較的安価
- 高速
- 1チップ制御であればコード作成が簡単

ということで、子機が1つしかない場合で高速通信がしたいのであればFT232H、複数チップに対応したいのであればどちらかコードが書きやすいと感じる方を選べばよいと思います。
 高速で複数チップが制御できてコードも書きやすいものが欲しければおとなしくRaspberry Piを買いましょう。


この記事が気に入ったらサポートをしてみませんか?