关于磁力计HMC5883L型号探索
一.序言
在众多磁力计中,HMC5883L因为其廉价实用而受到大多数嵌入式开发小白的追捧。然而某宝上不仅仅有HMC5883L这一款,还有QMC5883和HMC5983,常有商家搞不清芯片就出售,导致提供的数据手册不对应。同时,不同单片机对IIC的操作也略有差异,对小白而言也是件头疼的事情。
HMC5883L与QMC5883L区别对比
可以看到,QMC5883L的各寄存器地址与HMC5883L区别还是不小。这样,移植时候程序整体结构不用大改,把寄存器改掉即可。
(1) 修改I2C address
0x3C -> 0x1A
0x3D -> 0x1B
(2) X/Y/Z Register
起始:0x03 -> 0x00
顺序:X-Z-Y -> X-Y-Z
(3) Mode Register
0x02 -> 0x09
0x00 -> 0x01
二.HMC5983
HMC5983是HMC5883L的“加强版”,寄存器与5883基本一致,但同时支持I2C和SPI两种通信模式,更加快捷。因此,使用方法与5883几乎没有差别。这里,本人将官方提供的测试程序修改为STC15系列单片机,主要移植I2C通信中时序函数。
本人增加了一个定任意方向为0度的功能,主要为了解决各类机器人竞赛中场地不一定何种角度朝向的问题。不过由于时间仓促没有来的及写滤波。思路就是将坐标系进行转换。贴出程序,仅供参考。
1.电路图
2.所需元件
STC15W408AS x1
HMC5983 x1
1k电阻 x1
独立按键 x1
排针 若干
洞洞板 x1
3.程序结构
4.源码
(1)user_config.h
/*-----------------------------------------用户配置文件-------------------------------------------*/
#ifndef _CONFIG_H_
#define _CONFIG_H_/* 宏定义 */
#define uchar unsigned char
#define uint unsigned int typedef unsigned char BYTE;
typedef unsigned short WORD;/* 包含头文件 */
#include <STC15F2K60S2.H>
#include <math.h> //Keil library
#include <stdio.h> //Keil library
#include <INTRINS.H>
//USER Library
#include "HMC5983_Driver.h"
#include "HMC5983_Software.h"
#include "uart.h"
#include "delay.h"
#include "interrupt.h"//#include "LQ12864.h"//使用的端口,请按照以下接线
sbit SCL=P3^2; //IIC时钟引脚定义
sbit SDA=P3^3; //IIC数据引脚定义
sbit KEY=P3^6; //按键
//sfr INT_CLKO = 0x8f; //外部中断与时钟输出控制寄存器//全局变量引入
extern BYTE BUF[8];
extern uchar ge,shi,bai,qian,wan; //显示变量
extern int dis_data; //变量
extern double angle_ch;
#endif
(2)HMC5983_Driver.h
/*------------------------------------HMC5983驱动文件-------------------------------------*/
#ifndef _HMC5983_DRIVER_H_
#define _HMC5983_DRIVER_H_#define SlaveAddress 0x3C //定义器件5883在IIC总线中的从地址//************
//函数
//************void Init_HMC5883(void); //初始化5883void Single_Write_HMC5883(uchar REG_Address,uchar REG_data); //单个写入数据
//uchar Single_Read_HMC5883(uchar REG_Address); //单个读取内部寄存器数据
void Multiple_Read_HMC5883(void); //连续的读取内部寄存器数据
//以下是模拟iic使用函数-------------
void HMC5883_Start(void);
void HMC5883_Stop(void);
void HMC5883_SendACK(bit ack);
bit HMC5883_RecvACK(void);
void HMC5883_SendByte(BYTE dat);
BYTE HMC5883_RecvByte(void);
void HMC5883_ReadPage(void);
void HMC5883_WritePage(void);#endif
(3)interrupt.h
/*--------------------------中断服务头文件---------------------------*/
#ifndef _IT_H_
#define _IT_H_#define Init_INT2() INT_CLKO |= 0x10#endif
(4)uart.c
/*-----------------------------------串口配置STC15F408AS内置晶振波特率 9600 bps------------------------------------*/#include "user_config.h"BYTE BUF[8]; //接收数据缓存区 //*********************************************
//串口初始化
//9600 bps @ 11.059 MHz
void init_uart(void)
{SCON = 0x50;AUXR |= 0x04;T2L = 0xa0;T2H = 0xfc;AUXR |= 0x10;TI = 1;EA = 1;
}//*********串口数据发送******************
void SeriPushSend(uchar send_data)
{SBUF = send_data; while(!TI);TI=0;
}
(5)interrupt.c
/*-----------------------------中断服务-----------------------------*/
#include "user_config.h"double angle_ch = 0;void KEY_DOWN(void) interrupt 10
{Delay5ms();if(KEY == 0){while(!KEY);angle_ch = Angle_Update();}
}
(6)HMC5983_Driver.c
//***************************************
// HMC5883 51串口测试程序
// 使用单片机STC89C51
// 晶振:11.0592M
// 显示:PC串口
// 编译环境 Keil uVision2
// 参考宏晶网站24c04通信程序
// 时间:2011年3月1日
//****************************************#include "user_config.h"
#include "HMC5983_Driver.h"int dis_data; //变量//-----------------------------------/**************************************
起始信号
**************************************/
void HMC5883_Start(void)
{SDA = 1; //拉高数据线SCL = 1; //拉高时钟线Delay5us(); //延时SDA = 0; //产生下降沿Delay5us(); //延时SCL = 0; //拉低时钟线
}/**************************************
停止信号
**************************************/
void HMC5883_Stop(void)
{SDA = 0; //拉低数据线SCL = 1; //拉高时钟线Delay5us(); //延时SDA = 1; //产生上升沿Delay5us(); //延时
}/**************************************
发送应答信号
入口参数:ack (0:ACK 1:NAK)
**************************************/
void HMC5883_SendACK(bit ack)
{SDA = ack; //写应答信号SCL = 1; //拉高时钟线Delay5us(); //延时SCL = 0; //拉低时钟线Delay5us(); //延时
}/**************************************
接收应答信号
**************************************/
bit HMC5883_RecvACK(void)
{SCL = 1; //拉高时钟线Delay5us(); //延时CY = SDA; //读应答信号SCL = 0; //拉低时钟线Delay5us(); //延时return CY;
}/**************************************
向IIC总线发送一个字节数据
**************************************/
void HMC5883_SendByte(BYTE dat)
{BYTE i;for (i=0; i<8; i++) //8位计数器{dat <<= 1; //移出数据的最高位SDA = CY; //送数据口SCL = 1; //拉高时钟线Delay5us(); //延时SCL = 0; //拉低时钟线Delay5us(); //延时}HMC5883_RecvACK();
}/**************************************
从IIC总线接收一个字节数据
**************************************/
BYTE HMC5883_RecvByte(void)
{BYTE i;BYTE dat = 0;SDA = 1; //使能内部上拉,准备读取数据,for (i=0; i<8; i++) //8位计数器{dat <<= 1;SCL = 1; //拉高时钟线Delay5us(); //延时dat |= SDA; //读数据 SCL = 0; //拉低时钟线Delay5us(); //延时}return dat;
}//***************************************************void Single_Write_HMC5883(uchar REG_Address,uchar REG_data)
{HMC5883_Start(); //起始信号HMC5883_SendByte(SlaveAddress); //发送设备地址+写信号HMC5883_SendByte(REG_Address); //内部寄存器地址,请参考中文pdf HMC5883_SendByte(REG_data); //内部寄存器数据,请参考中文pdfHMC5883_Stop(); //发送停止信号
}/*
//********单字节读取内部寄存器*************************
uchar Single_Read_HMC5883(uchar REG_Address)
{uchar REG_data;HMC5883_Start(); //起始信号HMC5883_SendByte(SlaveAddress); //发送设备地址+写信号HMC5883_SendByte(REG_Address); //发送存储单元地址,从0开始 HMC5883_Start(); //起始信号HMC5883_SendByte(SlaveAddress+1); //发送设备地址+读信号REG_data=HMC5883_RecvByte(); //读出寄存器数据HMC5883_SendACK(1); HMC5883_Stop(); //停止信号return REG_data;
}
*/
//******************************************************
//
//连续读出HMC5883内部角度数据,地址范围0x3~0x5
//
//******************************************************
void Multiple_read_HMC5883(void)
{ uchar i;HMC5883_Start(); //起始信号HMC5883_SendByte(SlaveAddress); //发送设备地址+写信号HMC5883_SendByte(0x03); //发送存储单元地址,从0x3开始 HMC5883_Start(); //起始信号HMC5883_SendByte(SlaveAddress+1); //发送设备地址+读信号for (i=0; i<7; i++) //连续读取6个地址数据,存储中BUF{BUF[i] = HMC5883_RecvByte(); //BUF[0]存储0x32地址中的数据if (i == 6){HMC5883_SendACK(1); //最后一个数据需要回NOACK}else{HMC5883_SendACK(0); //回应ACK}}HMC5883_Stop(); //停止信号Delay5ms();
}//初始化HMC5883,根据需要请参考pdf进行修改****
void Init_HMC5883(void)
{Single_Write_HMC5883(0x02,0x00); //Single_Write_HMC5883(0x01,0xE0); //
}
(7)HMC5983_Software.c
/*---------------------------------HMC5983 角度变换将任意角度变为0度-----------------------------------*/
#include "user_config.h"uchar ge,shi,bai,qian,wan; //显示变量double Angle_Update(void)
{int x,y,z,t;Multiple_Read_HMC5883(); //连续读出数据,存储在BUF中x = BUF[0] << 8 | BUF[1]; z = BUF[2] << 8 | BUF[3]; y = BUF[4] << 8 | BUF[5]; t = atan2((double)y,(double)x) * (180 / 3.14159265) + 180; // angle in degreeswhile(abs(t-45) <= 0.001){Multiple_Read_HMC5883(); //连续读出数据,存储在BUF中x = BUF[0] << 8 | BUF[1]; z = BUF[2] << 8 | BUF[3]; y = BUF[4] << 8 | BUF[5]; t = atan2((double)y,(double)x) * (180 / 3.14159265) + 180; // angle in degrees}return t;
}void conversion(uint temp_data)
{ wan=temp_data/10000+0x30 ;temp_data=temp_data%10000; //取余运算qian=temp_data/1000+0x30 ;temp_data=temp_data%1000; //取余运算bai=temp_data/100+0x30 ;temp_data=temp_data%100; //取余运算shi=temp_data/10+0x30 ;temp_data=temp_data%10; //取余运算ge=temp_data+0x30;
}double Change_Target(void)
{double angle_now = Angle_Update();if(angle_now >= angle_ch) return angle_now-angle_ch;else return 360-angle_ch+angle_now;
}
(8)main.c
/*----------------------------------------------------------基于HMC5983智能电子罗盘2017/10/24 Listen C@ 型号:STC15F408AS@ 晶振:内置33.1776MHz@ 通信:IIC@ 显示:0.96 OLED@ 串口:9600 bps------------------------------------------------------------*/#include <STC15F2K60S2.H>
#include "user_config.h"//*********************************************************
// 主程序
//*********************************************************void main(void)
{ // bit sign_bit;unsigned int i;double angle;delay(100);Init_INT2();init_uart();Init_HMC5883();while(1) //循环{ angle = Change_Target();conversion(angle); //计算数据和显示Delay5ms(); SeriPushSend(0x0d);SeriPushSend(0x0a);SeriPushSend(bai);SeriPushSend(shi);SeriPushSend(ge);}
}
三.QMC5883L在Arduino上使用
在arduino中,I2C address用的是7位地址而不是8位,所以需要对应将address改掉。
比如QMC5883为0x1a,则二进制为0001_1010。而换算为7位,右移一位二进制为0000_1101。
则十六进制为0x0d。
源码如下:
#include <Wire.h>#define address 0x0d void setup() {//put your setup code here, to run once:Serial.begin(9600);delay(500);Wire.begin();Wire.beginTransmission(address);Wire.write(0x09);Wire.write(0x01);Wire.endTransmission();}void loop() {//put your main code here, to run repeatedly:intx,y,z;Wire.beginTransmission(address);Wire.write(0x00);Wire.endTransmission();Wire.requestFrom(address,6);if(Wire.available() >= 6){x= Wire.read() << 8;x |= Wire.read();y = Wire.read() << 8;y |= Wire.read();z= Wire.read() << 8;z |= Wire.read();}Serial.print("x: ");Serial.print(x);Serial.print(" y: ");Serial.print(y);Serial.print(" z: ");Serial.println(z);delay(250);}
网盘链接:http://pan.baidu.com/s/1gfEje8r 密码:dbvr