MPU9250をSPIで使用する方法

#MPU9250
9軸のセンサー
磁気3、ジャイロ3、加速度3軸持つセンサー

I2C、SPIどちらの通信でも可能。
SPIによる方法を説明する

データシート
ハードウェア　https://invensense.tdk.com/wp-content/uploads/2015/02/PS-MPU-9250A-01-v1.1.pdf
レジスタ　https://cdn.sparkfun.com/assets/learn_tutorials/5/5/0/MPU-9250-Register-Map.pdf

#接続
|MPU9250  |expression  |Arduino:マイコン  |
|---|---|---|--- |
|Vcc  |3.3V  | 3.3V |  
|GND |GND  |GND | 
|SCL|SPI-clock|CLK:13|
|SDA|Data Input|MOSI:11|
|EDA|センサを追加する場合|NoConection|
|ECL|に使用する|NoConection|
|ADO|Data Output|MISO:12|
|INT|割り込み出力らしい|NoConection|
|NCS|ChipSelect|CS:10|
|FSYNC|不明|GNDにしておけば良いらしい|

5V-3.3V変換はよくわからない。
変換を入れなくても、入れてもちゃんと動作した事例がある。
しかし、入れた結果、電圧が足りないのかArduino側が判定しない（ロジアナで見るとデータが出てるのに）事例があった。

変換ICを持っていない場合は直で繫いで試しても良いと思う
持っている場合は付けて見て、だめなら外しては試して見ると良いと思う


#SPI
通信モードは3、立ち上がりラッチ、基本SCK状態はHIGH
SCL周波数は、読み込みのみなら20MHzまで
書き込みもする場合は1MHzにすると良いらしい（1MHzで動いた）

#SPIプロトコル
読み込み
CS->HIGH
アドレス8bit（アドレスの先頭ビットが1=Read,0=Write ）
空データ8bit送信と同時に受信
CS->LOW

書き込み
CS->HIGH
アドレス8bit（アドレスの先頭ビットが1=Read,0=Write ）
書き込みデータ8bit
CS->LOW


#動作確認プログラム:MBED用

```C
#include "mbed.h"
#include "Serial.h"


Serial pc(USBTX, USBRX); // USBシリアルポートのインスタンス

SPI spi(D11,D12,D13);
DigitalOut cs(D10);

int main() {
    spi.format(8,3);
    spi.frequency(1000000);
    cs=1;
    
    pc.baud(250000);
    
    cs=0;
    int val = spi.write(0xF5);
    val = spi.write(0x00);
    cs = 1;
    
    pc.printf("START\r\n");
    pc.printf("%8x \r\n",val);

    
    while(1){
        
    }
}
```
これで、71が読み込めている場合、通信成功。
プログラムは、アドレス0x75(WHO_AM_I)を読み込んでいる。
正確には、Readなので先頭ビットを1にしてアドレス0xF5を読んでいる。

#動作確認：arduino用

```C
#include <SPI.h>
#include<avr/io.h>
#include<avr/interrupt.h>

#define cs 10

void setup() {
  pinMode(cs,OUTPUT);  //2020.4.09 追加
  SPI.setBitOrder(MSBFIRST);  //最上位ビット(MSB)から送信
  SPI.setClockDivider(SPI_CLOCK_DIV4);  //通信速度をデフォルト
  SPI.setDataMode(SPI_MODE3);   //アイドル5Vで0V→5Vの変化で送信する
  SPI.begin();  //開始
  Serial.begin(250000);
  
}

void loop() {
  byte data = Read(0x75);
  Serial.println(data, HEX);
  delay(100);
}

void Write(byte address, byte data) {
  digitalWrite(cs, LOW);
  SPI.transfer(address);
  SPI.transfer(data);
  digitalWrite(cs, HIGH);
}
byte Read(byte address) {
  digitalWrite(cs, LOW);
  SPI.transfer(address | 0x80);
  byte data = SPI.transfer(0);
  digitalWrite(cs, HIGH);
  return data;
}
```
0x71が読めれば、成功です。

