前一篇貼文介紹了AS5600感測器的主要特色,具備I2C介面,感測器內部的ADC(類比數位轉換器)解析度為12位元。編寫AS5600程式之前,必須知道兩個參數:
- AS5600元件的I2C位址:0x36。
- AS5600感測器「偵測角度」值的暫存器資料位址
這些參數都記載於AS5600磁編碼器的技術文件。技術文件第18頁 “Register Description(暫存器描述)”單元,列舉了每一項資料的儲存位址,包含感測角度範圍設置、角度檢測值、磁力值…等。
讀取AS56000磁編碼器的角度暫存器資料
底下是根據AS5600技術文件整理的輸出角度暫存器資料,有“raw angle(原始角度)”和“angle(角度)”兩種,「原始角度」代表感測器偵測到的絕對角度值,「角度」則是程式設定的感測範圍內的感測角度(可設定18°~360°,預設為360°)。
以讀取「原始角度」為例,資料位於暫存器位址0x0C(高位元組)和0x0D(低位元組),兩者合併成12位元長度資料。程式首先要連接到0x36位址的I2C裝置(AS5600),然後從0x0C位址開始,連續讀取兩個位元組值。
改寫《超圖解Arduino互動設計入門》動手做9-5「在I2C介面上傳送整數資料」的程式,讀取AS5600感測到的磁石原始角度值程式碼:
#include <Wire.h>
#define AS5600_ADDR 0x36 // I2C位址
#define RAW_ANGLE_ADDR 0x0C // 原始角度值的起始暫存器位址
void setup() {
Wire.begin();
Serial.begin(115200);
}
void loop() {
Wire.beginTransmission(AS5600_ADDR); // 連接AS5600
Wire.write(RAW_ANGLE_ADDR);
Wire.endTransmission();
Wire.requestFrom(AS5600_ADDR, 2); // 從AS5600請求兩個位元組值
while (Wire.available()) {
byte angle_h = Wire.read(); // 讀取高位元組資料
byte angle_l = Wire.read(); // 讀取低位元組資料
int angle = angle_h * 256 + angle_l; // 還原12位元整數值
Serial.println(angle);
}
delay(50);
}
上傳程式碼到Arduino開發板,然後把上一篇貼文製作的3D列印旋鈕蓋在AS5600模組上面,轉動旋鈕,即可從序列埠監控窗看到0~4095之間的數值。
還原12位元感測角度值
上面程式中,「還原12位元整數值」的敘述可以用底下的方式改寫,由於高位元組的前4個位元是「無意義值」,因此最好透過位元AND運算,明確將前4個位元清零,後面4個位元值保持不變,如下圖左(資料為虛構值):
然後,把高位元組值左移8位元(等同乘上28,256,但計算效率較高),再和低位元組OR在一起,變數的資料型態最好也明確指定成16位元無號整數(uint16_t),因為足夠存入12位元正整數值。這個運算的敘述寫成:
uint16_t angle = (angle_h & 0x0F) << 8 | angle_l; // 還原12位元整數值
指定ESP32的I2C接腳
ESP32晶片可用程式設定I2C的接腳,假設SDA腳是GPIO 9,SCL腳是GPIO 10,麵包板接線改成:
程式需要在Wire.begin()函式指定SDA和SCL的腳位編號,其餘程式碼不變:
#include <Wire.h>
#define SDA_PIN 9 // SDA腳
#define SCL_PIN 10 // SCL腳
#define AS5600_ADDR 0x36 // I2C位址
#define RAW_ANGLE_ADDR 0x0C // 原始角度值的起始暫存器位址
void setup() {
Wire.begin(SDA_PIN, SCL_PIN); // 指定I2C腳位(SDA, SCL)
Serial.begin(115200);
}
void loop() {
Wire.beginTransmission(AS5600_ADDR); // 連接AS5600
Wire.write(RAW_ANGLE_ADDR);
Wire.endTransmission();
Wire.requestFrom(AS5600_ADDR, 2); // 從AS5600請求兩個位元組值
while (Wire.available()) {
byte angle_h = Wire.read(); // 讀取高位元組資料
byte angle_l = Wire.read(); // 讀取低位元組資料
uint16_t angle = (angle_h & 0x0F) << 8 | angle_l; // 還原12位元整數值
Serial.println(angle);
}
delay(50);
}
安裝AS5600 Arduino程式庫
操作AS5600磁編碼器,最簡單的方法還是用現成的程式庫,即便沒閱讀技術文件,修改現成的範例就能用了。在Arduino IDE的「程式庫管理員」搜尋“AS5600”關鍵字,就能找到幾個程式庫,筆者選用Adafruit公司編寫的版本:
底下是這個程式庫附帶的AS5600_basic.ino範例原始碼,筆者將其中的輸出訊息和註解改成中文:
#include <Adafruit_AS5600.h>
Adafruit_AS5600 as5600;
void setup() {
Serial.begin(115200);
while (!Serial)
delay(10);
if (!as5600.begin()) { // 使用開發板預設的I2C接腳連線
Serial.println("找不到AS5600感測器,請檢查接線。");
while (1)
delay(10);
}
Serial.println("AS5600找到了!");
as5600.enableWatchdog(false); // 取消看門狗
as5600.setPowerMode(AS5600_POWER_MODE_NOM); // 普通電力模式
// 遲滯(hysteresis)設定
as5600.setHysteresis(AS5600_HYSTERESIS_OFF);
// 類比電壓範圍輸出(0~100%)
as5600.setOutputStage(AS5600_OUTPUT_STAGE_ANALOG_FULL);
// 設置濾波器
as5600.setSlowFilter(AS5600_SLOW_FILTER_16X);
as5600.setFastFilterThresh(AS5600_FAST_FILTER_THRESH_SLOW_ONLY);
// 重設位置設定
as5600.setZPosition(0); // 設置初始位置
as5600.setMPosition(4095); // 設置最大位置
as5600.setMaxAngle(4095); // 設置最大角度
Serial.println("偵測磁場中…");
}
void loop() {
if (! as5600.isMagnetDetected()) {
return; // 若感測不到磁場,則離開迴圈。
}
// 連續讀取並顯示角度值
uint16_t rawAngle = as5600.getRawAngle();
uint16_t angle = as5600.getAngle();
Serial.print("原始角度:");
Serial.print(rawAngle); // 10進位原始角度
Serial.print(" (0x");
Serial.print(rawAngle, HEX); // 16進位原始角度
Serial.print(") | 縮放角度:");
Serial.print(angle); // 10進位角度
Serial.print(" (0x");
Serial.print(angle, HEX); // 16進位角度
Serial.print(")");
// 檢查感測器狀態
if (as5600.isAGCminGainOverflow()) {
Serial.print(" | MH: 磁場太強!");
}
if (as5600.isAGCmaxGainOverflow()) {
Serial.print(" | ML: 磁場太弱~");
}
Serial.println();
delay(50);
}
上傳程式到Arduino開發板,轉動旋鈕的結果如下,此例的「原始角度」和「縮放角度」值相同。
若把磁石(旋鈕)往上移開AS5600模組,它將顯示「磁場太弱」:
此程式庫的方法、自訂檢測角度範圍以及在此程式庫設置ESP32 I2C接腳的辦法,下一篇再說明。
