MAG3110 stm32f1xx调试心得附源代码(硬件iic和模拟iic ,融合程序尽量使用模拟iic,否则硬件iic有bug,难受)

MAG3110简介

这些无需多说,数据手册百度得到。
使用步骤:
一 首先初始化iic,硬件iic调用函数 I2C_Configuration(); 具体引脚配置自行解决
二 对MAG3110初始化 :
1 ,进入 MAG3110_Standby(); standby 模式 (具体standby模式数据手册有给,写那个寄存器,以及写某个值)(如果不懂用iic给寄存器写值,可能后面你就看不太懂了,但是可以直接copy后面的代码)
2 , 配置相关寄存器, I2C_WriteOneByte(I2C1,MAG3110_IIC_ADDRESS,CTRL_REG1, DATA_RATE_5MS);
I2C_WriteOneByte(I2C1,MAG3110_IIC_ADDRESS,0x11, 0x00);
3 , MAG3110_Active();(同standby模式一样,给某个寄存器写值,就能使模块active,具体见数据手册或者后面代码)
三 读数据:
1 ,在初始化之后,需要读一个名为WHO_AM_I_REG的寄存器,如果读出的数据是 0xC4 ,则才为初始化成功。否则初始化失败
2, 初始化完毕之后,需要读0x00地址的值,读出的值与0x08按位与运算
&0x08,如果为真则说明数据准备就绪,可以读出。
3 ,读取数据,读出x,y,z轴的高低数据然后合并,作为三轴的磁通量。

硬件iic

以下是c文件,借用24c02


#include "24C02.h"


/*******************************************************************************
* Function Name  : I2C_Configuration
* Description    : 
* Input          : None
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
void I2C_Configuration(void)
{
   I2C_InitTypeDef  I2C_InitStructure;
   GPIO_InitTypeDef  GPIO_InitStructure; 

   RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO , ENABLE);

   /* Configure I2C1 pins: PB6->SCL and PB7->SDA */
   GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
   GPIO_Init(GPIOB, &GPIO_InitStructure);
		
   I2C_DeInit(I2C1);
   I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
   I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
   I2C_InitStructure.I2C_OwnAddress1 = 0x1C;
   I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
   I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
   I2C_InitStructure.I2C_ClockSpeed = 400000;
    
   I2C_Cmd(I2C1, ENABLE);
   I2C_Init(I2C1, &I2C_InitStructure);

   I2C_AcknowledgeConfig(I2C1, ENABLE);
}
 
/*******************************************************************************
* Function Name  : I2C_delay
* Description    : 
* Input          : None
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
static void I2C_delay(uint16_t cnt)
{
	while(cnt--);
}

/*******************************************************************************
* Function Name  : I2C_AcknowledgePolling
* Description    : 
* Input          : I2C_TypeDef * , uint8_t
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
static void I2C_AcknowledgePolling(I2C_TypeDef *I2Cx,uint8_t I2C_Addr)
{
  vu16 SR1_Tmp;
  do
  {   
    I2C_GenerateSTART(I2Cx, ENABLE);

    SR1_Tmp = I2C_ReadRegister(I2Cx, I2C_Register_SR1);

#ifdef AT24C01A

	I2C_Send7bitAddress(I2Cx, I2C_Addr, I2C_Direction_Transmitter);
#else

	I2C_Send7bitAddress(I2Cx, 0, I2C_Direction_Transmitter);
#endif

  }while(!(I2C_ReadRegister(I2Cx, I2C_Register_SR1) & 0x0002));
  
  I2C_ClearFlag(I2Cx, I2C_FLAG_AF);
    
  I2C_GenerateSTOP(I2Cx, ENABLE);
}


/*******************************************************************************
* Function Name  : I2C_Read
* Description    : 
* Input          : 
* Output         : 
* Return         : 
* Attention		 : None
*******************************************************************************/
uchar I2C_Read(I2C_TypeDef *I2Cx,uint8_t I2C_Addr,uint8_t addr,uint8_t *buf,uint16_t num)
{
 uchar buffer1; 
 

	while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
		
	I2C_AcknowledgeConfig(I2Cx, ENABLE);


    I2C_GenerateSTART(I2Cx, ENABLE);
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));


    I2C_Send7bitAddress(I2Cx,  I2C_Addr, I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

	I2C_SendData(I2Cx, addr);
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
		
	I2C_GenerateSTART(I2Cx, ENABLE);
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
	
	I2C_Send7bitAddress(I2Cx, I2C_Addr, I2C_Direction_Receiver);
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
		

	
    while (num)
    {
		if(num==1)
		{
     		I2C_AcknowledgeConfig(I2Cx, DISABLE);
    		I2C_GenerateSTOP(I2Cx, ENABLE);
		}
	    
		while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED));  /* EV7 */
	  buffer1 = I2C_ReceiveData(I2Cx);
	   // buf++;
	    /* Decrement the read bytes counter */
	    num--;
    }

	I2C_AcknowledgeConfig(I2Cx, ENABLE);

	return buffer1;
}	

