i2c 传感器 ROHM传感器套件测评(二):五个I2C接口传感器
ROHM传感器套件测评(二):五个I2C接口传感器
这个套件的8个传感器中有5个是I2C接口的数字传感器:
在 SensorShield 底板上,有5个位置可以插I2C传感器小板。不过因为工作电压范围不一致,不能全插上去一起用。这5个传感器都是自带AD转换的,所以直接输出数字量了,加上可配置能力,适应性更强。比如,在不需要采集数据的时候可以关闭内部功能以节省电力。
加速度传感器 KX022-1020
这大概是这套传感器中配置最为复杂的一个。它是一个三轴加速度传感器,也就是能感知三维空间空间中的加速度方向。关于这个测量结果的物理意义我需要说明一下,因为两天前我自己也没想清楚:为什么加速度计放在地上不动,也测出来有加速度(大小是一个g,重力加速度的值),而不是加速度为0 ? 它明明就没有运动嘛。倘若把加速度计连同附属电路装置抛向空中,必然读出加速度是0,尽管这个时候它是向地面加速的。和人们的直观感受完全相反啊!实际是这样:运动是相对的,参照系不同,速度就不同(中学物理讲的是以静止或者匀速运动物体为参照,但是怎么定义静止呢?);加速度也会随参照系而变,因为是速度的微分(例子:伽利略实验,两个铁球同时着地)。当以地球表面自由落体的物体为参照时,这个加速度计的测量值就可以被解释了:放在桌上不动,它就有一个竖直向上的加速度。
对于加速度计,引力是不能让它产生测量值的;人也一样,人其实不能直接感受到引力的存在。或许看我这段话的朋友要反驳了:没有引力存在我不就一蹦飞上天了么?注意,人蹦起来离开地面时候是失重的,比如蹦极的时候闭上眼睛其实并不知道引力在什么方向。反而是我们站着、坐着或者躺着的时候,受到地面、椅子或者床的向上的拖着的力——身体切实感受到了,却习以为常而被忽略了。所以结论是:人站在地球上,和坐在原离太阳系正以9.8m/s/s加速的宇宙飞船里,感受到的“重力”是一样的。
回到说这个传感器,其内部在AD转换之后的数字部分还有DSP和FIFO buffer,才把数据送到接口电路,这是比直接读取AD转换值更高级的功能。在手册中对DSP的功能有描述,我没有工夫来详细研究了。KX022-1020 具有16-bit数字分辨率,最大每秒1600次转换输出的能力,最大正负8g的量程,对消费类一般运动检测(不是碰撞冲击)的话应该够用了。我没有惯性传感器的实际应用经验,就不作多的评价。作为类似手机重力方向感应的倾角检测应用的话,这个加速度计是足够。接口它是支持SPI和I2C的,SPI支持的速率更高,但是套件中只提供了I2C的连接方式。
KX022 有57个8位的寄存器,好在简单的测试只配CNTL1, CNTL2之后就可以读数了。
[C] 纯文本查看 复制代码
?
01020304050607080910111213141516171819202122232425262728293031323334353637383940414243void test_KX022(char slave_addr){ uint8_t buf[256]; i2c_w1rn(slave_addr,0x0f,buf,1); // check WHOAMI if(buf[0]==0x14) uart_wstr("\r\nKX022 detected."); else { uart_wstr("\r\nNo response."); return; } buf[0]=0x18; // CNTL1,2 buf[1]=0x60; // set RES, DRDYE buf[2]=0x00; i2c_write_n(slave_addr, buf, 3); _delay_ms(100); uart_wstr("\r\nConfigured mode"); buf[0]=0x18; // CNTL1 buf[1]=0xE0; // operating mode i2c_write_n(slave_addr, buf, 2); uart_wstr("\r\nACC operating"); _delay_ms(100); for(;;) { _delay_ms(10); i2c_w1rn(slave_addr,0x13,buf,1); // read INS2 if(buf[0] & 0x10) { int16_t acc[3]; i2c_w1rn(slave_addr,0x06,(uint8_t *)acc,6); // read data uart_wstr("\r\nXYZ: "); print_i16(acc[0]); uart_wstr(","); print_i16(acc[1]); uart_wstr(","); print_i16(acc[2]); } }}在小板上丝印标注了加速度的XYZ敏感方向。若SensorShield水平放置,Z轴是向上的。
本测试是正负2g的量程范围,每秒50次转换输出。板子平放时候串口输出:
大约是z方向正向(向上)的一个g加速度,符合前面的推断。因为没有严格水平,加速度方向没有严格在z方向上。倒过来朝下扣着,则Z读数应该变成负的:
侧着竖起来放看一下结果:
还是蛮好玩的。至于自由落体,现在这样拖着USB线就没法测试了(需要做个电池供电,把数据存到SD卡或者无线发送出来......)
RGB颜色传感器 BH1745
这是一个接收可见光的传感器,所以封装是透明的。
我先谈下我对“颜色”的理解。眼睛只所以能感受颜色,是因为有光进入了眼睛,刺激到了视网膜上的感光细胞。我们说一个不发光的物体,比如一块布料是什么颜色的,是指它的反射光。但是反射光必然又和入射光有关系,比如,在红光灯照射下,绿色的物体也只能反射红光。所以日常生活中谈论物体的颜色还有一个假设前提,就是在自然光下。但是,这还是定性讨论,自然光还没有定义呢。太阳光是具有连续光谱的,在人眼可见部分光谱的能量分布也和自然环境相关。一般笼统地把太阳光称为七种颜色的光组成,事实上并非七种单一波长的光组成。人眼能识别颜色,是因为视网膜上有3种视锥细胞,对不同波长有不同的敏感度:
这好比人眼睛有三种色彩传感器。于是,有同色异谱现象(不同的光谱分布可能产生同样的颜色感知),有了现在的RGB三基色系统来重现(模拟)自然界的色彩。
ROHM这个 BH1745NUC 传感器的颜色敏感曲线和人眼是有所不同的
除了RGB输出,还有一个"Clear color"通道。寄存器表比前面的加速度计要简单了
测试程序:
[C] 纯文本查看 复制代码
?
010203040506070809101112131415161718192021222324252627282930313233343536373839void test_BH1745NUC(char slave_addr){ uint8_t buf[256]; i2c_w1rn(slave_addr,0x92,buf,1); // check ID if(buf[0]==0xE0) uart_wstr("\r\nBH1745NUC detected."); else { uart_wstr("\r\nNo response."); return; } buf[0]=0x41; buf[1]=0x00; // default, 160ms buf[2]=0x10; // RGBC EN buf[3]=0x02; i2c_write_n(slave_addr, buf, 4); uart_wstr("\r\nWrite CONTROL1,2,3"); for(;;) { i2c_w1rn(slave_addr,0x42,buf,1); // MODE_CONTROL2 if(buf[0] & 0x80) // VALID { uint16_t *d=(uint16_t *)buf; i2c_w1rn(slave_addr,0x50,buf,8); // read RGBC uart_wstr("\r\nRGBC: "); uart_wdec(d[0]); uart_wstr(", "); uart_wdec(d[1]); uart_wstr(", "); uart_wdec(d[2]); uart_wstr(", "); uart_wdec(d[3]); uart_wstr(" "); } }}我用一块PCB板将传感器盖住,在室内环境下可以实现读数全为0. 在我的工作桌上(灯头是七年半前制作的,四只Cree XR-E Q3 5C串联),输出结果
而放到被太阳光照射的地方(隔着窗玻璃,空气还有雾霾),可以发现光更强
调整一下板子的角度,让太阳光接近垂直照射到传感器上,数值应该是更大的:
在太阳光下阅读,对人眼的刺激就太大了。光过强也不好。我的另一盏LED台灯(前年制作的,用了较新的,且色温更低的LED)下:
这个LED的红色成分明显加强了,是更接近白炽灯的。但家中已没有白炽灯了,无法测试对比数据。
另外一个用AA电池的LED小灯下,LED距离传感器比较近测量:
这个的蓝光成分就高了,作为照明用眼睛也不舒适。应急用不要紧。
把传感器靠在我现在的显示器屏幕上,显示白色区域:
最后玩一个,3mm的高亮度红色LED,距离传感器十几mm照射:
作为能定量分析光照颜色的传感器,BH1745NUC 在环境监测、智能家居方面可以发挥优势。直接作为视力保护的照明条件评估,我认为也是个理想的应用。
接近和环境光传感器 RPR-0521RS
这是一个复合传感器,把光传感器功能和红外LED结合在一起,做成了反射式的接近检测传感器加环境光(可见光和红外)传感器。用手册中的这个图可以说明它的原理和用途:
检测需要的灵敏度和阈值是需要设置后才能用的(比如需要产生中断输出). 寄存器表如下,和前一个颜色传感器的风格一致。
我的测试程序:
[C] 纯文本查看 复制代码
?
010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748void test_RPR0521(char slave_addr){ uint8_t buf[256]; i2c_w1rn(slave_addr,0x92,buf,1); // check ID if(buf[0]==0xE0) uart_wstr("\r\nRPR0521 detected."); else { uart_wstr("\r\nNo response."); return; } buf[0]=0x41; buf[1]=0xC7; // enable ALS, PS, 100ms/400ms buf[2]=0x00; // ALS Gain x1 buf[3]=0x20; // PS Gain x4 i2c_write_n(slave_addr, buf, 4); uart_wstr("\r\nWrite CONTROL regs"); buf[0]=0x4A; buf[1]=0x06; // unlatched i2c_write_n(slave_addr, buf, 2); uart_wstr("\r\nWrite INTERRUPT reg"); buf[0]=0x51; buf[1]=0xfe; buf[2]=0xff; i2c_write_n(slave_addr, buf, 3); uart_wstr("\r\nWrite ALS TL"); for(;;) { i2c_w1rn(slave_addr,0x4a,buf,1); // INTERRUPT if(buf[0] & 0x40) // ALS status { uint16_t *d=(uint16_t *)buf; i2c_w1rn(slave_addr,0x44,buf,6); // read PS,ALS uart_wstr("\r\nPS: "); uart_wdec(d[0]); uart_wstr(", ALS: "); uart_wdec(d[1]); uart_wstr(", "); uart_wdec(d[2]); uart_wstr(" "); } }}在我的工作桌上,没有遮挡的情况下输出结果:
似乎这个环境光检测的“红外”通道对可见光也是有感应的。
拿一张纸片,平放在传感器上放10cm处,挡住灯光照射
接近检测稍有所响应(GAIN=4x),环境光检测可以看到明显变化了。距离再减到5cm
这个距离下接近检测是可靠的了。这也是手册中的测量条件。 环境光检测数进一步缩小。若距离更近,接近检测的数值会迅速增大。
我设想RPR-0521RS作为接近传感的用途是近距离的手势控制,或者机械装置里的近距离位置反馈。作为人体接近危险区域的报警用途之类则不合适,距离太短了。我上面的测试中接近传感的测量间隔是400ms,检测手势的速度不够。不过它是可以配置为最短10ms检测一次的,应该可以用。
气压传感器 BM1383GLV
气压传感器的意义很直观,它封装里面的传感器和外面空气是相通的(壳上有个小孔),测量空气压强。在平常接触到的电子设备中,大概是血压计用得最多,此外它也被用来间接测量海拔高度。
BM1383GLV这个型号的手册我下载不到,ROHM网站的链接一直是Access denied错误。我只能找到 BM1383AGLV 型号的手册,可能是改进型,但是区别有多少不得而知。根据描述,这俩都是内建温度补偿的气压传感器。我只好以 BM1383AGLV 为参照,写程序的时候发现寄存器操作有误再把Rohm网站上能下载的Arduino程序找来参考。
测试程序(因为没有把DRDY检测弄成功,就改为延时100ms后读取一次结果了):
[C] 纯文本查看 复制代码
?
01020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546void test_BM1383GLV(char slave_addr){ uint8_t buf[256]; i2c_w1rn(slave_addr,0x0f,buf,2); // check ID if(buf[1]==0x31) uart_wstr("\r\nBM1383GLV detected."); else { uart_wstr("\r\nNo response."); return; } buf[0]=0x12; buf[1]=0x01; // POWER DOWN REG (active) i2c_write_n(slave_addr, buf, 2); _delay_ms(10); uart_wstr("\r\nLeave power down"); buf[0]=0x13; buf[1]=0x01; // RESET REG (active) i2c_write_n(slave_addr, buf, 2); _delay_ms(10); uart_wstr("\r\nLeave reset"); buf[0]=0x14; buf[1]=0xda; // Average 64, DRDY enable, continuous ?? not sure i2c_write_n(slave_addr, buf, 2); _delay_ms(10); uart_wstr("\r\nConfigured mode"); for(;;) { uint32_t p=0; _delay_ms(100); uart_wstr("\r\n"); i2c_w1rn(slave_addr,0x1c,buf,3); // read data *(uint8_t *)(&p)=buf[2]; *((uint8_t *)(&p)+1)=buf[1]; *((uint8_t *)(&p)+2)=buf[0]; p>>=2; uart_wstr("Pressure counts: "); uart_wdec(p); }}测量结果,这个数值除以2048就是百帕(hPa)单位的值。
估测数据的波动在0.1个百帕量级。那么对气压敏感程度如何?我拿出我的笔记本电脑来测试了,带着Nucleo, SensorShield边测边走。我住在4楼,走到楼下看测量数据已经有明显的变化了:
外面气温低,可能温度也影响了结果,也可能气压本来就跟温度有关。再上楼梯到4楼,在门外看看:
排除温度的变化,气压测量结果的确是有高度反映的。回到屋内稍暖和了一下,再看读数:
稍微有点变化。用气压测海拔本来就有精度范围的吧。不知道用这个传感器换算高度结果靠谱不。
三轴磁场传感器 BM1422GMV
这又是一个三轴传感器,测量磁场的方向和强度。主要的应用是电子罗盘,也就是测量地磁场来实现方向导航。在 BM1422GMV 内部集成了3个磁阻元件,经过AD转换输出12或14位的数据。
寄存器表:
工作流程图:
按照流程图写的测试程序:
[C] 纯文本查看 复制代码
?
010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051void test_BM1422GMV(char slave_addr){ uint8_t buf[256]; i2c_w1rn(slave_addr,0x0d,buf,3); // check ID if(buf[0]==0x01 &&buf[1]==0x01 &&buf[2]==0x41) uart_wstr("\r\nBM1422GMV detected."); else { uart_wstr("\r\nNo response."); return; } buf[0]=CNTL1; buf[1]=0xC0; // Power enable, 14-bit, continuous mode i2c_write_n(slave_addr, buf, 2); uart_wstr("\r\nSet CNTL1"); buf[0]=CNTL4; buf[1]=0x0; buf[2]=0x0; i2c_write_n(slave_addr, buf, 3); uart_wstr("\r\nSet CNTL4"); buf[0]=CNTL2; buf[1]=0x08; // DREN i2c_write_n(slave_addr, buf, 2); uart_wstr("\r\nSet CNTL2"); buf[0]=CNTL3; buf[1]=0x40; // FORCE i2c_write_n(slave_addr, buf, 2); uart_wstr("\r\nSet CNTL2"); for(;;) { i2c_w1rn(slave_addr,STA1,buf,1); if(buf[0] & 0x40) { int16_t *d=(int16_t *)buf; i2c_w1rn(slave_addr,DATAX,buf,6); // read DATAX,Y,Z uart_wstr("\r\nDATA: "); print_i14(d[0]); uart_wstr(", "); print_i14(d[1]); uart_wstr(", "); print_i14(d[2]); uart_wstr(" "); } }}然而,测量结果很奇怪。首先是沿敏感轴改变方向后数值并不是变成反号,其次读数不是很稳定,还有换用不同的插座位置时读数也有差别。第一个问题我试图用设定Offset来校正,失败,未得到合适的结果。在默认的参数下,某个摆放朝向是这样的测量结果:
X轴方向不变,Y, Z轴都变成反方向再测:
看起来好象有个负的offset在里面,但这个误差也太大了。我怀疑是否是板子上的电流产生的磁场造成的干扰,但是现在没有办法去检验。最后一图上X轴数据的突然跳变也无法解释……
遗憾,作为地磁测量,我还没法把BM1422用起来。
了解I2C总线工作原理、优缺点和应用,看这一篇就够了
如果您正在使用OLED显示器,气压传感器或陀螺仪/加速度计模块的项目,您可能会发现正在使用I2C总线。
I2C总线简介
I2C结合了SPI和UART的优点。使用I2C,您可以将多个从设备连接到单个主设备上(如SPI),并且可以让多个主器件控制单个或多个从器件。当您希望有多个微控制器记录数据到单个存储卡或将文本显示到单个LCD时,这非常有用。
与UART通信一样,I2C仅使用两条线在设备之间传输数据:
SDA(串行数据) - 主站和从站发送和接收数据的线路。
SCL(串行时钟) - 承载时钟信号的线路。
I2C是一种串行通信协议,因此数据沿着单线(SDA线)逐位传输。
与SPI一样,I2C是同步的,因此位输出通过主机和从机之间共享的时钟信号与位采样同步。时钟信号始终由主机控制。
I2C如何工作
使用I2C时,数据被转换成messages,messages则被分解为数据帧。每条messages都有一个地址帧,其中包含从站的二进制地址,以及一个或多个包含正在传输的数据的数据帧。该消息还包括每个数据帧之间的启动和停止条件,读/写位和ACK / NACK位:
启动条件:在SCL线路从高电平切换到低电平之前,SDA线路从高电平切换到低电平。
停止条件:SCL线路从低电平切换到高电平后,SDA线路从低电平切换到高电平。
地址帧:每个从站唯一的7或10位序列,用于在主站想与之通信时识别从站。
读/写位:单个位,指定主器件是向从器件发送数据(低电压电平)还是从器件请求数据(高电压电平)。
ACK / NACK位:消息中的每个帧后跟一个应答/不应答位。如果成功接收到地址帧或数据帧,则从接收设备向发送方返回ACK位。
地址
I2C没有像SPI这样的从选择线,因此它需要另一种方法让从器件知道数据正在发送给它,而不是另一个从器件。它通过地址来做到这一点。地址帧始终是新消息中起始位之后的第一帧。
主设备将与其通信的从设备的地址发送给与其连接的每个从设备。然后,每个从设备将从主设备发送的地址与其自己的地址进行比较。如果地址匹配,则将低电压ACK位发送回主机。如果地址不匹配,则从器件不执行任何操作,SDA线保持高电平。
读/写位
地址帧在末尾包括一个位,用于通知从设备,主设备是想要向其写入数据还是从主设备接收数据。如果主设备想要向从设备发送数据,则读/写位是低电平。如果主设备向从设备请求数据,则该位是高电平。
数据框架
在主设备检测到来自从设备的ACK位之后,准备好发送第一个数据帧。
数据帧始终为8位长,并以最高有效位先发送。紧接着每个数据帧的ACK / NACK位以验证帧已被成功接收。在发送下一个数据帧之前,主机或从机必须接收ACK位(取决于发送数据的人)。
在发送了所有数据帧之后,主设备可以向从设备发送停止条件以停止传输。停止条件是SCL线上从低电平到高电平转换后,SDA线上从低电平变为高电平,SCL线保持高电平。
I2C数据传输步骤
1.主机向每一个连接的从设备发送数据,然后将SDA信号从高切换到低,之后在将SCL从高切换到低电平。
2.主设备向每个从设备发送它想要与之通信的从设备的7或10位地址,以及读/写位:
3.每个从设备将主设备发送的地址与其自己的地址进行比较。如果地址匹配,则从器件通过将SDA线拉低一位来返回ACK位。如果主设备的地址与从设备的地址不匹配,则从设备将SDA线保持为高电平。
4.主设备发送或接收数据帧:
5.在传输了每个数据帧之后,接收设备将另一个ACK位返回给发送方以确认成功接收到该帧:
6.要停止数据传输,主机通过在将SDA切换为高电平之前切换SCL为高电平来向从机发送停止条件:
有多个从设备的单一主设备
由于I2C使用寻址,因此可以从单个主设备控制多个从设备。使用7位地址,可以使用128(27)个唯一地址。使用10位地址并不常见,但提供1,024(210)个唯一地址。要将多个从器件连接到单个主器件,请像这样连接它们,使用4.7K欧姆上拉电阻将SDA和SCL线连接到Vcc:
有多个从设备及多个主设备
多个主设备可以连接到单个从设备或多个从设备。当两个主设备尝试通过SDA线同时发送或接收数据时,同一系统中出现多个主设备的问题。为了解决这个问题,每个主设备需要在发送消息之前检测SDA线路是低还是高。如果SDA线为低电平,则表示另一个主控制器已控制总线,主控制器应等待发送消息。如果SDA线路很高,那么传输信息是安全的。要将多个主设备连接到多个从设备,请使用下图,使用4.7K欧姆上拉电阻将SDA和SCL线路连接到Vcc:
I2C的优点和缺点
与其他协议相比,I2C听起来很复杂,不容易在程序中实现而导致数据丢失、无应答、“死等”等问题。但却有很多优点:
优点
只使用两根电线
支持多个主服务器和多个从服务器
ACK / NACK位确认每个帧都已成功传输
硬件没有UART那么复杂
众所周知且广泛使用的协议
缺点
数据传输速率比SPI慢
数据帧的大小限制为8位
实现比SPI更复杂的硬件
相关问答
简单剖析几个常见的温度 传感器 型号[最佳回答]接触式温度传感器的型号有:双金属温度计、玻璃液体温度计、压力式温度计、电阻温度计、热敏电阻和温差电偶等。温度传感器采取PT100铂热电阻当感温...
一根 I2C 总线怎么同时连接2个温度 传感器 ?温度传感器一般输出是模拟信号,要输入到单片机就需要经过模数(ad)转换,可以考虑加ad转换芯片,当然有些单片机本身是有模拟输入口的,由内部进行ad转换。也有...
如何将温度 传感器 的数据上传到电脑?首先需要一支Pt100的温度传感器,然后接入一块数显变送仪表,通过通讯485通讯(通讯协议一半可选MODBUS-RTU)将信号在组态好的软件里显示。当然数显仪表也可以换...
传感器 的接线类型和方式有哪些?传感器的接线类型和方式因传感器类型和应用不同而有所不同。下面列出一些常见的传感器接线类型和方式:1.模拟信号输出型传感器:这种传感器的电路输出的是模...
角度 传感器 MMA7455模块怎么连接电路,与单片机 I2C 如何使用。...[最佳回答]INT1引脚一般作为数据准备好中断DRDY,用于提示测量数据已经准备好,同时在状态寄存器(STATUS地址0X09)中的DRDY位也会置位,中断时输出高电平,并一直...
角度 传感器 MMA7455模块怎么连接电路,与单片机 I2C 如何使用。...[最佳回答]INT1引脚一般作为数据准备好中断DRDY,用于提示测量数据已经准备好,同时在状态寄存器(STATUS地址0X09)中的DRDY位也会置位,中断时输出高电平,并一直...
SPI、 I2C 、USART等协议有什么共同点和区别?这个,还是先说说三种协议的特点吧。1、SPI。串行通信协议,全双工的同步通信总线协议,要分主从,要占四根线(四个管脚),算是速度比较高的一种了(这三种...它通讯...
光强 传感器 tsl2561怎样把光照强度转换成数字信号?原理应该是是把电阻(也可以是别的传函材料)的变化转换成电压信号,然后接ad进单片机进行采集,标定,温度补偿,线性化,然后指定数字输出协议给用户,一般是I2...
单片机、光耦1系统总体结构原理粮食在储藏期间,由于受环境、气候和通风条件等因素的变化,粮仓内温度或湿度会发生异常,这极易造成粮食的霉烂、或发生虫害。那么...
角度感应器如何连接舵机?1.将角度感应器与微控制器连接:根据角度感应器的类型,通过相应的接口将其连接到微控制器或控制模块上。例如,对于数字式传感器,可以通过串行通信(如I2C或SP...