博世BMI160六轴传感器I2C通信配置

博世BMI160简介


Bosch Sensortec公司推出的最新BMI160惯性测量单元将最顶尖的16位3轴重力加速度计和超低功耗3轴陀螺仪集成于单一封装。采用14管脚LGA封装,尺寸为2.5×3.0×0.8mm3。当加速度计和陀螺仪在全速模式下运行时,耗电典型值低至950µA,仅为市场上同类产品耗电量的50%或者更低。


BMI160的Datasheet的下载连接如下:

点击此处进入下载网页




BMI160数据流框图和地址


从BMI160的Datasheet中可以看到它的数据流框图如下:



从上面的框图中,我们可以看到,BMI160与外部进行双向数据传输的方式有两种:SPI和I2C。下面,我们来看下通过I2C与外部进行通信的方式。当BMI160通过I2C与外部进行通信的时候,BMI160将作为I2C从设备挂到主控芯片(主设备)的I2C总线上,所以,主控芯片在配置其对应的I2C驱动时就需要知道BMI160的从设备地址。

 

对于不同的硬件设计,BMI160的从设备地址是不同的,从Datasheet中可以看到详细介绍,截图如下:



从上述描述中可以看出来:当SDO脚接GND的时候,BMI160的I2C地址是 0x68;当SDO脚接VDDIO的时候,BMI160的I2C地址是 0x69。




BMI160寄存器表


BMI160的寄存器表在其Datasheet的45页,下面,我们重点来介绍一下常用的比较重要的寄存器:

1.芯片ID

地址:0x00。寄存器名:CHIP_ID。默认值:0xD1。该寄存器只可以读,不能写。


2.BMI160工作模式寄存器

地址:0x03。寄存器名:PMU_STATUS。默认值:0x00。该寄存器只可以读,不可以写。该寄存器的值一共8个字节,最高位的2个位保留。后面也是每两个位组成一个值,分别是acc_pmu_status、gyr_pmu_status和mag_pmu_status的状态值。

 

截图如下:



各个状态值代表的实际含义截图如下:



3.控制寄存器

地址:0x7e。寄存器名:CMD。默认值:0x00。该寄存器可以读也可以写。

 

我们通过向该地址写入不同的值来控制加速度或者陀螺仪的工作模式。

0x11:通过写入该命令值,可以使加速度模块切换到正常工作模式。

0x15:通过写入该命令值,可以使陀螺仪模块切换到正常工作模式。


4.加速度(Accelerometer)寄存器

通过读该寄存器上的数据,可以得到加速度三个轴的原始数据。各个轴的寄存器地址及数据格式如下图所示:



5.陀螺仪角速度(Gyroscope)寄存器

通过读该寄存器上的数据,可以得到陀螺仪角速度三个轴的原始数据。各个轴的寄存器地址及数据格式如下图所示:



6.加速度和陀螺仪量程配置寄存器

(1)加速度量程配置寄存器地址是 0x41,该寄存器地址上的数据格式表如下所示:



从上图,我们可以看出,加速度量程配置只占了低四位,即 bit 0-3。不同的值对应的量程值如下表:



(2)陀螺仪量程配置寄存器地址是 0x43,该寄存器地址上的数据格式表如下所示:



从上图中可以看出,陀螺仪量程配置只占用了低三位,即 bit 0-2,不同的值对应的量程值如下表:



注意:加速度量程配置寄存器默认值是 0x03,即默认量程是 ±2g;陀螺仪量程配置寄存器默认值是 0x00,即默认角速度量程是 ±2000°/s。建议使用默认配置的量程。




六轴数据获取


