1.简介
霍尼韦尔 HMC5883L 是一种表面贴装的高集成模块,并带有数字接口的弱磁传感器芯片,应用于低成本罗盘和磁场检测领域。HMC5883L 包括最先进的高分辨率 HMC118X 系列磁阻传感器,并附带霍尼韦尔专利的集成电路包括放大器、自动消磁驱动器、偏差校准、能使罗盘精度控制在 1°~2°的 12 位模数转换器.简易的 I2 C 系列总线接口。HMC5883L 是采用无铅表面封装技术,带有 16 引脚,尺寸为 3.0X3.0X0.9mm。HMC5883L 的所应用领域有手机、笔记本电脑、消费类电子、汽车导航系统和个人导航系统。
HMC5883L 采用霍尼韦尔各向异性磁阻(AMR)技术,该技术的优点是其他磁传感器技术所无法企及。这些各向异性传感器具有在轴向高灵敏度和线性高精度的特点.传感器带有的对于正交轴低敏感行的固相结构能用于测量地球磁场的方向和大小,其测量范围从毫高斯到 8高斯(gauss)。 霍尼韦尔的磁传感器在低磁场传感器行业中是灵敏度最高和可靠性最好的传感器。
2.硬件连接
下图为HMC5883L与STM32的连接图,可以看出它是用的IIC通讯方式,注意这边上拉电阻用的是10K的(官方推荐),我个人还是用4.7K进行测试读写。
3.寄存器介绍
提到寄存器就必须说一下,大家买HMC5883L的时候一定要注意!!!别错买QMC5883L,两者的寄存器地址是不一样的哦。下面简单介绍一下各个寄存器:
配置寄存器 A:
配置寄存器是用来配置该装置设置的数据输出速率和测量配置。 CRA0 通过 CRA7 表明位的位置,用 CAR 指示在配置寄存器中的位。 CRA7 指示数据流的第一位。括号中的数目显示是该位的默认值(不太可靠,仅供参考)。
上表为:配置寄存器A,下表尾配置寄存器A的位分配情况
下表的数据显示在连续测量模式下的所有可选的输出速率。所有这三个通道应在某一特定数据速率下测量。其他输出速率可以通过控制单测量模式下的 DRDY 中断引脚来获得,最大速率为 160Hz。
上表为:数据输出速率,下表为测量模式选择
配置寄存器 B
配置寄存器 B 设置装置的增益。 CRB0 通过 CRB7 识别位的位置,用 CRB 指示在配置寄存器里的位。CRB7 表示数据流中的第一位。括号中的数目显示的是位的默认值。下表为:配置寄存器B及位分配
下表描述增益设置。使用以下“增益”一栏将counts转换成Guass。在总共磁场强度引起所有数据输出存储器中一个溢位(饱和)时选择较低的增益值(高GN#值)。
模式寄存器
该寄存器是一个8位可读可写的寄存器。该寄存器是用来设定装置的操作模式。 MR0通过MR7识别位的位置,MR表明模式寄存器里的位。MR7指示数据流中的第一位。括号中的数字显示的是位的默认值。下表为:模式寄存器
上表为:模式寄存器位分配,下表为:操作模式
数据输出 X 寄存器 A 和B
数据输出 X 寄存器是两个 8 位寄存器,数据输出寄存器 A 和 B。这些寄存器储存从通道 X 所测量结果。数据输出 X 寄存器 A 储存一个来自测量结果中的 MSB(高位数据),数据输出 X 寄存器 B 储存一个来自测量结果中的 LSB(低位数据)。存储在这两个寄存器的值是一个 16 位值以二进制的补码形式存在,其范围是 0xF800到 0x07FF。DXRA0 至 DXRA7、DXRB0 至 DXRB7 标识出位置, DXRA 和 DXRB 标识出在数据输出寄存器 X 中的位。DXRA7 和 DXRB7 标识出数据流的第一位,括号中的数目显示该位的默认值。 在事件的ADC上溢或下溢阅读给定的通道,或者如果有一个数学溢出的过程,这种数据寄存器将包含-4096的值。在下一次有效测量完成进行之后,该寄存器上的值将被清除。下表为:数据输出X寄存器 A和B
下面的Y和Z和X类似就不介绍了。
4.软件设计
用到IIC通讯必须要注意一下HMC5883L的控制时序,各位注意修改,我这边直接用的HAL库自带的延时,下面看一下IIC的程序:
//--------------------------------------------i2c------------------------------------------- // 设置SDA为输出方向,对于双向I/O需切换为输出 void SDA_D_OUT() { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } // 设置SDA为输入方向,对于双向I/O需切换为输入 void SDA_D_IN() { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } /************************************** 起始信号 **************************************/ void HMC5883_Start() { SDA_D_OUT(); SDA_H; //拉高数据线 SCL_H; //拉高时钟线 HAL_Delay(1); //延时 SDA_L; //产生下降沿 HAL_Delay(1); //延时 SCL_L; //拉低时钟线 } /************************************** 停止信号 **************************************/ void HMC5883_Stop() { //SDA_D_OUT(); SCL_H; //拉高时钟线 SDA_L; //拉低数据线 HAL_Delay(1); //延时 SDA_H; //产生上升沿 HAL_Delay(1); //延时 } /************************************** 发送应答信号 入口参数:ack (0:ACK 1:NAK) **************************************/ void HMC5883_SendACK(uint8_t ack) { SDA_D_OUT(); if(ack==1) { SDA_H; }else { SDA_L; } SCL_H; //拉高时钟线 HAL_Delay(1); //延时 SCL_L; //拉低时钟线 HAL_Delay(1); //延时 } /************************************** 接收应答信号 **************************************/ uint8_t HMC5883_RecvACK() { SDA_D_IN(); SCL_H; //拉高时钟线 HAL_Delay(10); //延时 CY = READ_SDA_IN; //进位标志 读应答信号 SCL_L; //拉低时钟线 HAL_Delay(1); //延时 return CY; } //等待从机应答信号 //返回值:1 接收应答失败 // 0 接收应答成功 uint8_t HMC5883L_I2C_Wait_Ack(void) { uint8_t tempTime=0; SDA_D_IN(); SDA_H; HAL_Delay(1); SCL_H; HAL_Delay(1); while(READ_SDA_IN) { tempTime++; if(tempTime>250) { HMC5883_Stop(); return 1; } } SCL_L; return 0; } /************************************** 向IIC总线发送一个字节数据 **************************************/ void HMC5883_SendByte(uint8_t dat) { uint8_t i; SDA_D_OUT(); SCL_L; //拉低时钟开始数据传输 for (i=0; i<8; i++) //8位计数器 { //dat <<= 1; //移出数据的最高位 if( dat & 0x80 ) { SDA_SET; } else { SDA_CLR; } HAL_Delay(1); //延时 dat <<= 1; //移出数据的最高位 SCL_H; //拉高时钟线 HAL_Delay(1); //延时 SCL_L; //拉低时钟线 } HMC5883_RecvACK(); } /************************************** 从IIC总线接收一个字节数据 **************************************/ uint8_t HMC5883_RecvByte() { uint8_t i; uint8_t dat = 0; SDA_D_IN(); SDA_H; //使能内部上拉,准备读取数据, for (i=0; i<8; i++) //8位计数器 { dat <<= 1; SCL_H; //拉高时钟线 HAL_Delay(1); //延时 if(READ_SDA_IN)dat++; //dat <<= 1; //dat |= SDA; //读数据 SCL_L; //拉低时钟线 HAL_Delay(1); //延时 } return dat; } //************************写入单字节数据*************************** //void Single_Write_QMC5883(uint8_t REG_Address,uint8_t REG_data) //{ // QMC5883_Start(); //起始信号 // QMC5883_SendByte(Slave_Address); //发送设备地址+写信号 // //if(QMC5883L_I2C_Wait_Ack()) // //{ // // QMC5883_Stop(); // // printf("error\r\n"); // //} // QMC5883_SendByte(REG_Address); //内部寄存器地址,请参考中文pdf // QMC5883_SendByte(REG_data); //内部寄存器数据,请参考中文pdf // QMC5883_Stop(); //发送停止信号 //} //************************读取单字节数据************************* uint8_t Single_Read_HMC5883(uint8_t REG_Address) { uint8_t REG_data; HMC5883_Start(); //起始信号 HMC5883_SendByte(Slave_Address); //发送设备地址+写信号 HMC5883_SendByte(REG_Address); //发送存储单元地址,从0开始 HMC5883_Start(); //起始信号 HMC5883_SendByte(Slave_Address+1); //发送设备地址+读信号 REG_data=HMC5883_RecvByte(); //读出寄存器数据 HMC5883_SendACK(1); HMC5883_Stop(); //停止信号 return REG_data; } //****************************************************** //连续读出QMC5883内部角度数据,地址范围0x00~0x05 //****************************************************** void Multiple_Read_HMC5883(void) { uint8_t i=0; HMC5883_Start(); //起始信号 HMC5883_SendByte(Slave_Address); //发送设备地址+写信号 HMC5883_SendByte(0x03); //发送存储单元地址,从0x00开始 HMC5883_Start(); //起始信号 HMC5883_SendByte(Slave_Address+1); //发送设备地址+读信号 for (i=0; i<6; i++) //连续读取6个地址数据,存储中BUF { BUF[i] = HMC5883_RecvByte(); //BUF[0]存储数据 if (i == 5) { HMC5883_SendACK(1); //最后一个数据需要回非应答NOACK } else { HMC5883_SendACK(0); //应答ACK } } x=(BUF[0]<<8)|(BUF[1]); if(x>32767) x=0xffff-x+1; z=(BUF[2]<<8)|(BUF[3]); if(z>32767) z=0xffff-z+1; y=(BUF[4]<<8)|(BUF[5]); if(y>32767) y=0xffff-y+1; HMC5883_Stop(); //停止信号 HAL_Delay(10); } //2初始化HMC5883,根据需要请参考pdf进行修改**** void HMC5883L_Init() { HMC5883_Start(); HMC5883_SendByte(0x3c); //发送设备地址+写信号 HMC5883_SendByte(0x00); //内部寄存器地址,请参考中文pdf HMC5883_SendByte(0x78); //内部寄存器数据,请参考中文pdf HMC5883_Start(); HMC5883_SendByte(0x3c); //发送设备地址+写信号 HMC5883_SendByte(0x01); //内部寄存器地址,请参考中文pdf HMC5883_SendByte(0x00); //内部寄存器数据,请参考中文pdf HMC5883_Start(); HMC5883_SendByte(0x3c); //发送设备地址+写信号 HMC5883_SendByte(0x02); //内部寄存器地址,请参考中文pdf HMC5883_SendByte(0x00); //内部寄存器数据,请参考中文pdf HMC5883_Stop(); }IIC通讯
预定义和主程序如下:
// 设置系统参数命令 #define SCL_SET HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET) #define SCL_H HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET) #define SCL_CLR HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET) #define SCL_L HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET) #define SCL_D_OUT {} // 设置SCL为输出方向,对于双向I/O需切换为输出 #define SDA_SET HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET) #define SDA_H HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET) #define SDA_CLR HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET) #define SDA_L HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET) #define CH455_SDA_IN HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_9) // 读取SDA输入电平 #define READ_SDA_IN HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_9) // 读取SDA输入电平 #define Slave_Address 0x3C //定义器件在IIC总线中的从地址 write预定义
int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); printf("===========\r\n"); HMC5883L_Init(); for(int i=0;i<13;i++) { printf("%d:%d\r\n",i,Single_Read_HMC5883(i)); } while (1) { Multiple_Read_HMC5883(); //连续读取三轴角度数据,存储在BUF中 printf("x:%d\r\ny:%d\r\nz:%d\r\n",x,y,z); printf("=================================\r\n"); HAL_Delay(1000); } }主程序
下面看一下串口输出结果:
上面输出的0~12是相对应的寄存器值,10~12应该是固定值,xyz是读出的三轴场强值。