AS5600(磁気エンコーダー)をテストする

# 概要
データシート

https://ams.com/documents/20143/36005/AS5600_DS000365_5-00.pdf

公式ライブラリ

https://ams.com/documents/20143/36005/AS5600_AB-POT_UG000378_1-00.pdf/a10a50ac-77a2-16e0-12d1-99c1c4c1c434

Github

https://github.com/Seeed-Studio/Seeed_Arduino_AS5600

12bit DACの精度があるといっています。
自動的に、省エネモードになるみたいです。
![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/f1e6847b-81c3-6a01-46d7-5b13e4af810c.png)
矢印の方向的に、VDD（VCC）に5Vを入れて使うみたいです。3.3Vに3.3Vを入れても動くみたいです（3.3V mode）。

AGC:automatic gain control: adjust the amplification lelvel to compensate for temperature and magnetic field variations
AGCは、アンプの増幅率を、温度や周囲の磁気の変動に合わせて変化させます。

CORDICというシステムで、ホールセンサーの値を角度に変換します。
出力はPWMか、Analog出力かで選択可能のようです。

# 購入元

https://ja.aliexpress.com/item/1005001709088354.html?spm=a2g0s.9042311.0.0.3fb24c4dFV2spr

aliexpress で買いました。
磁石付きでよかった。300円ぐらいでした。

# pinについて

|pin  |説明  |
|---|---|
|DIR  |Ground->時計回り、VDD->反時計回り　を正とする  |
|PGO,GP0 |Ground->Programming option B ,OTPをするためのモードっぽい|
|Analog output|角度|
|SDA|A4|
|SCL|A5|
|VCC|5.0V(6.1V以下)|

# ひとまず、実際に使ってみる
RAW　ANGLEを読み出すだけなら、特に設定もなく使える。
![IMG_20211205_163512175.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/3ebc0c4b-3d7e-7ad3-d203-3a9b0291eb0f.jpeg)

## 接続
|pin  |Arduino接続 |
|---|---|
|DIR  |Ground-|
|PGO,GP0 |接続しない|
|output|接続しない|
|SDA|A4|
|SCL|A5|
|VCC|5.0V|
|GND|GND|

## サンプルプログラム

```C
#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(250000);
}

void loop() {
  Wire.beginTransmission(0x36);
  Wire.write(0x0C);
  Wire.endTransmission(false);

  Wire.requestFrom(0x36, 2);

  while (Wire.available()) {
    byte angle_h = Wire.read();
    byte angle_l = Wire.read();
    unsigned int angle = (0x0F & angle_h) << 8 | angle_l;
    Serial.print(angle);
    //Serial.print(angle,HEX);
    Serial.print(" ");
  }
  delay(1);


  Wire.beginTransmission(0x36);
  Wire.write(0x0B);
  Wire.endTransmission(false);

  Wire.requestFrom(0x36, 1);

  while (Wire.available()) {
    byte state = Wire.read();
    
    Serial.println(state, BIN);
  }
  
}
```
二個データがSerialプロッタに入ってきます。
最初の一個目は角度データ。二個目は状態情報

```
1100111 => 正常
1001111 => 磁力強すぎ
1010111 => 磁力弱すぎ
```
という意味です。

## 結果
二個目のstateデータの送信を停止し、Serialプロッタで表示すると

![27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/2ffbaba6-82d7-3ea4-34b6-1abebf3ae07c.png)
いい感じで変換されてます。

360度を2^12(4096)分割の精度なので、360/4096を掛ければ、角度に変換できます。

pinのDIRを5Vにすると、RAW ANGLEも方向が反転するみたいです。

## 感想
めっちゃ精度高いです。
磁石の上下位置は1mmぐらい上下しても、1度ぐらいしか変動しなかった（手の振動の影響かも）ので、上下方法は割とどこでもいい感じですね。



# 3.3V

![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/a37fee27-2800-1726-fd1b-59961689e07e.png)

3.3Vで運用する場合、VDD5Vと連結したほうがいいみたいです
右端の10μFは、OTP（one time programming）をする時のみ必要と書いてあります。