#ジャイロ、加速度読み込みプログラム


```C
#include <SPI.h>
#include<avr/io.h>
#include<avr/interrupt.h>

#define cs 10

void setup() {
　pinMode(cs,OUTPUT);  //2020.4.09追加
  SPI.setBitOrder(MSBFIRST);  //最上位ビット(MSB)から送信
  SPI.setClockDivider(SPI_CLOCK_DIV4);  //通信速度をデフォルト
  SPI.setDataMode(SPI_MODE3);   //アイドル5Vで0V→5Vの変化で送信する
  SPI.begin();  //開始
  Serial.begin(250000);
  byte data = Read(0x75);
  Serial.println(data, HEX);
  delay(100);
}

void loop() {
  byte dataH = Read(0x3B);
  byte dataL = Read(0x3C);
  int AX = dataH << 8 | dataL;

  dataH = Read(0x3D);
  dataL = Read(0x3E);
  int AY = dataH << 8 | dataL;

  dataH = Read(0x3F);
  dataL = Read(0x40);
  int AZ = dataH << 8 | dataL;


  dataH = Read(0x43);
  dataL = Read(0x44);
  int GX = dataH << 8 | dataL;
  dataH = Read(0x45);
  dataL = Read(0x46);
  int GY = dataH << 8 | dataL;
  dataH = Read(0x47);
  dataL = Read(0x48);
  int GZ = dataH << 8 | dataL;


  if (AX > 32768) {
    AX = AX - 65535;
  }
  if (AY > 32768) {
    AY = AY - 65535;
  }
  if (AZ > 32768) {
    AZ = AZ - 65535;
  }
  if (GX > 32768) {
    GX = GX - 65535;
  }
  if (GY > 32768) {
    GY = GY - 65535;
  }
  if (GZ > 32768) {
    GZ = GZ - 65535;
  }

  //if(Flag == 1){
  Serial.print(AX );
  Serial.print(" ");
  Serial.print(AY );
  Serial.print(" ");
  Serial.print(AZ );
  Serial.print(" ");

  Serial.print(GX );
  Serial.print(" ");
  Serial.print(GY );
  Serial.print(" ");
  Serial.print(GZ );
  Serial.println(" ");
}

void Write(byte address, byte data) {
  digitalWrite(cs, LOW);
  SPI.transfer(address);
  SPI.transfer(data);
  digitalWrite(cs, HIGH);
}
byte Read(byte address) {
  digitalWrite(cs, LOW);
  SPI.transfer(address | 0x80);
  byte data = SPI.transfer(0);
  digitalWrite(cs, HIGH);
  return data;
}
```
mbedでもやりましたが、なぜか読めないバイトがあったりして難しいので止めました

#各種設定について
sleep解除、スケール設定、LPF、出力周波数設定を上記ではしていません。
しなくてもなぜか動きました。I2Cで使うなら必要なはずなのですが・・・
初期設定では
＋－250dps,+-2g,1kHz更新　っぽいです
ジャイロの感度設定

```C
  Write(0x1B,0x00);   //250dps
  Write(0x1B,0x08);   //500dps
  Write(0x1B,0x10);   //1000dps
  Write(0x1B,0x18);   //2000dps
```
ローパスフィルタのカットオフ周波数設定
F_CHOICEは00にしておけばOK（デフォルト）
デフォルト250Hz

```C
  Write(0x1A, 0x06);  //gyro LPF5Hz
  Write(0x1A, 0x03);  //gyro LPF41Hz
  Write(0x1D, 0x03);  //acce LPF45Hz
```

加速度計の感度設定

```C
Write(0x1C, 0x04);  //acce +-4g
Write(0x1C, 0x10);  //acce +-8g
Write(0x1C, 0x14);  //acce +-16g
```
使うやつ以外をコメントアウトすれば使えます（確認済み）