/*******************************************************************************
* Function Name  : I2C_WriteOneByte
* Description    : 
* Input          : 
* Output         : None
* Return         : 
* Attention		 : None
*******************************************************************************/
uint8_t I2C_WriteOneByte(I2C_TypeDef *I2Cx,uint8_t I2C_Addr,uint8_t addr,uint8_t value)
{
  	while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
		
//	I2C_AcknowledgeConfig(I2Cx, ENABLE);


    I2C_GenerateSTART(I2Cx, ENABLE);
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));


    I2C_Send7bitAddress(I2Cx,  I2C_Addr, I2C_Direction_Transmitter);
  	    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

  	I2C_SendData(I2Cx, addr);
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));



  	I2C_SendData(I2Cx, value); 
  	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	
  	I2C_GenerateSTOP(I2Cx, ENABLE);
  
  	I2C_AcknowledgePolling(I2Cx,I2C_Addr);

	I2C_delay(1000);

	return 0;
}


/*******************************************************************************
* Function Name  : I2C_Write
* Description    : 
* Input          : 
* Output         : None
* Return         : 
* Attention		 : None
*******************************************************************************/
uint8_t I2C_Write(I2C_TypeDef *I2Cx,uint8_t I2C_Addr,uint8_t addr,uint8_t *buf,uint16_t num)
{
	uint8_t err=0;
	
	while(num--)
	{
		if(I2C_WriteOneByte(I2Cx, I2C_Addr,addr++,*buf++))
		{
			err++;
		}
	}
	if(err)
		return 1;
	else 
		return 0;	
}
/*********************************************************\
* Put MAG3110Q into Active Mode
\*********************************************************/
void MAG3110_Active ()
{
  byte n;
  n = I2C_Read(I2C1,MAG3110_IIC_ADDRESS,CTRL_REG1,0,1);
  I2C_WriteOneByte(I2C1,MAG3110_IIC_ADDRESS,CTRL_REG1,n&0XFC|ACTIVE_MASK);
}

/*********************************************************\
* Put MAG3110Q into Standby Mode
\*********************************************************/
void MAG3110_Standby (void)
{
  byte n;

  n = I2C_Read(I2C1,MAG3110_IIC_ADDRESS,CTRL_REG1,0,1);
   I2C_WriteOneByte(I2C1,MAG3110_IIC_ADDRESS,CTRL_REG1, n&0xFC|STANDBY_MASK);
}

/*********************************************************\
* Initialize MAG3110Q
\*********************************************************/
void MAG3110_Init (void)
{  
  MAG3110_Standby();   
  I2C_WriteOneByte(I2C1,MAG3110_IIC_ADDRESS,CTRL_REG1, DATA_RATE_5MS);  
	I2C_WriteOneByte(I2C1,MAG3110_IIC_ADDRESS,0x11, 0x00); 
  MAG3110_Active();
}


以下是24c02的头文件 :


#ifndef __24C02_H
#define __24C02_H
#define uchar unsigned char
#define uint unsigned int
#define byte unsigned char
#define word unsigned int
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#define byte unsigned char					  
/* Private define ------------------------------------------------------------*/
#define AT24C01A
//#define AT24C01
#define MAG3110_IIC_ADDRESS       0x1C
/***********************************************************************************************
**
**  Variable type definition: BIT_FIELD
*/
typedef union
{
  byte Byte;
  struct {
    byte _0          :1;
    byte _1          :1;
    byte _2          :1;
    byte _3          :1;
    byte _4          :1;
    byte _5          :1;
    byte _6          :1;
    byte _7          :1;
  } Bit;
} BIT_FIELD;


/***********************************************************************************************
**
**  Variable type definition: tword
*/
typedef union
{
  word mword;
  struct
  {
    byte hi;
    byte lo;
  } mbyte;
} tword; 

/***********************************************************************************************
**
**  MAG3110Q Sensor Internal Registers
*/
enum
{
  MAG3110_STATUS_00 = 0,          // 0x00
  MAG3110_OUT_X_MSB,              // 0x01
  MAG3110_OUT_X_LSB,              // 0x02
  MAG3110_OUT_Y_MSB,              // 0x03
  MAG3110_OUT_Y_LSB,              // 0x04
  MAG3110_OUT_Z_MSB,              // 0x05
  MAG3110_OUT_Z_LSB,              // 0x06 
  MAG3110_WHO_AM_I,               // 0x07
  MAG3110_SYSMOD,                 // 0x08
  MAG3110_OFF_X_MSB,              // 0x09
  MAG3110_OFF_X_LSB,              // 0x0A
  MAG3110_OFF_Y_MSB,              // 0x0B
  MAG3110_OFF_Y_LSB,              // 0x0C
  MAG3110_OFF_Z_MSB,              // 0x0D
  MAG3110_OFF_Z_LSB,              // 0x0E 
  MAG3110_DIE_TEMP,               // 0x0f
  MAG3110_CTRL_REG1,              // 0x10
  MAG3110_CTRL_REG2,              // 0x11
 
};

/*
**  STATUS Registers
*/
#define STATUS_00_REG         0x00
//
#define ZYXOW_BIT             Bit._7
#define ZOW_BIT               Bit._6
#define YOR_BIT               Bit._5
#define XOR_BIT               Bit._4
#define ZYXDR_BIT             Bit._3
#define ZDR_BIT               Bit._2
#define YDR_BIT               Bit._1
#define XDR_BIT               Bit._0
//
#define ZYXOW_MASK            0x80
#define ZOW_MASK              0x40
#define YOR_MASK              0x20
#define XOR_MASK              0x10
#define ZYXDR_MASK            0x08
#define ZDR_MASK              0x04
#define YDR_MASK              0x02
#define XDR_MASK              0x01