# 磁石について
![19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/d54a5f30-eeff-ce91-837b-fb4ca59b1c77.png)
Bz:おそらく、z軸方向の磁束密度
図の黄色の円に注目してほしい、この領域は、傾きが一定な平面である。このセンサーはこの特徴をもとに角度を計算する。

「The maximum allowed displacement of the rotational
axis of the reference magnet from the center of the package is
0.25 mm when using a magnet with a diameter of 6mm.」
6mmの磁石を使う場合、センサーの中心軸からの磁石の回転軸のずれは0.25mmいかにする必要がある。

結構精度がいるな

![20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/20f5727a-f1e4-4974-f037-bbc3cff4524d.png)
中心の直径1mmの円にホールセンサーを配置してるそうです。


軸方向のエアギャップは、磁石によって変化する。

# ヒステリシスについて
ホールセンサはおそらく4つ乗っています。
磁石が回転すると、それぞれのセンサーは、S->N->S・・・と磁気が変化していきます。
計測される磁束密度は
![21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/a2c891f3-03b0-0a87-f8c9-55f213c983f8.png)
こんな感じで変化します。

仮に、
![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/052aa4c8-ee5f-2ccb-32e5-037a82c06d21.png)

こんな感じに磁石とホールセンサを配置すると
磁束密度と磁石の位置xをの関係は
![24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/a1ef1256-c486-dee2-b9fc-f0e11dc5efaa.png)
こんな感じになります。
曲線が二つあるのがわかります。
なぜこうなるかというと、私の予想ですが、磁石を近づけたことで、一部が磁化され、磁石を離しても、磁化された部分の影響で、磁石の磁力にプラスアルファされた磁場が発生し、実際よりも大きな磁場があると計測されるというわけです。

よって、回転角度に変換する際も、時計回りだったのを反時計回りで回すと、ヒステリシスが現れることになります。

# Register mapの説明
![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/34af3f8a-ebca-402d-5f44-b29dd00e7176.png)

スレーブアドレスは0x36です。

ZPOS: start position
MPOS: stop position
MANG: maximum angle
ZMCO: 何回、永久書き込みが行われたかを示す。3回まで

角度範囲を狭めると精度が上がる。
範囲は18度以上でないと、精度の向上にはならない。18度以下にしてはいけない。

## 0x07

|bit  |説明  |
|---|---|
|PM(2bit)|0:normal ,01:LPM1,10:LPM2,11:LPM3  LPM= low power mode,磁気計測の間隔が変わるらしい|
|HYST(2bit)|0:OFF,01:1 LSB,10:2 LSB,11:3 LSB、シュミッドトリガがあるみたい|
|OUTS(2bit)|10:PWM出力,00:analog(0~100%),01:analog(10~90%),11?  ,I2Cで読み出す場合はどちらでも関係ない|
|PWMF|PWMの周波数 00:115Hz,01:230Hz,10:460Hz,11:920Hz|


## SF(Slow Filter),FTH(Fast Filter Threshold,3bit)について

フィルターの設定
![25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/87bb33c4-1367-5cce-df3b-e9da4a21c7c5.png)
ノイズが少なく精度を上げるぶん、遅れが発生するみたい

Fast Filter は、FTHで設定した値を超えるぐらい、入力がばらつくときに発動する。
逆に、入力のばらつきが小さくなると、Slow　Filterに切り替わる
![26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/8ffa2402-47b4-4949-76fb-278662938dc0.png)


## STATUS
|bit  |説明  |
|---|---|
|MH|1=磁力が強すぎる|
|ML|1=磁力が弱すぎる|
|MD|1=磁石があることを検知|

## Programming mode
以降、いくつか永久書き込みについて説明しますが、何に使うかまず説明します。
この角度センサーは原理的に絶対位置がわかります。
よって、初期位置は0度ではありません。

初期位置でRAW　ANGLEを読み込み
ZPOSに格納すれば、初期位置が0度になります。



### B  mode
OUT pinで設定の書き換えを行う
一度しか行えない、永久書き込みになる、注意

