BaseFlight HMC5883L - FabLabSeoul/WingProject GitHub Wiki

์˜คํ”ˆ์†Œ์Šค MultiWii Baseflight ํ”„๋กœ์ ํŠธ์—๋Š” 3์ถ• ์ง€์ž๊ธฐ ์„ผ์„œ์ธ HMC5883L๋ฅผ ์ธ์‹ํ•˜๋Š” hmc5883l.c,h ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋‹ค. ์ด ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด์„œ๋Š” i2c, gpio, system, board ํŒŒ์ผ์ด ํ•„์š”ํ•˜๋‹ค. i2c, gpio, system์€ ์—ฌ๋Ÿฌ๊ตฐ๋ฐ์—์„œ ์“ฐ์ด๋Š” ๊ธฐ๋ณธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ™์ด ๊ฐ€์ ธ์˜ค๋ฉด ๋˜๊ณ , board๋Š” Naze32๋ณด๋“œ๋ฅผ ์œ„ํ•œ ๋ณ„๋„์˜ ํŒŒ์ผ์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€์ ธ์™€์„œ ์“ธ ์ˆ˜ ์—†๋‹ค. ์ด ๋ถ€๋ถ„์€ Stm32 Value line discovery ๋ณด๋“œ์— ๋งž๊ฒŒ ์ˆ˜์ •๋˜์–ด์„œ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค. ์˜ˆ๋ฅผ๋“ค๋ฉด LED0, LED1์€ Naze32์™€ Stm32 ๋ณด๋“œ์˜ ํ•€๋ฒˆํ˜ธ๊ฐ€ ๋‹ค๋ฅด๋‹ค.

๋•Œ๋ฌธ์— Stm32๋ณด๋“œ ์ „์šฉ ์ •์˜๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” global.h ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด, ์ „์—ญ ๋ณ€์ˆ˜๋‚˜ ์—ด๊ฑฐํ˜•๋“ค์„ ์ •์˜ํ•œ๋‹ค. board.h๋ฅผ ๋Œ€์‹ ํ•˜๋Š” ์†Œ์Šค๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. i2c, gpio, system ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํŒŒ์ผ๋“ค์€ ํฌ๊ฒŒ ๊ณ ์น˜์ง€ ์•Š๊ณ , ์‰ฝ๊ฒŒ ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ๋‹ค. hmc5883l ์†Œ์Šค๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค.

๊ตฌํ˜„๋œ ์†Œ์Šค๋Š” I2C_Baseflight ํ”„๋กœ์ ํŠธ์— ์žˆ๋‹ค.

์ดˆ๊ธฐํ™”

systemInit() ํ•จ์ˆ˜๋Š” drv_system.h ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํ•จ์ˆ˜๋‹ค. ํด๋Ÿญ์„ ์ดˆ๊ธฐํ™”ํ•˜๊ณ , delay, tick ๊ธฐ๋Šฅ์ด ์ˆ˜ํ–‰๋  ์ˆ˜ ์žˆ๊ฒŒ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. SerialSetup()ํ•จ์ˆ˜๋Š” PA10,9(Rx,Tx)ํฌํŠธ๋ฅผ ์‹œ๋ฆฌ์–ผํฌํŠธ๋กœ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค. init_printf()ํ•จ์ˆ˜๋Š” printf()ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. ๋‹ค์Œ์œผ๋กœ Stm32 ๋ณด๋“œ์— ์žˆ๋Š” LED3,4๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ๊ณ , ๋งˆ์ง€๋ง‰์œผ๋กœ HMC5883L๊ณผ ํ†ต์‹ ํ•  i2c๋ฅผ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค. MCU๊ฐ€ ๋งˆ์Šคํ„ฐ๊ฐ€ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ownAddress๊ฐ’์€ 0์ด๋‹ค.

