HMC5883 3轴磁阻测方位角


//*************************************** // HMC5883 IIC测试程序 // 使用单片机STC89C52 // 晶振:12M // 显示:串口输出,波特率4800 // 编译环境 Keil uVision3 // 参考宏晶网站24c04通信程序 // 时间:2012年11月27日 // 修改:Mr Lee //**************************************** #include <REG51.H> #include <math.h> //Keil library #include <stdio.h> //Keil library #include <INTRINS.H> #define uchar unsigned char #define uint unsigned int sbitSCL=P1^0; //IIC时钟引脚定义 sbitSDA=P1^1; //IIC数据引脚定义 #defineSlaveAddress 0x3C//定义器件在IIC总线中的从地址 typedef unsigned char BYTE; typedef unsigned short WORD; BYTE BUF[8]; //接收数据缓存区 uchar ge,shi,bai,qian,wan; //显示变量 int dis_data; //变量 void delay(unsigned int k); void Init_HMC5883(void); //初始化5883 void conversion(uint temp_data); void Single_Write_HMC5883(uchar REG_Address,uchar REG_data); //单个写入数据 uchar Single_Read_HMC5883(uchar REG_Address); //单个读取内部寄存器数据 void Multiple_Read_HMC5883(); //连续的读取内部寄存器数据 //以下是模拟iic使用函数------------- void Delay5us(); void Delay5ms(); void HMC5883_Start(); void HMC5883_Stop(); void HMC5883_SendACK(bit ack); bit HMC5883_RecvACK(); void HMC5883_SendByte(BYTE dat); BYTE HMC5883_RecvByte(); void HMC5883_ReadPage(); void HMC5883_WritePage(); //----------------------------------- //********************************************************* 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; } /*******************************/ void delay(unsigned int k) { unsigned int i,j; for(i=0;i<k;i++) { for(j=0;j<121;j++) {;}} } /************************************** 延时5微秒(STC90C52RC@12M) 不同的工作环境,需要调整此函数,注意时钟过快时需要修改 当改用1T的MCU时,请调整此延时函数 **************************************/ void Delay5us() { _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); } /************************************** 延时5毫秒(STC90C52RC@12M) 不同的工作环境,需要调整此函数 当改用1T的MCU时,请调整此延时函数 **************************************/ void Delay5ms() { WORD n = 560; while (n--); } /************************************** 起始信号 **************************************/ void HMC5883_Start() { SDA = 1; //拉高数据线 SCL = 1; //拉高时钟线 Delay5us(); //延时 SDA = 0; //产生下降沿 Delay5us(); //延时 SCL = 0; //拉低时钟线 } /************************************** 停止信号 **************************************/ void HMC5883_Stop() { 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() { 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() { 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); //发送存储单元地址,从0x32开始 HMC5883_Start(); //起始信号 HMC5883_SendByte(SlaveAddress+1); //发送设备地址+读信号 for (i=0; i<6; i++) //连续读取6个地址数据,存储中BUF { BUF[i] = HMC5883_RecvByte(); //BUF[0]存储0x32地址中的数据 if (i == 5) { HMC5883_SendACK(1); //最后一个数据需要回NOACK } else { HMC5883_SendACK(0); //回应ACK } } HMC5883_Stop(); //停止信号 Delay5ms(); } //初始化HMC5883,根据需要请参考pdf进行修改**** void Init_HMC5883() { Single_Write_HMC5883(0x02,0x00); // } void uart_init() { TMOD=0x20; //TMOD=0 TH1=0xf3; //12MHZ ,BPS:4800,N,8,1,0xf3=243 TL1=0xf3; PCON=0x80; //方式一,8位数据位,一位起始位和一位结束位 TR1=1; // SCON=0x40; //串口通信控制寄存器 模式一 } void uart_putchar(unsigned char dat) { SBUF=dat; //把数据送给sbuf缓存器中 while(TI!=1);//发送标志位 TI如果发送了为1,没发送为0,没发送等待,到了退出循环 TI=0; //到了,TI清为0 } void uart_printf(unsigned char *buff) { while(*buff) uart_putchar(*buff++); } //********************************************************* //主程序******** //********************************************************* void main() { // bit sign_bit; unsigned int i; int x,y,z; double angle; unsigned char str[5]; #ifdef XYZ unsigned char str_tmp[2]; #endif delay(500); uart_init(); Init_HMC5883(); while(1) //循环 { Multiple_Read_HMC5883(); //连续读出数据,存储在BUF中 //---------显示X轴 x=BUF[0] << 8 | BUF[1]; //Combine MSB and LSB of X Data output register z=BUF[2] << 8 | BUF[3]; //Combine MSB and LSB of Z Data output register y=BUF[4] << 8 | BUF[5]; //Combine MSB and LSB of Y Data output register angle= atan2((double)y,(double)x) * (180 / 3.14159265) + 180; // angle in degrees angle*=10; conversion(angle); //计算数据和显示 str[0]=qian; str[1]=bai; str[2]=shi; str[3]='.'; str[4]=ge; uart_printf("angle is:"); uart_printf(str); uart_printf(" "); #ifdef XYZ uart_printf("x is:"); str_tmp[0]=x/10+'0'; str_tmp[1]=x%10+'0'; uart_printf(str_tmp); uart_printf(" "); uart_printf("y is:"); str_tmp[0]=y/10+'0'; str_tmp[1]=y%10+'0'; uart_printf(str_tmp); uart_printf(" "); uart_printf("z is:"); str_tmp[0]=z/10+'0'; str_tmp[1]=z%10+'0'; uart_printf(str_tmp); //uart_printf(" "); #endif uart_printf("\r\n"); for (i=0;i<10000;i++); //延时 } } 实物图: 串口输出图:

