前言
做一個項目需要用到,但是網(wǎng)上好多資料都沒有標(biāo)準庫的,故移植寫一個方便以后開發(fā)使用
一、模塊介紹
霍尼韋爾 HMC5883L 是一種表面貼裝的高集成模塊,并帶有數(shù)字接口的弱磁傳感器芯片,應(yīng)用于低成本羅盤和磁場檢測領(lǐng)域。HMC5883L 包括最先進的高分辨率HMC118X 系列磁阻傳感器,并附帶霍尼韋爾專利的集成電路包括放大器、自動消磁驅(qū)動器、偏差校準、能使羅盤精度控制在1°~2°的12 位模數(shù)轉(zhuǎn)換器.簡易的I2C 系列總線接口。HMC5883L 是采用無鉛表面封裝技術(shù),帶有16 引腳,尺寸為3.0X3.0X0.9mm。HMC5883L 的所應(yīng)用領(lǐng)域有手機、筆記本電腦、消費類電子、汽車導(dǎo)航系統(tǒng)和個人導(dǎo)航系統(tǒng)。
HMC5883L 采用霍尼韋爾各向異性磁阻(AMR)技術(shù),該技術(shù)的優(yōu)點是其他磁傳感器技術(shù)所無法企及。這些各向異性傳感器具有在軸向高靈敏度和線性高精度的特點.傳感器帶有的對于正交軸低敏感行的固相結(jié)構(gòu)能用于測量地球磁場的方向和大小,其測量范圍從毫高斯到 8 高斯(gauss)。 霍尼韋爾的磁傳感器在低磁場傳感器行業(yè)中是靈敏度最高和可靠性最好的傳感器。
二、資料獲取
關(guān)注微信公眾號--星之援工作室 發(fā)送關(guān)鍵字(HMC5883L)
????
實驗效果
我主要實驗一下對金屬的反饋效果,達到檢測車輛的作用,測試使用的是X軸,得到的結(jié)果還是符合預(yù)期,可以針對進出不同方向達到測試車輛行駛的方向進行記錄
接線方式如下
?首先確認一個位置,并且查看X軸數(shù)據(jù)(可以自信更新方位和檢測軸,道理一樣)
測試所得
當(dāng)金屬在左側(cè)時,X軸反饋數(shù)據(jù)會大于無物體數(shù)據(jù)
反之,當(dāng)金屬在左側(cè)時,X軸反饋數(shù)據(jù)會小于于無物體數(shù)據(jù)這樣就可以判斷我們車輛的行駛方向
?連接串口可以輸出我們獲取到的消息
三、代碼編寫
main.c
/*--------------------------------------------------------*
* *
* 星之援網(wǎng)絡(luò)科技工作室學(xué)習(xí)資料v1.0 *
* 時間:2022.7.14 *
* 程序介紹:HMC5883L 使用 *
* 實現(xiàn)效果:串口輸出 XYZ軸 以及偏轉(zhuǎn)角度 *
* *
*--------------------------------------------------------*
*/
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "usart.h"
#include "hmc5883l.h"
extern HMC_XYZ hmc_xyz_init; // 初始化結(jié)構(gòu)體
int main(void)
{
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設(shè)置NVIC中斷分組2:2位搶占優(yōu)先級,2位響應(yīng)優(yōu)先級
LED_GPIO_Config(); // LED 端口初始化
uart_init(115200);// 串口初始化
GY_IIC_Init() ;
HMC5883_Init(); // HMC5883L初始
printf("init okn");
while(1)
{
HMC5883_Get_Angle();// 讀數(shù)據(jù)
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"
// 結(jié)構(gòu)體
typedef struct
{
short HMC_X;
short HMC_Y;
short HMC_Z; // HMC5883三軸數(shù)據(jù)輸出
float Angle; // 偏轉(zhuǎn)角度
} 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操作函數(shù)
#define GY_IIC_SCL PBout(9) // SCL
#define GY_IIC_SDA PBout(8) // SDA
#define GY_READ_SDA PBin(8) // 輸入SDA
//IIC所有操作函數(shù)
void GY_IIC_Delay(void); //MPU IIC延時函數(shù)
void GY_IIC_Init(void); //初始化IIC的IO口
void GY_IIC_Start(void); //發(fā)送IIC開始信號
void GY_IIC_Stop(void); //發(fā)送IIC停止信號
void GY_IIC_Send_Byte(u8 txd); //IIC發(fā)送一個字節(jié)
u8 GY_IIC_Read_Byte(unsigned char ack);//IIC讀取一個字節(jié)
u8 GY_IIC_Wait_Ack(void); //IIC等待ACK信號
void GY_IIC_Ack(void); //IIC發(fā)送ACK信號
void GY_IIC_NAck(void); //IIC不發(fā)送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; // 初始化結(jié)構(gòu)體
//==============================模擬IIC函數(shù)區(qū)=====================================
void GY_IIC_Delay(void)
{
delay_us(2);
}
// 配置雙向I/O端口為輸出態(tài)
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端口為輸入態(tài)
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;
}
//產(chǎn)生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總線,準備發(fā)送或接收數(shù)據(jù)
}
//產(chǎn)生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;//發(fā)送I2C總線結(jié)束信號
GY_IIC_Delay();
}
//等待應(yīng)答信號到來
//返回值:1,接收應(yīng)答失敗
// 0,接收應(yīng)答成功
u8 GY_IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
GY_SDA_IN(); //SDA設(shè)置為輸入
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;
}
//產(chǎn)生ACK應(yīng)答
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;
}
//不產(chǎn)生ACK應(yīng)答
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發(fā)送一個字節(jié)
//返回從機有無應(yīng)答
//1,有應(yīng)答
//0,無應(yīng)答
void GY_IIC_Send_Byte(u8 txd)
{
u8 t;
GY_SDA_OUT();
GY_IIC_SCL=0;//拉低時鐘開始數(shù)據(jù)傳輸
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個字節(jié),ack=1時,發(fā)送ACK,ack=0,發(fā)送nACK
u8 GY_IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
GY_SDA_IN();//SDA設(shè)置為輸入
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();//發(fā)送nACK
else
GY_IIC_Ack(); //發(fā)送ACK
return receive;
}
//==================================模擬IIC-END==============================
//==========================HMC5883函數(shù)定義=============================
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(); //等待應(yīng)答
GY_IIC_Send_Byte(Register_Address); //寄存器地址
GY_IIC_Wait_Ack(); //等待應(yīng)答
GY_IIC_Start();
GY_IIC_Send_Byte(Slave_Address + 1);//0X3D
GY_IIC_Wait_Ack(); //等待應(yīng)答
Res_Data=GY_IIC_Read_Byte(0);//讀取數(shù)據(jù),發(fā)送nACK
GY_IIC_Stop(); //產(chǎn)生一個停止條件
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);//發(fā)送設(shè)備地址
if(GY_IIC_Wait_Ack()) //等待應(yīng)答
{
GY_IIC_Stop();
return 1;
}
GY_IIC_Send_Byte(Register_Address); //寫寄存器地址
GY_IIC_Wait_Ack(); //等待應(yīng)答
GY_IIC_Send_Byte(Register_Data); //發(fā)送數(shù)據(jù)
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數(shù)據(jù)輸出、采樣平均數(shù)0
// HMC5883_SB_Write(SlaveAddress, 0X01, 0X40); //寫寄存器B,傳感器量程+-0.88Ga、增益1370高斯
// HMC5883_SB_Write(SlaveAddress, 0X02, 0X00); //寫寄存器C,連續(xù)數(shù)據(jù)輸出
//
//}
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協(xié)議獲取HMC5883L電子羅盤磁角度數(shù)據(jù) (HAL)https://blog.csdn.net/hwytree/article/details/126407447【STM32+cubemx】0029 HAL庫開發(fā):HMC5883L磁力計的應(yīng)用(電子指南針)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
聯(lián)系方式 微信號:13648103287