void setup(void)
{
  gpio_config_t gpio;
	
  // ํด๋Ÿญ์„ ์ดˆ๊ธฐํ™”ํ•˜๊ณ , delay, tick ๊ธฐ๋Šฅ์ด ์ˆ˜ํ–‰๋  ์ˆ˜ ์žˆ๊ฒŒ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.
  systemInit();
	
  // PA10,9(Rx,Tx)ํฌํŠธ๋ฅผ ์‹œ๋ฆฌ์–ผํฌํŠธ๋กœ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค.
  SerialSetup();
	
  // printf()ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.
  init_printf(NULL, putc);
  printf( "Init baseflight i2c\n" );
	
  // Stm32 ๋ณด๋“œ์— ์žˆ๋Š” LED3,4๋ฅผ ํ™œ์„ฑํ™” ํ•œ๋‹ค.
  gpio.pin = Pin_All;
  gpio.speed = Speed_2MHz;
  gpio.mode = Mode_Out_PP;
  gpioInit(GPIOC, &gpio);
	
  // HMC5883L๊ณผ ํ†ต์‹ ํ•  i2c๋ฅผ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค.
  i2cInit(I2CDEV_1, 0x00); // Init Master i2c 
  printf( "init i2c\n");
}

์„ผ์„œ ๊ตฌ์กฐ์ฒด

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

์ง€์ž๊ธฐ ์„ผ์„œ ์ดˆ๊ธฐํ™”, hmc5883lDetect(), Mag_init()

์ง€์ž๊ธฐ ์„ผ์„œ์™€ ์—ฐ๊ฒฐ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์€ Identification Register A์˜ ์ •๋ณด๊ฐ€ 'H' ๊ฐ’์ธ์ง€ ํŒ๋‹จํ•˜๋Š” ๊ฒƒ์ด๋‹ค. Identification Register A,B,C๋Š” ๊ฐ๊ฐ ASCII์ฝ”๋“œ๋กœ H43 ๊ฐ’์„ ๊ฐ€์ง„๋‹ค.

HMC5883L ์„ผ์„œ๋ฅผ ํ™•์ธํ•œ ํ›„, ์ง€์ž๊ธฐ ์„ผ์„œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ์œ„ํ•œ ์ดˆ๊ธฐํ™” ๊ณผ์ •์„ mag.init()ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ด๋ค„์ง„๋‹ค.

bool hmc5883lDetect(sensor_t *mag)
{
    bool ack = false;
    uint8_t sig = 0;

    ack = i2cRead(MAG_ADDRESS, 0x0A, 1, &sig);
    if (!ack || sig != 'H')
        return false;

    mag->init = hmc5883lInit;
    mag->read = hmc5883lRead;
    
    return true;
}

sensor_t mag;  // mag access functions
void Mag_init(void)
{
    // initialize and calibration. turn on led during mag calibration (calibration routine blinks it)
    LED1_ON;
    mag.init(ALIGN_DEFAULT);
    LED1_OFF;
}

hmc5883lInit(), mag.init() ํ•จ์ˆ˜

HMC5883L ์„ผ์„œ์˜ ํ™˜๊ฒฝ์„ค์ • ์ฝ”๋“œ๋‹ค.

  • HMC58X3_R_CONFA = 0x010 + HMC_POS_BIAS(1)
    • Output Rate = 15 Hz (default)
    • X,Y ์ถ•์€ ์–‘์ˆ˜, Z์ถ•์€ ์Œ์ˆ˜๋กœ ๋ฐ”์ด์–ด์Šค๊ฐ€ ์ ์šฉ๋œ๋‹ค.
  • HMC58X3_R_CONFB = 0x60
    • ์„ผ์‹ฑ ๋ฒ”์œ„ +-2.5Ga, Scale Factor = 614
  • 500 millisecond ๋™์•ˆ ์–‘์˜ ๋ฐ”์ด์–ด์Šค๋กœ ์„ผ์„œ ์ธก์ •
    • HMC58X3_R_MODE = 1
      • Single-Measurement Mode๋กœ ์„ผ์‹ฑํ•œ๋‹ค.
  • 500 millisecond ๋™์•ˆ ์Œ์˜ ๋ฐ”์ด์–ด์Šค๋กœ ์„ผ์„œ ์ธก์ •
    • HMC58X3_R_CONFA = 0x010 + HMC_NEG_BIAS(2)
    • HMC58X3_R_MODE = 1
      • Single-Measurement Mode๋กœ ์„ผ์‹ฑํ•œ๋‹ค.
  • 1 ์ดˆ ๋™์•ˆ ์ธก์ •ํ•œ ์„ผ์„œ ๊ฐ’์œผ๋กœ ์ตœ์ข… ์ด๋“์„ ๊ณ„์‚ฐํ•œ๋‹ค.
    • ์ธก์ • ๋ฒ”์œ„๋ฅผ +-2.5g ๋กœ ๋Š˜๋ ธ๊ธฐ ๋•Œ๋ฌธ์—, Scale Factor๊ฐ€ 660์ด ๋˜์–ด์•ผ ํ•œ๋‹ค. ๊ทธ ๋ฐ–์— ๊ณ„์‚ฐ์‹์€ ์•„์ง ์ดํ•ด ๋ถˆ๊ฐ€.
