本文来自 制造技术研究社 的投稿。
HMC5883是霍尼韦尔公司生产的一款地磁场检测芯片,其国产替代产品为QMC5883。这两种芯片基本相似,QMC 5883也是号称得到了霍尼韦尔公司的授权。
两款芯片在外观上没有明显的区别,本人在某宝上购买的QMC系列芯片上居然堂而皇之写着HMC5883,如果两者搞混了,就难得出正确结果。
网上有很多采用单片机虚拟i2c总线读取数据的例程,也有采用Python读取i2c总线数据的例子,但是如何利用linux系统中直接用C语言来读取数据的例子比较少,而且不是全面。很多专业的linux系统书籍中偏重于介绍如何编写i2c总线驱动程序,较少介绍如何直接利用系统中现有的命令读取数据。
本文主要介绍如何利用树莓派(Linux系统,3B+和4B+型号都通用)下读取5883系列传感器的数据,直接调用Linux系统中的指令来读取i2c总线上的数据。
一、硬件连接
首先HMC 5883模块具有4个引脚,Vcc、GND、SCL和SDA,分别连接到树莓派对应的引脚,Vcc接树莓派的5V电源即可,SCL和SDA接到i2c-1总线上(注意,请不要接到0号i2c总线上,需要进行另外设置才能打开该总线)。
二、开启总线,安装i2ctool工具
连接成功之后,首先要打开i2c总线,在树莓系统默认中是关闭i2c总线的,可以通过raspi-config配置界面中的interface开启(enable)i2c总线。
开启总线后,还需要安装i2ctool工具,才方便调试i2c总线,这是一个很小的工具。在控制台输入:
sudo apt-get install i2c-tools 命令即可安装i2c-tools。
安装完成后可以使用i2cdetect -l查看i2c总线开启情况:
使用i2cdetect -y 1命令即可扫描接在总线上的所有I2C设备,并打印出该设备的I2C总线地址。
这时,显示了该总线上挂载了一个设备,地址为0x0d,0x0d(二进制为0000 1101)是QMC 5883的7位地址,(注意:HMC 5883的7位地址是0x1e,这是两种芯片的第一处不同。)
数据手册上会说到8位地址,8位地址是这样计算的:将7位地址左移1位,读地址为末位加1,则对应的8位地址为0x1b(二进制为0001 1011 ),写地址为末位加0,则对应的8位地址为0x1A(二进制为0001 1010 )。但是利用linux系统自带的驱动程序不需要使用8位地址,7位地址就足够了。
另外微信公众号“制造技术研究社”觉得几个有用的指令是:
1、查看数据:i2cdump -f -y 1 0x0d 查看0d地址下所有地址上的数据
2、写寄存器:i2cset -f -y 1 0x0d 0x09 0x01 设置相应寄存器的数值,这里是向地址为0x0d设备下的0x09地址写入0x01这个数据。
三、QMC 5883和HMC 5883的芯片设置
QMC 5883需要设置0x09,0x0B、0x20、0x21这四个寄存器的内容,其中0x0B是置位复位的周期,默认值为0x01, 0x09是控制寄存器,bit0和bit1控制采样模式,分别为连续模式和等待模式,00是等待,01是连续模式,bit2和bit3是采集速率,bit4和bit5是测量范围,00为正负2高斯,01为正负8高斯,bit6,bit7是过采样率,这里是说多采集一些数据,得平均值的意思,这里可以不管。总之0x09的默认配置为0x0d或者0x1d。总结以上,QMC 5883需要配置的寄存器为:(值得吐槽的是,0x20和0x21寄存器是一定需要设置的,但是这个点在datasheet里面居然没有说明!)
芯片地址0x09数据为=0x0d/0x1d;
芯片地址0x0B数据为= 0x01;
芯片地址0x20数据为= 0x40;
芯片地址0x21数据为= 0x01;
根据测试的需求,很明显需要先配置这四个寄存器。
而对于HMC 5883需要配置3个寄存器,也就是i2c设备的头三个地址0x00、0x01、0x02,分别被称为配置寄存器A、配置寄存器B,模式寄存器,配置寄存器A设置过采样率和测试频率,配置寄存器B设置增益大小(放大系数、检测数据和磁场值的转换系数);模式寄存器是设置采样模式。
详细的配置说明可以参见数据手册,也可以联系微信公众号:“制造技术研究社”,留言获得。
总之,HMC 5883的配置参数可以按照以下内容:
#define HMC5883_CRA 0x14 采样频率30Hz #define HMC5883_CRB 0xe0 测量范围正负8.1高斯 #define HMC5883_MR 0x00 连续测量模式
四、QMC 5883和HMC 5883的测量数据
QMC 5883的数据存储在0x00-0x05连续6个地址,0x00是X方向的低8位数据,0x01是X方向的高8位数据;0x02是Y方向的低8位数据,0x03是Y方向的高8位数据;0x04是Z方向的低8位数据,0x05是Z方向的高8位数据;
HMC 5883的数据存储在0x03-0x08连续6个地址,顺序为XZY,(这又是不同点),0x03是X方向的高8位数据,0x04是X方向的高8位数据(高低次序也不同),其余类推。
所以只要将这些数据读取出来,就可以实现对磁场的测量。
五、调用linux驱动程度读取数据
以上全部为准备知识,只有清楚认识了5883系列芯片,才能正确读写芯片。虽然网上有些利用wringPi程序来读取i2c总线的方法,但是没有关于如何采用C语言编程读写芯片的例子,下面以QMC 5883的读取程序为例,详细讲解如何用C语言调用i2c总线驱动程序。
首先C语言头文件要包含基本输入输出函数stdio.h、
IO控制头文件
标准库函数
文件控制头文件
Linux下的i2c驱动头文件
其中#define QMC5883 “/dev/i2c-1″是linux系统的特色,将所有的设备看作挂载的路径,
#define Gain 12000 是配置数据和磁场值的转换系数,在文末解释了如何计算磁场和系数。
第三部分 i2c读写函数及流程
在检测之前,先要将配置数据写入设备中,以写入0x09寄存器和0x0B寄存器为例,
首先获得文件ID,
int fd=open(QMC5883,O_RDWR);
通过open函数,就获得了文件的ID,其中O_RDWR是系统自定义的宏,表示以读写模式打开文件,open( )函数的返回值是一个大于0的数,被称之为文件ID(file_ID),如果返回值为0,或者负数,说明读写文件出现了问题。
在后面的程序中,只要调用file_ID,就可以实现了对接口的调用。
注意,在读取结束,要close文件。
第二步是定义一个数组QMC5883_init_config_1[4],
QMC5883_init_config_1[4]={0x09,QMC5883_0x09,0x00,QMC5883_0x0B};
数组的第一个值是0x09,表示i2c设备的子地址(寄存器的地址),就是说在从第9个地址开始写入,顺序将后面的数据写入设备,该数组后面的数据就是需要配置的值,分别为0x0d、0x00、0x01,执行该指令后,LINUX系统驱动程序会在0x09地址之后,陆续写入这3个数,(其中地址0x0A的数据不需要写入,这里写进去只是为了方便。)
接着定义另一个数组,配置其他寄存器
QMC5883_init_config_2[3]={0x20,QMC5883_0x20,QMC5883_0x21};
完成了四个寄存器的配置,接下来将读写指针调回到数据地址的首地址,
定义一个无符号数suba,unsigned char suba=0 这个数的指针提出来,写入文件,读写指针就回到了0x00位置。
第三步是读取数据,
读取数据的函数为read(),其格式和write()很相似
unsigned char QMC5883_read_data[6]; read(fd,QMC5883_read_data,8);
XYZ三个方向的数据分别存在连续的6个地址内,将低8位(LSB)的数据左移8位,再和高8位“求或运算”就得到了测量的磁场数据。
最后将数据显示出来就可以了。
注:这里通过while循环,实现连续不断的测量,想要中止的话,按下Ctrl+C跳出循环,中止程序即可。
HMC 5883的读写方式也与此类似,可以对照写出程序,也可以联系微信公众号“制造技术研究社”,进一步交流相关内容。
总结:
读写5883系列传感器,关键是掌握linux系统下设备的读写,首先要掌握传感器的配置内容,其次一定要注意各个地址的区别,在正确的地址读写正确的内容。
附1:磁场的计算
传感器测出的数据为一个整型数据hpx,当测量范围为2Guass的时候,增益系数为12 000,那么磁场值为hpx/12000(Guass)
注意一,磁场强度的单位为A/m,在空气中,A/m和高斯的转换关系为1高斯=79.62A/m,可以继续转换为磁场强度作为单位。
注意二:整型数和实数之间还要注意数据类型的转换。
附2:源代码
/************************************************/ /* *Magnetic testing Program */ /* Edit by: Doctor Han in HFUT */ /* Date:2019.12.23 */ /************************************************/ #include <stdio.h> #include <sys/ioctl.h> #include <unistd.h> #include <fcntl.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> #define QMC5883 "/dev/i2c-1" #define QMC5883_ADDR 0x0d //adress #define QMC5883_0x0B 0x01 //set/reset period #define QMC5883_0x20 0x40 //unkown configration #define QMC5883_0x21 0x01 //unkown configration #define QMC5883_0x09 0x0d //Configration #define Gain 12000 int main() { //printf("Testing by QMC5883L (From i2c Bus)\n"); int fd=open(QMC5883,O_RDWR); int i; int Num=1; unsigned char QMC5883_init_config_1[4]={0x09,QMC5883_0x09,0x00,QMC5883_0x0B}; unsigned char QMC5883_init_config_2[3]={0x20,QMC5883_0x20,QMC5883_0x21}; unsigned char suba=0; unsigned char QMC5883_read_data[6]; //unsigned char QMC5883_write_data_buff[4]={0,QMC5883_CRA,QMC5883_CRB,QMC5883_MR}; double Hx,Hy,Hz,Temp; short int hpx,hpy,hpz,temp; if(fd<0) { printf("Open QMC5883 failed\n"); } if(ioctl(fd,I2C_SLAVE_FORCE,QMC5883_ADDR)<0) { printf("Address fault\n"); close(fd); } //while(1) //{ write(fd,QMC5883_init_config_1,4); write(fd,QMC5883_init_config_2,3); write(fd,&suba,1); read(fd,QMC5883_read_data,8); hpx=*QMC5883_read_data; hpx=*(QMC5883_read_data+1)<<8|hpx; hpy=*(QMC5883_read_data+2); hpy=*(QMC5883_read_data+3)<<8|hpy; hpz=*(QMC5883_read_data+4); hpz=*(QMC5883_read_data+5)<<8|hpz; printf("No=%d ",Num); //printf("Hx=%d ;Hy=%d ;Hz=%d \n",hpx,hpy,hpz); Hx=hpx*79.61/Gain; Hy=hpy*79.61/Gain; Hz=hpz*79.61/Gain; Temp=temp*1.00/100; Num=Num+1; printf("Hx=%f A/m; Hy=%f A/m; Hz=%f A/m;\n",Hx,Hy,Hz); //} return 0; close(fd); }
这个代码要是能实现电子指南针的功能就更完美了。