霍尼韋爾 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
引腳連接電路如下