/*
**  XYZ Data Registers
*/
#define OUT_X_MSB_REG         0x01
#define OUT_X_LSB_REG         0x02
#define OUT_Y_MSB_REG         0x03
#define OUT_Y_LSB_REG         0x04
#define OUT_Z_MSB_REG         0x05
#define OUT_Z_LSB_REG         0x06   

/*
**  WHO_AM_I Device ID Register
*/
#define WHO_AM_I_REG          0x07
#define MAG3110Q_ID           0xC4    

/*
**  SYSMOD System Mode Register
*/
#define SYSMOD_REG            0x08
//   
#define SYSMOD1_BIT           Bit._1
#define SYSMOD0_BIT           Bit._0
// 
#define SYSMOD1_MASK          0x02
#define SYSMOD0_MASK          0x01
#define SYSMOD_MASK           0x03
#define STANDBY_MASK          0x00
#define ACTIVE_MASK           0x01
#define CORRECT_MASK          0x02

/*
**  INT_SOURCE System Interrupt Status Register
*/
#define INT_SOURCE_REG        0x0C
//
#define SRC_ASLP_BIT          Bit._7
#define SRC_FIFO_BIT          Bit._6
#define SRC_TRANS_BIT         Bit._5
#define SRC_LNDPRT_BIT        Bit._4
#define SRC_PULSE_BIT         Bit._3
#define SRC_FF_MT_1_BIT       Bit._2
#define SRC_FF_MT_2_BIT       Bit._1
#define SRC_DRDY_BIT          Bit._0
//
#define SRC_ASLP_MASK         0x80
#define SRC_FIFO_MASK         0x40
#define SRC_TRANS_MASK        0x20
#define SRC_LNDPRT_MASK       0x10
#define SRC_PULSE_MASK        0x08
#define SRC_FF_MT_1_MASK      0x04
#define SRC_FF_MT_2_MASK      0x02
#define SRC_DRDY_MASK         0x01   

/*
**  XYZ Offset Correction Registers
*/
#define OFF_X_MSB             0x09
#define OFF_X_LSB             0x0A
#define OFF_Y_MSB             0x0B 
#define OFF_Y_LSB             0x0C
#define OFF_Z_MSB             0x0D
#define OFF_Z_LSB             0x0E 

#define DIE_TEMP              0x0F 


/*
**  CTRL_REG1 System Control 1 Register
*/
#define CTRL_REG1             0x10
//
#define DR2_BIT               Bit._7
#define DR1_BIT               Bit._6
#define DR0_BIT               Bit._5
#define OS1_BIT               Bit._4
#define OS0_BIT               Bit._3
#define FR_BIT                Bit._2
#define TM_BIT                Bit._1
#define AC_BIT                Bit._0
//
#define DR2_MASK              0x80
#define DR1_MASK              0x40
#define DR0_MASK              0x20
#define OS1_MASK              0x10
#define OS0_MASK              0x08
#define FR_MASK               0x04
#define TM_MASK               0x02
#define AC_MASK               0x01

#define ASLP_RATE_MASK        0xC0
#define DR_MASK               0x38
//                      
#define ASLP_RATE_20MS        0x00
#define ASLP_RATE_80MS        ASLP_RATE0_MASK
#define ASLP_RATE_160MS       ASLP_RATE1_MASK
#define ASLP_RATE_640MS       ASLP_RATE1_MASK+ASLP_RATE0_MASK
//
#define DATA_RATE_1250US      0x00
#define DATA_RATE_2500US      DR0_MASK
#define DATA_RATE_5MS         DR1_MASK
#define DATA_RATE_10MS        DR1_MASK+DR0_MASK
#define DATA_RATE_20MS        DR2_MASK
#define DATA_RATE_80MS        DR2_MASK+DR0_MASK
#define DATA_RATE_160MS       DR2_MASK+DR1_MASK
#define DATA_RATE_640MS       DR2_MASK+DR1_MASK+DR0_MASK

/*
**  CTRL_REG2 System Control 2 Register
*/
#define CTRL_REG2             0x11
//
#define AUTO_MRST_EN_BIT      Bit._7
#define RAW_BIT               Bit._5
#define MAG_RST_BIT           Bit._4  
//
#define AUTO_MRST_EN_MASK     0x80 
#define RAW_MASK              0x20
#define MAG_RST_MASK          0x10
 
/* Private function prototypes -----------------------------------------------*/
void I2C_Configuration(void);
uchar I2C_Read(I2C_TypeDef *I2Cx,uint8_t I2C_Addr,uint8_t addr,uint8_t *buf,uint16_t num);
uint8_t I2C_Write(I2C_TypeDef *I2Cx,uint8_t I2C_Addr,uint8_t addr,uint8_t *buf,uint16_t num);
uint8_t I2C_WriteOneByte(I2C_TypeDef *I2Cx,uint8_t I2C_Addr,uint8_t addr,uint8_t value);

void MAG3110_Init(void);
void MAG3110_Standby(void);
void MAG3110_Active(void);	 

#endif 



以下是main.c文件


