您的位置:首页 > 其它

HMC5883L三轴陀螺仪(模拟IIC读取和传感器校准)

2015-11-24 20:46 239 查看
HMC5883L是一个三轴陀螺仪,能实现待测点的地磁场方向。

我在试验中只用到了水平方向的磁场方向。跟据地磁场的知识可知,在北半球,磁场方向指向地面。并且还有地理北极和地磁北极的磁偏角是11度。

使用磁传感器应注意以下几点:

1.读取到的传感器数据是当地磁场在空间x,y,z三个方向上的分量。

2.在水平方向,地磁偏角的计算公式是:磁偏角:Curent_Angle = (atan2(Y,X) * (180 / 3.14159265) + 180);

3.传感器应该以软件的方式校准。

好了,贴上代码:
static void i2c_Delay(void)
{
uint8_t i;

/* 
下面的时间是通过安富莱AX-Pro逻辑分析仪测试得到的。
CPU主频72MHz时,在内部Flash运行, MDK工程不优化
循环次数为10时,SCL频率 = 205KHz
循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us

IAR工程编译效率高,不能设置为7
*/
for (i = 0; i < 10; i++);
}
void i2c_Start(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
I2C_SDA_1();
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_0();
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
void i2c_Stop(void)
{
/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
I2C_SDA_0();
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_1();
}
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;

/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
I2C_SDA_1();
}
else
{
I2C_SDA_0();
}
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
I2C_SCL_0();
if (i == 7)
{
I2C_SDA_1(); // 释放总线
}
_ucByte <<= 1;	/* 左移一个bit */
i2c_Delay();
}
}
uint8_t i2c_ReadByte(void)
{
uint8_t i;
uint8_t value;
I2C_SDA_1();
/* 读到第1个bit为数据的bit7 */
value = 0;
for (i = 0; i < 8; i++)
{
value <<= 1;
I2C_SCL_1();
i2c_Delay();
if (I2C_SDA_READ())
{
value++;
}
I2C_SCL_0();
i2c_Delay();
}
return value;
}
uint8_t i2c_WaitAck(void)
{
uint8_t re;

I2C_SDA_1();	/* CPU释放SDA总线 */
i2c_Delay();
I2C_SCL_1();	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
i2c_Delay();
if (I2C_SDA_READ())	/* CPU读取SDA口线状态 */
{
re = 1;
}
else
{
re = 0;
}
I2C_SCL_0();
i2c_Delay();
return re;
}
void i2c_Ack(void)
{
I2C_SDA_0();	/* CPU驱动SDA = 0 */
i2c_Delay();
I2C_SCL_1();	/* CPU产生1个时钟 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
I2C_SDA_1();	/* CPU释放SDA总线 */
}
void i2c_NAck(void)
{
I2C_SDA_1();	/* CPU驱动SDA = 1 */
i2c_Delay();
I2C_SCL_1();	/* CPU产生1个时钟 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
void HMC5883L_Init()
{
i2c_CheckDevice(HMC5883L_Write_Address);
i2c_Start();
i2c_SendByte(HMC5883L_Write_Address | I2C_WR);
i2c_WaitAck();
i2c_SendByte(0x00);
i2c_WaitAck();
i2c_SendByte(0x70);
i2c_WaitAck();
i2c_SendByte(0x01);
i2c_WaitAck();
i2c_SendByte(0xe0);
i2c_WaitAck();
i2c_SendByte(0x02);
i2c_WaitAck();
i2c_SendByte(0x01);
i2c_Stop();
}
float Get_Current_Angle()
{
u8 i;
u8 a[6];
float Curent_Angle;
HMC5883L_Init();
i2c_Start();
i2c_SendByte(HMC5883L_Write_Address | I2C_WR);
i2c_WaitAck();
i2c_SendByte(0x03);
i2c_WaitAck();

i2c_Start();
i2c_SendByte(HMC5883L_Write_Address | I2C_RD);
i2c_WaitAck();
for(i=0;i<6;i++)
{
a[i] = i2c_ReadByte();
if(i==5)
{
i2c_NAck();
}
else
{
i2c_Ack();
}
}
i2c_Stop();
x=a[0];
x=x<<8;
x=x|a[1];
y=a[2];
y=y<<8;
y=y|a[3];
z=a[4];
z=z<<8;
z=z|a[5];
if(x>32768)
{
x = -(0xFFFF - x + 1);
}

if(y>32768)
{
y = -(0xFFFF - y + 1);
}
if(z>32768)
{
z = -(0xFFFF - z + 1);
}
X = (s16)x;    //x分量
Y = (s16)y;    //y分量
Z = (s16)z;    //z分量
Curent_Angle = (atan2(Y,X) * (180 / 3.14159265) + 180);  //实际水平角度
return Curent_Angle;
}
另外一个重要的事是校准。在网上找了很多资料,关于怎么校准,有些资料不公开,所以贴上一个百度文库网址,自己没有下载点券,囧。。。。
http://wenku.baidu.com/link?url=EqAo3QFYDZsgfzlAKcm9FTKAc-42zxWw0iUIll8Wde79OLLJzq_GvSUubk2X30zhBM-4_X8U4xLlhxce8W-28T8RIC28cIa8A9nzbUEHzlu
这上面说明了校准的具体操作和代码。
下面贴上我写的:<pre name="code" class="html">void calibrate()
{
float xMax, xMin, yMax, yMin, zMax, zMin;
xMax=xMin=x;
yMax=yMin=y;
zMax=zMin=z;
float offsetX = 0;
float offsetY = 0;
float offsetZ = 0;
for(int i=0;i<200;i++)
{
if (x > xMax)
xMax = x;
if (x < xMin )
xMin = x;
if(y > yMax )
yMax = y;
if(y < yMin )
yMin = y;
if(z > zMax )
zMax = z;
if(z < zMin )
zMin = z;

delay(100);
}
if(abs(xMax - xMin) > 0 )
offsetX = (xMax + xMin)/2;
if(abs(yMax - yMin) > 0 )
offsetY = (yMax + yMin)/2;
if(abs(zMax - zMin) > 0 )
offsetZ = (zMax +zMin)/2;
}



好了,以上就是我使用过程中的一些总结,不足之处还请各位指出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: