霍尼韋爾 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\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif
return res;
}
res = ComSendByte(regValue); //內部寄存器地址
if(res)
{
#ifdef HMC_DEBUG
printf("file=%s,func=%s,line=%d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif
return res;
}
res = ComSendByte(setValue); //內部寄存器數據
if(res)
{
#ifdef HMC_DEBUG
printf("file=%s,func=%s,line=%d\r\n",__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\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif
return res;
}
res = ComSendByte(regAddr); //發送存儲單元地址,從0開始
if(res)
{
#ifdef HMC_DEBUG
printf("file=%s,func=%s,line=%d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif
return res;
}
ComStart(); //起始信號
res = ComSendByte(HMC_ADDR+1); //發送設備地址+讀信號
if(res)
{
#ifdef HMC_DEBUG
printf("file=%s,func=%s,line=%d\r\n",__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\r\n",__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\r\n",__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\r\n",__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 \t y = %d \t z = %d \t axis = %f\r\n",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
引腳連接電路如下