#include "stm32f10x.h"
#include "24C02.h"
#include <string.h>
#include <stdio.h>
#include <math.h> 
#include "usart.h"
#ifdef __GNUC__
  /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
     set to 'Yes') calls __io_putchar() */
  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

/* Private function prototypes -----------------------------------------------*/
void GPIO_Configuration(void);

long MAG3110_DataProcess (int MAG3110_XData,int MAG3110_YData);
void MAG3110_STD(void);
/*******************************************************************************
* Function Name  : Delay
* Description    : Delay Time
* Input          : - nCount: Delay Time
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
void  Delay (uint32_t nCount)
{
  for(; nCount != 0; nCount--);
}

float MAG3110_XOFF=0,MAG3110_YOFF=0;
long MAG3110_XMax=0,MAG3110_YMax=0,MAG3110_XMin=0,MAG3110_YMin=0;
long MAG3110_XData=0,MAG3110_YData=0,MAG3110_ZData=0;
long ang;
/*******************************************************************************
* Function Name  : main
* Description    : Main program
* Input          : None
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
int main(void)
{

	int i;
 	
	//GPIO_Configuration();
	uart_init(115200);
	I2C_Configuration();
	printf("\r\n****************************************************************\r\n"); 
	MAG3110_Init(); //初始化MAG3110

/*******************************************************************************
芯片数据校正,写入的数值:校正后的数值=校正前的数值-写入的数值。用于读出数据过大超出65535。
应该将数值控制在0~3000之间最佳。
//	
//		I2C_WriteOneByte(I2C1,MAG3110_IIC_ADDRESS,OFF_X_MSB, 0xef); 
//		I2C_WriteOneByte(I2C1,MAG3110_IIC_ADDRESS,OFF_X_LSB, 0xff); 
//		I2C_WriteOneByte(I2C1,MAG3110_IIC_ADDRESS,OFF_Y_MSB, 0XF0); 
//		I2C_WriteOneByte(I2C1,MAG3110_IIC_ADDRESS,OFF_Y_LSB, 0x00); 
*******************************************************************************/
	  i= I2C_Read(I2C1,MAG3110_IIC_ADDRESS,WHO_AM_I_REG,0,1);
	  if (i == MAG3110Q_ID)	//确认初始化是否成功
	  {
	    printf("ID:MAG3110Q,OK!\n ");  	     
	  }  
	  else //初始化失败
	  {
	    printf("ID not identified,FAILED!\n"); 
		//for(;;);//初始化失败,停止在这里 	    
	  }
  	while(1)
	{	
		Delay(0xfffff);
		Delay(0xfffff);

	    i=I2C_Read(I2C1,MAG3110_IIC_ADDRESS,STATUS_00_REG,0,1); 
		if(i&ZYXDR_MASK) //数据就绪
		{ 	
				MAG3110_STD(); //读取MAG3110数据,标定中值,数据处理
			
				MAG3110_XData=~MAG3110_XData;
				MAG3110_YData=~MAG3110_YData;
			
			MAG3110_XData-=0xffff0000;
			MAG3110_YData-=0xffff0000;
			MAG3110_ZData-=0xffff0000;
			if(MAG3110_XData>=0xf000)
			{
				MAG3110_XData-=0xf000;
			}
				if(MAG3110_YData>=0xf000)
			{
				MAG3110_YData-=0xf000;
			}
				if(MAG3110_ZData>=0xf000)
			{
				MAG3110_ZData-=0xf000;
			}
			
			
			if(MAG3110_XData>=0x500)
			{
				MAG3110_XData=MAG3110_XData-0xfff;
				
			}
			
						if(MAG3110_YData>=0x500)
			{
				MAG3110_YData=MAG3110_YData-0xfff;	
			}
								if(MAG3110_ZData>=0x500)
			{
				MAG3110_ZData=MAG3110_ZData-0xfff;	
			}
			//	MAG3110_XData/=1.32299;
			
			MAG3110_XOFF=-116.5;
			MAG3110_YOFF=-83.5;
			ang=atan2((double)(MAG3110_YData*0.99701+MAG3110_YOFF),(double)MAG3110_XData+MAG3110_XOFF) * (180 / 3.14159265) +180;
				printf("The data needs to be calibrated, turning a lap");
				printf("\r\n\r\n");
				printf("\r\nPoint to the south angle:%d°\r\n",ang);
		}
		else //数据未就绪
		{
				printf("ID Failed!\n");
		}	
	}

}
long MAG3110_DataProcess (int MAG3110_XData,int MAG3110_YData)
{


	uint16_t MAG3110_Ang;

	MAG3110_XData -= MAG3110_XOFF;
	MAG3110_YData -= MAG3110_YOFF;
	
	

	
if (MAG3110_XData == 0)
{
		if (MAG3110_YData>0)
		{
				MAG3110_Ang= 90;		
		}	else{
				MAG3110_Ang	= 270;	
		}
}
else if (MAG3110_YData == 0)
{
		if (MAG3110_XData>0)
		{
				MAG3110_Ang= 0;		
		}else{
				MAG3110_Ang= 180;			
		}
}
else if ((MAG3110_XData > 0) && (MAG3110_YData > 0))
{
	
		MAG3110_Ang = (atan ( ( (float)MAG3110_YData) / ( (float) MAG3110_XData ) ) )* 180 / 3.14;
			
}
else if ((MAG3110_XData < 0) && (MAG3110_YData > 0))
{
	
		MAG3110_XData = -MAG3110_XData;
			
		MAG3110_Ang = 180-(atan ( ( (float)MAG3110_YData) / ( (float)MAG3110_XData ) ) ) * 180 / 3.14;
			
}
else if ((MAG3110_XData < 0) && (MAG3110_YData < 0))
{
		MAG3110_XData = -MAG3110_XData;
	
		MAG3110_YData = -MAG3110_YData;
	
		MAG3110_Ang = (atan ( ( (float)MAG3110_YData) / ( (float) MAG3110_XData ) ) )* 180 / 3.14 + 180;

}
else if ((MAG3110_XData > 0) && (MAG3110_YData < 0))
{
		MAG3110_YData = -MAG3110_YData;
	
		MAG3110_Ang = 360-(atan ( ( (float)MAG3110_YData) / ( (float)MAG3110_XData ) ) ) * 180 / 3.14;
}

return 	 MAG3110_Ang;
}


