嵌入式外设 -- HMC5883L电子指南针(电磁场)

目录

前言

一、模块介绍

二、资料获取

关注微信公众号--星之援工作室 发送关键字(HMC5883L)

实验效果

接线方式如下

测试所得

三、代码编写

main.c

hmc5883l.h

hmc5883l.C

四、参考资料


 

前言

做一个项目需要用到,但是网上好多资料都没有标准库的,故移植写一个方便以后开发使用

一、模块介绍

霍尼韦尔 HMC5883L 是一种表面贴装的高集成模块,并带有数字接口的弱磁
传感器芯片,应用于低成本罗盘和磁场检测领域。HMC5883L 包括最先进的高
分辨率HMC118X 系列磁阻传感器,并附带霍尼韦尔专利的集成电路包括放大
器、自动消磁驱动器、偏差校准、能使罗盘精度控制在1°~2°的12 位模数
转换器.简易的I2C 系列总线接口。HMC5883L 是采用无铅表面封装技术,带
有16 引脚,尺寸为3.0X3.0X0.9mm。HMC5883L 的所应用领域有手机、笔记本
电脑、消费类电子、汽车导航系统和个人导航系统。
HMC5883L 采用霍尼韦尔各向异性磁阻(AMR)技术,该技术的优点是其他磁传感器技术所无法企及。这些各向
异性传感器具有在轴向高灵敏度和线性高精度的特点.传感器带有的对于正交轴低敏感行的固相结构能用于
测量地球磁场的方向和大小,其测量范围从毫高斯到 8 高斯(gauss)。 霍尼韦尔的磁传感器在低磁场传感
器行业中是灵敏度最高和可靠性最好的传感器。

ce86ade925e8438c9eddc8f6e28c80c9.png

a7b1be86913f41feaca8b7ce0359e5af.png

二、资料获取