1.三轴加速度数据获取实例参考代码

  
  
  1. //加速度三轴数据获取源码
  2. void getAccelerometerValue(void)
  3. {
  4. signed short acc_x,acc_y,acc_z;
  5. unsigned short x,y,z;
  6. //向命令寄存器写入0x11,使加速度处于正常工作模式
  7. i2c_write_one_byte( 0x7e, 0x11);
  8. //切换工作模式之后,延时100ms
  9. Delay_Ms( 100);
  10. /////////////////////////////加速度 X轴///////////////////////////
  11. x =( i2c_read_one_byte( 0x12) & 0xff);
  12. x = x|(( i2c_read_one_byte( 0x13) & 0xff)<< 8);
  13. acc_x = ( signed short)x;
  14. //当量程为±2g时,转换为g/s的加速度换算公式
  15. acc_x = ( signed short)(acc_x* 9.8)/( 0x8000/ 2);
  16. /////////////////////////////加速度 Y轴///////////////////////////
  17. y =( i2c_read_one_byte( 0x14) & 0xff) ;
  18. y = y|(( i2c_read_one_byte( 0x15) & 0xff)<< 8);
  19. acc_y = ( signed short)y;
  20. //当量程为±2g时,转换为g/s的加速度换算公式
  21. acc_y = ( signed short)(acc_y* 9.8)/( 0x8000/ 2);
  22. /////////////////////////////加速度 Z轴///////////////////////////
  23. z =( i2c_read_one_byte( 0x16) & 0xff) ;
  24. z = z|(( i2c_read_one_byte( 0x17) & 0xff)<< 8);
  25. acc_z = ( signed short)z;
  26. //当量程为±2g时,转换为g/s的加速度换算公式
  27. acc_z = ( signed short)(acc_z* 9.8)/( 0x8000/ 2);
  28. }


2.三轴陀螺仪数据获取实例参考代码

  
  
  1. //陀螺仪角速度三轴数据获取
  2. void getGyroscopeValue(void)
  3. {
  4. signed short gyr_x,gyr_y,gyr_z;
  5. unsigned short x,y,z;
  6. //向命令寄存器写入0x15,使陀螺仪处于正常工作模式
  7. i2c_write_one_byte( 0x7e, 0x15);
  8. //切换工作模式之后,延时100ms
  9. Delay_Ms( 100);
  10. /////////////////////////////陀螺仪角速度 X轴///////////////////////////
  11. x =( i2c_read_one_byte( 0x0c) & 0xff) ;
  12. x = x|(( i2c_read_one_byte( 0x0d) & 0xff)<< 8);
  13. gyr_x = ( signed short)x;
  14. // range为±2000°/s时,转换为角速度°/s的公式
  15. gyr_x = (gyr_x* 2000)/ 0x8000;
  16. /////////////////////////////陀螺仪角速度 Y轴///////////////////////////
  17. y =( i2c_read_one_byte( 0x0e) & 0xff) ;
  18. y = y|(( i2c_read_one_byte( 0x0f) & 0xff)<< 8);
  19. gyr_y = ( signed short)y;
  20. // range为±2000°/s时,转换为角速度°/s的公式
  21. gyr_y = (gyr_y* 2000)/ 0x8000;
  22. /////////////////////////////陀螺仪角速度 Z轴///////////////////////////
  23. z =( i2c_read_one_byte( 0x10) & 0xff) ;
  24. z = z|(( i2c_read_one_byte( 0x11) & 0xff)<< 8);
  25. gyr_z = ( signed short)z;
  26. // range为±2000°/s时,转换为角速度°/s的公式
  27. gyr_z = (gyr_z* 2000)/ 0x8000;
  28. }

上述源码中,有三个函数: i2c_write_one_byte Delay_Ms i2c_read_one_byte ,这三个函数的具体实现跟主控芯片有关,需要大家根据自己的主控芯片来自行封装实现,另外涉及到I2C的时候一般开始需要先初始化,初始化过程需要的从设备地址在前面已经讲过了,具体初始化函数的封装也需要大家自行根据主控芯片的介绍来实现。


上述提供的源码中,i2c_read_one_byte接口封装的返回值是unsigned char类型的。下面,我们来看看上述源码中加速度和陀螺仪角速度换算的原理:

1.加速度换算原理

换算实现代码如下:

  
  
  1. /////////////////////////////加速度 X轴///////////////////////////
  2. x =( i2c_read_one_byte( 0x12) & 0xff);
  3. x = x|(( i2c_read_one_byte( 0x13) & 0xff)<< 8);
  4. acc_x = ( signed short)x;
  5. //当量程为±2g时,转换为g/s的加速度换算公式
  6. acc_x = ( signed short)(acc_x* 9.8)/( 0x8000/ 2);