magGain[X] = fabsf(660.0f * HMC58X3_X_SELF_TEST_GAUSS * 2.0f * 10.0f / xyz_total[X]);
magGain[Y] = fabsf(660.0f * HMC58X3_Y_SELF_TEST_GAUSS * 2.0f * 10.0f / xyz_total[Y]);
magGain[Z] = fabsf(660.0f * HMC58X3_Z_SELF_TEST_GAUSS * 2.0f * 10.0f / xyz_total[Z]);
  • ๋งˆ์ง€๋ง‰์œผ๋กœ ํ™˜๊ฒฝ์„ค์ •์„ ๋ณต๊ตฌํ•˜๊ณ , ์„ผ์‹ฑ์— ๋Œ์ž…ํ•œ๋‹ค.
    • HMC58X3_R_CONFA = 0x70
      • output rate: 15Hz, normal measurement mode
    • HMC58X3_R_CONFB = 0x20
      • default +-1.2Ga, Scale Factor = 1024
    • HMC58X3_R_MODE = 0
      • Continuous-Measurement Mode

์„ผ์„œ ์ดˆ๊ธฐํ™” ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

void hmc5883lInit(sensor_align_e align)
{
    gpio_config_t gpio;
    int16_t magADC[3];
    int i;
    int32_t xyz_total[3] = { 0, 0, 0 }; // 32 bit totals so they won't overflow.
    bool bret = true;           // Error indicator

    if (align > 0)
        magAlign = align;
    
    gpio.pin = Pin_12;
    gpio.speed = Speed_2MHz;
    gpio.mode = Mode_IN_FLOATING;
    gpioInit(GPIOB, &gpio);

    delay(50);
    i2cWrite(MAG_ADDRESS, HMC58X3_R_CONFA, 0x010 + HMC_POS_BIAS);   // Reg A DOR = 0x010 + MS1, MS0 set to pos bias
    // Note that the  very first measurement after a gain change maintains the same gain as the previous setting.
    // The new gain setting is effective from the second measurement and on.
    i2cWrite(MAG_ADDRESS, HMC58X3_R_CONFB, 0x60); // Set the Gain to 2.5Ga (7:5->011)
    delay(100);
    hmc5883lRead(magADC);

    for (i = 0; i < 10; i++) {  // Collect 10 samples
        i2cWrite(MAG_ADDRESS, HMC58X3_R_MODE, 1);
        delay(50);
        hmc5883lRead(magADC);       // Get the raw values in case the scales have already been changed.

        // Since the measurements are noisy, they should be averaged rather than taking the max.
        xyz_total[X] += magADC[X];
        xyz_total[Y] += magADC[Y];
        xyz_total[Z] += magADC[Z];

        // Detect saturation.
        if (-4096 >= min(magADC[X], min(magADC[Y], magADC[Z]))) {
            bret = false;
            break;              // Breaks out of the for loop.  No sense in continuing if we saturated.
        }
        LED1_TOGGLE;
    }

    // Apply the negative bias. (Same gain)
    i2cWrite(MAG_ADDRESS, HMC58X3_R_CONFA, 0x010 + HMC_NEG_BIAS);   // Reg A DOR = 0x010 + MS1, MS0 set to negative bias.
    for (i = 0; i < 10; i++) {
        i2cWrite(MAG_ADDRESS, HMC58X3_R_MODE, 1);
        delay(50);
        hmc5883lRead(magADC);               // Get the raw values in case the scales have already been changed.

        // Since the measurements are noisy, they should be averaged.
        xyz_total[X] -= magADC[X];
        xyz_total[Y] -= magADC[Y];
        xyz_total[Z] -= magADC[Z];

        // Detect saturation.
        if (-4096 >= min(magADC[X], min(magADC[Y], magADC[Z]))) {
            bret = false;
            break;              // Breaks out of the for loop.  No sense in continuing if we saturated.
        }
        LED1_TOGGLE;
    }

    magGain[X] = fabsf(660.0f * HMC58X3_X_SELF_TEST_GAUSS * 2.0f * 10.0f / xyz_total[X]);
    magGain[Y] = fabsf(660.0f * HMC58X3_Y_SELF_TEST_GAUSS * 2.0f * 10.0f / xyz_total[Y]);
    magGain[Z] = fabsf(660.0f * HMC58X3_Z_SELF_TEST_GAUSS * 2.0f * 10.0f / xyz_total[Z]);

    // leave test mode
    i2cWrite(MAG_ADDRESS, HMC58X3_R_CONFA, 0x70);   // Configuration Register A  -- 0 11 100 00  num samples: 8 ; output rate: 15Hz ; normal measurement mode
    i2cWrite(MAG_ADDRESS, HMC58X3_R_CONFB, 0x20);   // Configuration Register B  -- 001 00000    configuration gain 1.3Ga
    i2cWrite(MAG_ADDRESS, HMC58X3_R_MODE, 0x00);    // Mode register             -- 000000 00    continuous Conversion Mode
    delay(100);

    if (!bret) {                // Something went wrong so get a best guess
        magGain[X] = 1.0f;
        magGain[Y] = 1.0f;
        magGain[Z] = 1.0f;
    }
}