关注微信公众号--星之援工作室 发送关键字(HMC5883L

➡️🫡🫡🫡🫡🫡🫡🫡🫡➡️

452c0cf75b1d4e4895194df8a5022c34.png

实验效果

我主要实验一下对金属的反馈效果,达到检测车辆的作用,测试使用的是X轴,得到的结果还是符合预期,可以针对进出不同方向达到测试车辆行驶的方向进行记录

接线方式如下

a9c606d5b7194db282965d6aacb65e60.png

 首先确认一个位置,并且查看X轴数据(可以自信更新方位和检测轴,道理一样)

ecd558a228414e2d965c385dc59e9988.jpeg

测试所得

当金属在左侧时,X轴反馈数据会大于无物体数据
反之,当金属在右侧时,X轴反馈数据会小于于无物体数据

这样就可以判断我们车辆的行驶方向

290bf0b154354428b188847b65400d3f.jpeg

 连接串口可以输出我们获取到的消息

28e01e516b70427683f78363b5945175.png

三、代码编写

 首先,模块用到了IIC通信,我们需要进行模拟IIC通信,全部代码如下,完整工程可以在vx公众号获取

main.c

/*--------------------------------------------------------*
 *                                       								  *
 *   星之援网络科技工作室学习资料v1.0    									*
 *		 时间:2022.7.14                    								*
 *		 程序介绍:HMC5883L 使用               							*
 *		 实现效果:串口输出 XYZ轴 以及偏转角度  						*
 *                                      							    *
 *--------------------------------------------------------*
*/

#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "usart.h"
#include "hmc5883l.h"

extern HMC_XYZ hmc_xyz_init; // 初始化结构体

int main(void)
{
	delay_init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	LED_GPIO_Config();	// LED 端口初始化 
	uart_init(115200);// 串口初始化

  GY_IIC_Init() ;
	HMC5883_Init(); // HMC5883L初始
	printf("init ok\n");
  while(1)
	{

		HMC5883_Get_Angle();// 读数据
		printf("X:%d Y:%d Z:%d Angle:%.1f\n",hmc_xyz_init.HMC_X,hmc_xyz_init.HMC_Y,
																		 hmc_xyz_init.HMC_Z,hmc_xyz_init.Angle );
		delay_ms(250);
		LED1_TOGGLE;
  }   
}

hmc5883l.h

#ifndef __HMC_H
#define __HMC_H
 
#include "stm32f10x_conf.h"
#include "sys.h"
 
// 结构体
typedef struct
{
	short HMC_X;
	short	HMC_Y;
	short HMC_Z; // HMC5883三轴数据输出
	float Angle; // 偏转角度
	
} HMC_XYZ;
 
#define SlaveAddress 0X3C     //HMC5883L从机地址
#define SelfTest_EN 1

#define CRA 0x00
#define CRB 0x01
#define MR 0x02
#define DOXMR 0x03
#define DOXLR 0x04
#define DOZMR 0x05
#define DOZLR 0x06
#define DOYMR 0x07
#define DOYLR 0x08
#define SR 0x09
#define IRA 0x0A
#define IRB 0x0B
#define IRC 0x0C

#define GY_IN_GPIO_PORT GPIOB               /* GPIO端口 */
#define GY_IN_GPIO_CLK RCC_APB2Periph_GPIOB /* GPIO端口时钟 */
#define GY_IN_GPIO_PIN GPIO_Pin_7           /* 连接到SCL时钟线的GPIO */

#define GY_SCLK__GPIO_PORT GPIOB              /* GPIO端口 */
#define GY_SCLK_GPIO_CLK RCC_APB2Periph_GPIOB /* GPIO端口时钟 */
#define GY_SCLK__GPIO_PIN GPIO_Pin_9          /* 连接到SCL时钟线的GPIO */

#define GY_DATA__GPIO_PORT GPIOB              /* GPIO端口 */
#define GY_DATA_GPIO_CLK RCC_APB2Periph_GPIOB /* GPIO端口时钟 */
#define GY_DATA__GPIO_PIN GPIO_Pin_8          /* 连接到SCL时钟线的GPIO */

// IO操作函数
#define GY_IIC_SCL PBout(9) // SCL
#define GY_IIC_SDA PBout(8) // SDA
#define GY_READ_SDA PBin(8) // 输入SDA
 
//IIC所有操作函数
void GY_IIC_Delay(void);				//MPU IIC延时函数
void GY_IIC_Init(void);                 //初始化IIC的IO口				 
void GY_IIC_Start(void);				//发送IIC开始信号
void GY_IIC_Stop(void);	  			    //发送IIC停止信号
void GY_IIC_Send_Byte(u8 txd);			//IIC发送一个字节
u8   GY_IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8   GY_IIC_Wait_Ack(void); 				//IIC等待ACK信号
void GY_IIC_Ack(void);					//IIC发送ACK信号
void GY_IIC_NAck(void);				     //IIC不发送ACK信号
 
 
// HMC5883L
u8 HMC5883_SB_Read(u8 Slave_Address, u8 Register_Address) ;
u8 HMC5883_SB_Write(u8 Slave_Address, u8 Register_Address, u8 Register_Data) ;
void HMC5883_Init(void) ;
void HMC5883_Get_Angle(void);
void HMC5883_Get_XYZ(short* x, short* y, short* z);
 
#endif

hmc5883l.C

#include "hmc5883l.h"
#include "math.h"
#include "delay.h"
#include "usart.h"
HMC_XYZ hmc_xyz_init; // 初始化结构体
 
//==============================模拟IIC函数区===================================== 
void GY_IIC_Delay(void)
{
	delay_us(2);
}
// 配置双向I/O端口为输出态
static void GY_SDA_OUT()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(GY_DATA_GPIO_CLK, ENABLE);

	GPIO_InitStructure.GPIO_Pin = GY_DATA__GPIO_PIN; // PC.10  DATA
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GY_DATA__GPIO_PORT, &GPIO_InitStructure); // 初始化GPIOC.10
}
// 配置双向I/O端口为输入态
static void GY_SDA_IN()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(GY_DATA_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GY_DATA__GPIO_PIN; // PC.10 DATA
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GY_DATA__GPIO_PORT, &GPIO_InitStructure); // 初始化GPIOC.10
}
//初始化IIC
void GY_IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(GY_DATA_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GY_DATA__GPIO_PIN; // PC.11  CE
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
	GPIO_Init(GY_DATA__GPIO_PORT, &GPIO_InitStructure); // 初始化GPIOC.11

	GPIO_InitStructure.GPIO_Pin = GY_SCLK__GPIO_PIN; // PC.12  SCLK
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
	GPIO_Init(GY_SCLK__GPIO_PORT, &GPIO_InitStructure); // 初始化GPIOC.12

	GY_IIC_SCL = 1;
	GY_IIC_SDA = 1;
 
}
//产生IIC起始信号
void GY_IIC_Start(void)
{
	GY_SDA_OUT();     //sda线输出
	GY_IIC_SDA=1;	  	  
	GY_IIC_SCL=1;
	GY_IIC_Delay();
 	GY_IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	GY_IIC_Delay();
	GY_IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}	  
