我有两个含温度传感的模块,一个是AOSONG 奥松电子的 AM2320 温度湿度,另一个是九轴里面的 Bosch BMP280。由于 AM2320 用 I2C MODBUS,直接用 I2C Tools 它不理我,扫描后地址没在总线出现,不知道是它没实现 SMBus 还是要给它功能码 0x03 唤醒才有东西,代码我还没写出来(其实到现在我都不知道是传感器坏了还是姿势不对),AM2320 稍后再试。

 

GY-91 MPU9250+BMP280

先玩个容易一点的,BMP280,温度与气压传感芯片。BBB 和 BMP280 都是 3.3V,不需要逻辑转换,方便。这次我只看温度,气压读取除了地址和代码外其他操作大致相同。

虽说二合一方便,但还是好贵…。耗那么多买这个当然是为了做灰机,做出来之前要学习,首先是 I2C。问客服拿原理图,给了我这个:

看到 MPU9250 的 SDO 有下拉,但不知道是不是跟 BMP280 的 SDO 一起的,后面检查地址时候发现,应该是的。从原理图看到 SDA 和 SCL 都有上拉接了个 4.7KΩ,自己连时候就SDA与SCL都不需上拉电阻,直接插 BBB,方便。好,开始动手。

 

接线

默认 BBB 开机后 I2C-1 已经启用,地址不冲突就直接插来试试。P9_20 接 SDA,P9_19 接 SCL,P9_1 GND,P9_3 3V3,上电后含BMP280的九轴模块 LED 绿灯亮起。

 

命令行工具 i2c-tools

BBB 自带的 i2c-tools 共有四个命令,用它们就不用写代码也能与从机交互:

i2cdetect 检查元件地址和i2c slots
i2cdump 倒出所有 register 值
i2cset 对 register 写入值
i2cget 从 register 取出值

除非特别说明,以下全部图片和内容的地址和值都是 HEX,16进位。看看,I2C-1 当前元件,用 I2C Detect 工具,列出 i2c-1 的所有从机:

 

扫描总线上的从机地址

i2cdetect –y –r –a 1

参数 –y 是无视交互问题直接执行,-r 是 SMBus read byte 命令,-a 是所有地址,1 是指 i2c-1。

上图的 UU 是代表系统驱动已占用,刚才说的没被占用就是指这些。

如果各位在试 I2C 而你的的 I2C 元件地址刚好是 0x54 至 0x57,你可以启用另一个 I2C,I2C-2 ,一次性连接的做法是 echo BB-I2C2 > /sys/devices/bone-capemgr.9/slots,然后它就会在 i2cdetect –l 里面出现,/dev/ 里面也有多了一个 i2c-2。要开机自动启用的话请自行修改开机档启用。

上图可见,其他元件的地址出现了,0x68 是我的九轴芯片 MPU9250,请无视。0x76 才是这次要通信的 BMP280,0x76 就是说明书第28页介绍 I2C 上说的地址:

由于这 BMP280 与九轴是集成在一起成为一个模块,我貌似是没办法控制 SDO 拉高拉低,不过地址不是 1110110 (0x76) 就是 1110111 (0x77)的了,经 i2cdetect 证实是前者 0x76(就是原理图上 九轴的 SDO 和 BMP280 的 SDO 是一起拉低了,“Connecting SDO to GDN results in slave address 1110110(0x76)”)。

 

读取 256 个 register 值

其实BMP280 没有 256 个,但 i2cdump 默认是 0x00 至 0xFF 寄存器地址的,SPI 交互时 MSB(bit 7) 是 R/W,值 1 时候是读取,地址 7 bit 长,所以下图是 10000000(0x80)开始的。挑剔一点的话,应该指定 i2cdump 范围,由 0x80 至 0xFF。

查到I2C地址后,我用 i2cdump 把 register 全部 dump 出来看看,快捷方便,(据说这样操作对个别元件有风险,但 BMP280 不在此列,详情请自行参考 i2cdetect 和 i2cdump 的相关说明):

i2cdump –y 1 0x76

参数 –y 是无视交互问题直接执行,1 是指 i2c-1,0x76 是元件的 I2C 地址。

 

BMP280 Device ID

很多其他元件的 ID 地址都是 0x00,这设计师把 Device ID 放了在 D0,上图 i2cdump 可见,0xD0 (d0) 值为 0x58,与说明书 4.3.1 所示的一致:

 

温度补偿与温度测量原始值 Register

然后看看我关心的温度在哪里,说明书上有两个部分的地址是温度相关的,从 register 地址顺序来说,0x88 至 0x8D 是温度补偿值合共三个,和 0xFA 至 0xFC 温度测量结果,温度原始值。

首先温度补偿值,看看说明书怎么讲:

