L3G4200の使い方

#L3G4200
3軸ジャイロセンサです。
SPI、I2Cの両方の通信が可能です
インタラプトモード、FIFOモードが付いてます
今回はセンサデータをI2C,SPIで取得する方法とFS(Full Scale:分解能)、ODR(Output Data rate:出力周波数)の説明をします
何故か、このセンサーの出力には定常偏差が大きく乗っかっているので、除去が必要です。

#接続-I2C
|L3G  |expression  |Arduino  |
|---|---|---|--- |
|Vcc  |3.3V  | 3.3V |  
|GND |GND  |GND | 
|SDO|アドレス指定:0->0x68 1->0x69|任意(GNDとした)
|SDA|I2C-data|A4|
|SCL|I2C-clock|A5|
|CS|通信方法選択:0->I2C,1->SPI|GND|

CSをGNDにすることと、SDOのアドレス選択を間違えないようにしましょう

#I2Cについて
こういったセンサは基本的にセンサーのレジスタを読み込むことで利用します

レジスタを読み込む方法は、
1. I2CでL3G本体アドレスを送信、
2. レジスタアドレスを送信、
3. センサーがデータを送ってくるのを待つ
という方法です。

書き込む場合は、
1. L3G本体アドレスを送信、
2. レジスタアドレスを送信、
3. 書き込むデータ
を順に送るだけです。

Arduinoでこれを実装する場合は、

```C++
void writem(byte add, byte data) {          //アドレス、データ
  Wire.beginTransmission(0x68);
  Wire.write(add);
  Wire.write(data);
  Wire.endTransmission();
}

byte readm(byte add) {                      //アドレス
  byte k;
  Wire.beginTransmission(0x68);
  Wire.write(add);
  Wire.endTransmission();
  Wire.requestFrom(0x68, 1);
  while (Wire.available()) {
    k = Wire.read();
  }
  return k;
}
```
こんな感じです。0x68はL3Gの本体アドレスです。