//产生IIC停止信号
void GY_IIC_Stop(void)
{
	GY_SDA_OUT();//sda线输出
	GY_IIC_SCL=0;
	GY_IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	GY_IIC_Delay();
	GY_IIC_SCL=1;  
	GY_IIC_SDA=1;//发送I2C总线结束信号
	GY_IIC_Delay();							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 GY_IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	GY_SDA_IN();      //SDA设置为输入  
	GY_IIC_SDA=1;GY_IIC_Delay();	   
	GY_IIC_SCL=1;GY_IIC_Delay();	 
	while(GY_READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			GY_IIC_Stop();
			return 1;
		}
	}
	GY_IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void GY_IIC_Ack(void)
{
	GY_IIC_SCL=0;
	GY_SDA_OUT();
	GY_IIC_SDA=0;
	GY_IIC_Delay();
	GY_IIC_SCL=1;
	GY_IIC_Delay();
	GY_IIC_SCL=0;
}
//不产生ACK应答		    
void GY_IIC_NAck(void)
{
	GY_IIC_SCL=0;
	GY_SDA_OUT();
	GY_IIC_SDA=1;
	GY_IIC_Delay();
	GY_IIC_SCL=1;
	GY_IIC_Delay();
	GY_IIC_SCL=0;
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void GY_IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	GY_SDA_OUT(); 	    
    GY_IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        GY_IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		GY_IIC_SCL=1;
		GY_IIC_Delay(); 
		GY_IIC_SCL=0;	
		GY_IIC_Delay();
    }	 
} 	    
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 GY_IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	GY_SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        GY_IIC_SCL=0; 
        GY_IIC_Delay();
		GY_IIC_SCL=1;
        receive<<=1;
        if(GY_READ_SDA)receive++;   
		GY_IIC_Delay(); 
    }					 
    if (!ack)
        GY_IIC_NAck();//发送nACK
    else
        GY_IIC_Ack(); //发送ACK   
    return receive;
}
//==================================模拟IIC-END==============================
 
 
 
 
//==========================HMC5883函数定义=============================
 
u8 HMC5883_SB_Read(u8 Slave_Address, u8 Register_Address) 
{
    static u8 Res_Data = 0;
    GY_IIC_Start(); 
	GY_IIC_Send_Byte(Slave_Address);//0X3C	
	GY_IIC_Wait_Ack();		//等待应答 
    GY_IIC_Send_Byte(Register_Address);	//寄存器地址
    GY_IIC_Wait_Ack();		//等待应答
    GY_IIC_Start();
	GY_IIC_Send_Byte(Slave_Address + 1);//0X3D	
    GY_IIC_Wait_Ack();		//等待应答 
	Res_Data=GY_IIC_Read_Byte(0);//读取数据,发送nACK 
    GY_IIC_Stop();			//产生一个停止条件 
	return Res_Data;		
}
 
u8 HMC5883_SB_Write(u8 Slave_Address, u8 Register_Address, u8 Register_Data)
{
     GY_IIC_Start(); 
	 GY_IIC_Send_Byte(Slave_Address);//发送设备地址
	 if(GY_IIC_Wait_Ack())	//等待应答
	 {
	 	 GY_IIC_Stop();		 
 		 return 1;		
	 }
     GY_IIC_Send_Byte(Register_Address); //写寄存器地址
     GY_IIC_Wait_Ack();		          //等待应答 
	 GY_IIC_Send_Byte(Register_Data);    //发送数据
	 if(GY_IIC_Wait_Ack())	              //等待ACK
	 {
	 	 GY_IIC_Stop();	 
		 return 1;		 
	 }		 
     GY_IIC_Stop();	 
     return 0;
     
}

