---
tags: BW16, RTL8720, Ameba, Arduino, I2C, MPU6050, HMC5883L, 陀螺儀, 伴伴學
---
# BW16伴伴板 + I2C (MPU6050, HMC5883L)
# BW16伴伴板
* 伴伴學社群開發板,主核心採用瑞昱的BW16
* 外拉GPIO腳位完全依照BW16排列,無額外NC腳
* 板載電源LED與一顆可控LED於PA12,上拉3.3V時發光
* UART接頭與常見6PIN FT232相對應,方便直連不需額外拉線
# I2C
* IIC I2C TWI都是指一樣的東西,CLOCK與DATA兩條線
* 頻率由主機決定,每個設備端有一個不衝突的地址
* 每次傳輸固定8bit的資料為一個單位
* 主從式架構,只能由主機發送訊號啟始,設備端不可自行傳輸
起始訊號只有主機傳送資料或主機要求設備回傳資料兩種選擇
* 地址為7bit,但傳輸時會左移一個bit,最後一個bit為讀寫的設定
例如HMC5883L的地址為0x1E,僅發送資料時為0x3C,要求回傳資料時為0x3D
* BW16接腳位置: SCL = PA25,SDA = PA26,正好是最底部兩個GPIO
# MPU6050
* 6軸: 三軸加速度計與三軸陀螺儀
* 已停產,後續改用MPU9250(9軸)?
* 搭配指南針可啟用DMP自動計算俯仰,細節未公開在文件中
I2CDEV函式庫有解讀成功,但每次開機須矯正
# HML5883L
* 三軸指南針
# 程式資料
特別注意操作Bitwise控制時,最好的做法是先讀取原有數值,更改要變更的位置後再寫入
此次很剛好不需要保留原數值
## I2C
* 內建函式庫: Wire.h
* 初始化:
Wire.begin();
Wire.setClock(400000L); (預設為100KHz)
* 傳送:
Wire.beginTransmission(SLAVE_ADDR);
Wire.write(REG_ADDR);
Wire.write(DATA);
Wire.endTransmission(true);
* 讀取:
Wire.beginTransmission(SLAVE_ADDR);
Wire.write(REG_ADDR); (移動暫存器位置)
Wire.endTransmission(false);
Wire.requestFrom(SLAVE_ADDR, N, true);
Wire.read(); (N times)
## MPU6050 僅加速度儀
* I2C地址: 0x68 (7bit)
* 驗證正確讀取: 暫存器0x75 內容為0x68
暫存器位置: 10~12, 數值: 0x48,0x34, 0x33
* 運作頻率: 內部晶振8mHz, 關閉陀螺儀與溫度計,加速度計每秒採樣20次
暫存器位置: 0x6B, 數值: 0x28
暫存器位置: 0x6C, 數值: 0x87
* 低通過濾: 94Hz
暫存器位置: 0x1A, 數值: 0x02
* 加速度: +-2g
暫存器位置: 0x1C, 數值: 0x00
* 加速度資料:
暫存器位置: 0x3B~0x40, 數值: signed 16bit x3
* 標準化加速度數值:
+-2G時: 16384 LSB/G
:::spoiler 伴伴板讀取MPU6050加速度計
```c=
#include<Wire.h>
const int MPU_ADDR = 0x68;
#define MPU_ACC_CONST 16384 // for +-2G
void mpu6050_init(){
// Set gyroscope range to +-1000 deg/s
Serial1.println("Set to internal 8mHz clock.");
Serial1.println("Turn off GYRO and TEMP and set sampling at 20Hz");
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x6B); // PWR_MGMT_1 and PWR_MGMT_2
Wire.write(0x28);
Wire.write(0x87);
Wire.endTransmission(true);
delay(100);
Serial1.println("Set Low-pass filiter to 94Hz");
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x1A); // CONFIG
Wire.write(0x02);
Wire.endTransmission(true);
// Set accelerometer range to +-2g
Serial1.println("Set accel to 2G");
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x1C); // ACCEL_CONFIG
Wire.write(0x00);
Wire.endTransmission(true);
delay(100);
}
void mpu_read_acc(int16_t *x, int16_t *y, int16_t *z){
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADDR, 6, true);
*x = (Wire.read()<<8 | Wire.read());
*y = (Wire.read()<<8 | Wire.read());
*z = (Wire.read()<<8 | Wire.read());
}
void setup() {
Wire.begin();
Wire.setClock(400000L);
Serial1.begin(115200);
delay(100);
mpu6050_init();
}
void loop() {
int16_t acc_x = 0;
int16_t acc_y = 0;
int16_t acc_z = 0;
mpu_read_acc(&acc_x, &acc_y, &acc_z);
//Serial1.print("ACC: ");
Serial1.print((float)acc_x/MPU_ACC_CONST);
Serial1.print(", ");
Serial1.print((float)acc_y/MPU_ACC_CONST);
Serial1.print(", ");
Serial1.print((float)acc_z/MPU_ACC_CONST);
Serial1.println(", ");
delay(500);
}
```
:::
## HMC5883L
* I2C地址: 0x1E (7bit)
* 驗證正確讀取: 暫存器10~12 內容為"H43"
暫存器位置: 10~12, 數值: 0x48,0x34, 0x33
* 運作頻率: 8次平均,15Hz連續輸出
暫存器位置: 0, 數值: 0x70
暫存器位置: 2, 數值: 0x00
* 磁場資料:
暫存器位置: 3~8, 數值: signed 16bit x3
正常輸出為-2048~2047之間,若輸出為-4096則表示超過可偵測數值,調整Gain
* 標準化磁場數值:
+-1.3高斯時: 1090 LSB/Gauss
:::spoiler 伴伴板讀取HMC5883L磁力表
```c=
#include<Wire.h>
const int MAG_ADDR = 0x1E;
#define MAG_SCALE_CONST 1090 // for +-1.3Ga
void hmc5883l_init(){
// Disable sleep mode
Serial1.println("Init HMC5883L");
Wire.beginTransmission(MAG_ADDR);
Wire.write(0); // CONFIG REG A
Wire.write(0x70);
Wire.endTransmission(true);
Wire.beginTransmission(MAG_ADDR);
Wire.write(2); // MODE
Wire.write(0x00);
Wire.endTransmission(true);
delay(100);
}
void hmc5883l_read_value(int16_t *x, int16_t *y, int16_t *z){
Wire.beginTransmission(MAG_ADDR);
Wire.write(3); // 3~8
Wire.endTransmission(false);
Wire.requestFrom(MAG_ADDR, 6, true);
*x = (Wire.read()<<8 | Wire.read());
*y = (Wire.read()<<8 | Wire.read());
*z = (Wire.read()<<8 | Wire.read());
}
void setup() {
Wire.begin();
Wire.setClock(400000L);
Serial1.begin(115200);
delay(100);
hmc5883l_init();
}
void loop() {
int16_t mag_x = 0;
int16_t mag_y = 0;
int16_t mag_z = 0;
hmc5883l_read_value(&mag_x, &mag_y, &mag_z);
//Serial1.print("MAG: ");
Serial1.print((float)mag_x/MAG_SCALE_CONST);
Serial1.print(", ");
Serial1.print((float)mag_y/MAG_SCALE_CONST);
Serial1.print(", ");
Serial1.print((float)mag_z/MAG_SCALE_CONST);
Serial1.println(", ");
delay(500);
}
```
:::
# 參考資料
## 瑞昱 BW16
* 官方網站: https://www.amebaiot.com/zh/ameba-arduino-summary/
* 官方github: https://github.com/ambiot/ambd_arduino/
* BW16模塊接腳圖: ![模塊接腳圖](https://github.com/mikey60/BW16-RTL8720DN-Module-Arduino/raw/main/Info/RTL8720DN_Pinout_Small.png)
## 伴伴學與伴伴板
* 伴伴學共筆: https://hackmd.io/@accomdemy/SJsr63mkt
* 伴伴板開發共筆: https://hackmd.io/y3AZWm9URGGaHvRV8FjrMg
## I2C模塊相關資料
* Arduino I2CDev非官方函式庫: https://github.com/jrowberg/i2cdevlib
* I2C介紹: https://magicjackting.pixnet.net/blog/post/173061691
* I2C範例: https://forum.arduino.cc/t/i2c-protocol-tutorial-using-an-mpu6050/387512
* MPU6050資料: https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Datasheet1.pdf
* MPU6050暫存器表: https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf
* HMC5883L資料: https://cdn-shop.adafruit.com/datasheets/HMC5883L_3-Axis_Digital_Compass_IC.pdf