Baseflight MPU 6050 - FabLabSeoul/WingProject GitHub Wiki
์ด ๊ธ์ ์คํ์์ค MultiWii BaseFlight ํ๋ก์ ํธ๋ฅผ ๋ถ์ํ ๊ธ์ด๋ค.
Baseflight ์ MPU-6050 ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ ๊ฐ์๋, ์์ด๋ก ์ผ์ ์์ค๋ STM32\stm32vldiscovery_package\Project\Examples\MPU-6050_Baseflight๊ฒฝ๋ก์ ์๋ค.
MPU-6050 ์ผ์์ ์ ๋ณด๋ฅผ ์ฝ๊ธฐ์ํด, ์ฒ์ ๊ฑฐ์ณ์ผ ๊ณผ์ ์ drv_mpu6050.c ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ mpu6050Detect()
ํจ์์ ๋์์๋ค.
๊ฐ์๋ ์ผ์, ์์ด๋ก์ค์ฝํ๋ sensor_t ๊ตฌ์กฐ์ฒด๋ก ํํ๋๋ค. ๋ฉค๋ฒํจ์ํฌ์ธํฐ init, read๋ฅผ ํตํด ์ผ์๋ฅผ ์ด๊ธฐํํ๊ณ , ์ ๋ณด๋ฅผ ์ฝ์ด์จ๋ค. ๋ค์ํ ์ผ์์ ๋ง์ถฐ ํ์ฅ์ํฌ ์ ์๋๋ก ๋์์ธ ๋์๋ค. C++์ ์ถ์ ํด๋์ค์ ๋ฎ์๋ค.
typedef struct sensor_t {
sensorInitFuncPtr init; // initialize function
sensorReadFuncPtr read; // read 3 axis data function
sensorReadFuncPtr temperature; // read temperature if available
float scale; // scalefactor (currently used for gyro only, todo for accel)
} sensor_t;
typedef void (*sensorInitFuncPtr)(sensor_align_e align); // sensor init prototype
typedef void (*sensorReadFuncPtr)(int16_t *data); // sensor read and align prototype
typedef enum {
ALIGN_DEFAULT = 0, // driver-provided alignment
CW0_DEG = 1,
CW90_DEG = 2,
CW180_DEG = 3,
CW270_DEG = 4,
CW0_DEG_FLIP = 5,
CW90_DEG_FLIP = 6,
CW180_DEG_FLIP = 7,
CW270_DEG_FLIP = 8
} sensor_align_e;
mpu6050Detect()
ํจ์๋ ๋์ด์จ ์ธ์ acc, gyro, scale ๊ฐ์ ์ฑ์๋ฃ์ด์ ๋ฆฌํดํ๋ ํจ์๋ค. ์คํจํ๋ฉด false๋ฅผ ๋ฆฌํดํ๋ค. lpf๊ฐ์ low pass filter ๊ฐ์ผ๋ก, ๊ธฐ๋ณธ์ ์ผ๋ก 42๊ฐ์ ๋ฃ๋๋ค.
์ด ํจ์๋ฅผ ์ฐจ๊ทผ์ฐจ๊ทผ ๋ฐ๋ผ๊ฐ๋ณด์.
-
์ฐ์ MPU-6050๊ณผ ์ฐ๊ฒฐ๋์๋์ง ํ์ธํ๋ ๊ณผ์ ์ ๊ฑฐ์น๋ค. MPU-6050์
MPU_RA_WHO_AM_I
๋ ์ง์คํฐ์ ์ ๊ทผํด ๊ฐ์ ํ์ธํ๋ค. ์ด ๋ ๊ฐ์ 0x68 ์ด์ด์ผ ํ๋ค. -
๋ค์ ๊ณผ์ ์ MPU-6050์ revision, product id๋ฅผ ๊ฐ์ ธ์ค๋ ๋ถ๋ถ์ด๋ค. ์ด ๋ถ๋ถ์ MPU-6050์ register map์๋ ๋ฌธ์ํ ๋์ง ์์ ๋ด์ฉ์ด๋ผ ํ์คํ ๋ชจ๋ฅด๊ฒ ๋ค. ์ผ๋จ ๋์ด๊ฐ์.
-
๊ฐ์๋ ์ผ์๋ฅผ ์ด์ฉํ๊ธฐ ์ํ ๋ณ์๋ค์ ์ด๊ธฐํํ๋ ํจ์, ๊ฐ์๋ ์ผ์ ๊ฐ์ ๊ฐ์ ธ์ค๋ ํจ์, ์์ด๋ก์ค์ฝํ ์ ๋ณด๋ฅผ ์ด๊ธฐํํ๊ณ , ๊ฐ์ ๊ฐ์ ธ์ค๋ ํจ์ ํฌ์ธํฐ๋ฅผ ์ค์ ํ๋ค. ๋ค์ํ ์ผ์๋ค์ ์ ์ฉํ๊ธฐ ์ฝ๊ฒํ๊ธฐ์ํ ์ฝ๋๋ผ๊ณ ๋ณด๋ฉด๋๋ค.
-
์์ด๋ก์ค์ฝํ ๊ฐ์ ๊ฐ๋จํ๊ฒ ์ฝ์ด์ค๊ธฐ ์ํ ์ค์ผ์ผ๊ฐ ์ด๊ธฐํ (MPU-6050_Baseflight์์๋ ์ด ๊ฐ์ ์ฌ์ฉํ์ง ์๋๋ค.)
// 16.4 dps/lsb scalefactor
gyro->scale = (4.0f / 16.4f) * (M_PI / 180.0f) * 0.000001f;
-
+-2000๋ ์ด๋น ๊ฐ์๋๋ฅผ ์ฝ์ด์ค๊ฒ ์ค์ ๋์ด ์์ผ๋ฏ๋ก, ์ผ์ ์ถ๋ ฅ์ผ๋ก ๋์จ ๊ฐ์ 16.4(scale factor)๋ก ๋๋์ด์ผ ํ๋ค. ๊ฐ์๋์ด๋ฏ๋ก, ๋ผ๋์ ๋จ์๋ก ๋ฐ๊พธ๊ธฐ ์ํด (M_PI / 180.0f) ๋ฅผ ๊ณฑํ๋ค. Stm32์์ micro seconds ๋จ์๋ก ์๊ฐ์ ๊ณ์ฐํ๊ณ ์์ผ๋ฏ๋ก, ์ด๋น๊ฐ์๋์ 0.000001f๋ฅผ ๊ณฑํ๋ค.
-
x4 ์ ์๋ฏธ๋ ์์ง ๋ชจ๋ฅด๊ฒ ๋ค.
-
์ค์ gyro->scale ๊ฐ์ ๋ค์์ฒ๋ผ ์ฌ์ฉ๋๋ค. ์ต์ข ๊ฒฐ๊ณผ ๊ฐ์ ์ผ์์ ์์ธ๋ฅผ ๋ผ๋์์ผ๋ก ๊ณ์ฐํ ๊ฐ์ด๋ค. ์ด ์ฝ๋๋ imu.c ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์๋ค.
actual angle[ axis] = gyro->angle[ axis] * gyro->scale * deltaT;
- low pass filter ๊ฐ์ 42๋ฅผ ๋ฃ๊ณ , ์ผ์ ์ด๊ธฐํ ์ฝ๋๋ ๋๋๋ค.
extern uint16_t acc_1G;
static uint8_t mpuAccelHalf = 0;
bool mpu6050Detect(sensor_t *acc, sensor_t *gyro, uint16_t lpf, uint8_t *scale)
{
bool ack;
uint8_t sig, rev;
uint8_t tmp[6];
delay(35); // datasheet page 13 says 30ms. other stuff could have been running meanwhile. but we'll be safe
ack = i2cRead(MPU6050_ADDRESS, MPU_RA_WHO_AM_I, 1, &sig);
if (!ack)
return false;
// So like, MPU6xxx has a "WHO_AM_I" register, that is used to verify the identity of the device.
// The contents of WHO_AM_I are the upper 6 bits of the MPU-60X0๋ญฉ 7-bit I2C address.
// The least significant bit of the MPU-60X0๋ญฉ I2C address is determined by the value of the AD0 pin. (we know that already).
// But here's the best part: The value of the AD0 pin is not reflected in this register.
// ์ ์์ ์ผ๋ก ์๋ํ๋ค๋ฉด sig ๊ฐ์ 1101000(b) = 0x68 ๊ฐ์ ๊ฐ์ง๋ค.
if (sig != (MPU6050_ADDRESS & 0x7e))
return false;
// determine product ID and accel revision
i2cRead(MPU6050_ADDRESS, MPU_RA_XA_OFFS_H, 6, tmp);
rev = ((tmp[5] & 0x01) << 2) | ((tmp[3] & 0x01) << 1) | (tmp[1] & 0x01);
if (rev) {
/* Congrats, these parts are better. */
if (rev == 1) {
mpuAccelHalf = 1;
} else if (rev == 2) {
mpuAccelHalf = 0;
} else {
failureMode(5);
}
} else {
i2cRead(MPU6050_ADDRESS, MPU_RA_PRODUCT_ID, 1, &sig);
rev = sig & 0x0F;
if (!rev) {
failureMode(5);
} else if (rev == 4) {
mpuAccelHalf = 1;
} else {
mpuAccelHalf = 0;
}
}
acc->init = mpu6050AccInit;
acc->read = mpu6050AccRead;
gyro->init = mpu6050GyroInit;
gyro->read = mpu6050GyroRead;
// 16.4 dps/lsb scalefactor
gyro->scale = (4.0f / 16.4f) * (M_PI / 180.0f) * 0.000001f;
// give halfacc (old revision) back to system
if (scale)
*scale = mpuAccelHalf;
// default lpf is 42Hz
if (lpf >= 188)
mpuLowPassFilter = INV_FILTER_188HZ;
else if (lpf >= 98)
mpuLowPassFilter = INV_FILTER_98HZ;
else if (lpf >= 42)
mpuLowPassFilter = INV_FILTER_42HZ;
else if (lpf >= 20)
mpuLowPassFilter = INV_FILTER_20HZ;
else if (lpf >= 10)
mpuLowPassFilter = INV_FILTER_10HZ;
else
mpuLowPassFilter = INV_FILTER_5HZ;
return true;
}
๊ฐ์๋ ์ผ์๋ฅผ ์ด๊ธฐํํ๋ ์ฝ๋๋ ๋ณต์กํ์ง ์๋ค. ๊ทธ๋ฆฌ๊ณ MPU-6050_Baseflight์์๋ mpu6050AccInit()
ํจ์์์ ์ด๊ธฐํ๋ ๋ณ์๋ ์ฐ์ด์ง ์๊ณ ์๋ค. ๊ฐ์๋ ์ผ์ ํ๊ฒฝ์ค์ ์ฝ๋๋ ํ์ฌ mpu6050GyroInit()
ํจ์์ ์๋ค.
์ด ์ฝ๋๋ ๊ฐ์๋ ์ผ์ ์ธ์ ๋ฒ์๋ฅผ +-8g ๊ฐ ๋๊ฒ ํ๋ค. ๊ทธ๋์ Scale Factor๋ 4,096์ด ๋๋ค.
// ACC Init stuff. Moved into gyro init because the reset above would screw up accel config. Oops.
// Accel scale 8g (4096 LSB/g)
i2cWrite(MPU6050_ADDRESS, MPU_RA_ACCEL_CONFIG, INV_FSR_8G << 3);
์์ด๋ก์ผ์๋ฅผ ์ด๊ธฐํํ๋ ์ฝ๋๋ ์กฐ๊ธ ๋ณต์กํ๋ค. ์ค์ ํ๋ ๊ฐ์ ๋ค์๊ณผ ๊ฐ๋ค.
- MPU_RA_SMPLRT_DIV = 0
- MPU_RA_PWR_MGMT_1 = 3
- Z Axis ์ผ์ ํด๋ญ์ผ๋ก ๋งค์ธ ํด๋ญ์ ๋ง์ถ๋ค๋ ์๋ฏธ๋ค. 27 KHz๋ค. ์ข๋ ์์ ์ ์ธ ์ผ์ฑ์์ํด ์ ์กฐ์ฌ์์ ์ด ๋ฐฉ์์ ์ถ์ฒํ๊ณ ์๋ค.
- MPU_RA_INT_PIN_CFG = 2
- MPU_RA_CONFIG = 42
- MPU_RA_GYRO_CONFIG = 0x18
- ์์ด๋ก์ค์ฝํ ์ผ์ฑ ๋ฒ์๋ฅผ +-2000๋ ๋ก ํ๊ฒ ํ๋ค. ๊ทธ๋์ Scale Factor ๊ฐ์ 16.4 ๊ฐ ๋๋ค.
static void mpu6050AccInit(sensor_align_e align)
{
if (mpuAccelHalf)
acc_1G = 255 * 8;
else
acc_1G = 512 * 8;
if (align > 0)
accAlign = align;
}
static void mpu6050GyroInit(sensor_align_e align)
{
gpio_config_t gpio;
gpio.pin = Pin_13;
gpio.speed = Speed_2MHz;
gpio.mode = Mode_IN_FLOATING;
gpioInit(GPIOC, &gpio);
i2cWrite(MPU6050_ADDRESS, MPU_RA_PWR_MGMT_1, 0x80); //PWR_MGMT_1 -- DEVICE_RESET 1
delay(100);
i2cWrite(MPU6050_ADDRESS, MPU_RA_SMPLRT_DIV, 0x00); //SMPLRT_DIV -- SMPLRT_DIV = 0 Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)
i2cWrite(MPU6050_ADDRESS, MPU_RA_PWR_MGMT_1, 0x03); //PWR_MGMT_1 -- SLEEP 0; CYCLE 0; TEMP_DIS 0; CLKSEL 3 (PLL with Z Gyro reference)
i2cWrite(MPU6050_ADDRESS, MPU_RA_INT_PIN_CFG, 0 << 7 | 0 << 6 | 0 << 5 | 0 << 4 | 0 << 3 | 0 << 2 | 1 << 1 | 0 << 0); // INT_PIN_CFG -- INT_LEVEL_HIGH, INT_OPEN_DIS, LATCH_INT_DIS, INT_RD_CLEAR_DIS, FSYNC_INT_LEVEL_HIGH, FSYNC_INT_DIS, I2C_BYPASS_EN, CLOCK_DIS
i2cWrite(MPU6050_ADDRESS, MPU_RA_CONFIG, mpuLowPassFilter); //CONFIG -- EXT_SYNC_SET 0 (disable input pin for data sync) ; default DLPF_CFG = 0 => ACC bandwidth = 260Hz GYRO bandwidth = 256Hz)
i2cWrite(MPU6050_ADDRESS, MPU_RA_GYRO_CONFIG, INV_FSR_2000DPS << 3);
// ACC Init stuff. Moved into gyro init because the reset above would screw up accel config. Oops.
// Accel scale 8g (4096 LSB/g)
i2cWrite(MPU6050_ADDRESS, MPU_RA_ACCEL_CONFIG, INV_FSR_8G << 3);
if (align > 0)
gyroAlign = align;
}
๊ฐ์๋, ์์ด๋ก์ค์ฝํ ์ผ์ ๊ฐ์ ๊ฐ์ ธ์ค๋ ํจ์๋ acc.read(); gyro.read();
int main()
{
if (!setup())
return 0;
calibrate_sensors();
set_last_read_angle_data(millis(), 0, 0, 0, 0, 0, 0);
while(1)
{
int16_t accelgyro[6];
acc.read(accelgyro);
gyro.read(accelgyro+3);
SendSerialAccelGryro(accelgyro);
delay(10);
}
}