關于磁力計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); //初始化5883
void 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); //内部寄存器資料,請參考中文pdf
HMC5883_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 degrees
while(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