uint8_t SF=0;  //Self Test Flag
void HMC5883_Init(void)
{
	uint8_t Reg[13];
	GY_IIC_Init() ;
	
	Reg[0] = HMC5883_SB_Read(SlaveAddress,CRA);
	Reg[1] = HMC5883_SB_Read(SlaveAddress,CRB);
	Reg[2] = HMC5883_SB_Read(SlaveAddress,MR);
	Reg[3] = HMC5883_SB_Read(SlaveAddress,DOXMR);
	Reg[4] = HMC5883_SB_Read(SlaveAddress,DOXLR);
	Reg[5] = HMC5883_SB_Read(SlaveAddress,DOZMR);
	Reg[6] = HMC5883_SB_Read(SlaveAddress,DOZLR);
	Reg[7] = HMC5883_SB_Read(SlaveAddress,DOYMR);
	Reg[8] = HMC5883_SB_Read(SlaveAddress,DOYLR);
	Reg[9] = HMC5883_SB_Read(SlaveAddress,SR);
	Reg[10] = HMC5883_SB_Read(SlaveAddress,IRA); //data = 0x48
	Reg[11] = HMC5883_SB_Read(SlaveAddress,IRB); //data = 0x34
	Reg[12] = HMC5883_SB_Read(SlaveAddress,IRC); //data = 0x33

	if(!((Reg[10]==0x48)&&(Reg[11]==0x34)&&(Reg[12]==0x33)))
	{
		printf("HMC5883 I2C access failure!\r\n");
		printf("HMC5883 regs: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\r\n", Reg[0], Reg[1], Reg[2], Reg[3], Reg[4], Reg[5], Reg[6], Reg[7], Reg[8], Reg[9], Reg[10], Reg[11], Reg[12]);
	}
	else
	{
		printf("HMC5883 I2C access OK!\r\n");
		printf("HMC5883 regs: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\r\n", Reg[0], Reg[1], Reg[2], Reg[3], Reg[4], Reg[5], Reg[6], Reg[7], Reg[8], Reg[9], Reg[10], Reg[11], Reg[12]);


		HMC5883_SB_Write(SlaveAddress,CRA, 0X70);//samples average: 8; data output rate: 15Hz; measure mode: normal
		HMC5883_SB_Write(SlaveAddress,CRB, 0X20);//gain: ± 1.3 Ga
		HMC5883_SB_Write(SlaveAddress,MR, 0X00);//continuous-measurement mode

	}
}

//void HMC5883_Init(void)
//{
//    GY_IIC_Init() ;
//    HMC5883_SB_Write(SlaveAddress, 0X00, 0X58); //写寄存器A,30Hz数据输出、采样平均数0
//    HMC5883_SB_Write(SlaveAddress, 0X01, 0X40); //写寄存器B,传感器量程+-0.88Ga、增益1370高斯
//    HMC5883_SB_Write(SlaveAddress, 0X02, 0X00); //写寄存器C,连续数据输出
//    
//}
 
 
void HMC5883_Get_Angle(void)
{
    u8 i ;

    short Recive_Data[6] ;   //store temperary data
    //HMC5883_Init() ;
    for(i=0; i<6; i++)
    {
        Recive_Data[i] = HMC5883_SB_Read(SlaveAddress, i+3) ;  //get data
    }
    
    hmc_xyz_init.HMC_X = Recive_Data[0]<<8 | Recive_Data[1];//Combine MSB and LSB of X Data output register
    hmc_xyz_init.HMC_Y = Recive_Data[2]<<8 | Recive_Data[3];//Combine MSB and LSB of Z Data output register
    hmc_xyz_init.HMC_Z = Recive_Data[4]<<8 | Recive_Data[5];//Combine MSB and LSB of Y Data output register
    
    hmc_xyz_init.Angle = atan2((double)hmc_xyz_init.HMC_Y,(double) hmc_xyz_init.HMC_X) * (180 / 3.14159265) + 180; // angle in degrees
}
 
void HMC5883_Get_XYZ(short* x, short* y, short* z)
{
	u8 i;
	short Recive_Data[6];
	//HMC5883_Init();
	for (i = 0; i < 6; i++) {
		Recive_Data[i] = HMC5883_SB_Read(SlaveAddress, i + 3);
	}
	*x = Recive_Data[0] << 8 | Recive_Data[1];
	*y = Recive_Data[2] << 8 | Recive_Data[3];
	*z = Recive_Data[4] << 8 | Recive_Data[5];
}

四、参考资料

STM32模拟I2C协议获取HMC5883L电子罗盘磁角度数据 (HAL)https://blog.csdn.net/hwytree/article/details/126407447【STM32+cubemx】0029 HAL库开发:HMC5883L磁力计的应用(电子指南针)https://blog.csdn.net/little_grapes/article/details/127895372?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171573961816800211552065%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171573961816800211552065&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-127895372-null-null.142%5Ev100%5Epc_search_result_base7&utm_term=HMC5883L&spm=1018.2226.3001.4187

 

  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿柒学起来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值