/*************************************************************************/
void MAG3110_STD(void)
// 此函数需多次执行以保证旋转一圈中
{
// 能够采集到真实的最大值和最小值
	tword wx, wy, wz; 
	static   uint8_t	First_Flag=0;
	wx.mbyte.hi = I2C_Read(I2C1,MAG3110_IIC_ADDRESS,OUT_X_MSB_REG,0,1); //读取X轴高字节
	wx.mbyte.lo = I2C_Read(I2C1,MAG3110_IIC_ADDRESS,OUT_X_LSB_REG,0,1);//读取X轴低字节
	wy.mbyte.hi = I2C_Read(I2C1,MAG3110_IIC_ADDRESS,OUT_Y_MSB_REG,0,1);//读取Y轴高字节
	wy.mbyte.lo = I2C_Read(I2C1,MAG3110_IIC_ADDRESS,OUT_Y_LSB_REG,0,1);	//读取Y轴低字节
	wz.mbyte.hi = I2C_Read(I2C1,MAG3110_IIC_ADDRESS,OUT_Z_MSB_REG,0,1);//读取Z轴高字节
	wz.mbyte.lo = I2C_Read(I2C1,MAG3110_IIC_ADDRESS,OUT_Z_LSB_REG,0,1);	 //读取Z轴低字节
	
	printf("X:%d  ",wx.mbyte.hi*256+wx.mbyte.lo);
	printf("Y:%d  ",wy.mbyte.hi*256+wy.mbyte.lo);
	printf("Z:%d  ",wz.mbyte.hi*256+wz.mbyte.lo);
	
	
	MAG3110_XData=wx.mbyte.hi*256+wx.mbyte.lo;
	MAG3110_YData=wy.mbyte.hi*256+wy.mbyte.lo;
	MAG3110_ZData=wz.mbyte.hi*256+wz.mbyte.lo;
    

	if (!First_Flag)
	{
		MAG3110_XMax = MAG3110_XData;
		MAG3110_XMin = MAG3110_XData;
		MAG3110_YMax = MAG3110_YData;
		MAG3110_YMin = MAG3110_YData;
		
		First_Flag = 1;
	}
	if (MAG3110_XData > MAG3110_XMax)
	{
		
		MAG3110_XMax =  MAG3110_XData;
			
	}
	else if (MAG3110_XData < MAG3110_XMin)
	{
		MAG3110_XMin =  MAG3110_XData;
	}
	if (MAG3110_YData > MAG3110_YMax)
	{
		MAG3110_YMax =  MAG3110_YData;
	}
	else if (MAG3110_YData < MAG3110_YMin)
	{
		MAG3110_YMin =  MAG3110_YData;
	}
	
	
	MAG3110_XOFF = (MAG3110_XMax + MAG3110_XMin) / 2;
	MAG3110_YOFF = (MAG3110_YMax + MAG3110_YMin) / 2;
	


	ang=MAG3110_DataProcess(wx.mbyte.hi*256+wx.mbyte.lo,wy.mbyte.hi*256+wy.mbyte.lo);
}



/*******************************************************************************
* Function Name  : GPIO_Configuration
* Description    : Configure GPIO Pin
* Input          : None
* Output         : None
* Return         : None
* Attention		 : None
*******************************************************************************/
void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC , ENABLE); 						 
/**
 *  LED1 -> PC9 , LED2 -> PC10 , LED3 -> PC11 , LED4 -> PC12
 */					 
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
  GPIO_Init(GPIOC, &GPIO_InitStructure);
}

#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *   where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{ 
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif



以上硬件iic是我从淘宝的模块资料里面拷贝的,有些删改。删改在数据处理的地方,对MAG3110数据处理或者数据输出有疑惑的可以直接看最下面,有我对数据的分析,当然以上代码可以直接单个使用,测试有效,可以和手机上的指南针媲美,但是只是水平放置的时候,(侵权联系删除)
**

模拟iic(需要包含正点原子的myiic.h才可以使用,使用PB6,7)

代码我是写在定时器中断里面的,需要另作修改的可以拷贝出代码出来,自行修改

以下是timer.c文件 (源代码是我从我的飞控代码上面拷贝过来的,需要的自行修改)