对于校准的含义其实很简单;就相当于把5883L的基准线转换到你所设备上的环境(Sensor所贴片的产品);
目前我所见比较多的是,硬磁;修正offset的值方式如下(以X轴说明为例):
第一步:是用需校准的sample平放,这样sensor会在各个轴采集到一个max 和min
把其这定义为Xmax(1)和Xmin(1),其它轴类似;同时将该数据作为初始化的值;
第二步:进行360degree旋转,会得到另外的Xmax(2)和Xmin(2);然后将Xmax(1)和Xmax(2)进行比较,从而选择Xmax=Xmax(1)(while the Xmax(1)>Xmax(2)),Xmin也进行同样的比较;

Last:then the Xoffset:Xoffset=(Xmax(1)+Xmin(1))/2;

该模块主要是考虑校正设备本身磁场的干扰,从而使测量数据更为准确。当然这只是对于要求精度不高的情况下进行设置的;但对于高精度的话,Honeywell 应该也会给你提供一些计算方式的,这你可以询问你买产品的地方,
Offset其实就是一个补偿校准值,而对于补偿校准值的得出,就是通过我上一个贴所说的步骤来进行计算的;
如果你能对每个轴都旋转360度的话,效果更好;而你提高的Max和Min,不是平均后的值,如果平均后那就不存在Max和Min一说,这两个值,是实际你在旋转过程中传感器所采集的数据,通过这样你就可以大概的计算出offset的值(这动作是在calibration时做),在得到offset的值后,后面你所测量的数据就用实际测量的减去offset,那才是你所测量环境的比较准确的磁场值.
你可以通过设定5883L和你的MCU,“单一测量模式”确实只采集一个数据,但是“连续测量模式”就可以进行多个数据的采集,因此在calibration的时候,Honeywell datasheet上有进行说明,使用“连续测量模式”进行校准.
5883水平放置,以Z轴为参考旋转轴,旋转360度。期间MCU不断采样X轴和Y轴数据,将得到的数据筛选出Xmax Xmin Ymax Ymin.
Xoffset=(Xmax+Xmin)/2
Yoffset=(Ymax+Ymin)/2

优质内容筛选与推荐>>
1、BZOJ:3209: 花神的数论题
2、表达式计算器类的设计4(面向对象的表达式计算器7)
3、Unbuntu 14.04 64位 搭建交叉编译环境
4、Django基础
5、1、了解嵌入式系统、arm、内核、驱动、OS、MMU等专有名词


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号