【问题标题】:Difference I2C Sensor Reading Raspberry Pi and ArduinoI2C 传感器读取树莓派和 Arduino 的区别
【发布时间】:2020-06-29 15:28:27
【问题描述】:

我正在使用 Sensirion SFM3300 流量传感器,并且可以通过 Arduino 使用以下代码 (I2C) 读取正确的值:

#include <Wire.h>

void setup() {
  // put your setup code here, to run once:
  Wire.begin();
  Serial.begin(115200);
  Wire.beginTransmission(byte(0x40));
  Wire.write(byte(0x10));
  Wire.write(byte(0x00));
  Wire.endTransmission();
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(100);
  Wire.requestFrom(0x40,2);
  uint16_t a = Wire.read();
  uint8_t  b = Wire.read();
  a = (a<<8) | b;
  float flow = ((float)a - 32768) / 120;
  Serial.println(flow);
}

但是使用 Raspberry Pi,我编写了几乎相同的代码,希望它也能正常工作。 这是代码:

from smbus2 import SMBus
import time
import numpy as np

address=0x40
bus = SMBus(1)

def write(value):
    bus.write_byte(address,value)

write(0x10)
write(0x00)

while True:
    time.sleep(0.1)
    a = np.uint16(bus.read_byte(0x40))
    b = np.uint8(bus.read_byte(0x40))
    a = (a<<8) | b
    flow = (float(a)-32768)/120
    print(flow)

代码看起来确实一样,但我只得到 -273,06666666666 作为返回值。有人知道 Raspberry Pi 和 Arduino I2C 之间的区别在哪里,可以帮助我在 Pi 上获得正确的值吗?

【问题讨论】:

  • 预期的正确值是多少?我建议使用 struct 单位而不是 np 需要丑陋的转变和或方法。这可能是字节顺序问题。
  • 预期值应为 0(无任何流量),传感器应对流量作出反应。但是在 Raspbery 上,我只得到 -273,0666666 有和没有任何浮动。在这种情况下如何使用 struct ?你能帮帮我吗?
  • 先打印字节值,在计算前查看结果。真的是printf() 调试类型的101 :) 啊,是的,答案中的好建议。顺便说一句,你读过数据表吗?

标签: python arduino raspberry-pi i2c smbus


【解决方案1】:

我认为您在 python 中的读取过程不正确。从端口 40 读取两次与从端口 40 读取两个字节是不同的。

我建议使用 read_byte_data(0x40, 0, 2) 并使用 struct.unpack("&gt;H") 处理它。

【讨论】:

    【解决方案2】:

    您可以使用read_i2c_block_data(addr, offset, numOfBytes) 方法从i2c 获取超过1 字节的数据。返回数据是一个字节列表。所以很容易转换成整数。

    根据数据表和 Arduino 草图编辑

    下面是 Python 的完整代码,应该与 Arduino 示例相匹配:

    from SMBus2 import SMBus
    import time
    
    offset = 32768
    scale = 120
    
    addr = 0x40
    cmd = [0x10, 0x00]
    
    with SMBus(1) as bus:
        bus.write_i2c_block_data(addr, 0, cmd)
        time.sleep(0.1)
        block = bus.read_i2c_block_data(addr, 0, 3)
    reading = block[0] * 256 + block[1]
    crc = block[2]    # should do some crc check for error
    flow = (reading - offset)/scale
    print(flow)
    

    【讨论】:

    • 感谢您的推荐。您的代码对我有帮助,但我认为问题出在 I2C 总线上。使用您的代码,我还得到一个恒定的负值 -273,.....使用 i2cset -y 1 0x40 0x10 和 i2cset -y 1 0x40 0x00 我尝试开始测量 SFM3300 传感器。使用 i2cget -y 1 0x40 我想读出一个字节,但我只得到 0x00。
    • 我认为你的公式是错误的。你得到的是流量值,它不需要减去32768然后除以120。你得到-273的原因是因为value = 0,所以(0-32768)/120 = -273。我已经编辑了我的代码。
    • 根据数据表,您说 Arduino 代码有效,我编辑我的答案以包含完整的 python 代码。同样,我无法测试它,因为我没有传感器和设置,您必须运行和调试它。
    • 我不知道为什么它在 Raspberry Pi 上不起作用。流量仍为 -271,45(有流量和无流量)。 block[0] 的 print 总是 0,block[1] 194 和 block[2] 37。
    【解决方案3】:

    我找到了一个可行的解决方案。如果 I2C 专家能告诉我为什么下面的代码可以工作而不是上面的 python 代码,那就太好了。

    from fcntl import ioctl
    from struct import unpack
    from smbus import SMBus
    
    address = 0x40
    
    SMBus(1).write_byte_data(address,16,0)
    i2c = open("/dev/i2c-1", "rb", buffering=0)
    ioctl(i2c,0x0703,address)
    i2c.read(3)
    
    d0,d1,c = unpack('BBB', i2c.read(3))
    d = d0 << 8 | d1
    a = (float(d)-32768.)/120
    print(a)
    

    【讨论】:

    • 啊,Linux 上的i2c-dev.c i2cdev-read 函数有一个explanation 说明为什么需要它。 ioctl() 所做的是让 SDA 浮动,如果存在从站,它将拉下 SDA。这与运行 linux 命令i2cdetect -y 1 来检测 i2c 地址具有相同的效果。事实上,一旦您在较新的 Raspbian 上运行一次 i2cdetect -y 1(与早期版本相比),您将不再需要 ioctl() 代码,并且我提供或您编写的普通 SMBus 代码可能会工作。
    猜你喜欢
    • 1970-01-01
    • 2021-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-14
    • 2018-04-16
    • 1970-01-01
    相关资源
    最近更新 更多