#include "timer.h"
#include "led.h"
#include "24C02.h"
#include "math.h"
#include "stdio.h"
#include "myiic.h"
#include "delay.h"
#include "MS5611.h"

float MAG3110_XOFF=0,MAG3110_YOFF=0,MAG3110_ZOFF=0;
long MAG3110_XMax=0,MAG3110_YMax=0,MAG3110_XMin=0,MAG3110_YMin=0;
long MAG3110_XData=0,MAG3110_YData=0,MAG3110_ZData=0;
long ang;

long MAG3110_DataProcess (int MAG3110_XData,int MAG3110_YData);
void MAG3110_STD(void);
float pitch,roll,yaw;


float altitude=0;

float xdata,ydata;

void TIM2_Int_Init(u16 arr,u16 psc)
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能
	
	//定时器TIM2初始化
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
 
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE ); //使能指定的TIM2中断,允许更新中断

	//中断优先级NVIC设置
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器


	TIM_Cmd(TIM2, ENABLE);  //使能TIMx					 
}
//定时器2中断服务程序
void TIM2_IRQHandler(void)   //TIM2中断
{
	
	 int i;
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)  //检查TIM2更新中断发生与否
		{

			i=Read_MAG3110(STATUS_00_REG); 
			if(i&ZYXDR_MASK) //数据就绪
			{ 	
				MAG3110_STD(); //读取MAG3110数据,标定中值,数据处理
			
				MAG3110_XData=~MAG3110_XData;
				MAG3110_YData=~MAG3110_YData;
			
			MAG3110_XData-=0xffff0000;
			MAG3110_YData-=0xffff0000;
			MAG3110_ZData-=0xffff0000;
			if(MAG3110_XData>=0xf000)
			{
				MAG3110_XData-=0xf000;
			}
				if(MAG3110_YData>=0xf000)
			{
				MAG3110_YData-=0xf000;
			}
				if(MAG3110_ZData>=0xf000)
			{
				MAG3110_ZData-=0xf000;
			}
			
			
			if(MAG3110_XData>=0xe00)
			{
				MAG3110_XData=MAG3110_XData-0xfff;	
			}
			
			if(MAG3110_YData>=0xe00)
			{
				MAG3110_YData=MAG3110_YData-0xfff;	
			}
			if(MAG3110_ZData>=0xe00)
			{
				MAG3110_ZData=MAG3110_ZData-0xfff;	
			}
//			//	MAG3110_XData/=1.32299;
			
			MAG3110_XOFF=-116.5;
			MAG3110_YOFF=-83.5;
			MAG3110_ZOFF=440;
			
			MAG3110_XData=MAG3110_XData+MAG3110_XOFF;
			MAG3110_YData=MAG3110_YData+MAG3110_YOFF;//*0.99701
			MAG3110_ZData=MAG3110_YData-MAG3110_ZOFF;
			
			xdata=(float)(MAG3110_XData*cos(pitch*3.1415925/180)+MAG3110_YData*sin(roll*3.1415925/180)*sin(pitch*3.1415925/180)-MAG3110_ZData*cos(roll*3.1415925/180)*sin(pitch*3.1415925/180));
			ydata=(float)(MAG3110_YData*cos(roll*3.1415925/180)+MAG3110_ZData*sin(roll*3.1415925/180));
			
			//ydata=(float)(MAG3110_YData*cos(pitch)+MAG3110_XData*sin(roll)*sin(pitch)-MAG3110_ZData*cos(roll)*sin(pitch));
		//	xdata=(float)(MAG3110_XData*cos(roll)+MAG3110_ZData*sin(roll));
			
			ang=atan2((double)(ydata),(double)xdata) * (180 / 3.14159265) ;
		//	printf(" x = %f ,y = %f   z =%ld   , ANG = %ld   \r\n",xdata,ydata,MAG3110_ZData,ang);   // 130  750
				
			
		}
		else //数据未就绪
		{
			
		}	
		
		
		MS561101BA_GetTemperature();//获取温度
		MS561101BA_getPressure();   //获取大气压
		
		altitude = 5716*(float)(Pressure/101325.0)*(Pressure/101325.0) - 18885*(float)(Pressure/101325.0) + 13181;  ///获取气压通过定时器中断来读,每20ms读取一次
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  //清除TIMx更新中断标志 
		LED1=!LED1;
		}
}

long MAG3110_DataProcess (int MAG3110_XData,int MAG3110_YData)
{


	uint16_t MAG3110_Ang;

	MAG3110_XData -= MAG3110_XOFF;
	MAG3110_YData -= MAG3110_YOFF;
	
	

	
if (MAG3110_XData == 0)
{
		if (MAG3110_YData>0)
		{
				MAG3110_Ang= 90;		
		}	else{
				MAG3110_Ang	= 270;	
		}
}
else if (MAG3110_YData == 0)
{
		if (MAG3110_XData>0)
		{
				MAG3110_Ang= 0;		
		}else{
				MAG3110_Ang= 180;			
		}
}
else if ((MAG3110_XData > 0) && (MAG3110_YData > 0))
{
	
		MAG3110_Ang = (atan ( ( (float)MAG3110_YData) / ( (float) MAG3110_XData ) ) )* 180 / 3.14;
			
}
else if ((MAG3110_XData < 0) && (MAG3110_YData > 0))
{
	
		MAG3110_XData = -MAG3110_XData;
			
		MAG3110_Ang = 180-(atan ( ( (float)MAG3110_YData) / ( (float)MAG3110_XData ) ) ) * 180 / 3.14;
			
}
else if ((MAG3110_XData < 0) && (MAG3110_YData < 0))
{
		MAG3110_XData = -MAG3110_XData;
	
		MAG3110_YData = -MAG3110_YData;
	
		MAG3110_Ang = (atan ( ( (float)MAG3110_YData) / ( (float) MAG3110_XData ) ) )* 180 / 3.14 + 180;

}
else if ((MAG3110_XData > 0) && (MAG3110_YData < 0))
{
		MAG3110_YData = -MAG3110_YData;
	
		MAG3110_Ang = 360-(atan ( ( (float)MAG3110_YData) / ( (float)MAG3110_XData ) ) ) * 180 / 3.14;
}

return 	 MAG3110_Ang;
}