start position と stop position　をPG0（GP0）をGNDにすることで教える。

### A mode
I2Cで設定の書き換えを行う
ZPOS,MPOSをRAW ANGLEを読み込み、書き込むことで使用する。

以下は永久書き込みする場合の手順

* BURN_ANGLE(0x80)をBURN　Register(0xFF)に書き込む
* 0.1ms待って、
* 0x01,0x11,0x10をBURN　Register(0xFF)に書き込む

ことで、永久書き込みができる

3回しかできないので注意

### C mode
Aと違い、MANGとZPOSを永久書き込みする場合の手順

まず、

* MANGに角度範囲を書き込む
* CONFIGも書き込む
* BURN_SETTINGS(0x40)をBURN　Register(0xFF)に書き込む
* 0x01,0x11,0x10をBURN　Register(0xFF)に書き込む
* ZPOSを書き込む




### Burn angle command
ZPOS,MPOSの永久書き込みに使用
3回まで行える。
ZMCOが一回ごとに増えていく

### Burn setting command
MANGを永久書き込みするのに使う
ZMCO=0の時のみ使用可能
一度のみ可能

磁石とセンサーを動かさないと確定したら、実行する
これをしない場合、使うたびに、初めに磁石の位置を教える必要がある。

# ZPOS,MPOSを読み込んでみる
デフォルトでは

```
ZPOS = 0xFOF
MPOS = 0x000
RAW ANGLE = 0x46B
ANGLE = 0x46B
```
でした。

ZPOSにRAW　ANGLEで書き換えてみます。

```
ZPOS = 4003
MPOS = 4003
RAW ANGLE = 4003
ANGLE = 0
```
となりました。
ZPOSを書き換えるだけで、自動的にMPOSが書き換わるようです。
さらに、ANGLEも0にずれました。初期位置の設定がこれで行えます。

永久書き込みについては、怖いうえに、今のところ必要そうでないので、チェックはしません。

## プログラム

```C
#include <Wire.h>
unsigned int AS5600_read_2byte(byte address);
byte AS5600_read(byte address);
void AS5600_write(byte address,byte data);

void setup() {
  Wire.begin();
  Serial.begin(250000);
  
  byte data1;
  byte data2;
  data1 = AS5600_read(0x0C);
  data2 = AS5600_read(0x0D);

  AS5600_write(0x01,data1);
  AS5600_write(0x02,data2);
  
  
}

void loop() {
  unsigned int raw_angle;
  raw_angle = AS5600_read_2byte(0x0C);      //RAW agle
  delay(100);

  unsigned int angle;
  angle = AS5600_read_2byte(0x0E);      //agle
  delay(100);

  unsigned int zpos;
  zpos = AS5600_read_2byte(0x01);      //ZPOS
  delay(100);

  unsigned int mpos;
  mpos = AS5600_read_2byte(0x03);      //MPOS
  delay(100);

  Serial.print(raw_angle);
  Serial.print(" ");
  Serial.print(angle);
  Serial.print(" ");
  Serial.print(zpos);
  Serial.print(" ");
  Serial.print(mpos);
  Serial.println(" ");
  
}

void AS5600_write(byte address,byte data){
  Wire.beginTransmission(0x36);
  Wire.write(address);
  Wire.write(data);  
  Wire.endTransmission();
}

byte AS5600_read(byte address){
  Wire.beginTransmission(0x36);
  Wire.write(address);
  Wire.endTransmission(false);

  Wire.requestFrom(0x36, 1);

  byte data;
  while (Wire.available()) {
    data = Wire.read();
  }
  return data;
}

unsigned int AS5600_read_2byte(byte address){
  Wire.beginTransmission(0x36);
  Wire.write(address);
  Wire.endTransmission(false);

  Wire.requestFrom(0x36, 2);
  unsigned int mpos;
  while (Wire.available()) {
    byte mpos_h = Wire.read();
    byte mpos_l = Wire.read();
    mpos = (0x0F & mpos_h) << 8 | mpos_l;
  }
  return mpos;
}


```