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\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
引脚连接电路如下