前言
做一个项目需要用到,但是网上好多资料都没有标准库的,故移植写一个方便以后开发使用
一、模块介绍
霍尼韦尔 HMC5883L 是一种表面贴装的高集成模块,并带有数字接口的弱磁传感器芯片,应用于低成本罗盘和磁场检测领域。HMC5883L 包括最先进的高分辨率HMC118X 系列磁阻传感器,并附带霍尼韦尔专利的集成电路包括放大器、自动消磁驱动器、偏差校准、能使罗盘精度控制在1°~2°的12 位模数转换器.简易的I2C 系列总线接口。HMC5883L 是采用无铅表面封装技术,带有16 引脚,尺寸为3.0X3.0X0.9mm。HMC5883L 的所应用领域有手机、笔记本电脑、消费类电子、汽车导航系统和个人导航系统。
HMC5883L 采用霍尼韦尔各向异性磁阻(AMR)技术,该技术的优点是其他磁传感器技术所无法企及。这些各向异性传感器具有在轴向高灵敏度和线性高精度的特点.传感器带有的对于正交轴低敏感行的固相结构能用于测量地球磁场的方向和大小,其测量范围从毫高斯到 8 高斯(gauss)。 霍尼韦尔的磁传感器在低磁场传感器行业中是灵敏度最高和可靠性最好的传感器。
二、资料获取
关注微信公众号--星之援工作室 发送关键字(HMC5883L)
➡️➡️
实验效果
我主要实验一下对金属的反馈效果,达到检测车辆的作用,测试使用的是X轴,得到的结果还是符合预期,可以针对进出不同方向达到测试车辆行驶的方向进行记录
接线方式如下
首先确认一个位置,并且查看X轴数据(可以自信更新方位和检测轴,道理一样)
测试所得
当金属在左侧时,X轴反馈数据会大于无物体数据
反之,当金属在左侧时,X轴反馈数据会小于于无物体数据这样就可以判断我们车辆的行驶方向
连接串口可以输出我们获取到的消息
三、代码编写
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 okn");
while(1)
{
HMC5883_Get_Angle();// 读数据
printf("X:%d Y:%d Z:%d Angle:%.1fn",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!rn");
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%xrn", 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!rn");
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%xrn", 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
联系方式 微信号:13648103287