์„ผ์„œ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ

์ง€์ž๊ธฐ ์„ผ์„œ ์ •๋ณด๋ฅผ ์ฝ์–ด์™€ magGain[]์„ ๊ณฑํ•ด์„œ ์ €์žฅํ•œ๋‹ค.

void hmc5883lRead(int16_t *magData)
{
    uint8_t buf[6];
    int16_t mag[3];

    i2cRead(MAG_ADDRESS, MAG_DATA_REGISTER, 6, buf);
    // During calibration, magGain is 1.0, so the read returns normal non-calibrated values.
    // After calibration is done, magGain is set to calculated gain values.
    mag[X] = (int16_t)(buf[0] << 8 | buf[1]) * magGain[X];
    mag[Z] = (int16_t)(buf[2] << 8 | buf[3]) * magGain[Z];
    mag[Y] = (int16_t)(buf[4] << 8 | buf[5]) * magGain[Y];
    
    alignSensors(mag, magData, magAlign);	
}

Main() ํ•จ์ˆ˜

HMC5883 ์„ผ์„œ์™€ ์—ฐ๊ฒฐ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ์ดˆ๊ธฐํ™” ๊ณผ์ •์„ ๊ฑฐ์นœ ํ›„, ์„ผ์„œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค. i2cํ†ต์‹ ์ด ์ธํ„ฐ๋ŸฝํŠธ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•ด์กŒ๋‹ค. ์„ผ์„œ์™€์˜ i2cํ†ต์‹ ์€ drv_i2c์—์„œ ์ฒ˜๋ฆฌํ•˜๊ณ , ์ด๋ฅผ ๋ฐ›์•„์„œ ์›ํ•˜๋Š” ์ •๋ณด๋กœ ๋ฐ”๊พธ๋Š”๊ฒƒ์€ drv_hmc5883l์—์„œ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค.

sensor_t mag;  // mag access functions

void Mag_init(void)
{
    // initialize and calibration. turn on led during mag calibration (calibration routine blinks it)
    LED1_ON;
    mag.init(ALIGN_DEFAULT);
    LED1_OFF;
}

int main()
{
  setup();
	
  // HMC5883L๊ณผ ์—ฐ๊ฒฐ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ.
  if (!hmc5883lDetect(&mag))
  {
     return 1;
  }
	
  // HMC5883L ์ง€์ž๊ธฐ์„ผ์„œ ์ดˆ๊ธฐํ™”
  Mag_init();
  printf( "Init Magnetometer\n" );
		
  while(1)
  {
    int16_t Mag[3];

    // ์ง€์ž๊ธฐ ์„ผ์„œ์ •๋ณด๋ฅผ i2cํ†ต์‹ ์„ ํ†ตํ•ด ์ฝ์–ด์˜จ๋‹ค.
    mag.read(Mag);

    print_byte('S');
    print_short( Mag[ 0] );
    print_short( Mag[ 1] );
    print_short( Mag[ 2] );
		
    delayMicroseconds(10000);		
  }
}