# 馬達編碼器解讀
馬達編碼器一般使用兩個正交 (也就是差 90°) 的霍爾感測器, 偵測編碼器上磁石的磁性變化, 我測試時的馬達編碼器如下圖:
![image](https://hackmd.io/_uploads/ryGCha9G0.png)
兩個感測器的位置如圖中標示, 當磁石轉動時, 磁石上的磁性會交替變化, 霍爾感測器就會隨之產生高低電位變化。由於兩個霍爾感測器位置正交, 所以會相差 1/4 個週期, 例如當磁石受馬達帶動**順時針**旋轉時, 感測器 ② 的變化會領先感測器 ① 的變化 1/4 個週期, 以[Desmos 網頁繪製的圖](https://www.desmos.com/calculator/gfmuvfem7d)表示如下:
![image](https://hackmd.io/_uploads/BkwToTcz0.png)
以下方的感測器 ① 來看, 每次電位改變 (圖中箭頭標示處) 時, 它改變後的電位都會和感測器 ② 相同。
如果磁石是逆時針旋轉, 變成感測器 ② 的變化會落後感測器 ① 的變化 1/4 個週期:
![image](https://hackmd.io/_uploads/H15O2p5G0.png)
同樣以下方的感測器 ① 來看, 每次電位改變 (圖中箭頭標示處) 時, 它改變後的電位都會和感測器 ② 相反。
所以只要在感測器 ① 電位變化時檢查感測器 ② 的電位, 就可以知道現在編碼器是順時鐘還是逆時鐘轉動了。
利用這樣的想法, 就可以計算感測器 ① 在編碼器順時鐘或是逆時鐘轉的電位變化次數, 以下就使用 ESP32, 在 C1 接腳設置一個電位上升時觸發的中斷, 計算從低電位變高電位的時候, 判斷編碼器磁石旋轉方向, 並以順時鐘轉遞增計數, 反之遞減計數:
```arduino=
#define C1_PIN 33 // 霍爾輸出C1的接腳
#define C2_PIN 34 // 霍爾輸出C2的接腳
volatile int32_t count = 0; // 將被中斷常式改變的值,要設成volatile。
int32_t lastCount = 0; // 儲存「上次」脈衝數
void IRAM_ATTR encISR() { // 計算脈衝數的中斷處理常式
bool c2 = digitalRead(C2_PIN); // 讀取C2的狀態
if(c2) // 若c2為1(高電位)…
count++; // 代表輪子那一面是順時針轉動
else
count--;
}
void setup() {
Serial.begin(115200);
pinMode(C1_PIN, INPUT);
pinMode(C2_PIN, INPUT);
// 偵測C1腳的變化,於脈衝上升階段觸發。
attachInterrupt(C1_PIN, encISR, RISING);
}
void loop() {
if (count != lastCount) { // 若脈衝數變動,顯示脈衝數。
lastCount = count;
Serial.printf("脈衝數:%ld\n", count);
}
}
```
以下是編碼器逆時鐘旋轉的結果, 可以看到計數遞減:
{%youtube zsWUgUhZqRo %}
以下則是編碼器順時鐘旋轉的結果, 可以看到計數遞增:
{%youtube svN_PaLkocs %}
有了變化技術後, 只要知道磁石一圈的磁性變化次數, 以及減速齒輪箱的齒輪減速比, 就可以推算最後齒輪箱轉軸的旋轉圈數, 這些都可以在馬達的規格中找到, 像是以下就是我測試的[GM25-370](https://item.taobao.com/item.htm?spm=a1z09.2.0.0.67002e8dBeRVLp&id=523820484160&_u=ns4b4pq219b):
![](https://i.imgur.com/ZiWcCqr.png)
馬達轉一圈圓形磁石上會有 11 次脈衝, 每個脈衝會出發 2 次電位變化, 總共會觸發感測器 22 次電位變化。根據底下的參數, 齒輪箱的減速比是 1:45:
![](https://i.imgur.com/EHIKX0M.png)
所以只要把剛剛的程式計出來的計數除以 22 就是馬達的轉動圈數, 再除以 45 就是實際接在齒輪箱轉軸轉動圈數, 只是馬達轉動的方向與編碼器磁石轉動的方向相反。