Wednesday, March 31, 2010

Turning a robot with HMC6343


The HMC6343 is a solid-state compass module with tilt compensation from Honeywell. The HMC6343 has three axis of magneto, three axis of accelerometer, and a PIC core running all the calculations. What you get is a compass heading over I2C that stays the same even as you tilt the board. Solid-state (and many classic water based) compasses fail badly when not held flat. The tilt compensated HMC6343 is crucial for those real-world compass applications.

Features:
-I2C interface
-2 degree heading accuracy
-All in one compass solution
-Compass heading and tilt outputs
-Dimensions: 0.8x0.8"



1) Data Reading
Despite all the good things mentioned on the webpage, when i started to link to atmega128, I found out that I cannot read all 6 bytes in sequence as the example shown in the datasheet.

//Gets bulk data for the four different post types (Accel, Mag, Heading, Tilt)
void send_command(uns8 command_byte)
{
uns8 in_byte, i;

i2c_ack_polling(DEVICE_WRITE);

i2c_start();
i2c_send_byte(DEVICE_WRITE);
i2c_send_byte(command_byte); //Send command
//i2c_stop();

i2c_start(); //Repeat start (SR)
i2c_send_byte(DEVICE_READ); //Now ask the IC to report on the last command
for(i = 0 ; i < in_byte =" i2c_read_bytes();">


Rather, I have to send the device read address before reading each byte. The program becomes like the one below. It requires more time in reading heading data as the data transfer is in serial.

void getheading(){
uns8 hmc_data[6];
bled_on;
i2c_start();
i2c_write(0x32);
i2c_write(0x50);
delay_ms(2);
i2c_start();
i2c_write(0x33);
hmc_data[0] = i2c_read(0);
i2c_start();
i2c_write(0x33);
hmc_data[1] = i2c_read(0);
i2c_start();
i2c_write(0x33);
hmc_data[2] = i2c_read(0);
i2c_start();
i2c_write(0x33);
hmc_data[3] = i2c_read(0);
i2c_start();
i2c_write(0x33);
hmc_data[4] = i2c_read(0);
i2c_start();
i2c_write(0x33);
hmc_data[5] = i2c_read(0);
i2c_stop();
heading = hmc_data[0] <<>
tilt = hmc_data[2] <<>
roll = hmc_data[4] <<>
if ((tilt & 0xE000) != 0x0000){ tilt = ~tilt; }
if ((roll & 0xE000) != 0x0000){roll = ~roll; }
}


2) Data Reading During Movement

It is also found out that after turning the robot in a few milliseconds, the heading data from HMC6343 is not yet updated. About 400 ms is required to get a stable reading. The datasheet described that the normal updating rate is 5Hz. Therefore, it is very clear that if we read the heading and check at every step of the motor, the motor steps should take longer than 200 msec. Therefore, it is easier to calculate the number steps it should turn to make the require degree and read the heading from the compass after taking all steps. If there is any difference in the heading desired and heading read from the compass, another the number of steps need to compensate is calculated again. Then turn again until the error is smaller than designed allowance.

The calculation needs to be careful when it crosses the north. If the robot was turn 100 degree
from the heading 300 degrees to the right (east), it will pass 360 degrees and ends at 40 degrees
heading. If the robot is at heading 100 degrees and turn left (-200) degrees then the final heading read from the compass is 260 degrees.


So the total degrees turned should be calculated differently for right and left turns. Especially the amount of degrees turned should be calculated carefully if the compass turning cross the north.

The simplified turning code can be like the one below:


void turn(float deg){
signed char lr;
unsigned char noofturn,i;
int turnerror;
int oldhead;
if (deg > 360) deg = deg-360;
if (deg < -360) deg = deg+360;
turnerror = (int)(deg * 10);
noofturn = (int)abs(turnerror)/DEGPERTURN;
while (noofturn > 0){
lr = sign(turnerror);
noofturn = (int)abs(turnerror)/DEGPERTURN;
if (noofturn>0) {
getheading();
oldhead = heading;
for (i =0; i
if (lr>0)left();
else right();
}
delay_ms(400); // it is found out that 400msec delay
// can stabilized the heading reading
getheading();
if (lr>0) {
if (heading > oldhead) {
turnerror = turnerror - (3600 - heading + oldhead);
}
else {
turnerror = turnerror - (oldhead - heading);
}
} else {
if (oldhead > heading) {
turnerror = turnerror + (3600 - oldhead + heading);
}
else {
turnerror = turnerror + (heading - oldhead);
}
}
}
}
}

No comments:

Post a Comment

Followers