dig_T1 至 dig_T3 是温度补偿值。我一开始大意,没看出这个坑,你看出来了么?LSB 和 MSB 地址是反的,我傻了拿着 LSB 以为是 MSB 花了两个晚上反复计算和检查后,才发现自己这个错误,在这里温馨提示,要取温度补偿,记得看清楚它的 LSB 地址和 MSB 地址,希望大家不要步我后尘。

dig_P1 至 dig_P9 是气压相关的补偿值,本实验我不关心这些。

然后是温度的测量值,说明书的 4.2 Memory map 可以看到是 0xFA 至 0xFC,这次没有反了,0xFA 是 MSB:

注意,temp_xlsb (0xFC)是 <7:4>,temp_xlsb 地址的 bit0 至 bit3 值是零,永远都是零,没有用到的(说明书也写着“Both pressure and temperature values are expected to be received in 20 bit format, positive ......”)。

就是说,要取得温度原始值,需要读取 0xFA,0xFB,0xFC 三个接在一起,去掉最后四个 bit 即可。

 

运行模式

在 Reset 状态时候,MSB 值是 0x80,LSB 和 XLSB 是 0x00,从之前 dump 的结果可以验证,启动后什么都还没做的时候,是 reset state,值就是 0x80。

这是省电模式嘛。要启动它,最简单的办法就是用 i2cset 对执行模式的 register 写入值,让它测量然后放置值于 0xFA 至 0xFC 内。ctrl_meas 0xF4 就是控制相关的 register,从 4.2 Memory Map 可见,0xF4 的 8 bit 值分成了三个部分,osrs_t(3 bit)、osrs_p(3 bit) 和 mode(2 bit),分别代表温度和气压的 oversampling (精度)和 mode 模式。做个试验而已不讲究了,全部调成 1就是全开。写入 0xFF 进去 0xF4 它就工作了:

i2cset –y 1 0x76 0xF4 0xFF

然后 dump 出来看看:

这次 0xFA 到 0xFC 都有值了。

 

计算温度值

用计算器算算现在温度多少 ~ 公式如下,来自说明书的:

上图中的 BMP280_S32_t 数据类型是 32 bit signed integer。adc_T 就是 0xFA 至 0xFC 去掉 4 bit 后的值,dig_T1 至 dig_T3 补偿值中 T1 是 unsigned,T2 和 T3 是 signed,转换见下表:

Register Addresses & Values (MSB first) HEX Binary Signed ? Actual Dec Value
adc_T 0xFA = 7B, 0xFB = BB, 0xFC = 60 7BBB6(*) 01111011 10111011 0110 (*) YES 506806
dig_T1 0x89 = 6D, 0x88 = DB 6DDB 01101101 11011011 NO 28123
dig_T2 0x8B = 67, 0x8A = 39 6739 01100111 00111001 YES 26425
dig_T3 0x8D = FC, 0x8C = 18 FC18 11111100 00011000 YES -1000
var1       YES 91671.395874023438
var2       YES -188.04323882795870
T 实际温度       YES 17.867842

(*) 去掉 bit3 至 bit0

 

成功,17.86 °C,凉快。最后是官方的 C 代码温度补偿、和转换原始温度值和°C的,供参考。注:当然需要自行 define BMP280_S32_t ,类型为 32 bit Signed Integer。

// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
// t_fine carries fine temperature as global value
BMP280_S32_t t_fine;
BMP280_S32_t bmp280_compensate_T_int32(BMP280_S32_t adc_T)
{
BMP280_S32_t var1, var2, T;
var1 = ((((adc_T>>3) - ((BMP280_S32_t)dig_T1<<1))) * ((BMP280_S32_t)dig_T2)) >> 11;
var2 = (((((adc_T>>4) - ((BMP280_S32_t)dig_T1)) * ((adc_T>>4) - ((BMP280_S32_t)dig_T1))) >> 12) *
((BMP280_S32_t)dig_T3)) >> 14;
t_fine = var1 + var2;
T = (t_fine * 5 + 128) >> 8;
return T;
}

我用以上的代码算不出来,估计是跟 BBB 数据类型的大小有关,需要调整类型和/或 bit shift 大小,没研究了不管了。

C 代码(输出 256 个 register 值)

首先来个 C 代码 dump 出来:

