HMC5883L地磁传感器驱动

   霍尼韦尔 HMC5883L 是一种表面贴装的高集成模块,并带有数字接口的弱磁传感器芯片,应用于低成本罗盘和磁场检测领域。HMC5883L 包括最先进的高分辨率 HMC118X 系列磁阻传感器,并附带霍尼韦尔专利的集成电路包括放大器、自动消磁驱动器、偏差校准、能使罗盘精度控制在 1°~2°的 12 位模数转换器.简易的 I2C 系列总线接口。HMC5883L 是采用无铅表面封装技术,带有 16 引脚,尺寸为 3.0X3.0X0.9mm。HMC5883L 的所应用领域有手机、笔记本电脑、消费类电子、汽车导航系统和个人导航系统

   HMC5883主要有接口是IIC,同时提供一个数据中断引脚,一般而言,电路设计如下


  该传感器芯片的驱动并不复杂,主要是设置几个寄存器,如下所示

//HMC5883寄存器定义
//寄存器地址定义
#define HMC_CONFIG_A_REG	0X00	//配置寄存器A
//bit0-bit1 xyz是否使用偏压,默认为0正常配置
//bit2-bit4 数据输出速率, 110为最大75HZ 100为15HZ 最小000 0.75HZ
//bit5-bit5每次采样平均数 11为8次 00为一次

#define HMC_CONFIG_B_REG	0X01	//配置寄存器B
//bit7-bit5磁场增益 数据越大,增益越小 默认001

#define HMC_MODE_REG		0X02	//模式设置寄存器
//bit0-bit1 模式设置 00为连续测量 01为单一测量

#define HMC_XMSB_REG		0X03	//X输出结果
#define HMC_XLSB_REG		0X04

#define HMC_ZMSB_REG		0X05	//Z输出结果
#define HMC_ZLSB_REG		0X06

#define HMC_YMSB_REG		0X07	//Y输出结果
#define HMC_YLSB_REG		0X08

#define HMC_STATUS_REG		0X09	//只读的状态
//bit1 数据更新时该位自动锁存,等待用户读取,读取到一半的时候防止数据改变
//bit0 数据已经准备好等待读取了,DRDY引脚也能用

#define HMC_CHEAK_A_REG		0X0A	//三个识别寄存器,用于检测芯片完整性
#define HMC_CHEAK_B_REG		0X0B
#define HMC_CHEAK_C_REG		0X0C

#define HMC_CHECKA_VALUE	0x48	//三个识别寄存器的默认值
#define HMC_CHECKB_VALUE	0x34
#define HMC_CHECKC_VALUE	0x33

建议选择最大速率,八倍采样输出,同时该传感器支持读写地址指针自动变化,但是针对随机读取的驱动而言,该特性不重要

另外,一般而言,地址为0x3c,驱动代码如下

#include "hmc5883.h"
#include "math.h"

struct HMCVALUE hmcValue;
u8 dataReady = 0;


//IO方向设置
#define HMC_SDA_IN()  {GPIOC->CRH&=0XFFFFFFF0;GPIOC->CRH|=8;}
#define HMC_SDA_OUT() {GPIOC->CRH&=0XFFFFFFF0;GPIOC->CRH|=3;}

//IO操作函数	 
#define HMC_SCL    PCout(9) //SCL
#define HMC_SDA    PCout(8) //SDA	 
#define HMC_READ_SDA   PCin(8)  //输入SDA 

#define HMC_DRDY	PCin(7)

/**************************HMC5883 IIC驱动函数*********************************/

static void Hmc5883IOInit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOC, ENABLE );	
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    
	//PC7 drdy引脚 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING ;   //浮空输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
	
    HMC_SCL = 1;
    HMC_SDA = 1;
}