#追記：Arduino Dueを使用する場合
DueにはSPIが一つあり、ICSPヘッダのみから利用できます。Pin13,12,11はSPIピンにはなりません。![Arduino-Due-Pin-mapping.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/5e3bd270-36e6-83b6-6ed6-526417b067e7.png)
この図の中央の6本のオスヘッダのSCK,MOSI,MISOにさして利用します。
プログラムは#includeの部分の

```C:forArduinoDue
#include<avr/io.h>
#include<avr/interrupt.h>
```
を除くことで動作できます。

```C
#include <SPI.h>

#define cs 10

void setup() {
  pinMode(cs,OUTPUT);
  SPI.setBitOrder(MSBFIRST);  //最上位ビット(MSB)から送信
  SPI.setClockDivider(SPI_CLOCK_DIV32);  //通信速度をデフォルト
  SPI.setDataMode(SPI_MODE3);   //アイドル5Vで0V→5Vの変化で送信する
  SPI.begin();  //開始
  Serial.begin(250000);
  byte data = Read(0x75);
  Serial.println(data, HEX);
  delay(100);
}

void loop() {
  byte dataH = Read(0x3B);
  byte dataL = Read(0x3C);
  int AX = dataH << 8 | dataL;

  dataH = Read(0x3D);
  dataL = Read(0x3E);
  int AY = dataH << 8 | dataL;

  dataH = Read(0x3F);
  dataL = Read(0x40);
  int AZ = dataH << 8 | dataL;


  dataH = Read(0x43);
  dataL = Read(0x44);
  int GX = dataH << 8 | dataL;
  dataH = Read(0x45);
  dataL = Read(0x46);
  int GY = dataH << 8 | dataL;
  dataH = Read(0x47);
  dataL = Read(0x48);
  int GZ = dataH << 8 | dataL;


  if (AX > 32768) {
    AX = AX - 65535;
  }
  if (AY > 32768) {
    AY = AY - 65535;
  }
  if (AZ > 32768) {
    AZ = AZ - 65535;
  }
  if (GX > 32768) {
    GX = GX - 65535;
  }
  if (GY > 32768) {
    GY = GY - 65535;
  }
  if (GZ > 32768) {
    GZ = GZ - 65535;
  }

  //if(Flag == 1){
  /*
  Serial.print(AX );
  Serial.print(" ");
  Serial.print(AY );
  Serial.print(" ");
  Serial.print(AZ );
  Serial.print(" ");
*/
  Serial.print(GX );
  Serial.print(" ");
  Serial.print(GY );
  Serial.print(" ");
  Serial.print(GZ );
  Serial.println(" ");
}

void Write(byte address, byte data) {
  digitalWrite(cs, LOW);
  SPI.transfer(address);
  SPI.transfer(data);
  digitalWrite(cs, HIGH);
}
byte Read(byte address) {
  digitalWrite(cs, LOW);
  SPI.transfer(address | 0x80);
  byte data = SPI.transfer(0);
  digitalWrite(cs, HIGH);
  return data;
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/5f711a97-62af-9cba-5777-882af36b68bc.png)
SPIの通信速度は

```C
SPI.setClockDivider(SPI_CLOCK_DIV32);
```
で4MHzクロックになってました。


#追記：　Arduino Mega接続
|MPU9250  |expression  |Mega  |
|---|---|---|--- |
|Vcc  |3.3V  | 3.3V |  
|GND |GND  |GND | 
|SCL|SPI-clock|CLK:52|
|SDA|Data Input|MOSI:51|
|EDA|センサを追加する場合|NoConection|
|ECL|に使用する|NoConection|
|ADO|Data Output|MISO:50|
|INT|割り込み出力らしい|NoConection|
|NCS|ChipSelect|CS:53|
|FSYNC|不明|GNDにしておけば良いらしい|