/*
* app.c
*
* Created on: 2016-2-23
* Author: Lepton
*/
#include<stdio.h>
#include<fcntl.h>
#include<sys/ioctl.h>
#include<linux/i2c.h>
#include<linux/i2c-dev.h> #define DEVID 0xD0
#define BUFFER_SIZE 256 int main(){
int file;
if((file=open("/dev/i2c-1", O_RDWR)) < 0) {
perror("failed to open the bus\n");
return 1;
}
if(ioctl(file, I2C_SLAVE, 0x76) < 0) {
perror("Failed to connect to the sensor\n");
return 1;
}
char writeBuffer[1] = {0x00} ;
if(write(file, writeBuffer, 1)!=1) {
perror("Failed to reset the read address\n");
return 1;
}
char readBuffer[BUFFER_SIZE];
if(read(file, readBuffer, BUFFER_SIZE)!=BUFFER_SIZE){
perror("Failed to read in the buffer\n");
return 1;
}
int i;
printf("Device ID: 0x%02x\n", readBuffer[DEVID]);
for (i = 0; i<BUFFER_SIZE; i++){
if(i!=0 && i%16==0) printf("\n");
printf("%02x ",readBuffer[i]);
}
printf("\n");
close(file);
return 0;
}

256 bytes 一口气读出来,耗内存狂是我。效果:

以上代码改自于 Exploring Beaglebone 一书上的示范。

 

C 代码 (获取温度值)

再来,正式获取温度了,这次要省省内存,分开了两个独立方法做写入 register 与读取,读取时,只读仅需要的 byte。这次只打开温度计,不管气压不测量。另外,温度补偿部分的数据其实基本不变的,所以,我分开了两次读取,相隔几毫秒没有太大关系:

/*
* app.c
*
* Created on: 2016-2-23
* Author: Lepton
*/
#include<stdio.h>
#include<stdint.h>
#include<fcntl.h>
#include<sys/ioctl.h>
#include<linux/i2c.h>
#include<linux/i2c-dev.h> #define BMP280_ADD 0x76
#define DIG_START 0x88
#define TEMP_START 0xFA
#define CTRL_MEAS 0xF4
#define TEMP_ONLY_NORMAL_MODE 0xE3 // 111 000 11 int writeReg(int pt, unsigned char add, char value){
unsigned char w_buff[2];
w_buff[0] = add;
w_buff[1] = value;
if (write(pt, w_buff, 2) != 2){
perror("Failed write to device");
return 1;
}
return 0;
}
int readReg(int pt, unsigned char s_add, char buf[], int size){
char writeBuff[1] = {s_add};
if(write(pt, writeBuff, 1)!=1) {
perror("Failed to reset the read address\n");
return 1;
}
if(read(pt, buf, size)!=size){
perror("Failed to read in the buffer\n");
return 1;
}
return 0;
} float myFunc(uint32_t adc_T, unsigned short dig_T1, short dig_T2, short dig_T3){
uint32_t var1, var2;
float T;
var1 = (((double)adc_T)/16384.0-((double)dig_T1)/1024.0)*((double)dig_T2);
var2 = ((((double)adc_T)/131072.0-((double)dig_T1)/8192.0)*(((double)adc_T)/131072.0-((double)dig_T1)/8192.0))*((double)dig_T2);
T = (var1+var2)/5120.0;
return T;
} int main(){
int file;
if((file=open("/dev/i2c-1", O_RDWR)) < 0) {
perror("failed to open the bus\n");
return 1;
}
if(ioctl(file, I2C_SLAVE, BMP280_ADD) < 0) {
perror("Failed to connect to the sensor\n");
return 1;
} char dig_buff[6];
char tmp_buff[3]; if(writeReg(file, CTRL_MEAS, TEMP_ONLY_NORMAL_MODE)==1){
return 1;
}
if(readReg(file, DIG_START, dig_buff, 6)==1){
return 1;
}
if(readReg(file, TEMP_START, tmp_buff, 3)==1){
return 1;
}
close(file); printf("Dump :\n");
int i;
for (i=0; i<6; i++){
printf("%02x ",dig_buff[i]);
}
printf("\n");
for (i=0; i<3; i++){
printf("%02x ",tmp_buff[i]);
}
printf("\n");
int adc_T = ((tmp_buff[0]<<16)|(tmp_buff[1]<<8)|(tmp_buff[2]))>>4;
unsigned short dig_T1 = (dig_buff[1]<<8)|(dig_buff[0]);
short dig_T2 = (dig_buff[3]<<8)|(dig_buff[2]);
short dig_T3 = (dig_buff[5]<<8)|(dig_buff[4]);
printf("adc_T is : %d \n", adc_T);
printf("dig_T1 is : %d \n", dig_T1);
printf("dig_T2 is : %d \n", dig_T2);
printf("dig_T3 is : %d \n", dig_T3);
printf("Temperature is : %f \n", myFunc(adc_T, dig_T1, dig_T2, dig_T3));
return 0;
}

 

效果:

还有那些看到就烦的拼音档案名呢 ~ 现在几度了~ 17.02°C ~

今天到此为止。

我在这群里,欢迎加入交流:
开发板玩家群 578649319
硬件创客 (10105555)

