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

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
这上面说明了校准的具体操作和代码。
下面贴上我写的:
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;
}

 好了,以上就是我使用过程中的一些总结,不足之处还请各位指出。

你可能感兴趣的:(嵌入式)