天天看點

【子產品學習】關于磁力計HMC5883L型号探索關于磁力計HMC5883L型号探索

關于磁力計HMC5883L型号探索

一.序言

         在衆多磁力計中,HMC5883L因為其廉價實用而受到大多數嵌入式開發小白的追捧。然而某寶上不僅僅有HMC5883L這一款,還有QMC5883和HMC5983,常有商家搞不清晶片就出售,導緻提供的資料手冊不對應。同時,不同單片機對IIC的操作也略有差異,對小白而言也是件頭疼的事情。

HMC5883L與QMC5883L差別對比

【子產品學習】關于磁力計HMC5883L型号探索關于磁力計HMC5883L型号探索

         可以看到,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.電路圖

【子產品學習】關于磁力計HMC5883L型号探索關于磁力計HMC5883L型号探索

2.所需元件

STC15W408AS    x1

HMC5983        x1

1k電阻          x1

獨立按鍵        x1

排針           若幹

洞洞闆          x1

3.程式結構

【子產品學習】關于磁力計HMC5883L型号探索關于磁力計HMC5883L型号探索

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

繼續閱讀