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;
์ง์๊ธฐ ์ผ์์ ์ฐ๊ฒฐ๋์๋์ง ํ์ธํ๋ ๋ฐฉ๋ฒ์ 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;
}
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๋ก ์ผ์ฑํ๋ค.
- HMC58X3_R_MODE = 1
- 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
- HMC58X3_R_CONFA = 0x70
์ผ์ ์ด๊ธฐํ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
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);
}
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);
}
}