/*************************************************************************/
void MAG3110_STD(void)
// 此函数需多次执行以保证旋转一圈中
{
// 能够采集到真实的最大值和最小值
	tword wx, wy, wz; 
	static   uint8_t	First_Flag=0;
	wx.mbyte.hi = Read_MAG3110(OUT_X_MSB_REG); //读取X轴高字节
	wx.mbyte.lo = Read_MAG3110(OUT_X_LSB_REG);//读取X轴低字节
	wy.mbyte.hi = Read_MAG3110(OUT_Y_MSB_REG);//读取Y轴高字节
	wy.mbyte.lo = Read_MAG3110(OUT_Y_LSB_REG);	//读取Y轴低字节
	wz.mbyte.hi = Read_MAG3110(OUT_Z_MSB_REG);//读取Z轴高字节
	wz.mbyte.lo = Read_MAG3110(OUT_Z_LSB_REG);	 //读取Z轴低字节
	

	
	
	MAG3110_XData=wx.mbyte.hi*256+wx.mbyte.lo;
	MAG3110_YData=wy.mbyte.hi*256+wy.mbyte.lo;
	MAG3110_ZData=wz.mbyte.hi*256+wz.mbyte.lo;
    

	if (!First_Flag)
	{
		MAG3110_XMax = MAG3110_XData;
		MAG3110_XMin = MAG3110_XData;
		MAG3110_YMax = MAG3110_YData;
		MAG3110_YMin = MAG3110_YData;
		
		First_Flag = 1;
	}
	if (MAG3110_XData > MAG3110_XMax)
	{
		
		MAG3110_XMax =  MAG3110_XData;
			
	}
	else if (MAG3110_XData < MAG3110_XMin)
	{
		MAG3110_XMin =  MAG3110_XData;
	}
	if (MAG3110_YData > MAG3110_YMax)
	{
		MAG3110_YMax =  MAG3110_YData;
	}
	else if (MAG3110_YData < MAG3110_YMin)
	{
		MAG3110_YMin =  MAG3110_YData;
	}
	
	
	MAG3110_XOFF = (MAG3110_XMax + MAG3110_XMin) / 2;
	MAG3110_YOFF = (MAG3110_YMax + MAG3110_YMin) / 2;
	


	ang=MAG3110_DataProcess(wx.mbyte.hi*256+wx.mbyte.lo,wy.mbyte.hi*256+wy.mbyte.lo);
}


以下是timer.h文件

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"

extern long MAG3110_XData,MAG3110_YData,MAG3110_ZData,ang;
extern float altitude;
extern float pitch,roll,yaw;
void TIM2_Int_Init(u16 arr,u16 psc);
 
#endif

(说明一下,以上中出现的变量pitch,yaw,roll是陀螺仪的输出数据,单位是角度制的,有陀螺仪的朋友可以把你的陀螺仪输出变量赋值给这三个,便可以在定时器中断里面计算航向角来稳定yaw了。这个可以抬起角度,但是会有一定影响,偏差在几度到十度的样子。)

主程序里面,在main 和while (1)中间需要插入初始化代码

	IIC_Init();

	printf("\r\n****************************************************************\r\n"); 
	MAG3110_Init(); //初始化MAG3110
	i= Read_MAG3110(WHO_AM_I_REG);
	  if (i == MAG3110Q_ID)	//确认初始化是否成功
	  {
	    printf("ID:MAG3110Q,OK!\n ");  	     
	  }  
	  else //初始化失败
	  {
	    printf("ID not identified,FAILED!\n"); 
		//for(;;);//初始化失败,停止在这里 	    
	  }//i在这里没有定义,需要自己 int 一个 i出来就够了

while(1)里面可以跑空循环,因为我写在了定时器中断里面,或者while(1)里面干其他事情都可以。

数据输出和分析