Beaglebone Black–I2C 接 BMP280 获取当前温度的更多相关文章

  1. Linux系统获取CPU温度

    Linux系统获取CPU温度 摘自:https://jingyan.baidu.com/article/cbf0e500407d072eab289343.html 各位好,本篇将简单介绍如何在不同系列 ...

  2. 转载:c# 获取CPU温度(非WMI,直接读取硬件)

    c#获取cpu温度 很早一个项目做远控,所以需要用到获取cpu温度,但是就是不知从何下手,无意中发现了Open Hardware Monitor,令我的项目成功完成 亲测20台清装xp sp2的机器, ...

  3. linux获取CPU温度

    Centos系列 1 yum install lm_sensors 2 sensors-detect 3 sensors Ubuntu系列(多了service module-init-tools st ...

  4. 2022年写的香橙派 OrangePi Zero 用python获取dht11温度和湿度

    感谢网上资料和个人的不放弃,终于方便的解决了香橙派 OrangePi Zero用python获取dht11温湿度的问题. 网上关于香橙派的资料比起树莓派真是少之又少,现在香橙派zero能干的活暂时也只 ...

  5. 使用树莓派3获取CPU温度

    一.命令: cat /sys/class/thermal/thermal_zone0/temp 二.上图:

  6. orangepi获取cpu温度

    cat /sys/devices/virtual/hwmon/hwmon1/temp1_input

  7. 用python+selenium从百度获取本地明日的天气信息并根据温度情况设置提醒

    从百度天气获取当地明天的天气情况,如果明天下雨,请发送邮件通知全体同事带伞, 如果明天气温低于10度,请邮件提醒同事注意保暖,如果气温高于30度则提醒同事注意高温. 假设存在发送邮件的方法self.s ...

  8. 【转】使用Beaglebone Black的I2C (二)——使用C语言和i2c-dev驱动

    在本博客的<使用Beaglebone Black的I2C(一)>中,介绍了BBB上无需编程对i2c总线进行读写操作的方法,本文将介绍如何在c语言程序中使用i2c-dev驱动来操作i2c设备 ...

  9. 玩转X-CTR100 l STM32F4 l BMP280气压计传感器

    我造轮子,你造车,创客一起造起来!塔克创新资讯[塔克社区 www.xtark.cn ][塔克博客 www.cnblogs.com/xtark/ ]      本文介绍X-CTR100控制器 扩展BMP ...

随机推荐

  1. node js 模块分类

    核心模块 require('fs'); 核心模块是被编译成二进制代码 文件模块 require('../fs.js'); 对于加载模块时既没指出./ ../ /.../时,加载模块的搜索路径.如果'/ ...

  2. Android 更新sdk

    http://blog.csdn.net/xiao_ping_ping/article/details/45621663 不FQ意味着不能直接到android官网下载android SDK,但是国内有 ...

  3. [转]Haroopad Markdown 编辑器代码语法高亮支持

    代码语法高亮 书写格式为: ` ` ` language_key if (condition){ return true } ` ` ` 在 ` ` ` (三个反引号)之间的是代码,其中languag ...

  4. Codevs 1021 玛丽卡

    Codevs 1021 玛丽卡 题目地址:http://codevs.cn/problem/1021/ 题目描述 Description 麦克找了个新女朋友,玛丽卡对他非常恼火并伺机报复. 因为她和他 ...

  5. 在Win7系统中搭建Web服务器

    局 域网Web服务器的主要功能是实现资源共享,同时借助于局域网服务器访问页面可有效的实现信息的同步.利用Web服务器,我们随时随地都可以将自己的信息 上传到服务器端,让其它关注你的用户能在第一时间内了 ...

  6. __weak

    需要使用弱引用的 三种情况: 1. 如果这个block不被持有,那么你完全没有必要使用__weak 2. 如果被持有了,那么__weak是必然的 3. 如果在多线程并发的情况下,不仅要使用__weak ...

  7. Windwos服务器远程桌面不能复制粘贴的解决方法

    今天使用远程桌面连接登陆服务器,发现不能在本地电脑和远程服务器之间复制粘贴文件了,复制粘贴文本也不行. 网上搜了一下,主要有两种情况: 1.复制粘贴功能原本可以用,突然失灵了2.从头到尾都无法使用这个 ...

  8. 去除行内(inline/inline-block)元素之间的间距

    先展示一下,行内元素之间存在间距,实例代码如下: <style> div { color: #fff; padding: 25px 50px; } .inline-f00 { displa ...

  9. spring设置webAppRootKey

    今天一个同事来问webAppRootKey 在哪设置的 <context-param> <param-name>webAppRootKey</param-name> ...

  10. Android中悬浮窗口

    调用WindowManager,并设置WindowManager.LayoutParams的相关属性,通过WindowManager的addView方法创建View,这样产生出来的View根据Wind ...