#通信確認
![REG.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/4714592f-6121-e8d9-e1b2-6ccb1abd99b1.png)
レジスタマップ、アドレス0x0FのWHO_I_AMを読み込んで、0xD3(211＜－10進数）を取得できれば成功です。失敗した場合は、I2C_ScanerというArduino参考プログラムを実行して、I2C端末がつながっているが確認すると良いと思います。

```C++

#include <Wire.h>
void setup() {
  Wire.begin();
  Serial.begin(250000);
  
}
void loop() {
  Wire.beginTransmission(0x68);
  Wire.write(0x0F);
  Wire.endTransmission();
  Wire.requestFrom(0x68,1);
  int hoge = 0;
  while(Wire.available()){
    hoge=Wire.read();
  }
  Serial.println(hoge,HEX);
  delay(10);
}
```

#とりあえず、ジャイロデータを取得
各種設定はデフォルトでデータ取得方法を説明します。

1. SLEEP解除、ジャイロ使用可能に　　=  アドレス0x20にデータ0x0Fを書き込む
2. ジャイロデータ読み込み　 = アドレス 0x28~0x2Dを読み込む

これだけです。

```C++

#include <Wire.h>
#include<avr/io.h>
#include<avr/interrupt.h>

volatile int serialdata = 0;

void setup() {
  Wire.begin();
  Serial.begin(250000);
  writem(0x20, 0x4F);            //sleep解除:ODR100Hz ,LPF12.5Hz
  writem(0x23, 0x00);            //FS 3:2000  1:500 0:245 dps  
  writem(0x24, 0x10);            //HPF enable
  writem(0x21,0x29);             //HPF0.02Hz

}
void loop() {
  byte k=0;
  byte gyrodata2[10];
  int gyrodata[4];        //x,y,z
 
  gyrodata2[0]=readm(0x28);
  gyrodata2[1]=readm(0x29);
  
  gyrodata2[2]=readm(0x2A);
  gyrodata2[3]=readm(0x2B);
  gyrodata2[4]=readm(0x2C);
  gyrodata2[5]=readm(0x2D);
  
  gyrodata[0] = gyrodata2[1] << 8 | gyrodata2[0];
  gyrodata[1] = gyrodata2[3] << 8 | gyrodata2[2];
  gyrodata[2] = gyrodata2[5] << 8 | gyrodata2[4];

  
  Serial.print(gyrodata[0]);  
  Serial.print(" ");
  Serial.print(gyrodata[1]);
  Serial.print(" ");
  Serial.println(gyrodata[2]);
  

  delay(5);

}

void writem(byte add, byte data) {          //アドレス、データ
  Wire.beginTransmission(0x0068);
  Wire.write((int)add);
  Wire.write(data);
  Wire.endTransmission();
}

byte readm(byte add) {                      //アドレス
  byte k;
  Wire.beginTransmission(0x0068);
  Wire.write((int)add);
  Wire.endTransmission();
  Wire.requestFrom(0x0068, 1);
  while (Wire.available()) {
    k = Wire.read();
  }
  return k;
}
```


#SPIで使用する場合
I2Cで使う場合より信頼できます。通信も速いです。

接続

|L3G  |expression  |Arduino  |
|---|---|---|--- |
|Vcc  |3.3V  | 3.3V |  
|GND |GND  |GND | 
|SDO|Data Output|MISO:12|
|SDA|Data Input|MOSI:11|
|SCL|SPI-clock|CLK:13|
|CS|CS|10|

一応、3.3V_5V変換を通しました。

通信方法は、
1. アドレス送信
2. データ送信、同時に読み込み
という形でやります

アドレス送信時に、R/W、複数読み込み、シングル読み込みの指定をします。

通信のサブルーチンはこのようになります。CSの下がりは、1Byte目の表示の意味があるため、毎度下げる必要があります。

```C++
void writem(byte add, byte data) {          //アドレス、データ  
  digitalWrite(10,LOW);
  SPI.transfer(add) ; 
  SPI.transfer(data) ; 
  digitalWrite(10,HIGH);
}

byte readm(byte add) {                      //アドレス  
  byte hoge;
  byte hoge2;
  hoge = 0x80 | (0b00111111 & add);         //R/W = 1にしてアドレスを送る
  digitalWrite(10,LOW);
  SPI.transfer(hoge) ; 
  hoge2 = SPI.transfer(0x8F) ; 
  digitalWrite(10,HIGH);  
  return hoge2;
}
void readm2(byte add,int num,byte *data) {                      //アドレス  
  byte hoge;
  byte hoge2;
  hoge = 0xC0 | (0b00111111 & add);         //R/W = 1,MS=Multi にしてアドレスを送る
  digitalWrite(10,LOW);
  SPI.transfer(hoge) ; 
  for(int i = 0;i<=num-1;i++){
    *(data+i) = SPI.transfer(0x8F) ; 
  }
  digitalWrite(10,HIGH);  
}
```

実際にジャイロデータを読み込ませる場合はこのようになります。

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

volatile int serialdata = 0;

void setup() {
  Serial.begin(250000);
  pinMode(10,INPUT);
  digitalWrite(10,HIGH);
  SPI.begin() ;                        // ＳＰＩを行う為の初期化
  SPI.setBitOrder(MSBFIRST) ;          // ビットオーダー
  SPI.setClockDivider(SPI_CLOCK_DIV8) ;// クロック(CLK)をシステムクロックの1/8で使用(16MHz/8)
  SPI.setDataMode(SPI_MODE3) ;         

  writem(0x20, 0x4F);            //sleep解除:  0x4F = ODR200Hz ,LPF12.5Hz
  writem(0x23, 0x00);            //FS 0x30:2000  0x10:500 0x00:245 dps
  writem(0x24, 0x00);            //HPF enable 0x10 ,dis:0x00
  //writem(0x21, 0x29);            //HPF0.02Hz
  
}
void loop() {
  byte k = 0;
  byte gyrodata2[10];
  int gyrodata[4];        //x,y,z
  byte DR;
  DR = readm(0x27);
  readm2(0x28,6,gyrodata2);

  gyrodata[0] = gyrodata2[1] << 8 | gyrodata2[0];
  gyrodata[1] = gyrodata2[3] << 8 | gyrodata2[2];
  gyrodata[2] = gyrodata2[5] << 8 | gyrodata2[4];
  /*
  Serial.print(gyrodata[0]);
  Serial.print(" ");
  Serial.print(gyrodata[1]);
  Serial.print(" ");
  Serial.println(gyrodata[2]);
*/
  float GX[6];
  GX[0] = gyrodata[0] + 2293.5;             //定常偏差除去
  GX[0] = GX[0] *0.000130493;         //FS = 245dps の時　rad/sに直す  

  Serial.println(GX[0]);
  delay(5);

}

void writem(byte add, byte data) {          //アドレス、データ  
  digitalWrite(10,LOW);
  SPI.transfer(add) ; 
  SPI.transfer(data) ; 
  digitalWrite(10,HIGH);
}

byte readm(byte add) {                      //アドレス  
  byte hoge;
  byte hoge2;
  hoge = 0x80 | (0b00111111 & add);         //R/W = 1にしてアドレスを送る
  digitalWrite(10,LOW);
  SPI.transfer(hoge) ; 
  hoge2 = SPI.transfer(0x8F) ; 
  digitalWrite(10,HIGH);  
  return hoge2;
}
void readm2(byte add,int num,byte *data) {                      //アドレス  
  byte hoge;
  byte hoge2;
  hoge = 0xC0 | (0b00111111 & add);         //R/W = 1,MS=Multi にしてアドレスを送る
  digitalWrite(10,LOW);
  SPI.transfer(hoge) ; 
  for(int i = 0;i<=num-1;i++){
    *(data+i) = SPI.transfer(0x8F) ; 
  }
  digitalWrite(10,HIGH);  
}
```
SPIの通信モードは3だと思ってやってますが、間違っている可能性があります。問題なく通信できますが


#各種設定について-CTRL1-0x20
![CTR1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/9de4c0e7-affe-82ed-789c-e74cb41bd190.png)
Sleep解除はPD=1,ジャイロ起動はXen,Yen,Zen=1に
ODRはジャイロデータの更新速度です。ODR=100Hzなら、10msに一度センサデータが更新されます。
Cut_of_frequencyはローパスフィルタ（LPF）のカット周波数です。
![bw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/f17ab5b3-c55e-8556-ec56-425fd3177282.png)

Low_ODRはデフォルトで0です。システムのブロック線図は次のようになります。
![System.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/49393ff9-2dbb-a75f-5876-4bf709b2a930.png)
ハイパスフィルタ(HPF)はデフォルトで使用しない形になります。付けても定常偏差が何故か消えないのであまり意味がない気もします。

センサ感度は245dps,500dps,2000dpsの三種です。dpsとはdegree per second で一秒あたりの度です。
245dps = 4.276rad/s

```C++
writem(0x23, 0x00);            //FS 0x30:2000  0x10:500 0x00:245 dps
```
で調整できます。

#参考データシート
Mouser:ソフト面
https://www.mouser.jp/Sensors/Sensor-Development-Tools/Position-Sensor-Development-Tools/Datasheets/_/N-d0qz5?Keyword=L3G4200&FS=True

ハード面
http://www.kontest.ru/datasheet/STMICR0ELECTR0NICS/L3G4200D.pdf