问题分析:
在刚开始按照淘宝客服给的代码,直接移植过去,发现出现之前和hmc5883l一样的问题,那就是有一个轴的输出变化不大,或者根本没变化,但是有一个轴数据正常。(就论x, y轴而言)。
问题解决过程
对mag3110的输出,我按照数据手册上面的提示,对x,y,z三轴都施加了一个自测模式,具体见数据手册上面,有对相关寄存器的配置。并且同时开启了yaw模式。这时候,x,y轴的数据开始出现巨大的变化,按照所学知识,磁通量的确应该在垂直的时候发生巨大变化,但是变化不至于这么明显。查了下手册,数据在寄存器的存储,是用二进制的补码形式存在,并且按照adc的规则,最高位不是数据位,而是符号位才对。
所以在debug的时候,使用16进制显示,这时候才发现了问题。
我对x,y轴水平旋转一周收集了数据,如下图所示:
手写的有点乱
首先需要明白磁北和地北的区别,有一个夹角。
其次,原本16位的数据,应该是0xabcd 这种格式的数据,但是最高位被用去做了符号位,所以只能是0 或 F,图片中收集到的数据的确如此,证明了我的猜想。朋友们可以仔细看看数据跟随方位的变化,是有规律可循的。

再看下图,这是对x,y轴的增减做的分析,在这里插入图片描述
这时候更能看出问题来了,可以看见x轴增加到最大或者减少到最小,跟y轴的恰好有90读的夹角。

所以在验证的猜想之后,在原有数据处理的基础上,我首先取补码,然后去掉最高位的0xffff0000,我的方法是如果带有符号位的,把它的符号位的0xf000减去,就得到了数据位。但是不能直接把人家的符号莫名其妙去掉把。所以后面如果大于0xe00的数,就是负数,需要再减去一个0xfff。所以刚开始总结的规律,它是从0xffff 往下减才得到的。所以只要知道那些几万的数据,是从0xffff往下减的,就有很多方法可以做了。我的是先去掉符号位,再用数据减去0xfff,其实有很多方法可以实现,我的其实方法很烂。

看不懂我的方法的,其实想明白,去掉因为补码导致的最高位变成0xffffxxxx 的类型,减去0xffff0000,就是后面的数据,而数据,如果大于了0xf000,那么就是说明它是个负数,并且如果算绝对值,它是和0xfffff的差值,而不是就是0xfxxx。

举个例子,0xff2e,其实值应该是0xff2e-0xffff才对,而不是debug显示出来的几万的数值。

	
				MAG3110_XData=~MAG3110_XData;
				MAG3110_YData=~MAG3110_YData;
			
			MAG3110_XData-=0xffff0000;
			MAG3110_YData-=0xffff0000;
			MAG3110_ZData-=0xffff0000;  //去掉因为补码导致的最高位置f
			
			if(MAG3110_XData>=0xf000)
			{
				MAG3110_XData-=0xf000;
			}
				if(MAG3110_YData>=0xf000)
			{
				MAG3110_YData-=0xf000;
			}
				if(MAG3110_ZData>=0xf000)
			{
				MAG3110_ZData-=0xf000;
			}
			
			
			if(MAG3110_XData>=0xe00)
			{
				MAG3110_XData=MAG3110_XData-0xfff;	
			}
			
			if(MAG3110_YData>=0xe00)
			{
				MAG3110_YData=MAG3110_YData-0xfff;	
			}
			if(MAG3110_ZData>=0xe00)
			{
				MAG3110_ZData=MAG3110_ZData-0xfff;	
			}//得到真正的数据值
//			//	MAG3110_XData/=1.32299;
			
			MAG3110_XOFF=-116.5;
			MAG3110_YOFF=-83.5;
			MAG3110_ZOFF=440;  //这里是我所得到的修正值,旋转机体得到。
			
			MAG3110_XData=MAG3110_XData+MAG3110_XOFF;
			MAG3110_YData=MAG3110_YData+MAG3110_YOFF;//*0.99701
			MAG3110_ZData=MAG3110_YData-MAG3110_ZOFF;
			
			xdata=(float)(MAG3110_XData*cos(pitch*3.1415925/180)+MAG3110_YData*sin(roll*3.1415925/180)*sin(pitch*3.1415925/180)-MAG3110_ZData*cos(roll*3.1415925/180)*sin(pitch*3.1415925/180));
			ydata=(float)(MAG3110_YData*cos(roll*3.1415925/180)+MAG3110_ZData*sin(roll*3.1415925/180));  //用陀螺仪数据融合输出。

最后再次说下数据不能正常输出的问题:
一 ,首先你可以调节一下配置寄存器2?比如配置上yaw模式,以及三个轴的自检。直到你观察到你的数据会随着你转动90°大幅变化,比如从几万变成了个位数,那就是数据输出没问题。
二,配置上三轴自检后,三轴的数据应该都是保持在6w多的幅度,随后把它关闭之后,在一个轴和磁感线平行的时候,应该也能达到6w多的数值,如果正交,可以变成个位数甚至0.
三,如果以上两步都行的话,那说明没有问题,源码里面我是关闭了yaw模式,感兴趣的也可以开启yaw模式看会怎么样。

(我目前在搞飞控相关的东西,如果感兴趣diy四轴飞行器的可以关注下我,我会更新下其他相关的传感器,以及相应的pid,或者adrc算法,以及卡尔曼滤波算法,欢迎各位捧个人场!)

如果不能解决的,可以私我,如果在的话我会帮你看一下的,虽然不一定能弄出来。淘宝上面的货真假不一,hmc5883l 和qmc5883l鱼龙混杂,mag3110也是乱七八糟的。
www.waveshare.net/wiki/MAG3110_Board (此链接是淘宝客服发给的,可以参考参考,内有代码,硬件iic代码,以及数据手册,侵权删)

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