加速度每个轴占两个字节,所以从传感器中读取的值需要按照高低位进行拼接,并且因为加速度每个轴都是有方向的,也就是说加速度每个轴的值应该是有正负之分的,因此拼接之后的值强制类型转化为signed short。转换之后,我们需要将转换的值换算成加速度的单位。换算公式如下:


acc_x = (signed short)(acc_x*9.8)/(0x8000/2);


9.8是重力加速度的标准值;因为默认量程是 ±2g,最大和最小值相差 4g,而上述公式,我们取的是量程的一半,也就是2g;因为acc_x对应的signed short的范围是32768 ~ +32767最大到最小值一共是 65536个值,相当于65536个刻度,同样取一半的话就是0x8000。上述公式通过对应转换之后将采样读取的值乘以 g,这样采样获取值的范围0x8000就需要除以2。


2.陀螺仪角速度换算原理

换算实现代码如下:

  
  
  1. /////////////////////////////陀螺仪角速度 X轴///////////////////////////
  2. x =( i2c_read_one_byte( 0x0c) & 0xff) ;
  3. x = x|(( i2c_read_one_byte( 0x0d) & 0xff)<< 8);
  4. gyr_x = ( signed short)x;
  5. // range为±2000°/s时,转换为角速度°/s的公式
  6. gyr_x = (gyr_x* 2000)/ 0x8000;
陀螺仪角速度每个轴也是占用2个字节,所以从传感器中读取的值需要按照高低位进行拼接,并且因为陀螺仪角速度每个轴都是有方向的,也就是说陀螺仪角速度每个轴的值也应该是有正负之分的,因此拼接之后的值强制类型转化为 signed short 。转换之后,我们需要将转换的值换算成陀螺仪角速度的单位。换算公式如下:


gyr_x = (gyr_x*2000)/0x8000; 


因为默认量程是 ±2000°/s,最大和最小值相差 4000,而上述公式,我们取的是量程的一半,也就是2000;因为gyr_x对应的signed short的范围是-32768 ~ +32767最大到最小值一共是 65536个值,相当于65536个刻度,同样取一半的话就是0x8000。所以对应换算之后就出现了上述公式。



注意事项


默认BMI160开机上电启动的时候处于挂起模式,这个时候加速度和陀螺仪都处于未工作状态,无法读取数据。如果想读取相应数据,需要通过向0x7E命令控制寄存器写入相应命令来切换工作状态,具体命令前面介绍过了。

 

每次读取加速度数据前,都需要调用i2c_write_one_byte(0x7e,0x11);使加速度模块进入正常工作模式;每次读取陀螺仪角速度前,当然也需要调用i2c_write_one_byte(0x7e,0x15);使陀螺仪模块进入正常工作模式。这样才能成功读取到数据。





<script>
					(function(){
						function setArticleH(btnReadmore,posi){
							var winH = $(window).height();
							var articleBox = $("div.article_content");
							var artH = articleBox.height();
							if(artH > winH*posi){
								articleBox.css({
									'height':winH*posi+'px',
									'overflow':'hidden'
								})
								btnReadmore.click(function(){
									if(typeof window.localStorage === "object" && typeof window.csdn.anonymousUserLimit === "object"){
										if(!window.csdn.anonymousUserLimit.judgment()){
											window.csdn.anonymousUserLimit.Jumplogin();
											return false;
										}else if(!currentUserName){
											window.csdn.anonymousUserLimit.updata();
										}
									}
									
									articleBox.removeAttr("style");
									$(this).parent().remove();
								})
							}else{
								btnReadmore.parent().remove();
							}
						}
						var btnReadmore = $("#btn-readmore");
						if(btnReadmore.length>0){
							if(currentUserName){
								setArticleH(btnReadmore,3);
							}else{
								setArticleH(btnReadmore,1.2);
							}
						}
					})()
				</script>
				</article>