//发送IIC起始信号
static void ComStart(void)
{
	HMC_SDA_OUT();     //sda线输出
    HMC_SDA=1;	  	  
    HMC_SCL=1;
    DelayUs(5);
    HMC_SDA=0;//START:when CLK is high,DATA change form high to low 
    DelayUs(5);
    HMC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//发送IIC停止信号
static void ComStop(void)
{
	HMC_SDA_OUT();//sda线输出
    HMC_SDA=0;//STOP:when CLK is high DATA change form low to high
    HMC_SCL=1;
    DelayUs(5);
    HMC_SDA=1;//发送I2C总线结束信号
    DelayUs(5);		
}
//等待ACK,为1代表无ACK 为0代表等到了ACK
static u8 ComWaitAck(void)
{
	u8 waitTime = 0;
	HMC_SDA_OUT();//sda线输出
	HMC_SDA = 1;
	DelayUs(5);
    HMC_SDA_IN();      //SDA设置为输入
	HMC_SCL=1;
	DelayUs(5);
	while(HMC_READ_SDA)
	{
		waitTime++;
		DelayUs(1);
		if(waitTime > HMC_ACK_WAIT_TIME)
		{
			ComStop();
			return 1;
		}
	}
	HMC_SCL = 0;
	return 0;
	
}

static void ComSendAck(void)
{
	HMC_SCL = 0;
	HMC_SDA_OUT();
    HMC_SDA = 0;
	DelayUs(2);
    HMC_SCL = 1;
    DelayUs(5);
    HMC_SCL = 0;
    DelayUs(5);
}

static void ComSendNoAck(void)
{
	HMC_SCL = 0;
	HMC_SDA_OUT();
    HMC_SDA = 1;
	DelayUs(2);
    HMC_SCL = 1;
    DelayUs(5);
    HMC_SCL = 0;
    DelayUs(5);
}
//返回0 写入收到ACK 返回1写入未收到ACK
static u8 ComSendByte(u8 byte)
{
	u8 t;   
    HMC_SDA_OUT(); 	
    for(t=0;t<8;t++)
    {              
        HMC_SDA=(byte&0x80)>>7;
        byte<<=1; 	   
        HMC_SCL=1;
        DelayUs(5); 
        HMC_SCL=0;	
        DelayUs(5);
    }	 
    return ComWaitAck();
}

static void ComReadByte(u8* byte)
{
	u8 i,receive=0;
    HMC_SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
    {
        receive <<= 1;
        HMC_SCL=1; 
        DelayUs(5);
        if(HMC_READ_SDA)receive++;
        HMC_SCL=0; 
        DelayUs(5); 
    }					  
    *byte = receive;
}

/**************************HMC5883 IIC驱动函数*********************************/


//向HMC写入一个字节数据,失败返回1 成功返回0
u8 Hmc5883WriteReg(u8 regValue,u8 setValue)
{
	u8 res;
    ComStart();                 	//起始信号
    res = ComSendByte(HMC_ADDR);    //发送设备地址+写信号
	if(res)
	{
		#ifdef HMC_DEBUG
		printf("file=%s,func=%s,line=%d
",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}
    res = ComSendByte(regValue);    //内部寄存器地址
	if(res)
	{
		#ifdef HMC_DEBUG
		printf("file=%s,func=%s,line=%d
",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}
    res = ComSendByte(setValue);    //内部寄存器数据
	if(res)
	{
		#ifdef HMC_DEBUG
		printf("file=%s,func=%s,line=%d
",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}
    ComStop();                   	//发送停止信号
	return res;
}

//**************************************
//从I2C设备读取一个字节数据
//**************************************
u8 Hmc5883ReadReg(u8 regAddr,u8* readValue)
{
    u8 res;
    ComStart();                 		//起始信号
    res = ComSendByte(HMC_ADDR);    	//发送设备地址+写信号
	if(res)
	{
		#ifdef HMC_DEBUG
		printf("file=%s,func=%s,line=%d
",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}
    res = ComSendByte(regAddr);     	//发送存储单元地址,从0开始	
	if(res)
	{
		#ifdef HMC_DEBUG
		printf("file=%s,func=%s,line=%d
",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}
    ComStart();                 		//起始信号
    res = ComSendByte(HMC_ADDR+1);  	//发送设备地址+读信号
	if(res)
	{
		#ifdef HMC_DEBUG
		printf("file=%s,func=%s,line=%d
",__FILE__,__FUNCTION__,__LINE__);
		#endif
		return res;
	}
    ComReadByte(readValue);     		//读出寄存器数据
    ComSendNoAck();               		//发送非应答信号
    ComStop();                  		//停止信号
    return res;
}


static void HmcDefaultConfig(void)
{
	Hmc5883WriteReg(HMC_CONFIG_A_REG,HMC_DEFAULT_CONFIGA_VALUE);
	Hmc5883WriteReg(HMC_CONFIG_B_REG,HMC_DEFAULT_CONFIGB_VALUE);
	Hmc5883WriteReg(HMC_MODE_REG,HMC_DEFAULT_MODE_VALUE);
}

//配置HMC drdy引脚中断
void HmcDrdyPinIntInit(void)
{
	EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
	
	//GPIOC.7 中断线以及中断初始化配置   下降沿触发
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource7);
    
    EXTI_InitStructure.EXTI_Line=EXTI_Line7;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;	//下降沿中断
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);	 	//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;			//使能所在的外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = HMC_DRDY_PreemptionPriority;	//抢占优先级 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = HMC_DRDY_SubPriority;					//子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;								//使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);
}

//对应的外部中断处理函数
void EXTI9_5_IRQHandler()
{
	if(EXTI_GetFlagStatus(EXTI_Line7) == SET)
	{
		EXTI_ClearFlag(EXTI_Line7);
		dataReady = 1;
	}
}
//失败返回1,初始化通过之后基本通讯就没有问题了就可以不再判定通讯结果了
u8 HmcInit(void)
{
    u8 hmcCheckValue = 0x00;
	u8 res = 0;
    Hmc5883IOInit();	//接口初始化
	while(HMC_DRDY == 1);//等待数据引脚正常
	HmcDrdyPinIntInit();//配置中断
    DelayMs(100);
	//验证A识别
    res = Hmc5883ReadReg(HMC_CHEAK_A_REG,&hmcCheckValue);
	if(res)
	{
		#ifdef HMC_DEBUG
		printf("file=%s,func=%s,line=%d
",__FILE__,__FUNCTION__,__LINE__);
		#endif
	}
    if(hmcCheckValue != HMC_CHECKA_VALUE)	//自检通过
    {
        return 1;
    }
	//验证B识别
	res = Hmc5883ReadReg(HMC_CHEAK_B_REG,&hmcCheckValue);
	if(res)
	{
		#ifdef HMC_DEBUG
		printf("file=%s,func=%s,line=%d
",__FILE__,__FUNCTION__,__LINE__);
		#endif
	}
    if(hmcCheckValue != HMC_CHECKB_VALUE)	//自检通过
    {
        return 1;
    }
	//验证C识别
	res = Hmc5883ReadReg(HMC_CHEAK_C_REG,&hmcCheckValue);
	if(res)
	{
		#ifdef HMC_DEBUG
		printf("file=%s,func=%s,line=%d
",__FILE__,__FUNCTION__,__LINE__);
		#endif
	}
    if(hmcCheckValue != HMC_CHECKC_VALUE)	//自检通过
    {
        return 1;
    }
	HmcDefaultConfig();
	//全部验证通过
    return 0;
}



void Hmc5883Reflush(void)
{
    u16 xValue = 0x00,yValue = 0x00,zValue = 0x00;
	u8 temp;
	Hmc5883ReadReg(HMC_XMSB_REG,&temp);
    xValue |= temp;
	xValue <<=8;
	Hmc5883ReadReg(HMC_XLSB_REG,&temp);
	xValue |=  temp;
	
	Hmc5883ReadReg(HMC_ZMSB_REG,&temp);
	zValue |=  temp;
	zValue <<=8;
	Hmc5883ReadReg(HMC_ZLSB_REG,&temp);
	zValue |=  temp;
	
	Hmc5883ReadReg(HMC_YMSB_REG,&temp);
	yValue |=  temp;
	yValue <<=8;
	Hmc5883ReadReg(HMC_YLSB_REG,&temp);
	yValue |=  temp;
	
	hmcValue.hmcXvalue = (s16)xValue;
	hmcValue.hmcYvalue = (s16)yValue;
	hmcValue.hmcZvalue = (s16)zValue;
	
	hmcValue.hmcAxis = atan2((double)hmcValue.hmcYvalue,(double)hmcValue.hmcXvalue) * (180 / 3.14159265) + 180; // angle in degrees
	#ifdef HMC_DEBUG
	printf("x = %d 	 y = %d 	 z = %d 	 axis = %f
",xValue,yValue,zValue,hmcValue.hmcAxis);
	#endif
}



头文件定义如下(中断有优先级请自行定义)

#ifndef __HMC5883_H_
#define __HMC5883_H_

#include "common.h"
#include "stm32f10x.h"
#include "delay.h"
#include "ioremap.h"
#include "uart.h"

#define HMC_DEBUG	1	//是否输出调试信息

//没有主机改变地址的话地址是可以自动更新的,不过该驱动没有用这个模式


//HMC5883寄存器定义
//寄存器地址定义
#define HMC_CONFIG_A_REG	0X00	//配置寄存器A
//bit0-bit1 xyz是否使用偏压,默认为0正常配置
//bit2-bit4 数据输出速率, 110为最大75HZ 100为15HZ 最小000 0.75HZ
//bit5-bit5每次采样平均数 11为8次 00为一次

#define HMC_CONFIG_B_REG	0X01	//配置寄存器B
//bit7-bit5磁场增益 数据越大,增益越小 默认001

#define HMC_MODE_REG		0X02	//模式设置寄存器
//bit0-bit1 模式设置 00为连续测量 01为单一测量

#define HMC_XMSB_REG		0X03	//X输出结果
#define HMC_XLSB_REG		0X04

#define HMC_ZMSB_REG		0X05	//Z输出结果
#define HMC_ZLSB_REG		0X06

#define HMC_YMSB_REG		0X07	//Y输出结果
#define HMC_YLSB_REG		0X08

#define HMC_STATUS_REG		0X09	//只读的状态
//bit1 数据更新时该位自动锁存,等待用户读取,读取到一半的时候防止数据改变
//bit0 数据已经准备好等待读取了,DRDY引脚也能用

#define HMC_CHEAK_A_REG		0X0A	//三个识别寄存器,用于检测芯片完整性
#define HMC_CHEAK_B_REG		0X0B
#define HMC_CHEAK_C_REG		0X0C

#define HMC_CHECKA_VALUE	0x48	//三个识别寄存器的默认值
#define HMC_CHECKB_VALUE	0x34
#define HMC_CHECKC_VALUE	0x33


//HMC5883地址定义
#define HMC_ADDR          0X3C          //写地址,读地址+1


//HMC5883 初始化宏定义
#define HMC_DEFAULT_CONFIGA_VALUE		0x78	 //75hz 8倍采样 正常配置
#define HMC_DEFAULT_CONFIGB_VALUE		0x00	 //+-0.88GA增益
#define HMC_DEFAULT_MODE_VALUE			0x00     //连续测量模式


#define HMC_ACK_WAIT_TIME	200		//iic通讯时的ack等待时间

//HMC5883驱动结构体定义
typedef struct HMCVALUE 
{
	s16 hmcXvalue;
	s16 hmcYvalue;
	s16 hmcZvalue;
	double hmcAxis;
}HMCVALUE;



u8 HmcInit(void);

u8 Hmc5883WriteReg(u8 regValue,u8 setValue);

u8 Hmc5883ReadReg(u8 regAddr,u8* readValue);

void Hmc5883Reflush(void);



extern struct HMCVALUE hmcValue;
extern u8 dataReady;


#endif







引脚连接电路如下


原文地址:https://www.cnblogs.com/dengxiaojun/p/4279443.html