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() ํ•จ์ˆ˜

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;
}

์„ผ์„œ ์ดˆ๊ธฐํ™”, acc.init(), gyro.init()

๊ฐ€์†๋„ ์„ผ์„œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ์ฝ”๋“œ๋Š” ๋ณต์žกํ•˜์ง€ ์•Š๋‹ค. ๊ทธ๋ฆฌ๊ณ  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;
}

Main() ํ•จ์ˆ˜

๊ฐ€์†๋„, ์ž์ด๋กœ์Šค์ฝ”ํ”„ ์„ผ์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜๋Š” 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);
	}
}