著者:根岸孝次
この資料では、Arduinoでよく使われる3つの通信方法(UART、I2C、SPI)について学びます。これを読めば、電子工作でマイコン同士の通信やセンサーとのやり取りを自分で実装できるようになります。
通信の概要
何に使うのか?
通信は、複数のマイコン同士やセンサー、PCとArduinoの間で情報をやり取りするための手段です。通信は、以下のような用途に活用されます:
- マイコン同士の通信:複数のArduinoやマイコンボードが協力して動作する際に使われます。
- PCでのデバッグ:ArduinoとPCを接続し、動作確認やデータの送受信を行うときに使われます。
- センサとの通信:温度や加速度センサなどからデータを取得するために使われます。
各通信の特徴
通信方式 |
使用例 |
メリット |
デメリット |
UART(シリアル通信) |
PCとのデバッグ ArduinoのSerial |
配線がTxとRxの2本で済む プログラムが単純 |
1:1の通信であるため複数デバイスと相性が悪い |
I2C |
センサ(6軸センサ、温度センサなど) |
複数デバイスを少ない信号線で管理できる |
通信速度が遅い
|
SPI |
SDカード |
高速かつ多くのデバイスが接続可能 |
2本の通信線(SCK, MISO, MOSI)に加えデバイス選択用の信号がデバイス数分必要になる |
各通信の回路
UART通信の回路
- 必要な信号線:Tx(送信)、Rx(受信)、GND
- 回路例:ArduinoのTxピンと相手のRxピン、ArduinoのRxピンと相手のTxピンを接続し、GND同士も接続します。TxとRxをクロスさせることが重要。
I2C通信の回路
- 必要な信号線:SDA(データ)、SCL(クロック)、GND
- 回路例:ArduinoのSDAピンとI2CデバイスのSDAピン、SCLピンとI2CデバイスのSCLピンを接続します。複数デバイスを使う場合でもSDAとSCLを共通に接続し、GNDも全デバイスで共有します。
- 各デバイスにはアドレスが割り振られている。通信の冒頭でアドレスを指定することでどのデバイスと通信を行うか選択できる。
- 一般的にセンサのアドレスを変更することができるので、同じ製品であっても複数つなげることができる。
SPI通信の回路
- 必要な信号線:MOSI(マスター出力/スレーブ入力)、MISO(マスター入力/スレーブ出力)、SCK(クロック)、SS(スレーブセレクト)、GND
- 回路例:ArduinoのMOSI、MISO、SCK、SSピンをそれぞれデバイスの対応ピンに接続します。SSピンを使って、どのデバイスが通信するかを指定できます。
- マスターとは通信を司る親のこと(基本的にマイコン)。スレーブとはマスターと通信したい各デバイス(センサなど)。
- マスターがSSピンを利用してどのデバイスと通信を行うかを指定した後、通信が開始される。
各通信をArduinoで利用する際のプログラムの書き方
各通信で必要となる最低限の関数の使い方などを解説します。
UART
- Serialクラス
- シリアル通信に必要な関数などが揃っているクラス
- 1つ1つのシリアル通信に対してインスタンスを作る必要がある。
- 基本的にArduinoでは内部的にインスタンスが用意されている。
- Serial, Serial1, Serial2 など
- どのインスタンスがどのtx,rxと結びついているかは事前に決まっている。ピン番号のところにTxN、RxN(N∈ℕ)と書いてあれば、基本的にSerialNとなる。USBを介してパソコンと接続されるシリアルはSerialとなる。
- 以後解説するすべての関数はメンバ関数であるので、”Serial.(関数名)”のように記述して利用する必要がある。
- begin(ボーレート[bps])
- setup関数内で呼ぶのが一般的
- シリアル通信を指定のボーレート(通信速度)で開始する
- 一般的に 9600, 115200 などがボーレートとして使用される。
- print(送信したい内容)
- 通信相手にデータを送る。
- 引数にはint型、float型、string型のいずれで送っても適切に送ってくれる。
- データは文字列として送られるので基本的にはPCとのデバッグ用で使われる。
- 私はPC以外とのシリアル通信で使ったことは1度も無い。
- 送られた文字列を改行するには自分で改行コード”\n”(”\r\n”)を加える必要がある。
- println(送信したい内容)
- ほぼprint関数と一緒。
- 送る文字列の最後に改行コード”\n”(”\r\n”)を付け加えて送信してくれる。
- デバッグでは専らこっちを利用する。
- write(送信したい内容(8bit/1バイト))
- 1バイトのデータをそのまま送ってくれる
- 0~255の整数
- print関数とは異なり文字列に変換されない
- マイコン同士の通信に有用
- 1バイトしか送れない
- int型、float型などは32bit、double型は64bitなのでこの関数で送ろうとするとあふれる
- 遅れる情報が少ないので、元データを圧縮するか分割する必要がある。
- wirte(送信したい内容の配列(1バイトのデータの配列), 送信するバイト数)
- 上述のwrite関数の上位互換
- 送信したい内容を1バイト事に分割して、配列に格納すれば一気に送れる
- read()
- 受信したデータを読み出す
- バッファの先頭1バイトしか読み出さない
- 受信したデータはバッファに貯められている。受信が多すぎるとバッファが溢れて、古いデータが捨てられてしまう
- for文と組み合わせて複数バイトを読み出す
- 受け取ったデータをそのまま返してくれる
- 文字列などに変換はしてくれない
- write関数で0~255で整数を送って、read関数で読み出せばそのままの数字を得られ
I2C
- “Wire.h”をインクルードする
- Wireクラス
- begin()
- beginTransmission(アドレス(16進数/8bit))
- write(送信したい内容(16進数/8bit))
- 引数に与えたデータをそのまま送信
- 16進数の形で表記することがほとんど
- endTransmission()
- 現在行っているセッションを終了
- I2C通信を終了するわけではない
- 特定のデバイスとのセッションを終えるだけ
- これを行わないと次のデバイスとの通信ができない
SPI
- “SPI.h”をインクルードする
- SPIクラス
- begin()
- すべてのSSピンをHIGHにする
- スレーブデバイスはSSピンがHIGHであると通信を行わない
- transfer(送信したい内容(16進数/8bit))
- 引数に与えたデータをそのまま送信
- 16進数の形で表記することがほとんど
- 送信と受信の両方を兼ねる
- 返り値として受信データを取得できる
- 受信だけをしたいときにも何かしらデータを送る必要がある。0x00などを送ることが多い。
- 通信相手の選択の仕方
- 特定のデバイスのSSピンだけをLOWにする
- セッションを終える時に必ずHIGHに戻す