Arduino学 Ⅱ ~データ通信編~

Arduino学 Ⅱ ~データ通信編~

目次

この資料の目的

 この資料はArduinoについてある程度勉強した人でArduinoでの通信の仕組みを理解したい人に向けて書きました。まだArduinoについて詳しくない人はArduino学 Ⅰを読んでみてください。

Arduino学 Ⅰ - NKMRHYTのブログ

Arduinoでは自分でライブラリを作らなくてもネットで見つかることが多く自分で作らない分にはこの内容を理解しなくても困りません。仕組みを理解したい人は読んでみてください。僕があまり詳しくないのでUART通信については書いていません。

はじめに

 マイコンから指令を送ったり、センサの値をデジタル値で読み取る際には通信を行う必要があります。どのようにデータをやり取りするのでしょうか?
 電圧がかかっている状態とかかっていない状態をタイミングよく切り替えることで通信を行っています。今回はセンサと通信を行い必要なデータを読み取ったり指令を送ったりする方法について説明します。説明の際には具体的なセンサを用いて説明しようと思います。

SPI通信

 MAX31855という温度センサを見てみましょう(MAX31855のデータシート)
データシートは説明書です。はじめは英語でいろいろ書いているので難しいと感じるかもしれませんがすぐに慣れます。

接続方法

 通信の開始終了を決めるSS、マスターからデータを送るMOSI、スレーブからのデータを受け取るMISO、タイミングを合わせるSCLを接続します。基本的にはマスターはArudinoです。SPI通信のセンサを複数使う際にはMOSI、MISO、SCLはマスターの同じピンでSSだけ変えます。データシートの1ページ目に書いています。

f:id:NKMRHYT:20220313232043p:plain
図1 配線図
このセンサではマイコンからのデータの送信はないのでマイコンのMOSIはつながっていません。このセンサではMISOはSO、SSはCSとなっています。

通信方法

 データシートの5ページ目にどのようにデータを送っているかが書いています。

f:id:NKMRHYT:20220313233058p:plain
図2 通信の様子
線が下側にある時がLOW上側にある時がHIGHです。図2のようにCSがLOWの時にCSLとSOが変化して通信をしています。通信を行っている間SCLは一定の間隔でHIGHとLOWを繰り返しています。どのタイミングで通信を行うかですが今回は立上りエッジです(図3参照)。
f:id:NKMRHYT:20220314001526p:plain:w300
図3 SPI_MODE
つまりSCLがHIGHになっているときにSOがHIGHかLOWかを見ることでデータを読み取ります。どのようにデータを決めているかは10ページに書いています(図4参照)。1がHIGH、0がLOWです。
f:id:NKMRHYT:20220314121314p:plain
図4 Memory Map

プログラム

#include <SPI.h>

SPISettings mySPISettings = SPISettings(5000000, MSBFIRST, SPI_MODE0);
int data;
float temp;

void setup() {
  Serial.begin(9600);
  SPI.begin();
}

void loop() {
  SPI.beginTransaction(mySPISettings);
  digitalWrite(SS, LOW);
  data = SPI.transfer16(0x0000);
  digitalWrite(SS, HIGH);
  SPI.endTransaction();
  temp = (data >> 2) * 0.25;
  Serial.println(temp);
  delay(10);
}

 3行目のSPISettings mySPISettings = SPISettings(5000000, MSBFIRST, SPI_MODE0);でSPI通信の設定値を入力しています。5000000(5MHz)が最大の通信速度です。MSBFIRSTは上の位から読んでいくという意味です。データシートに書いています。

D[30:18] contain the converted temperature in the order of MSB to LSB

先ほど述べたように立上りエッジで通信を行っていないときにSCLはLOWなのでMODE0です。SPI.begin();で初期化、SPI.beginTransaction(mySPISettings);で設定の初期化、digitalWrite(SS, LOW);で通信開始、data = SPI.transfer16(0x0000);で16bitのデータを受信(送信データがないので0x0000)、digitalWrite(SS, HIGH);で通信終了、SPI.endTransaction();でほかのライブラリがSPIバスを使用できるようにする、 temp = (data >> 2) * 0.25;で2進数から10進数に変換しています。

I2C通信

 ADS1115というADコンバータを使って説明します(ADS1115のデータシート)

接続方法

 図5を見てください。データを送受信するSDA、タイミングを合わせるSCLを接続します。SPI通信から送信と受信のピンが1つになったことと送信開始を決めるピンがなくなり4本から2本になりました。I2CではSDAとSCLをプルアップする必要があります。データシートでは31ページに書いています。

f:id:NKMRHYT:20220315010711p:plain
図5 配線図

通信方法

f:id:NKMRHYT:20220317173227p:plain
図6 通信の様子
 図6のようにSCLがHIGHの時にSDAがHIGHからLOWになったら通信開始です。最初の7bitはアドレスです。これはセンサの名前でどのセンサと通信するかを指定します。8bit目で通信の方向を指定します。9bit目で区切りとしてLOWを出力します。次の8bitからデータの通信を行います。最後にSCLがHIGHの時にSDAがLOWからHIGHになったら通信を終了します。

プログラム

#include <Wire.h>
#define ADDR 0b1001000
#define FSR 4.096

int data;
float volt;

void setup() {
  Wire.begin();
  Serial.begin(9600);
  
  Wire.beginTransmission(ADDR);
  Wire.write(0b00000001);
  Wire.write(0b11000011);
  Wire.write(0b10000011);
  Wire.endTransmission();
}

void loop() {
  Wire.beginTransmission(ADDR);
  Wire.write(0b00000000);
  Wire.endTransmission();

  Wire.requestFrom(ADDR, 2);
  while (Wire.available()) {
    data = Wire.read() << 8 | Wire.read();
  }

  volt = data * (FSR / pow(2, 15));
  Serial.println(volt);
  delay(100);
}

I2CはWire.hライブラリを使用します。Wire.begin();でライブラリを開始、Wire.beginTransmission(ADDR);で送信開始(かっこの中にアドレスを書く)、Wire.write(0b00000001);で書き込み(かっこ内に送信データを書く)、Wire.endTransmission();で送信終了、Wire.requestFrom(ADDR, 2);でデータの受信要求(かっこの中にアドレスと要求するデータのバイト数を書く)、 Wire.available()は読み取りのバイト数を返す、Wire.read()で読み取り、volt = data * (FSR / pow(2, 15));で2進数から10進数に変換しています。