基于STM32F1的HMC5883L电子罗盘驱动——妈妈从此再也不担心我迷路了

HMC5883L是一种IIC通信的电子罗盘芯片,输出与北的角度偏差,数值是0-360度,靠西方向增长,可以理解为逆时针为正。配合GPS模块简直就是四轴飞行器的铁杆标配。驱动分为驱动IIC和转换角度两块。

1.驱动IIC

void IIC_IO_Init(void)
{					 
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	//ê1?üGPIOBê±?ó
  
  GPIO_InitStructure.GPIO_Pin = GPIO_SCL|GPIO_SDA;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //í?íìê?3?
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(IIC_PERIPH, &GPIO_InitStructure);
  GPIO_SetBits(IIC_PERIPH,GPIO_Pin_8|GPIO_Pin_9); 	//PB6,PB7 ê?3???
}

void i2c_Start(void)  
{  
  /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */  
  SDA_OUT();
  IIC_SDA = 1;  
  delay_us(10);  
  IIC_SCL = 1;  
  delay_us(10);  
  IIC_SDA = 0;  
  delay_us(10);  
  IIC_SCL = 0;  
  delay_us(10);  
}  
void i2c_Stop(void)  
{  
  /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */  
  SDA_OUT();
  IIC_SDA = 0;  
  delay_us(10);  
  IIC_SCL = 1;  
  delay_us(10);  
  IIC_SDA = 1;  
}  
void i2c_SendByte(u8 _ucByte)  
{  
  u8 i;  
  SDA_OUT();
  /* 先发送字节的高位bit7 */  
  for (i = 0; i < 8; i++)  
  {       
    if (_ucByte & 0x80)  
    {  
      IIC_SDA = 1;  
    }  
    else  
    {  
      IIC_SDA = 0;  
    }  
    delay_us(10);  
    IIC_SCL = 1;  
    delay_us(10);      
    IIC_SCL = 0;  
    if (i == 7)  
    {  
      IIC_SDA = 1; // 释放总线  
    }  
    _ucByte <<= 1;    /* 左移一个bit */  
    delay_us(10);  
  }  
}  
u8 i2c_ReadByte(void)  
{  
  u8 i;  
  u8 value;  
  IIC_SDA = 1;  
  SDA_IN();
  /* 读到第1个bit为数据的bit7 */  
  value = 0;  
  for (i = 0; i < 8; i++)  
  {  
    value <<= 1;  
    IIC_SCL = 1;  
    delay_us(10);  
    if (READ_SDA)  
    {  
      value++;  
    }  
    IIC_SCL = 0;  
    delay_us(10);  
  }  
  return value;  
}  
u8 i2c_WaitAck(void)  
{  
  u8 re;  
    SDA_IN();
  IIC_SDA = 1;  /* CPU释放SDA总线 */  
  delay_us(10);  
  IIC_SCL = 1;  /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */  
  delay_us(10);  
  if (READ_SDA)   /* CPU读取SDA口线状态 */  
  {  
    re = 1;  
  }  
  else  
  {  
    re = 0;  
  }  
  IIC_SCL = 0;  
  delay_us(10);  
  return re;  
}  
void i2c_Ack(void)  
{  
  SDA_OUT();
  IIC_SDA = 0;  /* CPU驱动SDA = 0 */  
  delay_us(10);  
  IIC_SCL = 1;  /* CPU产生1个时钟 */  
  delay_us(10);  
  IIC_SCL = 0;  
  delay_us(10);  
  IIC_SDA = 1;  /* CPU释放SDA总线 */  
}  
void i2c_NAck(void)  
{  
  SDA_OUT();
  IIC_SDA = 1;  /* CPU驱动SDA = 1 */  
  delay_us(10);  
  IIC_SCL = 1;  /* CPU产生1个时钟 */  
  delay_us(10);  
  IIC_SCL = 0;  
  delay_us(10);    
}  
void HMC5883L_Init()  
{  
  IIC_IO_Init();
  //i2c_CheckDevice(HMC5883L_Write_Address);  
  i2c_Start();  
  i2c_SendByte(HMC5883L_Write_Address);  
  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();  
}  

总结:都是常规的IIC设置,注意延时长短。宏定义了一些东西,参数如下

#define SDA_IN()  {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=(u32)8<<4;}
#define SDA_OUT() {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=(u32)3<<4;}
#define IIC_SCL    PBout(8) //SCL
#define IIC_SDA    PBout(9) //SDA	 
#define GPIO_SCL    GPIO_Pin_8
#define GPIO_SDA    GPIO_Pin_9
#define IIC_PERIPH GPIOB
#define READ_SDA   PBin(9) 

SDA_IN()和SDA_OUT()是直接操作寄存器,IO口地址是0XFFFFFF0F,(u32)8中的8是输入模式,左移4是移到当前IO的寄存器设置地址(1x4)

2.转换角度

int x,y,z,X,Y,Z;
float Get_Current_Angle()  
{  
  u8 i;  
  u8 a[6];  
  float Curent_Angle;  
  HMC5883L_Init();  
  i2c_Start();  
  i2c_SendByte(HMC5883L_Write_Address);  
  i2c_WaitAck();  
  i2c_SendByte(0x03);  
  i2c_WaitAck();  
    
  i2c_Start();  
  i2c_SendByte(HMC5883L_Read_Address);  
  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;  
}  

这个实际上就是数据处理函数,只要IIC初始化OK直接copy过来就能用上。函数返回的直接是和北偏离的度数,指南针就做好了。现在手机上自带罗盘,一般打开地图点位置模式就能看到箭头方向。


猜你喜欢

转载自blog.csdn.net/schumi2000/article/details/79712481